在Dart中解密Java AES编码的字符串

5cnsuln7  于 2023-02-20  发布在  Java
关注(0)|答案(2)|浏览(139)

我需要解密Flutter移动应用程序中的AES(PKCS#7)编码字符串。
字符串从QR码中获取,QR码由Java应用程序生成,包含AES编码字符串。
Java编码:

import java.security.Security;
import java.nio.charset.StandardCharsets;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

public class MyClass {

     public static void main(String[] args) throws Exception {
         String toEncode = "firstname.lastname@mycompany.com;12";
         String encoded = pleaseEncodeMe(toEncode);
         System.out.println(encoded);
     }

     private static String pleaseEncodeMe(String plainText) throws Exception {
         Security.addProvider(new BouncyCastleProvider());
         final String encryptionAlgorithm = "AES/CBC/PKCS7PADDING";
         final String encryptionKey = "WHatAnAWEsoMeKey";
         final SecretKeySpec keySpecification = new SecretKeySpec(encryptionKey.getBytes(StandardCharsets.UTF_8), encryptionAlgorithm);
         final Cipher cipher = Cipher.getInstance(encryptionAlgorithm, "BC");
         cipher.init(Cipher.ENCRYPT_MODE, keySpecification);
         final byte[] encryptedBytes = cipher.doFinal(plainText.getBytes());
         return Base64.encodeBase64URLSafeString(encryptedBytes);
    }

}

输出:AIRTEuNmSuQtYuysv93w3w83kJJ6sg7kaU7XzA8xrAjOp-lKYPp1brtDAPbhSJmT
Dart解码:

void main() {
    print(decodeMeOrDie("AIRTEuNmSuQtYuysv93w3w83kJJ6sg7kaU7XzA8xrAjOp-lKYPp1brtDAPbhSJmT"));
}

String decodeMeOrDie(String encryptedString) {
    final key = Key.fromUtf8("WHatAnAWEsoMeKey");
    final iv = IV.fromLength(16);
    final encrypter = Encrypter(AES(key, mode: AESMode.cbc, padding: "PKCS7"));
    return encrypter.decrypt64(encryptedString, iv: iv);
}

输出:Y��=X�Rȑ �"Qme@mycompany.com;12
您可以看到只有字符串的一部分被解码。

sqyvllje

sqyvllje1#

  • 必须考虑两件事:

1)对于解密,需要用于加密的IV。
2)出于安全原因,必须为每次加密随机生成新的IV,使得没有IV被同一密钥here使用多于一次。
因此,IV必须从加密端传递到解密端,这不会自动发生,但必须实现。

  • 一种可能性是将IV和密文的字节数组连接起来。通常IV放在密文之前,并且结果是Base64编码的(如果需要),例如在Java中:
// Concatenate IV and ciphertext
byte[] iv = ...
byte[] ciphertext = ...
byte[] ivAndCiphertext = new byte[iv.length + ciphertext.length];
System.arraycopy(iv, 0, ivAndCiphertext, 0, iv.length);
System.arraycopy(ciphertext, 0, ivAndCiphertext, iv.length, ciphertext.length);
// If required: Base64-encoding

这个数据被传输到解密端,在Base64解码后将两部分分开。在AES-CBC的情况下,IV是16个字节长,所以前16个字节代表IV,其余的代表密文。IV不需要加密,因为它不是秘密的。
具体到您的情况,这意味着您必须在Java端连接IV和密文,并对结果进行Base64编码。在Dart端,您必须首先进行Base64解码,然后IV和密文这两部分可以分离并用于以下解密。

  • 有两种方法可以在加密前生成IV:通过Cipher-示例隐式生成(如示例中所示)或通过SecureRandom显式生成(如通过here)。两种方法都在here中讨论。如果IV是隐式生成的(通过Cipher-示例),则必须通过Cipher-示例确定此IV,因为稍后需要解密:
// Determine IV from cipher for later decryption
byte[] iv = cipher.getParameters().getParameterSpec(IvParameterSpec.class).getIV();

如果IV是显式确定的(例如使用SecureRandom),它必须传递给Cipher-示例,以便在运行加密时使用它。

// Assign IV to cipher so that it is used for current encryption
byte[] iv = ...
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
cipher.init(Cipher.ENCRYPT_MODE, secretkeySpec, ivParameterSpec);
  • 硬编码的密钥通常不是好的实践(测试目的除外)。但是,密钥生成/管理的主题超出了本答案的范围。已经有很多关于这个主题的问题和答案。如果这些答案没有涵盖您的问题,请发布一个新的问题。硬编码的IV不会出现在上述架构中,应该只用于测试目的。
4c8rllxm

4c8rllxm2#

如果它能帮助某些人,下面是我在dart中得到的代码(它使用encrypt包):

/// Decode the specified QR code encrypted string
static String decodeQrCode(String encryptedString) {
  try {
    // pad the encrypted base64 string with '=' characters until length matches a multiple of 4
    final int toPad = encryptedString.length % 4;
    if (toPad != 0) {
      encryptedString = encryptedString.padRight(encryptedString.length + toPad, "=");
    }

    // get first 16 bytes which is the initialization vector
    final iv = encrypt.IV(Uint8List.fromList(base64Decode(encryptedString).getRange(0, 16).toList()));

    // get cipher bytes (without initialization vector)
    final encrypt.Encrypted encrypted = encrypt.Encrypted(Uint8List.fromList(
        base64Decode(encryptedString).getRange(16, base64Decode(encryptedString).length).toList()));

    // decrypt the string using the key and the initialization vector
    final key = encrypt.Key.fromUtf8(YOUR_KEY);
    final encrypter = encrypt.Encrypter(encrypt.AES(key, mode: encrypt.AESMode.cbc, padding: "PKCS7"));
    return encrypter.decrypt(encrypted, iv: iv);
  } catch (e) {
    _log.severe("Error while decoding QR code : $e");
    return null;
  }
}

相关问题