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

时间:2019-09-11 15:06:00

标签: java encryption dart aes pkcs#7

我需要在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

您会看到只有一部分字符串被解码。

2 个答案:

答案 0 :(得分:2)

  • 必须考虑两点:

    1)对于解密,需要用于加密的IV。

    2)出于安​​全原因,必须为每种加密随机生成一个新的IV,以便同一密钥here不能多次使用IV。

    因此,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实例进行隐式生成,或者例如通过显式生成。通过SecureRandomhere讨论了这两种选择。如果IV是通过Cipher实例隐式生成的,则此IV必须通过Cipher实例确定,因为以后需要解密:

    // Determine IV from cipher for later decryption
    byte[] iv = cipher.getParameters().getParameterSpec(IvParameterSpec.class).getIV();
    

    如果明确确定了IV(例如,使用SecureRandom),则必须将其传递到Cipher实例,以便将其用于正在运行的加密中。这是通过IvParameterSpec完成的。

    // 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不会在上述架构内出现,而应仅用于测试目的。


答案 1 :(得分:0)

如果可以帮助某人,这是我最后用dart编写的代码(它使用#include <stdio.h> __global__ void fetch(cudaTextureObject_t tex, std::size_t width, std::size_t height) { for (int j = 0; j < height; j++) { for (int i = 0; i < width; i++) { float u = (i + 0.5f) / width; float v = (j + 0.5f) / height; auto p = tex2D<uchar4>(tex, u, v); printf("i=%d, j=%d -> u=%3.2f, v=%3.2f, r=%d, g=%d, b=%d, a=%d\n", i, j, u, v, p.x, p.y, p.z, p.w); // -> always returns p = {0, 0, 0, 0} } } } int main() { constexpr std::size_t width = 2; constexpr std::size_t height = 2; // creating a dummy texture uchar4 image[width*height]; for(std::size_t j = 0; j < height; ++j) { for(std::size_t i = 0; i < width; ++i) image[j*width+i] = make_uchar4(255*j/height, 255*i/width, 55, 255); } cudaArray_t cuArray; auto channelDesc = cudaCreateChannelDesc<uchar4>(); cudaMallocArray(&cuArray, &channelDesc, width, height); cudaMemcpy2DToArray(cuArray, 0, 0, image, width*sizeof(uchar4), width*sizeof(uchar4), height, cudaMemcpyHostToDevice); struct cudaResourceDesc resDesc; memset(&resDesc, 0, sizeof(resDesc)); resDesc.resType = cudaResourceTypeArray; resDesc.res.array.array = cuArray; struct cudaTextureDesc texDesc; memset(&texDesc, 0, sizeof(texDesc)); texDesc.addressMode[0] = cudaAddressModeBorder; texDesc.addressMode[1] = cudaAddressModeBorder; texDesc.filterMode = cudaFilterModeLinear; texDesc.readMode = cudaReadModeElementType; texDesc.normalizedCoords = 1; cudaTextureObject_t texObj = 0; cudaCreateTextureObject(&texObj, &resDesc, &texDesc, NULL); fetch<<<1, 1>>>(texObj, width, height); cudaDeviceSynchronize(); cudaDestroyTextureObject(texObj); cudaFreeArray(cuArray); return 0; } 包):

encrypt