Java:通过套接字发送后公钥不同
我正在尝试通过 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
在现实世界的代码中,我强烈建议不要以这种方式直接使用加密类。如果可能,请使用 Java 安全套接字扩展.
也就是说,我看到的错误是您将
InputStreamReader
访问与底层的原始InputStream
混合在一起。InputStreamReader
可能会读取比您在readLine
中要求的更多的字节 - 它的编写几乎假设它拥有底层InputStream
,因此可以提前读取在缓冲块中。引用 javadoc:
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 rawInputStream
underneath. TheInputStreamReader
may read more bytes than you ask for inreadLine
— it's written to pretty much assume it owns the underlyingInputStream
and so can read ahead in buffered blocks.To quote the javadoc:
可以通过套接字通过对象发送公钥
例如,我们可以像下面这样编写一个 Frame 类:
在客户端只需定义 Frame 和套接字并写入其中:
在服务器端解码公钥:
It is possible to send the public key by object over socket
for example we can write a class as Frame like below:
in client side just define the Frame and socket and just write into it:
in the server side decode the public key:
首先感谢大家的帮助,非常感谢!距离截止日期还有 12 个小时,我开始担心了,我想从这里开始一帆风顺:)。
无论如何,修改后的代码:
服务器:
客户端:
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:
Client:
除了 Jeffrey 解释的 Writer 和 OutputStreams 的混合之外,以下内容也可能存在问题:
The JavaDoc for InputStream.read 写入:
也就是说,
read()
读取的字节数可能少于请求的字节数。如果您知道还有更多字节,则应该重复调用read
直到读取所有数据,如下所示:Beside the mixing of Writers and OutputStreams that Jeffrey has explained, the following might also be problematic:
The JavaDoc for InputStream.read writes:
That is,
read()
may read less bytes than requested. If you know there are more bytes, you should callread
repeatedly until all data has been read, something like: