AES/GCM Flutter (Dart) 中的加密
AES/GCM encryption in Flutter (Dart)
我正在尝试在 flutter 中实现 AES/GCM/NoPadding 加密。我已经在 JAVA 中成功实现了它,但是当我试图在 flutter 中解密它时,我没有成功。
我试过用 dart 写解密代码但是我得到了
未处理的异常:SecretBox 有错误的消息验证码 (MAC)
我的Java代码
public class GCMEncryption {
public static final int AES_KEY_SIZE = 128;
public static final int GCM_IV_LENGTH = 12;
public static final int GCM_TAG_LENGTH = 16;
public String getEncryptedText(String plainText) {
try {
byte[] IV = new byte[GCM_IV_LENGTH];
SecureRandom random = new SecureRandom();
random.nextBytes(IV);
String iv = Base64.getEncoder().encodeToString(IV);
System.out.println("IV : "+iv);
byte[] cipherText = encrypt(plainText.getBytes(), getKeySpec(), IV);
String text = Base64.getEncoder().encodeToString(cipherText);
text = iv+text; // Concating iv and encrypted text together
return text;
} catch (Exception e) {
e.printStackTrace();
return "";
}
}
public String getDecryptedText(String cipherText) {
try {
// Splitting IV and Encrypted text
String iv = cipherText.substring(0,16);
System.out.println("IV : "+iv);
byte[] IV = Base64.getDecoder().decode(iv);
cipherText = cipherText.substring(16);
byte[] data = Base64.getDecoder().decode(cipherText);
return decrypt(data, getKeySpec(), IV);
} catch (Exception e) {
e.printStackTrace();
return "";
}
}
private SecretKeySpec getKeySpec() {
SecretKeySpec spec = null;
try {
byte[] bytes = new byte[32];
String pwd = "Test!ng012345678"; //Temporary
bytes = pwd.getBytes();
spec = new SecretKeySpec(bytes, "AES");
return spec;
} catch (Exception e) {
e.printStackTrace();
}
return spec;
}
private byte[] encrypt(byte[] plaintext, SecretKey key, byte[] IV) throws Exception {
// Get Cipher Instance
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
// Create SecretKeySpec
SecretKeySpec keySpec = new SecretKeySpec(key.getEncoded(), "AES");
// Create GCMParameterSpec
GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(GCM_TAG_LENGTH * 8, IV);
// Initialize Cipher for ENCRYPT_MODE
cipher.init(Cipher.ENCRYPT_MODE, keySpec, gcmParameterSpec);
// Perform Encryption
byte[] cipherText = cipher.doFinal(plaintext);
return cipherText;
}
private String decrypt(byte[] cipherText, SecretKey key, byte[] IV) throws Exception {
// Get Cipher Instance
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
// Create SecretKeySpec
SecretKeySpec keySpec = new SecretKeySpec(key.getEncoded(), "AES");
// Create GCMParameterSpec
GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(GCM_TAG_LENGTH * 8, IV);
// Initialize Cipher for DECRYPT_MODE
cipher.init(Cipher.DECRYPT_MODE, keySpec, gcmParameterSpec);
// Perform Decryption
byte[] decryptedText = cipher.doFinal(cipherText);
return new String(decryptedText);
}
}
我的飞镖代码
Future<String> decrypt(String textBlock) async {
Uint8List data = base64.decode("4NA09I5VpmdD0o1k3VP8eIfyZRKDtzOwgJv5nh0nmEPZ/Q==");
Uint8List passphrase = utf8.encode('Test!ng012345678');
SecretKey secretKey = new SecretKey(passphrase);
Uint8List iv = utf8.encode('/U0OI/AdHDM4QFVC');
SecretBox secretBox = new SecretBox(data, nonce: iv);
List<int> decrypted = await AesGcm.with128bits().decrypt(secretBox, secretKey: secretKey);
String dec = utf8.decode(decrypted);
print("DATA : "+dec);
return dec;
}
为此,我在 Flutter 中遇到了以下错误
未处理异常:SecretBox 消息验证码错误 (MAC)
#0 DartAesGcm.decryptSync(包:cryptography/src/dart/aes_gcm.dart:112:7)
#1 DartAesGcm.decrypt(包:cryptography/src/dart/aes_gcm.dart:58:12)
#2 EncryptionHandler.decrypt(包:investment_app/encryption/encryption_handler.dart:45:27)
你能帮我解决这个问题吗?示例代码会有所帮助。
Java 中的 SunJCE 提供程序连接密文和 MAC:密文|MAC。在 Dart 代码中,两者都必须单独指定,这在发布的代码中不会发生。此外,IV 不是 Base64 解码的。
以下代码是修复错误的可能实现:
Uint8List ivCiphertextMac = base64.decode("/U0OI/AdHDM4QFVC4NA09I5VpmdD0o1k3VP8eIfyZRKDtzOwgJv5nh0nmEPZ/Q=="); // from the Java code
Uint8List iv = ivCiphertextMac.sublist(0, 12);
Uint8List ciphertext = ivCiphertextMac.sublist(12, ivCiphertextMac.length - 16);
Uint8List mac = ivCiphertextMac.sublist(ivCiphertextMac.length - 16);
Uint8List passphrase = utf8.encode('Test!ng012345678');
SecretKey secretKey = new SecretKey(passphrase);
SecretBox secretBox = new SecretBox(ciphertext, nonce: iv, mac: new Mac(mac));
List<int> decrypted = await AesGcm.with128bits().decrypt(secretBox, secretKey: secretKey);
String dec = utf8.decode(decrypted);
print("Decrypted text : " + dec); // Decrypted text : Mayur, You got it!
解密给出:Mayur,你明白了!
实际上SecretBox
提供了方法fromConcatenation()
,它应该分离IV、密文和MAC的串联。但是这个实现似乎 return 一个损坏的密文,这可能是一个错误。
编辑:
关于您在评论中提出的问题:MAC 是在加密过程中自动 生成的。下面的代码实现了加密,其中ivCiphertextMacB64
包含了IV |的Base64编码。密文 | MAC:
Uint8List plaintext = utf8.encode("Mayur, You got it!");
Uint8List iv = AesGcm.with128bits().newNonce();
Uint8List passphrase = utf8.encode('Test!ng012345678');
SecretKey secretKey = new SecretKey(passphrase);
SecretBox secretBox = await AesGcm.with128bits().encrypt(plaintext, nonce: iv, secretKey: secretKey);
String ivCiphertextMacB64 = base64.encode(secretBox.concatenation()); // Base64 encoding of: IV | ciphertext | MAC
print("ivCiphertextMacB64 : " + ivCiphertextMacB64);
我正在尝试在 flutter 中实现 AES/GCM/NoPadding 加密。我已经在 JAVA 中成功实现了它,但是当我试图在 flutter 中解密它时,我没有成功。
我试过用 dart 写解密代码但是我得到了 未处理的异常:SecretBox 有错误的消息验证码 (MAC)
我的Java代码
public class GCMEncryption {
public static final int AES_KEY_SIZE = 128;
public static final int GCM_IV_LENGTH = 12;
public static final int GCM_TAG_LENGTH = 16;
public String getEncryptedText(String plainText) {
try {
byte[] IV = new byte[GCM_IV_LENGTH];
SecureRandom random = new SecureRandom();
random.nextBytes(IV);
String iv = Base64.getEncoder().encodeToString(IV);
System.out.println("IV : "+iv);
byte[] cipherText = encrypt(plainText.getBytes(), getKeySpec(), IV);
String text = Base64.getEncoder().encodeToString(cipherText);
text = iv+text; // Concating iv and encrypted text together
return text;
} catch (Exception e) {
e.printStackTrace();
return "";
}
}
public String getDecryptedText(String cipherText) {
try {
// Splitting IV and Encrypted text
String iv = cipherText.substring(0,16);
System.out.println("IV : "+iv);
byte[] IV = Base64.getDecoder().decode(iv);
cipherText = cipherText.substring(16);
byte[] data = Base64.getDecoder().decode(cipherText);
return decrypt(data, getKeySpec(), IV);
} catch (Exception e) {
e.printStackTrace();
return "";
}
}
private SecretKeySpec getKeySpec() {
SecretKeySpec spec = null;
try {
byte[] bytes = new byte[32];
String pwd = "Test!ng012345678"; //Temporary
bytes = pwd.getBytes();
spec = new SecretKeySpec(bytes, "AES");
return spec;
} catch (Exception e) {
e.printStackTrace();
}
return spec;
}
private byte[] encrypt(byte[] plaintext, SecretKey key, byte[] IV) throws Exception {
// Get Cipher Instance
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
// Create SecretKeySpec
SecretKeySpec keySpec = new SecretKeySpec(key.getEncoded(), "AES");
// Create GCMParameterSpec
GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(GCM_TAG_LENGTH * 8, IV);
// Initialize Cipher for ENCRYPT_MODE
cipher.init(Cipher.ENCRYPT_MODE, keySpec, gcmParameterSpec);
// Perform Encryption
byte[] cipherText = cipher.doFinal(plaintext);
return cipherText;
}
private String decrypt(byte[] cipherText, SecretKey key, byte[] IV) throws Exception {
// Get Cipher Instance
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
// Create SecretKeySpec
SecretKeySpec keySpec = new SecretKeySpec(key.getEncoded(), "AES");
// Create GCMParameterSpec
GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(GCM_TAG_LENGTH * 8, IV);
// Initialize Cipher for DECRYPT_MODE
cipher.init(Cipher.DECRYPT_MODE, keySpec, gcmParameterSpec);
// Perform Decryption
byte[] decryptedText = cipher.doFinal(cipherText);
return new String(decryptedText);
}
}
我的飞镖代码
Future<String> decrypt(String textBlock) async {
Uint8List data = base64.decode("4NA09I5VpmdD0o1k3VP8eIfyZRKDtzOwgJv5nh0nmEPZ/Q==");
Uint8List passphrase = utf8.encode('Test!ng012345678');
SecretKey secretKey = new SecretKey(passphrase);
Uint8List iv = utf8.encode('/U0OI/AdHDM4QFVC');
SecretBox secretBox = new SecretBox(data, nonce: iv);
List<int> decrypted = await AesGcm.with128bits().decrypt(secretBox, secretKey: secretKey);
String dec = utf8.decode(decrypted);
print("DATA : "+dec);
return dec;
}
为此,我在 Flutter 中遇到了以下错误
未处理异常:SecretBox 消息验证码错误 (MAC) #0 DartAesGcm.decryptSync(包:cryptography/src/dart/aes_gcm.dart:112:7) #1 DartAesGcm.decrypt(包:cryptography/src/dart/aes_gcm.dart:58:12) #2 EncryptionHandler.decrypt(包:investment_app/encryption/encryption_handler.dart:45:27)
你能帮我解决这个问题吗?示例代码会有所帮助。
Java 中的 SunJCE 提供程序连接密文和 MAC:密文|MAC。在 Dart 代码中,两者都必须单独指定,这在发布的代码中不会发生。此外,IV 不是 Base64 解码的。
以下代码是修复错误的可能实现:
Uint8List ivCiphertextMac = base64.decode("/U0OI/AdHDM4QFVC4NA09I5VpmdD0o1k3VP8eIfyZRKDtzOwgJv5nh0nmEPZ/Q=="); // from the Java code
Uint8List iv = ivCiphertextMac.sublist(0, 12);
Uint8List ciphertext = ivCiphertextMac.sublist(12, ivCiphertextMac.length - 16);
Uint8List mac = ivCiphertextMac.sublist(ivCiphertextMac.length - 16);
Uint8List passphrase = utf8.encode('Test!ng012345678');
SecretKey secretKey = new SecretKey(passphrase);
SecretBox secretBox = new SecretBox(ciphertext, nonce: iv, mac: new Mac(mac));
List<int> decrypted = await AesGcm.with128bits().decrypt(secretBox, secretKey: secretKey);
String dec = utf8.decode(decrypted);
print("Decrypted text : " + dec); // Decrypted text : Mayur, You got it!
解密给出:Mayur,你明白了!
实际上SecretBox
提供了方法fromConcatenation()
,它应该分离IV、密文和MAC的串联。但是这个实现似乎 return 一个损坏的密文,这可能是一个错误。
编辑:
关于您在评论中提出的问题:MAC 是在加密过程中自动 生成的。下面的代码实现了加密,其中ivCiphertextMacB64
包含了IV |的Base64编码。密文 | MAC:
Uint8List plaintext = utf8.encode("Mayur, You got it!");
Uint8List iv = AesGcm.with128bits().newNonce();
Uint8List passphrase = utf8.encode('Test!ng012345678');
SecretKey secretKey = new SecretKey(passphrase);
SecretBox secretBox = await AesGcm.with128bits().encrypt(plaintext, nonce: iv, secretKey: secretKey);
String ivCiphertextMacB64 = base64.encode(secretBox.concatenation()); // Base64 encoding of: IV | ciphertext | MAC
print("ivCiphertextMacB64 : " + ivCiphertextMacB64);