Encryption java中Cipher的解密问题



嗨,我正面临一个解密问题。解密后的值与原始值不匹配。

以下是我对encryption的逻辑:

public byte[] encrypt(String plainText) {
byte iv[] = new byte[ENCRYPTION_PARAM_SIZE];
SecureRandom secRandom = new SecureRandom();
secRandom.nextBytes(iv);

Cipher cipher = Cipher.getInstance(ENCRYPTION_INSTANCE);
SecretKeySpec key = new SecretKeySpec(fixSecret(encryptionKey), ENCRYPTION_ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(iv));
return cipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8));
}

这是Decryption

的逻辑
public String decrypt(byte[] cipherText) {
byte iv[] = new byte[ENCRYPTION_PARAM_SIZE];
SecureRandom secRandom = new SecureRandom();
secRandom.nextBytes(iv);

Cipher cipher = Cipher.getInstance(ENCRYPTION_INSTANCE);
SecretKeySpec key = new SecretKeySpec(fixSecret(encryptionKey), ENCRYPTION_ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv));
return new String(cipher.doFinal(cipherText), StandardCharsets.UTF_8);
}

加密选项:

ENCRYPTION_ALGORITHM = "DESede";
ENCRYPTION_INSTANCE = "DESede/CBC/PKCS5Padding";
Integer ENCRYPTION_PARAM_SIZE = 8;

我是这样验证的:

public static void main(String[] args){    
Long value = 9123456L;
String strval = value.toString();
byte[] encryptedVal = encrypt(strval);
String decryptedVal = decrypt(encryptedVal);

System.out.println("Original  value : " +strval);
System.out.println("Encrypted value : " +encryptedVal.toString());
System.out.println("Decrypted value : " +decryptedVal);
System.out.println("Final     value : " +Long.parseLong(decryptedVal));
}

我需要做什么才能使它工作

注意:如果我使用下面没有SecureRandom的逻辑,上面的代码可以正常工作:

cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(new byte[ENCRYPTION_PARAM_SIZE]));

就像其他人说的那样,您的问题是用于加密的IV与用于解密的IV不同。IV是不敏感的(从保密的角度来看),所以它可以与密文一起传输。一些应用程序将IV附加到密文之前,而其他应用程序则使用更标准的格式,例如CMS Encrypted Data。由于您正在使用Java,您可以使用Bouncy Castle的cmsencrypteddataggenerator类生成CMS加密数据结构。

你的代码还有两个问题:
  1. 密码原语的选择
  2. 密钥管理

加密原语

您正在使用的加密转换是"DESede/CBC/PKCS5Padding"除非您有充分的理由使用DES/3DES,否则您应该考虑切换到AES。此外,我建议使用AES-GCM或AES-GCM- siv等AEAD。

密钥管理

如果密钥管理不正确,世界上所有的密码学都没有多大意义。从代码中可以看出,您正在从密钥的字节中构造SecretKeySpec对象。如果可以,请尝试使用硬件安全模块(HSM)或密钥管理系统(KMS),其中密钥永远不会以明文格式从设备的篡改边界导出。AWS KMS、Azure Key Vault和Google KMS都提供了非常实惠的价格。当然,也有其他的选择。

在解密函数中,您生成一个随机的初始化向量(IV),因此这将永远不起作用。您需要存储来自加密函数的IV,并将其作为输入提供给解密函数。

下面是一个例子:

public byte[] encryptAndDecrypt(String plainText) {
byte iv[] = new byte[ENCRYPTION_PARAM_SIZE];
SecureRandom secRandom = new SecureRandom();
secRandom.nextBytes(iv);
Cipher cipher = Cipher.getInstance(ENCRYPTION_INSTANCE);
SecretKeySpec key = new SecretKeySpec(fixSecret(encryptionKey), ENCRYPTION_ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(iv));
byte[] cipherText=cipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8));
return decrypt(cipherText, iv)
}

public String decrypt(byte[] cipherText, byte[] iv) {
Cipher cipher = Cipher.getInstance(ENCRYPTION_INSTANCE);
SecretKeySpec key = new SecretKeySpec(fixSecret(encryptionKey), ENCRYPTION_ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv));
return cipher.doFinal(cipherText);
}

注意,根据定义,IV应该是随机的,但不应该被视为秘密,因此您可以将其存储为普通数据而不需要任何保护。IV背后的想法是随机化密文,所以如果你不使用IV,或者使用常量IV,加密"x",密文是"y",你可以很容易地将密文反转成明文,而随机IV的密文每次都是不同的。

最新更新