移动端和后端加解密

背景

最近做一个加解密的需求,虽然用的标准很老,但是还是被坑到了。

前后端加解密竟然不统一

前端

https://github.com/brix/crypto-js

前端加解密框架的选择较少,我看下来 能用的只有 crypto-js.

并且文档极少,官网实例都没写全,完全要找源代码才能调用。Github 13K star,疯了真的是。

ECB/Pkcs7 实例

const encryptMessage = (msg:string) => {
  let keyHex = CryptoJS.enc.Utf8.parse('secret_key');
  console.log(keyHex)
  return CryptoJS.DES
      .encrypt(msg, keyHex, {
        mode: CryptoJS.mode.ECB,
        padding: CryptoJS.pad.Pkcs7
      })
      .toString()
}

注意,key 非常关键,如果你传入的是简单的字符串,crypto-js 会计算密钥而不是用这个字符串,这会造成一个问题是 每次加密的结果都是不稳定的。

所以需要把这个 key 转换成 bytes

mode和padding选择也非常坑,最好选择 ECB,padding 不支持 Pkcs5。

而且最好显式的声明出来。

后端

此处用 Java 示例
Bouncy Castle: https://github.com/bcgit/bc-java

Java 这边也是比较坑,刚好 不支持 Pkcs7,即使导入包了,原生的 provider 加密后的结果仍然不匹配。

所以必须要用BC的实现。

ECB/PKCS7Padding 实例

static byte[] desEncrypt(String content, String key) throws InvalidKeyException, NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, NoSuchProviderException, IllegalBlockSizeException, BadPaddingException {
    DESKeySpec desKeySpec = new DESKeySpec(key.getBytes());
    SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("DES");
    SecretKey secretKey = secretKeyFactory.generateSecret(desKeySpec);

    Cipher cipher = Cipher.getInstance("DES/ECB/PKCS7Padding", "BC");
    cipher.init(Cipher.ENCRYPT_MODE, secretKey);
    return cipher.doFinal(content.getBytes(StandardCharsets.UTF_8));
}

static byte[] desDecrypt(byte[] content, String key) throws InvalidKeyException, NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, NoSuchProviderException, IllegalBlockSizeException, BadPaddingException  {
    DESKeySpec desKeySpec = new DESKeySpec(key.getBytes());
    SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("DES");
    SecretKey secretKey = secretKeyFactory.generateSecret(desKeySpec);

    Cipher cipher = Cipher.getInstance("DES/ECB/PKCS7Padding", "BC");
    cipher.init(Cipher.DECRYPT_MODE, secretKey);
    return cipher.doFinal(content);
}

public static void main(String[] args) {

    // https://zhuanlan.zhihu.com/p/40770583
    Security.addProvider(new BouncyCastleProvider());

    String key = "secret_key";
    String encryptText = "encryptText";

    try {

        byte[] encrypt = desEncrypt(encryptText, key);
        System.out.printf("encrypt: %s %n", Base64.encode(encrypt));

        byte[] decrypt = desDecrypt(encrypt, key);
        System.out.printf("decrypt: %s %n", new String(decrypt));


    } catch (NoSuchAlgorithmException | InvalidKeyException | NoSuchPaddingException |
             BadPaddingException | NoSuchProviderException | IllegalBlockSizeException | InvalidKeySpecException e) {
        throw new RuntimeException("encrypt failed", e);
    }

}

reference