[转]CryptoJS 使用

发布时间 2023-12-16 00:09:45作者: dirgo

原文地址:CryptoJS 使用 - 简书

GitHub: https://github.com/brix/crypto-js
文档:https://cryptojs.gitbook.io/docs/
中文版:https://yztldxdzhu.github.io/2019/07/23/cryptojs%E5%B0%8F%E8%AE%B0/


Hash

具体使用请参考官方文档

所有经过哈希算法之后,得到的都是一个 WordArray 对象,调用 toString 转化为字符串时,默认转成16进制的字符串,也可指定字符串的格式。

使用哈希算法时,传入的参数可以是 String 类型,或者是 CryptoJS.lib.WordArray 实例。当传入的是一个 String 时,会自动转换为一个以 Utf8 编码的 WordArray 对象

let md5 = CryptoJS.MD5('12345')
// 等同于
let md5 = CryptoJS.MD5(CryptoJS.enc.Utf8.parse('12345'))

 

Cipher

解密之后得到的是 WordArray 对象。

加密之后得到的是 CipherParams 对象,可以从中读取 key, iv, salt, ciphertext,可以调用 toString 方法得到对应的字符串,默认是OpenSSL兼容格式,也可指定字符格式。

加/解密时使用的 key, iv, salt 都是 WordArray 对象

 

Cipher输入

对于明文消息,cipher算法接受字符串或CryptoJS.lib.WordArray的实例。

对于密钥(key),当您传递字符串时,它被视为密码并用于派生实际密钥(key)和IV。 或者,您可以传递表示实际密钥(key)的WordArray如果传递实际密钥(key),则还必须传递实际的IV。(即都传 WordArray 对象)

对于密文,cipher算法接受字符串或CryptoJS.lib.CipherParams的实例。CipherParams 对象表示一组参数,例如IVsalt和原始密文本身。
传递字符串时,它会根据可配置的格式策略自动转换为CipherParams对象。

 

个人理解:

加密时,如果密钥(key)直接使用字符串,加密算法会内部根据密钥自动生成实际使用的密钥(WordArray对象),并生成 ivsalt,而生成 ivsalt又会用到一些随机的算法,这样就导致每次加密出来的密文是不一样的,而且必须保留加密过程中产生的 ivsalt,只有使用它们才能解密。

如果密钥使用 WordArray 对象,那么 iv 也必须是 WordArray 对象,这样加密出来的内容就是固定的,同时,指定 iv 时,加密算法便不再自动生成 ivsalt,所以在指定 iv 时,加密后得到的 CipherParams 对象里没有 salt(除非在加密是也指定了 salt)。

另外,如果密钥使用了字符串,也指定了 iv,此时的 iv 是没有用的,每次生成的密文还是不一样的。

 

Format

加密之后输出的是一个 CipherParams 对象,其中包含了 key, iv, salt, ciphertext,要想获取密文,需要取出 ciphertext 并调用 toString 方法,也可以直接在 CipherParams 对象上调用 toString 方法,但他们得到的值是不一样的。

如果我们想格式化输入的密文的话,就需要指定 format 参数,定义一个 format 对象,里面包含两个方法,stringifyparse

  • stringify:调用加密算法之后,得到 CipherParams 对象,在此对象上调用 toString 方法时,会触发 format 中的 stringify 方法,同时把 CipherParams 对象作为参数传入,取出其中的 ciphertext 对象(也是一个 WordArray),调用它的 toString 方法,同时传入自己需要的编码格式(CryptoJS.enc.Utf8CryptoJS.enc.HexCryptoJS.enc.Base64 等),即可得到对应的密文。对于 ivsalt 也是同样的操作。
  • parse:解密时,当第一个参数是一个字符串时,才会调用到 parse 方法,如果使用 CipherParams 对象则不会触发,使用 parse 的主要目的是为了配合 stringify 方法,在 parse 方法中,解析 stringify 方法产生的字符串,得到对应的 ciphertextivsalt,创建一个 CryptoJS.lib.CipherParams 实例并返回。



对于解密,有两种方式,一种是 decrypt 方法的第一个参数传入字符串,此时会触发 format 中的 parse 方法(如果配置了 format),逻辑见上文;
第二种是直接生成一个 CipherParams 对象,作为第一个参数,这样不会触发parse 方法,但同样需要传入 iv 等参数



例:

// Formatter
let CryptoJSFormat = {
  /**
   * 加密后得到的 CipherParams 对象,调用 toString 时会调用这个方法
   */
  stringify: function(cipherParams) {
    console.log('CryptoJSAESFormat stringify ----------- ')
    // 加密结束,得到 CipherParams 对象,获取其中需要的数据,进行格式化
    var result = {
      ct: cipherParams.ciphertext.toString(CryptoJS.enc.Base64) // 将密文转换成 Base64 格式字符串
    };

    if (cipherParams.iv) {
      result.iv = cipherParams.iv.toString(); // 默认转换成16进制字符串
    }

    if (cipherParams.salt) {
      result.salt = cipherParams.salt.toString();
    }

    return JSON.stringify(result);
  },

  /**
   * 解密
   * 在解密开始时便调用,将数据解析并生成 CipherParams 对象
   */
  parse: function(jsonStr) {
    let jsonObj = JSON.parse(jsonStr);

    // 获取密文并创建 CipherParams 对象
    let cipherParams = CryptoJS.lib.CipherParams.create({
      ciphertext: CryptoJS.enc.Base64.parse(jsonObj.ct), // 将密文字符串转换成 WordArray 对象
    })

    // 如果有 iv 和 salt ,获取并转换成 WordArray 对象
    if (jsonObj.iv) {
      cipherParams.iv = CryptoJS.enc.Hex.parse(jsonObj.iv); // 16 进制字符串转换 WordArray 对象
    }

    if (jsonObj.salt) {
      cipherParams.salt = CryptoJS.enc.Hex.parse(jsonObj.salt); // 16 进制字符串转换 WordArray 对象
    }

    return cipherParams;
  }
};



使用:

/**
 * AES 加密
 * @param plaintext 明文字符串
 */
export const AES_Encrypt = (plaintext) => {

  let ciphertext = CryptoJS.AES.encrypt(plaintext, kPassphrase, {
    mode: CryptoJS.mode.CFB, // mode 和 padding 的默认值分别为 CBC 和 Pkcs7,加解密时需要保持一致
    padding: CryptoJS.pad.AnsiX923,
    format: CryptoJSAESFormat
  }).toString();
  // console.log(ciphertext);

  return ciphertext;
}

/**
 * AES 解密
 * @param jsonStr
 */
export const AES_Decrypt = (jsonStr) => {
  let plaintext = CryptoJS.AES.decrypt(jsonStr, kPassphrase, {
    mode: CryptoJS.mode.CFB, // mode 和 padding 的默认值分别为 CBC 和 Pkcs7,加解密时需要保持一致
    padding: CryptoJS.pad.AnsiX923,
    format: CryptoJSAESFormat
  }).toString(CryptoJS.enc.Utf8);

  return plaintext;
}

上而的例子中,使用的是字符串密钥,在加密过程中会自动生成真正的密钥和 ivsalt,每次加密出来的密文是不一样的。

 

test10() {
    // 加密

    const kPassphrase = "pass";
    const ivStr = '123abc'
    let pass = 'superman'

    let key = CryptoJS.enc.Utf8.parse(kPassphrase)
    let iv = CryptoJS.enc.Utf8.parse(ivStr)

    let c = CryptoJS.AES.encrypt(pass, key, {
      iv: iv,
    }).ciphertext.toString(CryptoJS.enc.Base64)

    console.log(c)

    // 解密
    let cipherParams = CryptoJS.lib.CipherParams.create({
      ciphertext: CryptoJS.enc.Base64.parse(c),
    })
    let result = CryptoJS.AES.decrypt(cipherParams, key, {
      iv: iv,
    })
    console.log(result.toString(CryptoJS.enc.Utf8))
  }

key使用 WordArray 对象,此时 iv 也必须使用 WordArray 对象。

 

Encode

对于 Encode 方法的使用,如果密钥是一个 Utf8 的字符串,就使用 Utf8 的方法来转化 WordArray 对象,如果是一个 16 进制的字符串,就用 Hex 的方法来转化,总之,使用哪个方法取决于字符串是哪种。

let keyStr = 'test2018'
let ivStr = '1234567890abcdef'
let key = CryptoJS.enc.Utf8.parse(keyStr) // 以 utf8 的格式,转化为 WordArray 对象
let iv = CryptoJS.enc.Hex.parse(ivStr) // 以 16进制 的格式,转化为 WordArray 对象

 

项目中使用

在小程序中使用时,不能使用 npm 方式,安装和构建npm 都可以正常进行,但是在引入到 .js 文件中使用时,会报错。所以还是直接在 GitHub 上下载 release 的包,将文件引入到小程序项目中。

 
Snip20200106_1.png



在 Vue 项目中,直接使用 npm 安装,在文件中 import 引入即可使用

 

参考:
AES Wiki



作者:XiaoWhite
链接:https://www.jianshu.com/p/0689506403e7
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。