当今互联网世界中,数据安全是至关重要的。为了保护用户的隐私和保密信息,开发人员必须采取适当的安全措施。在前端开发中,加密和解密技术是一种常见的数据安全措施,本文将介绍几种最常用的 JavaScript 加密和解密方法。

友情提示:本文借助 ChatGPT 和 NewBing 辅助撰写,部分技术细节可能存在错误之处,使用具体某个库时请查看官方文档,本文所有代码仅为示例展示。

加密算法

加密算法分为对称加密算法和非对称加密算法两种。

  • 对称加密算法采用对称密码编码技术,加/解密使用相同密钥进行,效率较高。常见的对称加密算法有AES、DES、3DES等。

  • 非对称加密基于密钥交换协议,拥有公开密钥和私有密钥,使用公钥加密后需使用对应私钥才能进行解密。常见的非对称加密算法有RSA、DSA和ECC等。

前端常用加解密技术

Base64 加解密

Base64 是一种常用的加密技术,可以将任意类型的数据编码为 ASCII 字符,以便在不同系统之间安全地传输数据。JavaScript 中提供了内置的 btoa()atob() 方法,可用于将数据编码为 Base64 或从 Base64 解码。

简单示例

下面是使用这两个方法进行 Base64 加解密的示例代码:

1
2
3
4
5
6
7
8
// 加密
let message = "Hello World!";
let encoded = btoa(message);
console.log(encoded); // "SGVsbG8gV29ybGQh"

// 解密
let decoded = atob(encoded);
console.log(decoded); // "Hello World!"

需要注意的是,btoa() 方法和 atob() 方法只支持 ASCII 码范围内的字符串进行加解密,如果要处理含有非 ASCII 字符的字符串(如中文等),则需要使用一些第三方库或自己实现 Base64 算法。比如,直接使用 btoa() 加密中文内容,会报错 DOMException: Failed to execute 'btoa' on 'Window': The string to be encoded contains characters outside of the Latin1 range.

实现中文加解密

这时我们可以结合 encodeURIComponent()decodeURIComponent() 可以处理一些 Unicode 字符(如中文等),但这种方法不是真正的 Base64 加密解密,只是一种 hack 做法。具体来说,使用 encodeURIComponent() 方法将字符串转换成可被 URL 安全传输的编码字符串,然后对编码后的字符串进行 btoa() 方法的 Base64 编码。解密时先使用 atob() 方法进行 Base64 解码,然后再使用 decodeURIComponent() 方法对解码后的字符串进行 URL 解码,得到原始的 Unicode 字符串。下面是使用 encodeURIComponent()decodeURIComponent() 结合 btoa()atob() 实现 Base64 加解密的示例代码:

1
2
3
4
5
6
7
8
// 加密
let message = "你好!";
let encoded = btoa(encodeURIComponent(message));
console.log(encoded); // "JUU0JUJEJUEwJUU1JUE1JUJEJUVGJUJDJTgx"

// 解密
let decoded = decodeURIComponent(atob(encoded));
console.log(decoded); // "你好!"

其实,上述使用 encodeURIComponent()你好! 转换得到的是 URL 编码字符串 %E4%BD%A0%E5%A5%BD%EF%BC%81,然后对该字符串进行了 btoa() 方法的 Base64 编码,解码同理,所以本质上并不是直接对中文字符串进行 Base64 加密解密。

使用第三方 Base64 库

btoa()atob() 方法只能处理 ASCII 码范围内的字符,对于 Unicode 码范围内的字符(如中文等)则不能正确处理。

如果要处理中文内容,可以使用一些第三方的 Base64 库,例如 js-base64base64-js 等。下面是使用 js-base64 库进行 Base64 编解码的示例代码:

  1. 安装 js-base64 库:
1
npm install js-base64
  1. 在代码中引入 js-base64 库:
1
import { Base64 } from 'js-base64';
  1. 使用 js-base64 进行 Base64 编解码:
1
2
3
4
5
6
7
8
// Base64 加密
const plainText = "你好,ChatAi!";
const encodedText = Base64.encode(plainText);
console.log(encodedText); // "5L2g5aW977yMQUIh"

// Base64 解密
const decodedText = Base64.decode(encodedText);
console.log(decodedText); // "你好,ChatAi!"

在上面的示例中,我们首先引入了 js-base64 库,然后分别使用 Base64.encode() 方法进行 Base64 加密,使用 Base64.decode() 方法进行 Base64 解密。这些方法可以正确处理中文等 Unicode 字符。

MD5 加密(不可逆)

MD5 是一种不可逆的哈希函数。所谓不可逆,是在加密过程中,将原始数据按照特定的规则运算后生成一段固定长度的摘要信息(也叫作哈希值,通常是一个 32 位的字符串),并且是不可逆推的。也就是说,给定一个 MD5 加密后的结果,无法根据该结果还原出原始数据。因为 MD5 加密算法是单向的,它将明文通过哈希函数转换为哈希值,而在哈希值上是无法推算出原数据的。

MD5 加密主要用于生成数据的哈希值,以便验证数据的完整性和唯一性,以及对密码等敏感信息进行加密存储,通常用于密码验证和防篡改。但是,由于现在的计算机速度越来越快,MD5 的加密强度也越来越弱,因此在实际应用中,可能需要结合其他安全机制来保障数据的安全性。

JavaScript 中没有内置的 MD5 方法,前端可以借助第三方 JavaScript 库来实现 MD5 加密功能,常用的有 CryptoJS、SparkMD5、md5.js 等。

CryptoJS 库

这里以 CryptoJS 为例,介绍如何使用 JavaScript 实现 MD5 加密。

  1. 下载库文件并引入:

    1
    2
    3
    4
    // 引入 CryptoJS 库
    // npm install crypto-js
    // const CryptoJS = require("crypto-js");
    <script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.9-1/crypto-js.min.js"></script>
  2. 调用 CryptoJS.MD5() 方法进行加密:

    1
    2
    3
    4
    // 加密
    let message = "Hello World!";
    let encrypted = CryptoJS.MD5(message).toString();
    console.log(encrypted); // "65a8e27d8879283831b664bd8b7f0ad4"
  3. 验证密码,一般交给后端:

    1
    2
    3
    4
    5
    // 验证密码
    let pass1word = "myPass1word";
    let hashedPassword = "c019b8a60ee6ce9b7a36f655e026d7c2";
    let isValid = CryptoJS.MD5(password).toString() === hashedPassword;
    console.log(isValid); // true

在上面的示例中,我们首先引入 CryptoJS 库,然后使用 CryptoJS.MD5() 方法对明文字符串进行 MD5 加密,得到加密后的字符串 md5Hash。需要注意的是,CryptoJS.MD5() 返回的是一个 WordArray 对象,需要使用 toString() 方法将其转换为字符串。

此外,CryptoJS 还提供了一系列其他的哈希函数,比如 SHA1、SHA256 等,可以根据实际需求选择使用。详情可查看官方文档 :CryptoJS (gitbook.io)

md5.js 库

md5.js 是另外一个常用的 JavaScript 库,可以在前端实现 MD5 加密功能。下面介绍如何使用 md5.js 库进行 MD5 加密。

  1. 下载库文件并引入:

    1
    2
    3
    // 引入 md5.js 库
    // npm install blueimp-md5
    <script src="https://cdn.bootcdn.net/ajax/libs/blueimp-md5/2.18.0/js/md5.min.js"></script>
  2. 调用 md5() 方法进行加密:

    1
    2
    3
    const plainText = 'Hello, ChatAi!';
    const md5Hash = md5(plainText);
    console.log(md5Hash); // "5df8866adfdec2eea5d34cb5ba326e4a"

在上面的示例中,我们首先引入 md5.js 库,然后使用 md5() 方法对明文字符串进行 MD5 加密,得到加密后的字符串 md5Hash。需要注意的是,md5.js 中的 md5() 方法与 CryptoJS.MD5() 方法一样,都只能对字符串进行 MD5 加密,如果需要处理二进制数据或文件,需要使用专门的 MD5 库。

SparkMD5 库

SparkMD5 是另一个比较常用的 JavaScript 库,可以在前端实现 MD5 加密功能,并且具有较好的加密速度。下面介绍如何使用 SparkMD5 库进行 MD5 加密。

  1. 下载库文件并引入:

    1
    2
    3
    // 引入 SparkMD5 库
    // npm install spark-md5
    <script src="https://cdn.bootcdn.net/ajax/libs/spark-md5/3.0.0/spark-md5.min.js"></script>
  2. 调用 sparkMD5.hash() 方法进行加密:

    1
    2
    3
    const plainText = 'Hello, ChatAi!';
    const md5Hash = sparkMD5.hash(plainText);
    console.log(md5Hash); // "5df8866adfdec2eea5d34cb5ba326e4a"

在上面的示例中,我们首先引入 SparkMD5 库,然后使用 sparkMD5.hash() 方法对明文字符串进行 MD5 加密,得到加密后的字符串 md5Hash

实际上,SparkMD5 库是支持处理文件的,官方文档中有提到“Hash a file incrementally”。下面是使用 SparkMD5 库进行文件 MD5 加密的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const fileInput = document.querySelector('#fileInput');
const reader = new FileReader();
const spark = new SparkMD5.ArrayBuffer();

fileInput.addEventListener('change', () => {
const file = fileInput.files[0];

reader.readAsArrayBuffer(file);
reader.onload = (e) => {
spark.append(e.target.result);
const md5Hash = spark.end();
console.log(md5Hash); // "5df8866adfdec2eea5d34cb5ba326e4a"
}
});

在上面的示例中,我们使用 FileReader 对象读取文件内容,并使用 SparkMD5.ArrayBuffer() 创建一个 SparkMD5 实例。然后在加载完成文件内容后,通过调用 append() 方法将文件内容传递给 SparkMD5 实例进行处理,最后调用 end() 方法得到加密后的字符串。

需要注意的是,这里使用 ArrayBuffer 作为中间数据格式进行处理,而不是直接传递文件的二进制数据或 Blob 对象。这是因为在 JavaScript 中读取文件通常采用的是异步方式,如果直接传递二进制数据或 Blob 对象,可能会导致加密结果不正确或文件未完整读取等问题。因此需要先将文件内容读取到 ArrayBuffer 中,在进行 MD5 加密处理。

SHA1 加密(不可逆)

SHA1(Secure Hash Algorithm 1)是一种密码散列函数,美国国家安全局设计,并由美国国家标准技术研究所(NIST)发布为联邦资料处理标准(FIPS)。SHA-1可以生成一个被称为消息摘要的160位(20字节)散列值,散列值通常的呈现形式为40个十六进制数。SHA-1是一种不可逆的加密算法,目前是最牢靠的加密算法之一。

前端可以借助第三方 JavaScript 库来实现 SHA1 加密功能,常用的有 CryptoJS、sjcl 等。

CryptoJS 库

这里以 CryptoJS 为例,介绍如何使用 JavaScript 实现 SHA1 加密。

  1. 下载库文件并引入:

    1
    2
    3
    4
    // 引入 CryptoJS 库
    // npm install crypto-js
    // const CryptoJS = require("crypto-js");
    <script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.9-1/crypto-js.min.js"></script>
  2. 调用 CryptoJS.SHA1() 方法进行加密:

    1
    2
    3
    const plainText = 'Hello, ChatAi!';
    const sha1Hash = CryptoJS.SHA1(plainText).toString();
    console.log(sha1Hash); // "71c8b8f69f15466c8217eeda4c52fa5275e932cb"

在上面的示例中,我们首先引入 CryptoJS 库,然后使用 CryptoJS.SHA1() 方法对明文字符串进行 SHA1 加密,得到加密后的字符串 sha1Hash。需要注意的是,CryptoJS.SHA1() 返回的是一个 WordArray 对象,需要使用 toString() 方法将其转换为字符串。

此外,CryptoJS 还提供了一系列其他的哈希函数,比如 SHA256、MD5 等,可以根据实际需求选择使用。

其他库

你还可以使用 jsSHA 库进行SHA1加密。jsSHA是一个用 JS+TS 实现完整 SHA 系列加密算法的加密库,包括:SHA1; SHA-224/256/384/512; SHA3-224/256/384/512; SHAKE128/256; cSHAKE128/256; KMAC128/256。

你也可以使用 SJCL 库进行SHA1加密。SJCL是斯坦福大学计算机安全实验室创立的项目,旨在创建一个安全、快速、短小精悍、易使用、跨浏览器的JavaScript加密库。

AES/DES 加解密

AES(Advanced Encryption Standard)和DES(Data Encryption Standard)都是对称加密算法,其中AES是一种更安全的加密算法。

DES全称为Data Encryption Standard,即数据加密标准,是一种使用密钥加密的块算法,1977年被美国联邦政府的国家标准局确定为联邦资料处理标准(FIPS),并授权在非密级政府通信中使用,随后该算法在国际上广泛流传开来。需要注意的是,在某些文献中,作为算法的DES称为数据加密算法(Data Encryption Algorithm,DEA),已与作为标准的DES区分开来。DES 这种古老的算法还是作为简单的加密算法在被使用,因为它已经是可以破解的,所以对于安全性有较高要求的应用场景基本还是使用的RSA

CryptoJS 库

你可以使用 CryptoJS 库进行 AES/DES 加解密。CryptoJS是一个纯JavaScript编写的加密库,支持 AES、DES、Triple DES、Rabbit、RC4 等常见的加密算法,同时还支持 MD5、SHA1、SHA2、SHA3、RIPEMD-160 等哈希算法。

这里是一个使用CryptoJS进行AES加解密的例子:https://juejin.cn/post/7074863196959408158

这里是一个使用CryptoJS进行DES加解密的例子:https://blog.csdn.net/qq_21271511/article/details/110432602

使用总结

时代在不断进步,计算机技术也在不断发展,很多加解密技术也随着时间的浪潮逐渐变得没有发明之初那么安全。

比如 MD5 加密,理论上来说,MD5加密是不可逆的,但是它并不是绝对安全的。由于MD5算法输出长度固定为128位,而输入长度是不确定的,因此理论上存在多个不同的输入对应相同的输出,这种现象被称为碰撞。攻击者可以通过构造特定的输入来产生与目标输出相同的MD5值,从而破解MD5加密。

此外,攻击者还可以使用彩虹表攻击来破解MD5加密。彩虹表是一种预先计算好的数据表,它包含了大量常用密码和它们对应的MD5值。攻击者可以通过查找彩虹表来快速找到与目标MD5值对应的原始密码。

因此,尽管MD5加密理论上来说是不可逆的,但它并不是绝对安全的。在安全性要求较高的场合,建议使用更安全的哈希算法,如 SHA-256 或 SHA-3。


【2023-01-14】文章补充说明

加密、编码和哈希运算的概念区分

上月底,好厚米 @提莫酱 在QQ上对我这篇水文做了激烈的吐槽,今天我修正一下本文的部分知识性错误。

在本文中,我的描述有些混淆了加密编码哈希三种不同的概念。虽然它们在处理数据时有一些相似之处,但它们的目的和使用场景是不同的。

概念 目的/使用场景 可逆 补充
加密
(Encryption)
用于保护数据的安全性 这是一种可以逆转的过程,
意味着你可以从加密的数据中恢复原始数据,这通常需要一个密钥。
AES和RSA是加密算法的例子。
编码
(Encoding)
用于数据的传输或存储 这是一种可以逆转的过程,
用于将数据转换为另一种格式,通常用于数据的传输或存储。
Base64是一种编码方式。
哈希
(Hashing)
用于将任意长度的数据转换为固定长度的输出 这是一种不可逆的过程,
用于将任意长度的数据转换为固定长度的输出,会导致数据信息量的丢失。
哈希函数设计的目的是使得输出值在输入值发生微小变化时也会发生大的变化,这使得哈希函数在密码学中有很多应用,如数据完整性检查和密码存储。MD5和SHA系列是哈希函数的例子。

因此,将Base64编码和MD5哈希归类为加密方式是不准确的。它们都有各自的用途和场景,但不能提供加密所需的安全性。

RSA、AES两种加密算法的安全性

此外,本文在描述 RSA 和 AES 两种加密算法的安全性上有笼统和不准确的地方。

RSA和AES都是加密算法,但它们的工作方式和安全性有所不同。

加密算法 AES
(Advanced Encryption Standard)
RSA
Rivest、Shamir、Adleman三位数学家的缩写)
对称性 一种对称加密算法,它使用同一个密钥进行加密和解密 一种非对称加密算法,它使用两个密钥:一个公钥用于加密,一个私钥用于解密
安全性 AES的安全性主要取决于密钥的复杂性,而不是长度。因此,即使AES的密钥长度较短(例如256位),只要密钥足够复杂,它的安全性也可以与更长的RSA密钥相媲美。 主要取决于密钥的长度。一般来说,密钥越长,安全性越高。然而,RSA的计算复杂度也随着密钥长度的增加而增加,这可能会影响到性能。

因此,尽管RSA 1024位的密钥长度比AES 256位的密钥长度要长,但在实际的安全性上,AES 256可能更高。这是因为AES的加密过程更复杂,更难以破解。所以非对称加密并不一定比对称加密的加密强度高

补充总结,接受批评

因此,当我们在处理数据时,需要根据具体的需求和场景来选择合适的方法。

到底是使用可逆的编码方式、挑选可信的加解密算法,还是选择不可逆的哈希运算。我们不能简单地将所有的处理方法都归类为加密,因为这可能会导致我们在处理数据时忽视了一些重要的安全性考虑。

最后文末再次感谢好厚米 @提莫酱 喽,我想, 撰写发布、互动纠错、补充更正 这个流程,正是我当初搭建博客的目的之一。另外,以后再撰写公开文章的时候,我觉得需要进行更多一点的求知验证,以免再产出类似低质混乱的文章误人子弟。


【参考内容】

[1] 常见的js加密/js解密方法 - 掘金

[2] 前端常用的几种加密与解密 - CSDN博客

[3] 前端加密解密 && crypto-js - CSDN博客

[4] 前端js几种加密/解密方法 - 简书