簡體   English   中英

Java:通過套接字發送后,公鑰有所不同

[英]Java: Public key different after sent over socket

我正在嘗試通過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編碼,但是這似乎是從文件(也讀取為字節)中恢復密鑰的方式,所以我不明白為什么它不起作用?

在此先非常感謝您的幫助/建議。

編輯:問題已解決,請參閱傑弗里的回應。 修改后的(工作)代碼作為響應發布。

在現實世界的代碼中,我強烈建議您不要以這種方式直接使用加密類。 盡可能使用Java安全套接字擴展

也就是說,我看到的錯誤是您將InputStreamReader訪問與下面的原始InputStream混合在一起。 InputStreamReader可能讀取的字節數超出了您在readLine所要求的字節數–編寫該文件時,它非常假設它擁有基礎InputStream ,因此可以在緩沖的塊中提前讀取。

引用javadoc

InputStreamReader的read()方法之一的每次調用都可能導致從基礎字節輸入流中讀取一個或多個字節。 為了實現字節到字符的有效轉換,與滿足當前讀取操作所需的字節數相比,可以從基礎流中提前讀取更多字節。

可以通過套接字按對象發送公鑰,例如,我們可以像下面這樣編寫Frame類:

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

}

在客戶端,只需定義框架和套接字,然后將其寫入即可:

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);

首先,感謝大家的幫助,非常感謝! 我想這要走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);
        }   
    }   
}

除了Jeffrey所解釋的Writers和OutputStream的混合,以下內容也可能有問題:

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

用於InputStream.read的JavaDoc 寫道

從輸入流中讀取一定數量的字節,並將其存儲到緩沖區數組b中。 實際讀取的字節數以整數形式返回。 該方法將阻塞,直到輸入數據可用,檢測到文件結尾或引發異常為止。 如果b的長度為零,則不讀取任何字節,並返回0;否則,返回0。 否則,嘗試讀取至少一個字節。 如果由於流位於文件末尾而沒有字節可用,則返回值-1;否則返回值-1。 否則,讀取至少一個字節並將其存儲到b。

讀取的第一個字節存儲在元素b [0]中,下一個字節存儲在b [1]中,依此類推。 讀取的字節數最多等於b的長度。 令k為實際讀取的字節數; 這些字節將存儲在元素b [0]至b [k-1]中,而元素b [k]至b [b.length-1]不受影響。

也就是說, read()讀取的字節可能少於請求的字節。 如果您知道還有更多字節,則應該重復調用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;
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM