Android Java AES 加密
Android Java AES Encryption
我目前正在制作一个 Android 应用程序,其中包括使用 AES 加密字符串。但出于某种原因,我的应用程序无法正确解密。我尝试更改 Base64 格式,但没有修复。该代码类似于 Android Encryption with the Android Cryptography API
上的示例
有谁知道我的功能哪里出了问题?因为它不会解码为与我的编码字符串 ("pls") 相同的字符串。
非常感谢您的帮助。
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 byte[]
*/
private byte[] encryptFIN128AES(String fin) {
SecretKeySpec sks = null;
try {
sks = new SecretKeySpec(generateKey("Test1".toCharArray(), "Test2".getBytes()).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");
c.init(Cipher.ENCRYPT_MODE, sks);
encodedBytes = c.doFinal(fin.getBytes());
} 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("Test1".toCharArray(), "Test2".getBytes()).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");
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);
}
public static SecretKey generateKey(char[] passphraseOrPin, byte[] salt)
throws NoSuchAlgorithmException, InvalidKeySpecException {
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/AES_Test: b = cGxz
**
[编辑] 修改了我的代码,但现在有一个 NullPointerException
**
/**
* 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);
}
// generateKey(char[] passphraseOrPin, byte[] salt) remains the same
错误:
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.access0(DisplayQRActivity.java:29)
at testapp.ttyi.nfcapp.DisplayQRActivity.run(DisplayQRActivity.java:77)
at java.lang.Thread.run(Thread.java:818)
**
[EDIT2] 已解决(但不允许 Padding/Encryption 模式)
**
我设法解决了这个问题。 (解码为"pls")使用Codo的解决方案return new String(decodedBytes, StandardCharsets.UTF_8);
虽然它只在使用的算法是:
Cipher c = Cipher.getInstance("AES");
当我把Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");
如上所示的 "NullPointerException" 将会发生。我的观察表明在解密过程中:
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");
}
有些东西会失败,它总是会打印出来:
E/decryptFIN128AES: AES decryption error
因此 NullPointerException 将发生,因为 decodedBytes
始终初始化为 NULL。
您应该更多地研究如何正确使用 AES,因为您缺少 AES 安全的一些基本原理:没有 IV(假设使用 CBC)、没有指定模式(例如 CBC)和没有指定填充(例如PKCS5).
您的流程不平衡。对于加密,您可以:
- 使用默认字符集 (
fin.getBytes()
) 对字符串进行编码以获得二进制数据
- 加密二进制数据得到加密数据(
doFinal
)
对于解密,你做:
- 解密加密数据得到未加密的二进制数据(
doFinal
)
- 将二进制数据编码为 Base64 字符串
不是Base64编码,最后一步应该是加密步骤1的逆向,即你应该将二进制数据解码成字符串:
return String(decodedBytes);
强烈建议您不要使用默认的字符集进行编码和解码,因为它取决于系统的设置。所以加密和解密的系统可能不同。
所以使用:
fin.getBytes(StandardCharsets.UTF_8);
和:
return String(decodedBytes, StandardCharsets.UTF_8);
同样适用于盐。
另请注意,您应该指定填充和链接模式。如果不这样做,provider-specific 默认值适用。有关详细信息,请参阅@Ryan 的回答。
看起来像是字符编码问题。稍作修改即可使用。
在加密 FIN128AES 中:
encodedBytes = c.doFinal(Base64.getEncoder().encode(fin.getBytes()));
在解密FIN128AES中:
return new String(Base64.getDecoder().decode(decodedBytes));
我目前正在制作一个 Android 应用程序,其中包括使用 AES 加密字符串。但出于某种原因,我的应用程序无法正确解密。我尝试更改 Base64 格式,但没有修复。该代码类似于 Android Encryption with the Android Cryptography API
上的示例有谁知道我的功能哪里出了问题?因为它不会解码为与我的编码字符串 ("pls") 相同的字符串。
非常感谢您的帮助。
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 byte[]
*/
private byte[] encryptFIN128AES(String fin) {
SecretKeySpec sks = null;
try {
sks = new SecretKeySpec(generateKey("Test1".toCharArray(), "Test2".getBytes()).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");
c.init(Cipher.ENCRYPT_MODE, sks);
encodedBytes = c.doFinal(fin.getBytes());
} 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("Test1".toCharArray(), "Test2".getBytes()).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");
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);
}
public static SecretKey generateKey(char[] passphraseOrPin, byte[] salt)
throws NoSuchAlgorithmException, InvalidKeySpecException {
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/AES_Test: b = cGxz
**
[编辑] 修改了我的代码,但现在有一个 NullPointerException
**
/**
* 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);
}
// generateKey(char[] passphraseOrPin, byte[] salt) remains the same
错误:
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.access0(DisplayQRActivity.java:29)
at testapp.ttyi.nfcapp.DisplayQRActivity.run(DisplayQRActivity.java:77)
at java.lang.Thread.run(Thread.java:818)
**
[EDIT2] 已解决(但不允许 Padding/Encryption 模式)
**
我设法解决了这个问题。 (解码为"pls")使用Codo的解决方案return new String(decodedBytes, StandardCharsets.UTF_8);
虽然它只在使用的算法是:
Cipher c = Cipher.getInstance("AES");
当我把Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");
如上所示的 "NullPointerException" 将会发生。我的观察表明在解密过程中:
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");
}
有些东西会失败,它总是会打印出来:
E/decryptFIN128AES: AES decryption error
因此 NullPointerException 将发生,因为 decodedBytes
始终初始化为 NULL。
您应该更多地研究如何正确使用 AES,因为您缺少 AES 安全的一些基本原理:没有 IV(假设使用 CBC)、没有指定模式(例如 CBC)和没有指定填充(例如PKCS5).
您的流程不平衡。对于加密,您可以:
- 使用默认字符集 (
fin.getBytes()
) 对字符串进行编码以获得二进制数据 - 加密二进制数据得到加密数据(
doFinal
)
对于解密,你做:
- 解密加密数据得到未加密的二进制数据(
doFinal
) - 将二进制数据编码为 Base64 字符串
不是Base64编码,最后一步应该是加密步骤1的逆向,即你应该将二进制数据解码成字符串:
return String(decodedBytes);
强烈建议您不要使用默认的字符集进行编码和解码,因为它取决于系统的设置。所以加密和解密的系统可能不同。
所以使用:
fin.getBytes(StandardCharsets.UTF_8);
和:
return String(decodedBytes, StandardCharsets.UTF_8);
同样适用于盐。
另请注意,您应该指定填充和链接模式。如果不这样做,provider-specific 默认值适用。有关详细信息,请参阅@Ryan 的回答。
看起来像是字符编码问题。稍作修改即可使用。
在加密 FIN128AES 中:
encodedBytes = c.doFinal(Base64.getEncoder().encode(fin.getBytes()));
在解密FIN128AES中:
return new String(Base64.getDecoder().decode(decodedBytes));