Java:通过套接字发送后公钥不同

发布于 2024-12-09 08:27:17 字数 3638 浏览 0 评论 0原文

我正在尝试通过 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 编码一样简单,但这似乎是从文件中恢复密钥的方式(也读取为字节),因此我不明白为什么它不起作用?

预先非常感谢您的任何帮助/建议。

编辑:问题已解决,请参阅杰弗里的回复。修改后的(工作)代码作为响应发布。

I'm trying to send a public key over a socket connection in Java. While I'm very conscious Java provides SSL functionality for this sort of activity, this is a uni assignment; I cannot use the Java implementation.

The server encodes its public key and transmits it to the client via socket connection. When the client receives the key and decodes it, it appears different. Not only this, the data received by the client appears different to that transmitted by the server. I believe this is giving me problems when I attempt to then encrypt a user name and password using this key.

The problem can be reproduced with the following code:

Client:

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

    }

}

Server:

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

    }

}

This may be as simple as informing me my key is not X509 encoded, however this appears to be the way a key is recovered from a file (also read as bytes) so I can't understand why it won't work?

Thanks very much in advance for any help/suggestions.

Edit: problem solved, see Jeffrey's response. Modified (working) code posted as response.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

温柔戏命师 2024-12-16 08:27:17

在现实世界的代码中,我强烈建议不要以这种方式直接使用加密类。如果可能,请使用 Java 安全套接字扩展.

也就是说,我看到的错误是您将 InputStreamReader 访问与底层的原始 InputStream 混合在一起。 InputStreamReader 可能会读取比您在 readLine 中要求的更多的字节 - 它的编写几乎假设它拥有底层 InputStream,因此可以提前读取在缓冲块中。

引用 javadoc

每次调用 InputStreamReader 的 read() 方法之一都可能
导致从底层字节输入读取一个或多个字节
溪流。为了实现字节到字符的高效转换,
可以从底层流中提前读取更多字节
满足当前读取操作所必需的。

In real-world code, I strongly advise against making direct use of the cryptography classes in this way. If at all possible, use the Java Secure Socket Extension.

That said, the bug I see is that you're mixing InputStreamReader access with a raw InputStream underneath. The InputStreamReader may read more bytes than you ask for in readLine — it's written to pretty much assume it owns the underlying InputStream and so can read ahead in buffered blocks.

To quote the javadoc:

Each invocation of one of an InputStreamReader's read() methods may
cause one or more bytes to be read from the underlying byte-input
stream. To enable the efficient conversion of bytes to characters,
more bytes may be read ahead from the underlying stream than are
necessary to satisfy the current read operation.

半岛未凉 2024-12-16 08:27:17

可以通过套接字通过对象发送公钥
例如,我们可以像下面这样编写一个 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);

It is possible to send the public key by object over socket
for example we can write a class as Frame like below:

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

}

in client side just define the Frame and socket and just write into it:

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

in the server side decode the public key:

Frame frame = fromClient.readObject();
byte[] pubKey = frame.data;                 
X509EncodedKeySpec ks = new X509EncodedKeySpec(pubKey);
KeyFactory = KeyFactory.getInstance("RSA");
thepublicKey = kf.generatePublic(ks);
疯到世界奔溃 2024-12-16 08:27:17

首先感谢大家的帮助,非常感谢!距离截止日期还有 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);
        }   
    }   
}

Firstly, thanks everyone for all your help, it's very much appreciated! 12 hours to go before this is due and I was starting to get worried, smooth sailing from here I think :).

Anyway, the revised code:

Server:

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

    }

}

Client:

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);
        }   
    }   
}
风吹雪碎 2024-12-16 08:27:17

除了 Jeffrey 解释的 Writer 和 OutputStreams 的混合之外,以下内容也可能存在问题:

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

The JavaDoc for 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;
}

Beside the mixing of Writers and OutputStreams that Jeffrey has explained, the following might also be problematic:

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

The JavaDoc for InputStream.read writes:

Reads some number of bytes from the input stream and stores them into the buffer array b. The number of bytes actually read is returned as an integer. This method blocks until input data is available, end of file is detected, or an exception is thrown.
If the length of b is zero, then no bytes are read and 0 is returned; otherwise, there is an attempt to read at least one byte. If no byte is available because the stream is at the end of the file, the value -1 is returned; otherwise, at least one byte is read and stored into b.

The first byte read is stored into element b[0], the next one into b[1], and so on. The number of bytes read is, at most, equal to the length of b. Let k be the number of bytes actually read; these bytes will be stored in elements b[0] through b[k-1], leaving elements b[k] through b[b.length-1] unaffected.

That is, read() may read less bytes than requested. If you know there are more bytes, you should call read repeatedly until all data has been read, something like:

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;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文