1

使用crypto-js的角度代码:

let key = '12345123451234512345123451234509';// actual keys are different and has same length of 32 char
let iv = '12345123451234512345123451234509';

let secret_key = CryptoJS.enc.Hex.parse(key);
let secret_iv = CryptoJS.enc.Hex.parse(iv);
let encryptedString = CryptoJS.AES.encrypt(
    JSON.stringify(data),
    secret_key,
    {
      iv: secret_iv,
      padding: CryptoJS.pad.ZeroPadding
    }
 ).toString();

let requestObj = {
    input: encryptedString.trim()
  }

我无法在 android 中进行相同的加密。 安卓代码

String key32Char = "12345123451234512345123451234509";
String iv32Char = "12345123451234512345123451234509";
byte[] srcBuff = jsonString.getBytes("UTF-8");

//SecretKeySpec secretKeySpec = new SecretKeySpec(key32Char.getBytes(), "AES");
//IvParameterSpec ivParameterSpec = new IvParameterSpec(iv32Char.getBytes());

SecretKeySpec secretKeySpec = new SecretKeySpec(Base64.decode(key32Char, Base64.NO_WRAP), "AES");
IvParameterSpec ivParameterSpec = new IvParameterSpec(Base64.decode(iv32Char, Base64.NO_WRAP));

Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
byte[] dstBuff = cipher.doFinal(srcBuff);
String encryptedString = Base64.encodeToString(dstBuff, Base64.NO_WRAP);

JSONObject requestObj = new JSONObject();
requestObj.put("input", encryptedString);
  1. CryptoJS.enc.Hex.parse (key)行做什么?
  2. 如何进行相同的加密?
4

2 回答 2

1
  1. IV 和密钥:要匹配密钥和 IV 部分,都必须使用 base64 或十六进制解码。

    在十六进制编码字符串中,有 32 个十六进制字符构成 128 位。但是,base64 解码可以拒绝相同的字符串,如果没有拒绝,输出将不是 128 位。你需要使用

    byte[] bytes = new BigInteger("7F" + str, 16).toByteArray();
    SecretKeySpec key = new SecretKeySpec(bytes, 1, bytes.length-1, "AES");
    

    十六进制字符串转换为字节数组

  2. paddingCryptoJS.pad.ZeroPadding如果您的数据大小是 128 的精确倍数,则很有用。否则,您需要使用此参数来说明我将使用它来测试我的新填充方案。您需要更好地使用Pkcs7默认设置。

    在Java中你需要getInstance("AES/CBC/PKCS5Padding");

  3. 操作方式:JS中默认是CBC,所以Java中也一样,如上getInstance("AES/CBC/PKCS5Padding");

  4. 输出:要比较输出,您需要看到相同的结果。在 Java 中,您将输出转换为 base64,因此JS 需要相同的输出。

如您所见,您必须为两者执行相同的步骤和参数。

请注意: CBC 模式已经过时,您应该更喜欢经过身份验证的加密模式,如 AES-GCM 或 ChaCha20-Poly1305。它们不仅提供机密性,还提供完整性和身份验证。不幸的是,crypto-js 没有它们。但是您可以为此使用一些其他 JS 库

于 2019-11-28T20:40:08.650 回答
1

顾名思义,CryptoJS.enc.Hex.parse(key)解析一个十六进制字符串并将其用作密钥。所以你需要对你的java代码做同样的事情。

此外,您还需要选择正确的加密模式和填充。您的CryptoJs代码使用CBC模式,因此您需要在 Java 代码中执行相同操作。您在CryptoJs端使用零填充,这在 java 中不可用,因此您需要手动完成。但总的来说,使用零填充是一个坏主意,最好使用PKCS5填充,例如CryptoJs的默认值。

有了这些东西,这两个代码匹配:

let key = '12345123451234512345123451234509';// actual keys are different and has same length of 32 char
let iv = '12345123451234512345123451234509';

let secret_key = CryptoJS.enc.Hex.parse(key);
let secret_iv = CryptoJS.enc.Hex.parse(iv);
let encryptedString = CryptoJS.AES.encrypt(
    "0123456789012345x",
    secret_key,
    {
      iv: secret_iv,
    }
 ).toString();

let requestObj = {
    input: encryptedString.trim()
  }

爪哇:

  public void doit()
  {
    byte[] key32Char = hexStringToByteArray("12345123451234512345123451234509");
    byte[] iv32Char = hexStringToByteArray("12345123451234512345123451234509");
    byte[] srcBuff = "0123456789012345x".getBytes();

    SecretKeySpec secretKeySpec = new SecretKeySpec(key32Char, "AES");
    IvParameterSpec ivParameterSpec = new IvParameterSpec(iv32Char);
    try {
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
        byte[] dstBuff = cipher.doFinal(srcBuff);
        String encryptedString = new String(Base64.getEncoder().encode(dstBuff));
      System.out.print(encryptedString);
    }
    catch(Exception e) {
      System.out.print(e.toString());
      return;
    }

  }
  public byte[] hexStringToByteArray(String s) 
  {
    int len = s.length();
    byte[] data = new byte[len / 2];
    for (int i = 0; i < len; i += 2) {
        data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
                             + Character.digit(s.charAt(i+1), 16));
    }
    return data;
  }

更新:

如果您被迫使用零填充的坏主意,则需要保持数据的真实大小并手动进行填充:

public void doitZeroPadding()
{
   ...
   // For the simplicity, I assume that data size is smaller than 128. 
   // You need to change this part as needed.
   Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
   int dsize = srcBuff.length + 1; // 1 is for plain buffer size
   // This line align size to the multiple of block size.
   int newBufSize = ((dsize + cipher.getBlockSize() - 1) / cipher.getBlockSize()) * cipher.getBlockSize();
   byte[] newSrcBuf = new byte[newBufSize];
   // You need real buffer size, or you don't know how long is decrypted buffer.
   // I add it inside encrypting buffer to prevent other to see real decrypted buffer size.
   // But if you want to have exact same encrypted buffer on both sides, you must remove it.
   newSrcBuf[0] = (byte)(srcBuff.length);
   System.arraycopy(srcBuff, 0, newSrcBuf, 1, srcBuff.length);   
  // Now use newSrcBuf/newBufSize 
   ...
}

在解密方面,检查解密缓冲区的实际大小,并使用该大小的起始字节 1 来创建字符串。

于 2019-11-28T20:46:49.553 回答