互联网时代,网络上的数据量每天都在以惊人的速度增长。同时,各类网络安全问题层出不穷。在信息安全重要性日益凸显的今天,作为一名开发者,需要加强对安全的认识,并通过技术手段增强服务的安全性。
crypto 模块是 nodejs 的核心模块之一,它提供了安全相关的功能,如摘要运算、加密、电子签名等。很多初学者对着长长的 API 列表,不知如何上手,因此它背后涉及了大量安全领域的知识。
本文重点讲解 API 背后的理论知识,主要包括如下内容:
本文摘录自《Nodejs 学习笔记》,更多章节及更新,请访问 github 主页地址。
摘要(digest):将长度不固定的消息作为输入,通过运行 hash 函数,生成固定长度的输出,这段输出就叫做摘要。通常用来验证消息完整、未被篡改。
摘要运算是不可逆的。也就是说,输入固定的情况下,产生固定的输出。但知道输出的情况下,无法反推出输入。
伪代码如下。
digest = Hash(message)
常见的摘要算法 与 对应的输出位数如下:
nodejs 中的例子:
- var crypto =require('crypto');
- var md5 = crypto.createHash('md5');
- var message ='hello';
- var digest = md5.update(message,'utf8').digest('hex');
- console.log(digest);
- // 输出如下:注意这里是16进制
- // 5d41402abc4b2a76b9719d911017c592
备注:在各类文章或文献中,摘要、hash、散列 这几个词经常会混用,导致不少初学者看了一脸懵逼,其实大部分时候指的都是一回事,记住上面对摘要的定义就好了。
MAC(Message Authentication Code):消息认证码,用以保证数据的完整性。运算结果取决于消息本身、秘钥。
MAC 可以有多种不同的实现方式,比如 HMAC。
HMAC(Hash-based Message Authentication Code):可以粗略地理解为带秘钥的 hash 函数。
nodejs 例子如下:
- const crypto =require('crypto');
- // 参数一:摘要函数
- // 参数二:秘钥
- let hmac = crypto.createHmac('md5','123456');
- let ret = hmac.update('hello').digest('hex');
- console.log(ret);
- // 9c699d7af73a49247a239cb0dd2f8139
加密 / 解密:给定明文,通过一定的算法,产生加密后的密文,这个过程叫加密。反过来就是解密。
encryptedText = encrypt(plainText)
plainText = decrypt(encryptedText)
秘钥:为了进一步增强加 / 解密算法的安全性,在加 / 解密的过程中引入了秘钥。秘钥可以视为加 / 解密算法的参数,在已知密文的情况下,如果不知道解密所用的秘钥,则无法将密文解开。
encryptedText = encrypt(plainText, encryptKey)
plainText = decrypt(encryptedText, decryptKey)
根据加密、解密所用的秘钥是否相同,可以将加密算法分为对称加密、非对称加密。
加密、解密所用的秘钥是相同的,即
。
- encryptKey === decryptKey
常见的对称加密算法:DES、3DES、AES、Blowfish、RC5、IDEA。
加、解密伪代码:
encryptedText = encrypt(plainText, key); // 加密
plainText = decrypt(encryptedText, key); // 解密
又称公开秘钥加密。加密、解密所用的秘钥是不同的,即
。
- encryptKey !== decryptKey
加密秘钥公开,称为公钥。解密秘钥保密,称为秘钥。
常见的非对称加密算法:RSA、DSA、ElGamal。
加、解密伪代码:
encryptedText = encrypt(plainText, publicKey); // 加密
plainText = decrypt(encryptedText, priviteKey); // 解密
除了秘钥的差异,还有运算速度上的差异。通常来说:
两者可以结合起来使用,比如 HTTPS 协议,可以在握手阶段,通过 RSA 来交换生成对称秘钥。在之后的通讯阶段,可以使用对称加密算法对数据进行加密,秘钥则是握手阶段生成的。
备注:对称秘钥交换不一定通过 RSA,还可以通过类似 DH 来完成,这里不展开。
从签名大致可以猜到数字签名的用途。主要作用如下:
为了达到上述目的,需要有两个过程:
附:签名伪代码
digest = hash(message); // 计算摘要
digitalSignature = sign(digest, priviteKey); // 计算数字签名
附:签名验证伪代码
digest1 = verify(digitalSignature, publicKey); // 获取摘要
digest2 = hash(message); // 计算原始信息的摘要
digest1 === digest2 // 验证是否相等
由于 RSA 算法的特殊性,加密 / 解密、签名 / 验证 看上去特别像,很多同学都很容易混淆。先记住下面结论,后面有时间再详细介绍。
常见的对称加密算法,如 AES、DES 都采用了分组加密模式。这其中,有三个关键的概念需要掌握:模式、填充、初始化向量。
搞清楚这三点,才会知道 crypto 模块对称加密 API 的参数代表什么含义,出了错知道如何去排查。
所谓的分组加密,就是将(较长的)明文拆分成固定长度的块,然后对拆分的块按照特定的模式进行加密。
常见的分组加密模式有:ECB(不安全)、CBC(最常用)、CFB、OFB、CTR 等。
以最简单的 ECB 为例,先将消息拆分成等分的模块,然后利用秘钥进行加密。
- 01--if lth mod k = k-1
- 0202--if lth mod k = k-2
- .
- .
- .
- k k ... k k --if lth mod k =0
来源: https://segmentfault.com/a/1190000012677632