JAVA - AES 密钥长度不匹配
JAVA - Mismatched AES key lengths
我有两种方法用于客户端和服务器之间的 ciphering/deciphering JSON 消息。服务器有一对 RSA 密钥(一个 Private 和一个 Public),客户端也是如此。此外,两者都可以访问在首次建立通信时生成的对称会话密钥,该密钥用于加密 JSON 消息,因为通道本身并不安全。客户端生成一个 AES 密钥(会话密钥),然后他用服务器的 Public 密钥(所有人都知道)对其进行加密,并将其发送给服务器,服务器解密会话密钥并将其存储在 HashMap
在客户 ID 下。
不过问题来了:我在发送前检查了密钥长度,发现它有 16 个字节长。当服务器收到加密的会话密钥并对其进行解密时,它是 24 字节长。我假设在加密或解密操作期间出现了问题。这是一个问题,因为当客户端想要开始发送请求(以加密 JSON 消息的形式)时,服务器无法解密和读取请求,因为密钥的大小错误(它抛出一个 java.security.InvalidKeyException
: 非法密钥大小或默认参数)。
下面贴出cipher/decipher方法:
public String cipher(Key key, String alg, String msg){
try {
Cipher c = Cipher.getInstance(alg);
c.init(Cipher.ENCRYPT_MODE, key);
return encoder.encodeToString(c.doFinal(msg.getBytes("UTF-8")));
} catch (IllegalBlockSizeException e) {
(...)
}
}
encoder
/decoder
来自java.util.Base64
。
public String decipher(Key key, String alg, String msg){
try {
Cipher c = Cipher.getInstance(alg);
c.init(Cipher.DECRYPT_MODE,key);
return new String(c.doFinal(decoder.decode(msg)), "UTF-8");
} catch (IllegalBlockSizeException e) {
(...)
}
}
客户端加密并发送会话密钥:
SecretKey sk = KeyGenerator.getInstance("AES/CBC/PKCS5Padding").generateKey();
System.out.println(sk.getEncoded().length) //length = 16 bytes
String sessionKey = cipher(client.getServerPublicKey(),
"RSA/ECB/PKCS5Padding", encoder.encodeToString(sk.getEncoded()));
printWriter.write("{(...), \"key\":\"" + sessionKey + "\"}");
服务器接收加密的会话密钥并将其保存在HashMap
:
byte[] aux = decipher(registry.getServerPrivateKey(),
"RSA/ECB/PKCS5Padding", data.get("key").toString().replace("\"", ""))
.getBytes("UTF-8");
System.out.println(aux.length) //length = 24 bytes
registry.addSession(..., new SecretKeySpec(aux, 0, aux.length, "AES"));
我尝试了不同的算法和不同的填充,但我似乎无法找出错误所在。
您在使用以下语句加密之前对您的密钥进行 base64 编码:
String sessionKey = cipher(client.getServerPublicKey(),
"RSA/ECB/PKCS5Padding", encoder.encodeToString(sk.getEncoded()));
.. 将您的 16 字节密钥转换为 24 字符长的 base64 表示形式。您不需要在加密之前对密钥进行 base64 编码,因为加密完全能够处理二进制数据。试试这个:
String sessionKey = cipher(client.getServerPublicKey(),
"RSA/ECB/PKCS5Padding", sk.getEncoded());
同时更改您的 cipher(..)
方法以接受消息作为 byte[]
public String cipher(Key key, String alg, byte[] msg){
try {
Cipher c = Cipher.getInstance(alg);
c.init(Cipher.ENCRYPT_MODE, key);
return encoder.encodeToString(c.doFinal(msg));
} catch (IllegalBlockSizeException e) {
(...)
}
}
另外,将您的 decipher(..)
也更改为二进制,例如:
public byte[] decipher(Key key, String alg, String msg){
try {
Cipher c = Cipher.getInstance(alg);
c.init(Cipher.DECRYPT_MODE,key);
return c.doFinal(decoder.decode(msg));
} catch (IllegalBlockSizeException e) {
(...)
}
}
我有两种方法用于客户端和服务器之间的 ciphering/deciphering JSON 消息。服务器有一对 RSA 密钥(一个 Private 和一个 Public),客户端也是如此。此外,两者都可以访问在首次建立通信时生成的对称会话密钥,该密钥用于加密 JSON 消息,因为通道本身并不安全。客户端生成一个 AES 密钥(会话密钥),然后他用服务器的 Public 密钥(所有人都知道)对其进行加密,并将其发送给服务器,服务器解密会话密钥并将其存储在 HashMap
在客户 ID 下。
不过问题来了:我在发送前检查了密钥长度,发现它有 16 个字节长。当服务器收到加密的会话密钥并对其进行解密时,它是 24 字节长。我假设在加密或解密操作期间出现了问题。这是一个问题,因为当客户端想要开始发送请求(以加密 JSON 消息的形式)时,服务器无法解密和读取请求,因为密钥的大小错误(它抛出一个 java.security.InvalidKeyException
: 非法密钥大小或默认参数)。
下面贴出cipher/decipher方法:
public String cipher(Key key, String alg, String msg){
try {
Cipher c = Cipher.getInstance(alg);
c.init(Cipher.ENCRYPT_MODE, key);
return encoder.encodeToString(c.doFinal(msg.getBytes("UTF-8")));
} catch (IllegalBlockSizeException e) {
(...)
}
}
encoder
/decoder
来自java.util.Base64
。
public String decipher(Key key, String alg, String msg){
try {
Cipher c = Cipher.getInstance(alg);
c.init(Cipher.DECRYPT_MODE,key);
return new String(c.doFinal(decoder.decode(msg)), "UTF-8");
} catch (IllegalBlockSizeException e) {
(...)
}
}
客户端加密并发送会话密钥:
SecretKey sk = KeyGenerator.getInstance("AES/CBC/PKCS5Padding").generateKey();
System.out.println(sk.getEncoded().length) //length = 16 bytes
String sessionKey = cipher(client.getServerPublicKey(),
"RSA/ECB/PKCS5Padding", encoder.encodeToString(sk.getEncoded()));
printWriter.write("{(...), \"key\":\"" + sessionKey + "\"}");
服务器接收加密的会话密钥并将其保存在HashMap
:
byte[] aux = decipher(registry.getServerPrivateKey(),
"RSA/ECB/PKCS5Padding", data.get("key").toString().replace("\"", ""))
.getBytes("UTF-8");
System.out.println(aux.length) //length = 24 bytes
registry.addSession(..., new SecretKeySpec(aux, 0, aux.length, "AES"));
我尝试了不同的算法和不同的填充,但我似乎无法找出错误所在。
您在使用以下语句加密之前对您的密钥进行 base64 编码:
String sessionKey = cipher(client.getServerPublicKey(),
"RSA/ECB/PKCS5Padding", encoder.encodeToString(sk.getEncoded()));
.. 将您的 16 字节密钥转换为 24 字符长的 base64 表示形式。您不需要在加密之前对密钥进行 base64 编码,因为加密完全能够处理二进制数据。试试这个:
String sessionKey = cipher(client.getServerPublicKey(),
"RSA/ECB/PKCS5Padding", sk.getEncoded());
同时更改您的 cipher(..)
方法以接受消息作为 byte[]
public String cipher(Key key, String alg, byte[] msg){
try {
Cipher c = Cipher.getInstance(alg);
c.init(Cipher.ENCRYPT_MODE, key);
return encoder.encodeToString(c.doFinal(msg));
} catch (IllegalBlockSizeException e) {
(...)
}
}
另外,将您的 decipher(..)
也更改为二进制,例如:
public byte[] decipher(Key key, String alg, String msg){
try {
Cipher c = Cipher.getInstance(alg);
c.init(Cipher.DECRYPT_MODE,key);
return c.doFinal(decoder.decode(msg));
} catch (IllegalBlockSizeException e) {
(...)
}
}