我试图用Java加密和解密消息我总是得到同样的错误:
java.security.InvalidKeyException:键太长,无法解包
我试图模拟从服务器向客户端发送编写的邮件。
1st:客户端生成私钥和公钥。
第二个客户端将公钥发送到服务器。
3rd:服务器生成对称AES密钥。
4t:服务器使用他的AES密钥加密他想要发送给客户端的文本。
5e:服务器使用从客户端收到的公钥加密AES密钥。
6e:服务器将加密文本和加密的AES密钥发送给客户端。
7e:客户端用他的私钥解密AES密钥。
8e:客户端使用解密的AES密钥解密从服务器接收的文本。
public class Test {
private static SecretKey secretKey;
private static KeyPair keyPair;
private static final String STRINGTOENCRYPT = "This is a test";
private static String stringEncripted;
private static String keyEncripted;
public static final byte[] IV_PARAM = {0x00, 0x01, 0x02, 0x03,
0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0A, 0x0B,
0x0C, 0x0D, 0x0E, 0x0F};
public static void main(String[] args) {
generateKeys();
}
private static void generateKeys() {
secretKey = generateSecretKey(128);
keyPair = generateKeyPair(512);
stringEncripted = encriptString(STRINGTOENCRYPT);
keyEncripted = encriptKey(keyPair.getPublic());
decryptString();
}
private static void decryptString() {
Cipher cipher = null;
Key keyDecrypted = null;
try {
cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.UNWRAP_MODE, keyPair.getPrivate());
keyDecrypted = cipher.unwrap(keyEncripted.getBytes(), "AES", Cipher.SECRET_KEY);
cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
IvParameterSpec iv = new IvParameterSpec(IV_PARAM);
cipher.init(Cipher.DECRYPT_MODE, keyDecrypted, iv);
byte[] stringDecryptedBytes = cipher.doFinal(stringEncripted.getBytes());
String stringDecrypted = new String(stringDecryptedBytes);
System.out.println(stringDecrypted);
} catch (Exception e) {
e.printStackTrace();
}
}
private static KeyPair generateKeyPair(int lenght) {
KeyPair keyPublicAndPrivate = null;
try {
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(lenght);
keyPublicAndPrivate = keyGen.genKeyPair();
} catch (Exception ex) {
ex.printStackTrace();
}
return keyPublicAndPrivate;
}
private static SecretKey generateSecretKey(int lenght) {
SecretKey sKey = null;
if ((lenght == 128) || (lenght == 192) || (lenght == 256)) {
try {
KeyGenerator kgen = KeyGenerator.getInstance("AES");
kgen.init(lenght);
sKey = kgen.generateKey();
} catch (NoSuchAlgorithmException ex) {
ex.printStackTrace();
}
}
return sKey;
}
private static String encriptString(String stringtoencrypt) {
byte[] stringtoencryptBytes = stringtoencrypt.getBytes();
Cipher cipher = null;
String stringEncrypted = null;
try {
cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
IvParameterSpec iv = new IvParameterSpec(IV_PARAM);
cipher.init(Cipher.ENCRYPT_MODE, secretKey, iv);
byte[] stringEncryptedBytes = cipher.doFinal(stringtoencryptBytes);
stringEncrypted = new String(cipher.doFinal(stringtoencryptBytes), "UTF-8");
} catch (Exception e) {
e.printStackTrace();
}
return stringEncrypted;
}
private static String encriptKey(PublicKey aPublic) {
String keyEncryptedString = null;
try {
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.WRAP_MODE, aPublic);
byte[] keyEncriptedBytes = cipher.wrap(secretKey);
keyEncryptedString = new String(keyEncriptedBytes, "UTF-8");
} catch (Exception e) {
e.printStackTrace();
}
return keyEncryptedString;
}
}
知道为什么会这样吗?
答案 0 :(得分:4)
问题是您不能只假设RSA或AES加密导致表示字符串的字节。使用new String
生成字符串时,它将以静默方式删除所有不表示字符的字节。如果您想要更多控制并抛出异常(这应该是默认值),您可以使用CharsetDecoder
。
问题是,当您重新转换为字节时,某些字节可能会丢失。这意味着这个数字可能会明显变小,因为unwrap的输入现在太小了,会以某种方式抛出异常。
以下是一些显示如何使其正常运行的更改。我只是注释掉了你的代码,以便你进行比较。
import java.security.Key;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
public class Test {
private static SecretKey secretKey;
private static KeyPair keyPair;
private static final String STRINGTOENCRYPT = "This is a test";
private static String stringEncripted;
private static byte[] ct;
private static String keyEncripted;
private static byte[] wrapped;
public static final byte[] IV_PARAM = {0x00, 0x01, 0x02, 0x03,
0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0A, 0x0B,
0x0C, 0x0D, 0x0E, 0x0F};
public static void main(String[] args) {
generateKeys();
}
private static void generateKeys() {
secretKey = generateSecretKey(128);
keyPair = generateKeyPair(512);
stringEncripted = encriptString(STRINGTOENCRYPT);
keyEncripted = encriptKey(keyPair.getPublic());
decryptString();
}
private static void decryptString() {
Cipher cipher = null;
Key keyDecrypted = null;
try {
cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.UNWRAP_MODE, keyPair.getPrivate());
// keyDecrypted = cipher.unwrap(keyEncripted.getBytes(), "AES", Cipher.SECRET_KEY);
keyDecrypted = cipher.unwrap(wrapped, "AES", Cipher.SECRET_KEY);
cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
IvParameterSpec iv = new IvParameterSpec(IV_PARAM);
cipher.init(Cipher.DECRYPT_MODE, keyDecrypted, iv);
byte[] stringDecryptedBytes = cipher.doFinal(ct);
String stringDecrypted = new String(stringDecryptedBytes);
System.out.println(stringDecrypted);
} catch (Exception e) {
e.printStackTrace();
}
}
private static KeyPair generateKeyPair(int lenght) {
KeyPair keyPublicAndPrivate = null;
try {
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(lenght);
keyPublicAndPrivate = keyGen.genKeyPair();
} catch (Exception ex) {
ex.printStackTrace();
}
return keyPublicAndPrivate;
}
private static SecretKey generateSecretKey(int lenght) {
SecretKey sKey = null;
if ((lenght == 128) || (lenght == 192) || (lenght == 256)) {
try {
KeyGenerator kgen = KeyGenerator.getInstance("AES");
kgen.init(lenght);
sKey = kgen.generateKey();
} catch (NoSuchAlgorithmException ex) {
ex.printStackTrace();
}
}
return sKey;
}
private static String encriptString(String stringtoencrypt) {
byte[] stringtoencryptBytes = stringtoencrypt.getBytes();
Cipher cipher = null;
String stringEncrypted = null;
try {
cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
IvParameterSpec iv = new IvParameterSpec(IV_PARAM);
cipher.init(Cipher.ENCRYPT_MODE, secretKey, iv);
// byte[] stringEncryptedBytes = cipher.doFinal(stringtoencryptBytes);
ct = cipher.doFinal(stringtoencryptBytes);
stringEncrypted = new String(cipher.doFinal(stringtoencryptBytes), "UTF-8");
} catch (Exception e) {
e.printStackTrace();
}
return stringEncrypted;
}
private static String encriptKey(PublicKey aPublic) {
String keyEncryptedString = null;
try {
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.WRAP_MODE, aPublic);
// byte[] keyEncriptedBytes = cipher.wrap(secretKey);
// keyEncryptedString = new String(keyEncriptedBytes, "UTF-8");
wrapped = cipher.wrap(secretKey);
} catch (Exception e) {
e.printStackTrace();
}
return keyEncryptedString;
}
}
请注意,PKCS#1填充(RSA加密)和CBC都容易受到填充oracle攻击的影响。您可能希望将AES用于RSA / OAEP和GCM模式。