前端 实战项目·图片加解密二三事

作者 : jamin 本文共3471个字,预计阅读时间需要9分钟 发布时间: 2020-10-18 共1141人阅读

图片加解密二三事

近来公司新项目管理后台需要做图片上传并加解密功能,加密在服务端进行,加密成功后返回加密后图片地址,后台负责解密在线图片然后预览,折腾一天,此中曲折,闲做记录。

高级加密标准 AES

高级加密标准(Advanced Encryption Standard: AES)是美国国家标准与技术研究院(NIST)在 2001 年建立了电子数据的加密规范。它是一种分组加密标准,每个加密块大小为 128 位,允许的密钥长度为 128、192 和 256 位。

AES 加密有 ECB、CBC、CFB 和 OFB 多种加密模式,各种模式功用各不同。

密码学中,分组(block)密码的工作模式(mode of operation)允许使用同一个分组密码密钥对多于一块的数据进行加密,并保证其安全性。分组密码自身只能加密长度等于密码分组长度的单块数据,若要加密变长数据,则数据必须先被划分为一些单独的密码块。通常而言,最后一块数据也需要使用合适填充方式将数据扩展到匹配密码块大小的长度。一种工作模式描述了加密每一数据块的过程,并常常使用基于一个通常称为初始化向量的附加输入值以进行随机化,以保证安全。

ECB 模式

ECB 模式(电子密码本模式:Electronic codebook)是最简单的块密码加密模式,加密前根据加密块大小(如 AES 为 128 位)分成若干块,之后将每块使用相同的密钥单独加密,解密同理。

ECB 模式最大的缺点是相同的明文块会被加密成相同的密文块,这种方法在某些环境下不能提供严格的数据保密性。

ECB 加密

ECB 解密

CBC 模式

CBC 模式(密码分组链接:Cipher-block chaining)对于每个待加密的密码块在加密前会先与前一个密码块的密文异或然后再用加密器加密。在这种方法中,每个密文块都依赖于它前面的所有明文块。同时,为了保证每条消息的唯一性,第一个明文块与一个叫 初始化向量 的数据块异或。

CBC 加密

CBC 解密

CBC 是最为常用的工作模式。CBC 模式相比 ECB 有更高的保密性,但由于对每个数据块的加密依赖与前一个数据块的加密所以加密无法并行。与 ECB 一样在加密前需要对数据进行填充,不是很适合对流数据进行加密。

加密时,明文中的微小改变会导致其后的全部密文块发生改变,而在解密时,从两个邻接的密文块中即可得到一个明文块。因此,解密过程可以被并行化,而解密时,密文中一位的改变只会导致其对应的明文块完全改变和下一个明文块中对应位发生改变,不会影响到其它明文的内容。

CFB 模式

CFB 模式(密文反馈:Cipher feedback)模式类似于 CBC,可以将块密码变为自同步的流密码;工作过程亦非常相似,CFB 的解密过程几乎就是颠倒的 CBC 的加密过程。

CFB 加密

CFB 解密

注意:CFB、OFB 和 CTR 模式中解密也都是用的加密器而非解密器。 CFB 的加密工作分为两部分:

  1. 将一前段加密得到的密文再加密;
  2. 将第 1 步加密得到的数据与当前段的明文异或。

由于加密流程和解密流程中被块加密器加密的数据是前一段密文,因此即使明文数据的长度不是加密块大小的整数倍也是不需要填充的,这保证了数据长度在加密前后是相同的。

与 CBC 相似,明文的改变会影响接下来所有的密文,因此加密过程不能并行化;而同样的,与 CBC 类似,解密过程是可以并行化的。在解密时,密文中一位数据的改变仅会影响两个明文块:对应明文块中的一位数据与下一块中全部的数据,而之后的数据将恢复正常。

OFB 模式

OFB 模式(输出反馈:Output feedback)是先用块加密器生成密钥流(Keystream),然后再将密钥流与明文流异或得到密文流,解密是先用块加密器生成密钥流,再将密钥流与密文流异或得到明文,由于异或操作的对称性所以加密和解密的流程是完全一样的。

OFB 加密

OFB 解密

每个使用 OFB 的输出块与其前面所有的输出块相关,因此不能并行化处理。然而,由于明文和密文只在最终的异或过程中使用,因此可以事先对 IV 进行加密,最后并行的将明文或密文进行并行的异或处理。

采坑

图片上传后服务端采用的是 AES-256-CBC 加密方式,故此后台也须采用同样的解密方式。通过创建 XMLHttpRequest 请求访问加密图片链接,并设置 responseType 为 arraybuffer 便可得到加密后的图片流,然后将流转换为 base64,采用 crypto-js 解密,将解密后的流重新转为 base64 图片。

总体过程如下:

  1. 创建 XMLHttpRequest 请求图片流;
  2. 将图片流 utf8 解码后再转换为 base64;
  3. 采用 crypto-js 解密;
  4. 将解密后的流转为 base64 图片。

在将图片流 utf8 解码时踩了坑,一开始 buffer 解码时采用如下方法:

let base64String = String.fromCharCode(...new Uint8Array(buffer))

报错 Uncaught RangeError: Maximum call stack size exceeded

搜索到 stackflow 同款问题:Converting arraybuffer to string : Maximum call stack size exceeded

The error is caused by a limitation in the number of function arguments.

如上所述,报错原因是由于函数参数过长导致。在该问题下找到解决方法:Uint8Array to string in Javascript

let base64String = new TextDecoder('utf-8').decode(buffer)

TextEncoder and TextDecoder from the Encoding standard, which is polyfilled by the stringencoding library, converts between strings and ArrayBuffers。

查询 MDN web docs 对 TextDecoder 文档说明:

The TextDecoder interface represents a decoder for a specific method, that is a specific character encoding, like utf-8, iso-8859-2, koi8, cp1261, gbk, etc. A decoder takes a stream of bytes as input and emits a stream of code points.

大功告成

完整图片解密代码如下:

const CryptoJS = require('crypto-js')

let key = '95362058623512345678901234567890'
let iv = '0473bd1234567890'

key = CryptoJS.enc.Utf8.parse(key)
iv = CryptoJS.enc.Utf8.parse(iv)

export function decrypt(url) {
  if (!url) return
  return new Promise(resolve => {
    let xhr = new XMLHttpRequest()
    xhr.open('GET', url, true)
    xhr.responseType = 'arraybuffer'
    xhr.setRequestHeader('Access-Control-Allow-Origin', '*')
    xhr.onload = () => {
      if (xhr.readyState === 4) {
        if (xhr.status === 200) {
          let base64Img = process(xhr.response)
          resolve(base64Img)
        }
      }
    }
    xhr.send()
  })
}

function process(buffer) {
  // 将 buffer 转换为base64
  let view = new TextDecoder('utf-8').decode(buffer)
  let base64String = view.toString(CryptoJS.enc.Base64)
  // 解密
  let decryptedData = CryptoJS.AES.decrypt(base64String, key, {
    iv: iv,
    mode: CryptoJS.mode.CBC,
    padding: CryptoJS.pad.Pkcs7
  })
  // 把解密后的对象再转为 base64 编码
  let d64 = decryptedData.toString(CryptoJS.enc.Base64)
  let imgSrc = 'data:image/png;base64,' + d64
  return imgSrc
}
本站所提供的部分资源来自于网络,版权争议与本站无关,版权归原创者所有!仅限用于学习和研究目的,不得将上述内容资源用于商业或者非法用途,否则,一切后果请用户自负。您必须在下载后的24个小时之内,从您的电脑中彻底删除上述内容资源。如果上述内容资对您的版权或者利益造成损害,请提供相应的资质证明,我们将于3个工作日内予以删除。本站不保证所提供下载的资源的准确性、安全性和完整性,源码仅供下载学习之用!如用于商业或者非法用途,与本站无关,一切后果请用户自负!本站也不承担用户因使用这些下载资源对自己和他人造成任何形式的损失或伤害。如有侵权、不妥之处,请联系站长以便删除!
金点网络 » 前端 实战项目·图片加解密二三事

常见问题FAQ

免费下载或者VIP会员专享资源能否直接商用?
本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。
是否提供免费更新服务?
持续更新,永久免费
是否经过安全检测?
安全无毒,放心食用

提供最优质的资源集合

立即加入 友好社区
×