作为一项学习练习,我正在用Java构建一个聊天服务器,在C#中构建一个客户端。我正在使用RSA和AES在邮件发送之前对其进行加密。问题是我在使用RSA加密和解密AES密钥时遇到问题。
服务器通过序列化从文件加载私有和公共RSA密钥,或者如果找不到文件则生成新的密钥对:
public RSA(ChatServer chatServer) {
this.server = chatServer;
try {
FileInputStream fileInputStream = new FileInputStream("rsa.key");
ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
keyPair = (KeyPair)objectInputStream.readObject();
objectInputStream.close();
chatServer.log("RSA key pair loaded.");
} catch (FileNotFoundException e) {
chatServer.logWarning("No RSA key found.");
chatServer.log("Generating new RSA key pair (This may take a few moments)...");
keyPair = this.generateKeyPair();
try {
this.saveKeys();
} catch (IOException ioe) {
this.server.logException(ioe);
}
} catch (Exception e) {
this.server.logException(e);
}
}
public KeyPair generateKeyPair() {
KeyPairGenerator kpg;
try {
kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(3072);
return kpg.generateKeyPair();
} catch (Exception e) {
this.server.logException(e);
return null;
}
}
public void saveKeys() throws IOException {
ObjectOutputStream oout = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream("rsa.key")));
try {
oout.writeObject(this.keyPair);
} catch (Exception e) {
this.server.logException(e);
} finally {
oout.close();
}
}
当客户端连接时,服务器将其公钥转换为.NET使用的XML格式并发送它:
public String getPublicKeyXML() {
try {
KeyFactory factory = KeyFactory.getInstance("RSA");
RSAPublicKeySpec publicKey = factory.getKeySpec(this.keyPair.getPublic(), RSAPublicKeySpec.class);
byte[] modulus = publicKey.getModulus().toByteArray();
byte[] exponent = publicKey.getPublicExponent().toByteArray();
String modulusStr = Base64.encodeBytes(modulus);
String exponentStr = Base64.encodeBytes(exponent);
String format =
"<RSAKeyValue>" +
"<Modulus>%s</Modulus>" +
"<Exponent>%s</Exponent>" +
"</RSAKeyValue>";
return String.format(format, modulusStr, exponentStr);
} catch (Exception e) {
this.server.logException(e);
return null;
}
}
客户收到钥匙罚款,并毫不费力地加载它。客户端然后加密随机选择的AES密钥以发送到服务器(请注意,此代码来自客户端,因此是C#而不是Java):
public static byte[] encrypt(string xmlKey, byte[] bytes)
{
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
rsa.FromXmlString(xmlKey);
System.Diagnostics.Debug.WriteLine(rsa.ToXmlString(false));
byte[] cipherBytes = rsa.Encrypt(bytes, false);
rsa.Clear();
return cipherBytes;
}
此代码打印的公钥与服务器发送的公钥相同,到目前为止,所有内容似乎都运行正常。服务器尝试解密时会发生此问题:
public byte[] decrypt(byte[] data) {
try {
PrivateKey privateKey = this.keyPair.getPrivate();
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
this.server.logDebug("Decrypting data: " + this.bytesToHex(data));
byte[] cipherData = cipher.doFinal(data);
return cipherData;
} catch (Exception e) {
this.server.logException(e);
return new byte[0];
}
}
这是事情变得奇怪的地方。我收到BadPaddingException告诉我数据必须以0开头;但是,作为调试措施,我让服务器打印出要解密的数据,第一个字节实际上是00!我不知道该怎么做,谷歌没有找到任何有这个问题的人。有谁知道这里可能出现什么问题?