Java: открытый ключ отличается после отправки через сокет - PullRequest
2 голосов
/ 12 октября 2011

Я пытаюсь отправить открытый ключ через сокетное соединение в Java.Хотя я очень хорошо понимаю, что Java предоставляет функции SSL для этого вида деятельности, это уникальное задание;Я не могу использовать реализацию Java.

Сервер кодирует свой открытый ключ и передает его клиенту через сокетное соединение.Когда клиент получает ключ и декодирует его, он выглядит иначе.Кроме того, данные, полученные клиентом, отличаются от данных, передаваемых сервером.Я считаю, что это вызывает у меня проблемы при попытке зашифровать имя пользователя и пароль с помощью этого ключа.

Проблема может быть воспроизведена с помощью следующего кода:

Клиент:

public class TestClient {

    /**
     * @param args
     */
    public static void main(String[] args) {

        final int sPort = 4321;

        Socket sock = null;
        Key serverPubKey = null;
        BufferedReader clientIn = null;

        // Initialise server connection
        try{
            sock = new Socket(InetAddress.getLocalHost(), sPort);
            clientIn = new BufferedReader(new InputStreamReader(sock.getInputStream()));
        } catch (UnknownHostException e) {
            System.out.println("Unknown host.");
            System.exit(1);
        } catch  (IOException e) {
            System.out.println("No I/O");
            System.exit(1);
        }

        // Get server pub key
        try{
            int len = Integer.parseInt(clientIn.readLine());
            byte[] servPubKeyBytes = new byte[len];
            sock.getInputStream().read(servPubKeyBytes,0,len);
            System.out.println(servPubKeyBytes);
            X509EncodedKeySpec ks = new X509EncodedKeySpec(servPubKeyBytes);
            KeyFactory kf = KeyFactory.getInstance("RSA");
            serverPubKey = kf.generatePublic(ks);
            System.out.println(serverPubKey.getEncoded());
        } catch (IOException e) {
            System.out.println("Error obtaining server public key 1.");
            System.exit(0);
        } catch (NoSuchAlgorithmException e) {
            System.out.println("Error obtaining server public key 2.");
            System.exit(0);
        } catch (InvalidKeySpecException e) {
            System.out.println("Error obtaining server public key 3.");
            System.exit(0);
        }

    }

}

Сервер:

public class TestServer {

    public static void main(String[] args) {
        final int servPort = 4321;
        final int RSAKeySize = 1024;
        final String newline = "\n";

        Key pubKey = null;
        ServerSocket cServer = null;
        Socket cClient = null;
        PrintWriter cOut = null;

        // Initialise RSA
        try{
            KeyPairGenerator RSAKeyGen = KeyPairGenerator.getInstance("RSA");
            RSAKeyGen.initialize(RSAKeySize);
            KeyPair pair = RSAKeyGen.generateKeyPair();
            pubKey = pair.getPublic();
        } catch (GeneralSecurityException e) {
            System.out.println(e.getLocalizedMessage() + newline);
            System.out.println("Error initialising encryption. Exiting.\n");
            System.exit(0);
        }

        // Initialise socket connection
        try{
            cServer = new ServerSocket(servPort); 
            cClient = cServer.accept();
            cOut = new PrintWriter(cClient.getOutputStream(), true);
        } catch (IOException e) {
            System.out.println("Error initialising I/O.\n");
            System.exit(0);
        }

        // Send public key
        try {
            cOut.println(pubKey.getEncoded().length);
            System.out.println(pubKey.getEncoded());
            cClient.getOutputStream().write(pubKey.getEncoded());
            cClient.getOutputStream().flush();
        } catch (IOException e) {
            System.out.println("I/O Error");
            System.exit(0);
        }

    }

}

Это может быть так же просто, как сообщить мне, что мой ключ не закодирован в X509, однако похоже, что ключ восстанавливается из файла (также читается как байты) поэтому я не могу понять, почему это не сработает?

Большое спасибо заранее за любую помощь / предложения.

Редактировать: проблема решена, см. ответ Джеффри.Модифицированный (рабочий) код опубликован как ответ.

Ответы [ 4 ]

3 голосов
/ 12 октября 2011

В реальном коде я настоятельно не рекомендую прямое использование классов криптографии таким образом.Если это вообще возможно, используйте Java Secure Socket Extension .

Тем не менее, я вижу ошибку, что вы смешиваете InputStreamReader доступ с необработанным InputStreamпод.InputStreamReader может прочитать больше байтов, чем вы запрашиваете в readLine - он написан, чтобы в значительной степени предполагать, что он владеет базовым InputStream и поэтому может читать вперед в буферизованных блоках.

Цитировать javadoc :

Каждый вызов одного из методов read () InputStreamReader может привести к считыванию одного или нескольких байтов из базового потока байтового ввода.Чтобы обеспечить эффективное преобразование байтов в символы, из базового потока может быть прочитано больше байтов, чем необходимо для выполнения текущей операции чтения.

2 голосов
/ 15 января 2014

Можно отправить открытый ключ по объекту через сокет, например, мы можем написать класс как Frame, как показано ниже:

import java.io.Serializable;
public class Frame implements Serializable {
    byte[] data;

}

на стороне клиента, просто определить Frame и сокет и просто записать в него:

Frame frame = new Frame();
frame.data = thePublicKey.getEncoded();
toServer.writeObject(frame);

на стороне сервера декодирует открытый ключ:

Frame frame = fromClient.readObject();
byte[] pubKey = frame.data;                 
X509EncodedKeySpec ks = new X509EncodedKeySpec(pubKey);
KeyFactory = KeyFactory.getInstance("RSA");
thepublicKey = kf.generatePublic(ks);
1 голос
/ 12 октября 2011

Во-первых, спасибо всем за помощь, она очень ценится! Прошло 12 часов, прежде чем это произошло, и я начал волноваться, плавание отсюда, я думаю:).

В любом случае, пересмотренный код:

Сервер:

public class TestServer {

    public static void main(String[] args) {
        final int servPort = 4321;
        final int RSAKeySize = 1024;
        final String newline = "\n";

        Key pubKey = null;
        ServerSocket cServer = null;
        Socket cClient = null;

        // Initialise RSA
        try{
            KeyPairGenerator RSAKeyGen = KeyPairGenerator.getInstance("RSA");
            RSAKeyGen.initialize(RSAKeySize);
            KeyPair pair = RSAKeyGen.generateKeyPair();
            pubKey = pair.getPublic();
        } catch (GeneralSecurityException e) {
            System.out.println(e.getLocalizedMessage() + newline);
            System.out.println("Error initialising encryption. Exiting.\n");
            System.exit(0);
        }

        // Initialise socket connection
        try{
            cServer = new ServerSocket(servPort); 
            cClient = cServer.accept();
        } catch (IOException e) {
            System.out.println("Error initialising I/O.\n");
            System.exit(0);
        }

        // Send public key
        try {
        System.out.println(DatatypeConverter.printHexBinary(pubKey.getEncoded()));
            ByteBuffer bb = ByteBuffer.allocate(4);
            bb.putInt(pubKey.getEncoded().length);
            cClient.getOutputStream().write(bb.array());
            cClient.getOutputStream().write(pubKey.getEncoded());
            cClient.getOutputStream().flush();
        } catch (IOException e) {
            System.out.println("I/O Error");
            System.exit(0);
        }

    }

}

Клиент:

public class TestClient {

    /**
     * @param args
     */
    public static void main(String[] args) {

        final int sPort = 4321;

        Socket sock = null;
        Key serverPubKey = null;

        // Initialise server connection
        try{
            sock = new Socket(InetAddress.getLocalHost(), sPort);
        } catch (UnknownHostException e) {
            System.out.println("Unknown host.");
            System.exit(1);
        } catch  (IOException e) {
            System.out.println("No I/O");
            System.exit(1);
        }

        // Get server pub key
        try{
            byte[] lenb = new byte[4];
            sock.getInputStream().read(lenb,0,4);
            ByteBuffer bb = ByteBuffer.wrap(lenb);
            int len = bb.getInt();
            System.out.println(len);
            byte[] servPubKeyBytes = new byte[len];
            sock.getInputStream().read(servPubKeyBytes);
            System.out.println(DatatypeConverter.printHexBinary(servPubKeyBytes));
            X509EncodedKeySpec ks = new X509EncodedKeySpec(servPubKeyBytes);
            KeyFactory kf = KeyFactory.getInstance("RSA");
            serverPubKey = kf.generatePublic(ks);
            System.out.println(DatatypeConverter.printHexBinary(serverPubKey.getEncoded()));
        } catch (IOException e) {
            System.out.println("Error obtaining server public key 1.");
            System.exit(0);
        } catch (NoSuchAlgorithmException e) {
            System.out.println("Error obtaining server public key 2.");
            System.exit(0);
        } catch (InvalidKeySpecException e) {
            System.out.println("Error obtaining server public key 3.");
            System.exit(0);
        }   
    }   
}
0 голосов
/ 12 октября 2011

Помимо смешивания Writers и OutputStreams, которые объяснил Джеффри, могут возникнуть проблемы со следующим:

sock.getInputStream().read(servPubKeyBytes,0,len);

JavaDoc для InputStream.read пишет :

Считывает некоторое количество байтов из входного потока и сохраняет их в буферном массиве b. Количество фактически прочитанных байтов возвращается как целое число. Этот метод блокируется до тех пор, пока не будут доступны входные данные, не обнаружен конец файла или не сгенерировано исключение. Если длина b равна нулю, то байты не читаются и возвращается 0; в противном случае предпринимается попытка прочитать хотя бы один байт. Если байт недоступен, поскольку поток находится в конце файла, возвращается значение -1; в противном случае, по крайней мере один байт считывается и сохраняется в b.

Первое чтение байта сохраняется в элементе b [0], следующий - в b [1] и т. Д. Количество прочитанных байтов, самое большее, равно длине b. Пусть k будет количеством фактически прочитанных байтов; эти байты будут храниться в элементах b [0] - b [k-1], оставляя элементы b [k] - b [b.length-1] без изменений.

То есть read() может прочитать меньше байтов, чем запрошено. Если вы знаете, что есть еще байты, вы должны повторно вызывать read, пока все данные не будут прочитаны, что-то вроде:

for (int p = 0; p < len; ) {
    int read = in.read(servPubKeyBytes, p, len - p);
    if (read == -1) {
        throw new RuntimeException("Premature end of stream");
    }
    p += read;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...