我正在尝试通过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编码一样简单,但这似乎是从文件中恢复密钥的方式(也读作字节)所以我无法理解为什么它不起作用?
非常感谢您提供任何帮助/建议。
编辑:问题解决了,请参阅Jeffrey的回复。修改后的(工作)代码作为响应发布。
答案 0 :(得分:3)
在实际代码中,我强烈建议不要以这种方式直接使用加密类。如果可能,请使用Java Secure Socket Extension。
也就是说,我看到的错误是您将InputStreamReader
访问权限与下面的原始InputStream
混合在一起。 InputStreamReader
可能会读取比readLine
中要求的字节更多的字节 - 它几乎可以假设它拥有底层InputStream
,因此可以在缓冲块中预读。
引用javadoc:
每次调用一个InputStreamReader的read()方法都可以 导致从底层字节输入中读取一个或多个字节 流。要实现将字节有效转换为字符, 可以从底层流中读取更多字节 必须满足当前的读操作。
答案 1 :(得分:2)
可以通过套接字上的对象发送公钥 例如,我们可以像下面一样编写一个类:
import java.io.Serializable;
public class Frame implements Serializable {
byte[] data;
}
客户端只需定义Frame和socket,然后写入:
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);
答案 2 :(得分:1)
首先,感谢大家的帮助,非常感谢!在此之前的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);
}
}
}
答案 3 :(得分:0)
除了Jeffrey所解释的Writers和OutputStream的混合之外,以下内容也可能存在问题:
sock.getInputStream().read(servPubKeyBytes,0,len);
InputDream.read {4}的JavaDoc:
从输入流中读取一些字节数并将它们存储到缓冲区数组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;
}