我有以下加密代码
public static String encrypt(String value, char[] secret) {
try {
final byte[] bytes = value != null ? value.getBytes(StandardCharsets.UTF_8) : new byte[0];
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWithMD5AndDES");
SecretKey key = keyFactory.generateSecret(new PBEKeySpec(secret));
Cipher pbeCipher = Cipher.getInstance("PBEWithMD5AndDES");
pbeCipher.init(Cipher.ENCRYPT_MODE, key, new PBEParameterSpec(IsoGame.$().crossPlatformManager.getCrossPlatformUtilsInstance().getDeviceUniqueIdentifier().getBytes(StandardCharsets.UTF_8), 20));
return new String(Base64.encodeBase64(pbeCipher.doFinal(bytes)), StandardCharsets.UTF_8);
} catch (Exception e) {
e.printStackTrace();
}
return value;
}
和以下代码解密。
public static String decrypt(String value, char[] secret) {
try {
final byte[] bytes = value != null ? Base64.decodeBase64(value.getBytes(StandardCharsets.UTF_8)) : new byte[0];
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWithMD5AndDES");
SecretKey key = keyFactory.generateSecret(new PBEKeySpec(secret));
Cipher pbeCipher = Cipher.getInstance("PBEWithMD5AndDES");
pbeCipher.init(Cipher.DECRYPT_MODE, key, new PBEParameterSpec(IsoGame.$().crossPlatformManager.getCrossPlatformUtilsInstance().getDeviceUniqueIdentifier().getBytes(StandardCharsets.UTF_8), 20));
return new String(pbeCipher.doFinal(bytes), StandardCharsets.UTF_8);
} catch (Exception e) {
e.printStackTrace();
}
return value;
}
但是,有时会抛出异常
pbeCipher.doFinal(bytes)
在解密方法中。
唯一的例外是javax.crypto.BadPaddingException: pad block corrupted
这很奇怪,因为我有时会使用相同的值来获得此异常。
有任何想法吗?谢谢。
最可能的原因只是提供错误的密码。如果提供了错误的密码,则会导出错误的密钥。然后密文将被解密为垃圾明文。只有在抛出填充异常时才会注意到这一点:取消填充随机字节可能会失败。
你可以,例如首先通过使用派生密钥对已知数据执行HMAC来验证派生密钥是否正确。此外,使用某种经过身份验证的加密是个好主意,这样如果密钥或数据错误或损坏,解密确实会失败。如果你运气不好,那么 - 此时 - 数据将被解密,unpadding将成功,你最终得到垃圾明文。
当然,您最好升级到PBKDF2以进行密钥派生,并将AES升级到例如AES-GCM代替DES。目前,即使您使用强密码,您的加密也是完全不安全的。
你的问题是
IsoGame.$().crossPlatformManager.getCrossPlatformUtilsInstance().getDeviceUniqueIdentifier().getBytes(StandardCharsets.UTF_8)
我多次运行以下代码并且没有发生异常,解密数据等于“Hello there!”:
public static void main(String[] args)
{
new CryptographyError();
}
private CryptographyError()
{
char[] secret = "MySecret".toCharArray();
String mesasge = "Hello there!";
EncryptedData encryptedData = encrypt(mesasge, secret);
System.out.println("ENCRYPTED " + encryptedData.encryptedString);
String decrypted = decrypt(encryptedData, secret);
System.out.println("DECRYPTED " + decrypted);
}
private static final SecureRandom RANDOM = new SecureRandom();
public static EncryptedData encrypt(String value, char[] secret) {
try {
final byte[] bytes = value != null ? value.getBytes(StandardCharsets.UTF_8) : new byte[0];
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWithMD5AndDES");
SecretKey key = keyFactory.generateSecret(new PBEKeySpec(secret));
Cipher pbeCipher = Cipher.getInstance("PBEWithMD5AndDES");
byte[] salt = new byte[8];
RANDOM.nextBytes(salt);
pbeCipher.init(Cipher.ENCRYPT_MODE, key, new PBEParameterSpec(salt, 20));
return new EncryptedData(salt, new String(Base64.getEncoder().encode(pbeCipher.doFinal(bytes)), StandardCharsets.UTF_8));
} catch (Exception e) {
e.printStackTrace();
System.out.println(value);
}
return null;
}
public static String decrypt(EncryptedData encryptedData, char[] secret) {
try {
String value = encryptedData.encryptedString;
byte[] salt = encryptedData.salt;
final byte[] bytes = value != null ? Base64.getDecoder().decode(value.getBytes(StandardCharsets.UTF_8)) : new byte[0];
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWithMD5AndDES");
SecretKey key = keyFactory.generateSecret(new PBEKeySpec(secret));
Cipher pbeCipher = Cipher.getInstance("PBEWithMD5AndDES");
pbeCipher.init(Cipher.DECRYPT_MODE, key, new PBEParameterSpec(salt, 20));
return new String(pbeCipher.doFinal(bytes), StandardCharsets.UTF_8);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
private static class EncryptedData
{
private final byte[] salt;
private final String encryptedString;
private EncryptedData(byte[] salt, String encryptedString)
{
this.salt = salt;
this.encryptedString = encryptedString;
}
}
我的代码和代码之间唯一的主要区别是
IsoGame.$().crossPlatformManager.getCrossPlatformUtilsInstance().getDeviceUniqueIdentifier().getBytes(StandardCharsets.UTF_8)
这意味着加密和解密时不得返回相同的值。此外,如果你想测试它,你可以只改变它们之间的盐,并注意再次抛出异常。
Maarten Bodewes还为您提供了一些关于如何改进代码的好注意事项。