这是我昨天提出的问题的延续: Android Java AES Encryption
我目前正在Android上测试AES加密。在我之前的问题中,我能够使用Cipher c = Cipher.getInstance("AES");
我在回复中被告知我应该指定IV,加密模式和填充以防止将来出现任何潜在问题,因为没有规范意味着程序将使用系统的默认值。所以我将代码更改为c = Cipher.getInstance("AES/CBC/PKCS5Padding");
但现在我的代码不再有效,它将返回NullPointerException。
我的代码:
byte[] a = encryptFIN128AES("pls");
String b = decryptFIN128AES(a);
Log.e("AES_Test", "b = " + b);
/**
* Encrypts a string with AES (128 bit key)
* @param fin
* @return the AES encrypted string
*/
private byte[] encryptFIN128AES(String fin) {
SecretKeySpec sks = null;
try {
sks = new SecretKeySpec(generateKey(PASSPHRASE, SALT.getBytes(StandardCharsets.UTF_8)).getEncoded(), "AES");
} catch (Exception e) {
Log.e("encryptFIN128AES", "AES key generation error");
}
// Encode the original data with AES
byte[] encodedBytes = null;
try {
Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");
c.init(Cipher.ENCRYPT_MODE, sks);
encodedBytes = c.doFinal(fin.getBytes(StandardCharsets.UTF_8));
} catch (Exception e) {
Log.e("encryptFIN128AES", "AES encryption error");
}
return encodedBytes;
}
/**
* Decrypts a string with AES (128 bit key)
* @param encodedBytes
* @return the decrypted String
*/
private String decryptFIN128AES(byte[] encodedBytes) {
SecretKeySpec sks = null;
try {
sks = new SecretKeySpec(generateKey(PASSPHRASE, SALT.getBytes(StandardCharsets.UTF_8)).getEncoded(), "AES");
} catch (Exception e) {
Log.e("decryptFIN128AES", "AES key generation error");
}
// Decode the encoded data with AES
byte[] decodedBytes = null;
try {
Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");
c.init(Cipher.DECRYPT_MODE, sks);
decodedBytes = c.doFinal(encodedBytes);
} catch (Exception e) {
Log.e("decryptFIN128AES", "AES decryption error");
}
//return Base64.encodeToString(decodedBytes, Base64.DEFAULT);
return new String(decodedBytes, StandardCharsets.UTF_8);
}
/**
* Build private key from a passpharase/PIN (incl. key derivation (Uses PBKDF2))
* @param passphraseOrPin
* @param salt
* @return The generated SecretKey (Used for AES-encryption, key size specified in outputKeyLength)
*/
public static SecretKey generateKey(char[] passphraseOrPin, byte[] salt)
throws NoSuchAlgorithmException, InvalidKeySpecException {
// Number of PBKDF2 hardening rounds to use. Larger values increase
// computation time. You should select a value that causes computation
// to take >100ms.
final int iterations = 1000;
// Generate a 256-bit key
final int outputKeyLength = 128;
SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
KeySpec keySpec = new PBEKeySpec(passphraseOrPin, salt, iterations, outputKeyLength);
SecretKey secretKey = secretKeyFactory.generateSecret(keySpec);
return secretKey;
}
输出:
E/decryptFIN128AES: AES decryption error
E/AndroidRuntime: FATAL EXCEPTION: Thread-176
Process: testapp.ttyi.nfcapp, PID: 2920
java.lang.NullPointerException: Attempt to get length of null array
at java.lang.String.<init>(String.java:371)
at testapp.ttyi.nfcapp.DisplayQRActivity.decryptFIN128AES(DisplayQRActivity.java:254)
at testapp.ttyi.nfcapp.DisplayQRActivity.access$100(DisplayQRActivity.java:29)
at testapp.ttyi.nfcapp.DisplayQRActivity$1.run(DisplayQRActivity.java:77)
at java.lang.Thread.run(Thread.java:818)
testapp.ttyi.nfcapp.DisplayQRActivity.decryptFIN128AES(DisplayQRActivity.java:254)
指向decryptFIN128AES
的最后一行,即:
return new String(decodedBytes, StandardCharsets.UTF_8);
我知道发生NullPointerException是因为解密过程出了问题。由于它必须进入catch
案例,因此decodedBytes
仍为NULL
,因此当我想要返回decodedBytes
时会导致错误。所以现在我的问题是:为什么会发生这种情况,我该如何解决这个问题?
非常感谢您的帮助。
答案 0 :(得分:0)
非常感谢@Thomas W的帮助。我将catch
更改为throw
,现在我可以看到实际错误与BadPaddingException: error:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad decrypt
经过一些谷歌搜索后,我发现解决方案是c.init
中缺少IV参数。以前我使用的是"AES"
,其中Java默认为"AES/ECB/PKCS5Padding"
,并且在没有IV的情况下工作正常。
(来源:Android: Encrypt a string with AES 256bit Encryption with iv and secret key)
但是一旦我改为"AES/CBC/PKCS5Padding"
,Java就会出现没有声明IV的问题。因此,将c.init(Cipher.ENCRYPT_MODE, sks);
和c.init(Cipher.DECRYPT_MODE, sks);
更改为
c.init(Cipher.ENCRYPT_MODE, sks, new IvParameterSpec(new byte[16]));
和
c.init(Cipher.DECRYPT_MODE, sks, new IvParameterSpec(new byte[16]));
解决了这个问题。
现在我的程序可以正确加密和解密。