赞
踩
使用消息摘要算法对消息体计算和验证摘要,可以防止消息传输过程中被篡改为非法消息值;使用加密算法加密消息体,可以防止消息传输过程中被拦截并读取。二者结合则可以实现较强的安全性消息交换。
消息传输过程中可能被中间人篡改。比如 A 发送消息转账给 B,中间人在消息转发过程中进行了拦截,解密消息体并篡改为转账给 C,而且篡改转账额度,由此造成可能不可逆转的损失。
消息摘要可以保证消息传输的正确性。主要思路比较简单:对要发送对消息体使用消息摘要算法(如 md5、sha256、sha1、hashMAC 等)计算出摘要值,接收方收到消息后,使用相同的摘要算法对消息体计算摘要值,将其与接收到的摘要值比对验证是否一致。
该过程中需要注意的重点是要保障摘要算法的保密性。
对称加密算法使用共同的密文对消息体加密,其特点是速度快效率高,缺点是需发送和接收方共享秘钥,任何一方泄露密文和加密算法则加密失效。非对称加密算法采用公私钥对,发送方使用公钥对消息体加密、接收方使用私钥对消息体解密(反之亦可),接收方只需保管好自己独有的秘钥,即可保障加密消息不被泄露。但其加密速度慢效率低,故一般不用其进行大消息体的加密。
将对称加密算法和非对称加密算法结合使用,则可以达到相对可行有效的消息交换加密传输。下文中非对称加密算法以 RSA 为例,对称加密算法以 AES 为例。
主要思路为:通过 RSA 算法加密 AES 算法使用的密钥,通过 AES 算法加密消息体,然后将加密的密钥和消息体发送给对方;对方收到消息后,使用 RSA 算法解密 AES 密钥,用解密后的密钥解密消息体,由此得到解密后的消息;接收方在回复消息的过程中重复类似过程,由此实现消息交换加密传输。
我们以客户端与服务端消息通信为例,详细流程概述如下:
准备:
消息交换流程:
通过以上流程,使得整个传输过程中的消息都是加密无法被读取的,当然前提是需保证双方的 RSA 秘密未被泄露。
此外,如果加上消息摘要算法对消息体签名,中间人不知道摘要算法则无法伪造有效的消息。所以结合消息摘要算法和消息加密算法,则可以进一步增强消息传递过程。当然,在对等双方的消息传递中,对方不一定是可信的,还是存在摘要算法泄露的风险。
上述流程可以保证消息的安全性,但无法保证其真实性,因为中间人也可以不解密消息,使用拦截到的公钥加密消息,发送伪造的消息体(伪造消息体和发送方公钥)。
解决办法则是引入第三方权威中间人机构,消息接收方在收到消息时,先通过中间人机构对来自消息发送方的公钥进行可靠性认证。
上述流程默认客户端与服务端为对等的双方。其实该流程也可以进行简化:客户端不维护本地 RSA 公私钥,但缓存随机生成的 RES 秘钥;服务端在得到消息后,回复消息时使用相同的 RES 算法和解密得到的客户端 RES 秘钥加密消息体,客户端收到消息后使用 RES 秘钥解密。其实简化流程中再加上颁发证书的第三方权威机构认证流程,就是典型的 HTTPS 消息加密传输的基本原理。
使用 crypto
模块:
- import crypto from 'crypto';
-
- /** 使用 crypto 模块生成 RSA 公私钥 */
- export function genRsaKeyByCrypto(options?: crypto.RSAKeyPairOptions<"pem", "pem">) {
- options = {
- modulusLength: 1024,
- publicKeyEncoding: {
- type: 'pkcs1',
- format: 'pem'
- },
- privateKeyEncoding: {
- type: 'pkcs8',
- format: 'pem',
- cipher: 'aes-256-cbc',
- passphrase: '',
- },
- ...options,
- };
- const result = crypto.generateKeyPairSync('rsa', options);
-
- return result;
- }
使用 node-rsa
库:
- import NodeRSA from 'node-rsa';
-
- export function genRsaKeyPairByNodeRsa() {
- const key = new NodeRSA({ b: 1024 });
- key.setOptions({encryptionScheme: 'pkcs1'});
- return { publicKey: key.exportKey('public'), privateKey: key.exportKey('private') };
- }
生成并保存 RSA 公私钥:
- export function saveRsaKey(publicKeyPath='public.pem', privateKeyPath='private.pem', isForce = false) {
- publicKeyPath = path.resolve(publicKeyPath);
- privateKeyPath = path.resolve(privateKeyPath);
-
- if (existsSync(publicKeyPath) && !isForce) {
- // console.log('公钥文件已存在');
- return { publicKey: readFileSync(publicKeyPath), privateKey: readFileSync(privateKeyPath) };
- } else {
- console.log('重新生成公私钥文件');
- }
-
- // const { publicKey, privateKey } = await genRsaKeyByCrypto();
- const { publicKey, privateKey } = genRsaKeyPairByNodeRsa();
-
- if (!existsSync(path.dirname(publicKeyPath))) mkdirSync(path.dirname(publicKeyPath), {recursive: true});
-
- writeFileSync(publicKeyPath, publicKey);
- console.log('写入公钥文件:', publicKeyPath);
- writeFileSync(privateKeyPath, privateKey);
- console.log('写入私钥文件:', privateKeyPath);
-
- return { publicKey, privateKey };
- }
使用 Node.js 自带的 crypto
模块示例:
- /**
- * RSA最大加密明文大小
- */
- const MAX_ENCRYPT_BLOCK = 117 - 31;
- /**
- * RSA最大解密密文大小
- */
- const MAX_DECRYPT_BLOCK = 128;
-
- /**
- * rsa 公钥加密
- */
- export function publicEncrypt(data, publicKey, outputEncoding: BufferEncoding = 'base64') {
- // 加密信息用buf封装
- const buf = Buffer.from(data, 'utf-8');
- const inputLen = buf.byteLength;
- const bufs = [];
- let offSet = 0;
- let endOffSet = MAX_ENCRYPT_BLOCK;
- // 分段加密
- while (inputLen - offSet > 0) {
- if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {
- const bufTmp = buf.slice(offSet, endOffSet);
- bufs.push(crypto.publicEncrypt({key: publicKey, passphrase: '', padding: crypto.constants.RSA_PKCS1_PADDING}, bufTmp));
- } else {
- const bufTmp = buf.slice(offSet, inputLen);
- bufs.push(crypto.publicEncrypt({key: publicKey, passphrase: '', padding: crypto.constants.RSA_PKCS1_PADDING}, bufTmp));
- }
- offSet += MAX_ENCRYPT_BLOCK;
- endOffSet += MAX_ENCRYPT_BLOCK;
- }
- const result = Buffer.concat(bufs).toString(outputEncoding);
- return result;
- }
-
- /**
- * rsa 私钥解密
- */
- export function privateDecrypt(data, privateKey, inputEncoding: BufferEncoding = 'base64') {
- // 经过base64编码的密文转成buf
- const buf = data instanceof Buffer ? data : Buffer.from(data, inputEncoding);
- const inputLen = buf.byteLength;
- const bufs = [];
- let offSet = 0;
- let endOffSet = MAX_DECRYPT_BLOCK;
- // 分段加密
- while (inputLen - offSet > 0) {
- if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
- const bufTmp = buf.slice(offSet, endOffSet);
- bufs.push(crypto.privateDecrypt({key: privateKey, passphrase: '', padding: crypto.constants.RSA_PKCS1_PADDING}, bufTmp));
- } else {
- const bufTmp = buf.slice(offSet, inputLen);
- bufs.push(crypto.privateDecrypt({key: privateKey, passphrase: '', padding: crypto.constants.RSA_PKCS1_PADDING}, bufTmp));
- }
- offSet += MAX_DECRYPT_BLOCK;
- endOffSet += MAX_DECRYPT_BLOCK;
- }
- const result = Buffer.concat(bufs).toString();
-
- return result;
- }
使用 node-rsa
库:
- import NodeRSA from 'node-rsa';
-
- /**
- * rsa 公钥加密
- */
- export function rsaEncrypt(data, publicKey, outputEncoding = 'base64') {
- const key = new NodeRSA({ b: 1024 });
- key.importKey(publicKey, 'public');
- let encryData = key.encrypt(data, outputEncoding, 'utf8');
- if (outputEncoding === 'hex' || outputEncoding === 'binary') encryData = Buffer.from(encryData, outputEncoding);
- return encryData;
- }
- /**
- * rsa 私钥解密
- */
- export function rsaDecrypt(data, privateKey) {
- const key = new NodeRSA({ b: 1024 });
- key.importKey(privateKey, 'private');
- const decryptData = key.decrypt(data, 'utf8');
- return decryptData;
- }
AES 加密与解密相对比较简单,只需要传入对应的 data 数据与解密密文即可。
使用 Node.js 自带的 crypto
模块示例:
- import crypto from 'crypto';
-
- /** aes 加密 */
- export aesEncrypt(data, passKey, outputEncoding = 'base64') {
- if (typeof data !== 'string') data = JSON.stringify(data);
- const cipherChunks = [];
- // const key = Buffer.from(passKey, 'utf8');
- // 对原始秘钥点加盐
- const key = crypto.scryptSync(passKey, 'salt', 16);
- const iv = key; // Buffer.alloc(16, 0);
- const cipher = crypto.createCipheriv('aes-128-cbc', key, iv);
-
- cipher.setAutoPadding(true);
- cipherChunks.push(cipher.update(data, 'utf8', outputEncoding as any));
- cipherChunks.push(cipher.final(outputEncoding as any));
-
- return cipherChunks.join('');
- }
- /** aes 解密 */
- export aesDecrypt(data, passKey, inputEncoding = 'base64') {
- const cipherChunks = [];
- // const key = Buffer.from(passKey, 'utf8');
- const key = crypto.scryptSync(passKey, 'salt', 16);
- const iv = key; // Buffer.alloc(16, 0);
- const decipher = crypto.createDecipheriv('aes-128-cbc', key, iv);
-
- decipher.setAutoPadding(true);
- cipherChunks.push(decipher.update(data, inputEncoding as any, 'utf8'));
- cipherChunks.push(decipher.final('utf8'));
-
- return cipherChunks.join('');
- }
使用 crypto-js
模块示例:
- import CryptoJS from 'crypto-js';
-
- const key = CryptoJS.enc.Utf8.parse("1234123412ABCDEF"); //十六位十六进制数作为密钥
- const iv = CryptoJS.enc.Utf8.parse('ABCDEF1234123412'); //十六位十六进制数作为密钥偏移量
-
- //解密方法
- function Decrypt(data, passKey, iv?) {
- if (!iv) iv = passKey;
- passKey = CryptoJS.enc.Utf8.parse(passKey);
- iv = CryptoJS.enc.Utf8.parse(iv);
-
- const hex = CryptoJS.enc.Hex.parse(data);
- const base64 = CryptoJS.enc.Base64.stringify(hex);
- const decrypt = CryptoJS.AES.decrypt(base64, passKey, { iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 });
-
- return decrypt.toString(CryptoJS.enc.Utf8);
- }
-
- //加密方法
- function Encrypt(data, passKey, iv?) {
- if (!iv) iv = passKey;
- passKey = CryptoJS.enc.Utf8.parse(passKey);
- iv = CryptoJS.enc.Utf8.parse(iv);
-
- const utf8 = CryptoJS.enc.Utf8.parse(data);
- const encrypted = CryptoJS.AES.encrypt(utf8, key, { iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 });
-
- return encrypted.ciphertext.toString().toUpperCase();
- }
数据加密与解密实现:
- /** 生成指定长度的字符串 */
- export function genRandomAesKey(len = 16) {
- // return crypto.randomBytes(len).toString('utf-8');
-
- const result = [];
- for (let i = 0; i < len; i++) {
- let code = Math.round(Math.random() * 126);
- if (code < 33) code += 32;
- result.push( String.fromCharCode(code));
- }
-
- return result.join('');
- }
- /** (使用对方的公钥)数据加密,返回加密后的 key、data 以及本地公钥(用于给对方加密回据使用) */
- export function dataEncrypt(data, remotePublicKey) {
- const aesKey = genRandomAesKey();
- // const localPublicKey = fs.readFileSync(config.publicKeyPath, {encoding: 'utf-8'});
- // if (!remotePublicKey) remotePublicKey = localPublicKey;
- const aesKeyEncrypted = rsaEncrypt(aesKey, remotePublicKey);
- const encryptedData = aesEncrypt(data, aesKey);
-
- return {key: aesKeyEncrypted, data: encryptedData, publicKey: localPublicKey};
- }
-
- export function dataDecrypt(encryptedData, aesKeyEncrypted, localPrivateKey) {
- // if (!localPrivateKey) localPrivateKey = fs.readFileSync(config.privateKeyPath, {encoding: 'utf-8'});
- const aesKey = rsaDecrypt(aesKeyEncrypted, localPrivateKey);
- const result = aesDecrypt(encryptedData, aesKey);
- try {
- return JSON.parse(result);
- } catch (_e) {
- return result;
- }
- }
消息发送方数据加密与接收方数据解密:
- // 发送方加密发送消息
- const data = { user: 'lzwme', pwd: '123456' };
- const remotePublicKey = '...';
- const encryptedInfo = dataEncrypt(data);
- console.log('数据加密:', encryptedInfo);
-
- // 接受方收到消息体后解密消息
- const localPrivateKey = '...';
- const deCryptedInfo = dataDecrypt(encryptedInfo.key, encryptedInfo.data, localPrivateKey);
- console.log('数据解密:', deCryptedInfo);
使用const crypto = require('crypto');
即可引入该模块。
hash 算法也被称为摘要算法,该算法可以将任意长度的数据,转换为固定长度的 hash 值,这种方式具有不可逆性。你可以把一本小说转换为 hash 数据,但无法从这 hash 数据再逆转回一本小说。因此,若要获取 hash 的原数据,只能靠字典碰撞。
该算法通常在文本校验、存储密码时用的比较多。虽然摘要算法会用于密码的存储,但严格来说,摘要算法不算做是加密算法。
使用getHashes()
方法,可以获取到所有支持的 hash 算法:
crypto.getHashes();
获取到一个数组:
- [
- "RSA-MD4",
- "RSA-MD5",
- "RSA-MDC2",
- "RSA-RIPEMD160",
- "RSA-SHA1",
- "RSA-SHA1-2",
- "RSA-SHA224",
- "RSA-SHA256",
- "RSA-SHA3-224",
- "RSA-SHA3-256",
- "RSA-SHA3-384",
- "RSA-SHA3-512",
- "RSA-SHA384",
- "RSA-SHA512",
- "RSA-SHA512/224",
- "RSA-SHA512/256",
- "RSA-SM3",
- "blake2b512",
- "blake2s256",
- "id-rsassa-pkcs1-v1_5-with-sha3-224",
- "id-rsassa-pkcs1-v1_5-with-sha3-256",
- "id-rsassa-pkcs1-v1_5-with-sha3-384",
- "id-rsassa-pkcs1-v1_5-with-sha3-512",
- "md4",
- "md4WithRSAEncryption",
- "md5",
- "md5-sha1",
- "md5WithRSAEncryption",
- "mdc2",
- "mdc2WithRSA",
- "ripemd",
- "ripemd160",
- "ripemd160WithRSA",
- "rmd160",
- "sha1",
- "sha1WithRSAEncryption",
- "sha224",
- "sha224WithRSAEncryption",
- "sha256",
- "sha256WithRSAEncryption",
- "sha3-224",
- "sha3-256",
- "sha3-384",
- "sha3-512",
- "sha384",
- "sha384WithRSAEncryption",
- "sha512",
- "sha512-224",
- "sha512-224WithRSAEncryption",
- "sha512-256",
- "sha512-256WithRSAEncryption",
- "sha512WithRSAEncryption",
- "shake128",
- "shake256",
- "sm3",
- "sm3WithRSAEncryption",
- "ssl3-md5",
- "ssl3-sha1",
- "whirlpool"
- ]
这么多 hash 算法,我们平时用的比较多的是md5
, sha1
, sha256
, sha512
。这里把同一个文本,按照不同的摘要算法来生成 hash 值:
- // text 要摘要的文本
- // hashtype 摘要的算法
- function createHash(text, hashtype) {
- const hash = crypto.createHash(hashtype).update(text).digest("hex");
- console.log(hashtype, hash, hash.length);
- }
- hashes.forEach((type) => {
- createHash("蚊子", type);
- });
生成的结果:
- md5 37725295ea78b626efcf77768be478cb 32
- sha1 21f226b5a07ed3f74e6ae07e994f36d6a9bf6fac 40
- sha256 a200ce289b67afbfb6fbc3d7dd33f7ef493daef64fb159c2e48e8534a0289a9b 64
- sha512 b88bd9eac191f58e06c99c256bbcfdf2945aa94b47d5e0242be1f0739bf4adccebf4753e9f38f92603fe3f52f331121540c1dda2ed91796410abcfe49a677fba 128
不同的算法,生成的 hash 值的长度也不一样,碰撞成功的难度也越大。
同时,update
方法不止可以接收字符串,还可以接收 stream 流:
- const filename = "./node-crypto.md";
- const hash = crypto.createHash("sha1");
- const fsStream = fs.createReadStream(filename);
-
- fsStream.on("readable", () => {
- // 哈希流只会生成一个元素。
- const data = fsStream.read();
- if (data) {
- hash.update(data);
- } else {
- // 数据接收完毕后,输出hash值
- console.log(`${hash.digest("hex")} ${filename}`);
- }
- });
既然可以接收 stream 流的格式,那么就使用 pipe 管道进行处理:
- const filename = "./node-crypto.md";
- const hash = crypto.createHash("sha1");
- const fsStream = fs.createReadStream(filename);
-
- fsStream.pipe(hash).pipe(process.stdout);
hash 后传给下个管道进行处理,不过这里输出的通常会是乱码,因此这里我们自己写一个可写流:
- const { Writable } = require("stream");
-
- const write = Writable();
- write._write = function (data, enc, next) {
- // 将流中的数据写入底层
- process.stdout.write(hash.digest("hex") + "\n");
- // 写入完成时,调用`next()`方法通知流传入下一个数据
- process.nextTick(next);
- };
-
- fsStream.pipe(hash).pipe(write); // 正常输出hash值
我们先看下 hmac 算法的用法:
- const result = crypto.createHmac("sha1", "123456").update("蚊子").digest("hex");
-
- console.log(result); // 0bdd6c1192e321e34887d965c1140be4361ada65
hmac 算法与 hash 算法的调用方式很像,但createHmac()
方法这里多了一个参数,这个参数相当于密钥。密钥不一样,即使要加密的文本一样,生成的结果也会不一样。
- function createHmac() {
- const text = "蚊子";
- const key = Math.random().toString().slice(-6);
-
- const result = crypto.createHmac("sha1", key).update(text).digest("hex");
-
- console.log(text, key, result);
- }
-
- let n = 10;
- while (n--) {
- createHmac();
- }
生成的结果:
- 蚊子 508028 486d1f539e4bb8adfd601fd6a3302fae74043bfe
- 蚊子 644233 dcd6501e6eee9e1462625b50c1ff91c613559b35
- 蚊子 479257 752945c62b87ce1edb24661103b65e612bb849b7
- 蚊子 445857 0c6399758a2348ea31bc778f87f503b050e036d5
- 蚊子 954174 a78ff9d4301bb09d249db9fa6c9a3a28c04acff7
- 蚊子 629736 b7fd4d3836363f029dd9009f51ad6c14280987c1
- 蚊子 343366 7a8cadf5dd620f8c82315f38de1f6dc60bfc5336
- 蚊子 168627 cc51e4531449642a5a10357cbf8f206319fb1b1f
- 蚊子 103054 49b1ad9dc2de5da2cd67dc892f51718aa9475a05
- 蚊子 477238 82615006638be235a220bcfdee0705b5cc6551fc
hmac 算法相当于加盐版的 hash 算法,但内部具体的实现原理,可自行查看:github-node-crypto-hmac。
这种算法实现密码存储就非常的合适,碰撞成功的概率大大减少。在数据库中,我们可以这样存储:
- {
- "username": "蚊子",
- "password": "486d1f539e4bb8adfd601fd6a3302fae74043bfe",
- "key": "508028"
- }
即使脱库得到了这些数据,反向获取到原密码的机会也非常的低。
在 stream 流的操作上,hmac 算法和 hash 算法的用法一样。
前面的两种方法都是不可逆的 hash 加密算法,这里我们介绍下可加密和可解密的算法。常见的对称加密算法有aes
和des
。
crypto 模块中提供了createCipheriv
和createDecipheriv
来进行加密和解密的功能。之前的 createCipher 和 createDecipher 在 10.0.0 版本已经废弃了,我们这里以新的方法为例,写下加密和解密的算法。
这两个方法都接收 3 个参数:
key 和 iv 两个参数都必须是 'utf8' 编码的字符串、Buffer、 TypedArray 或 DataView。 key 可以是 secret 类型的 KeyObject。 如果密码不需要初始化向量,则 iv 可以为 null。
加密的算法:
- function encode(src, key, iv) {
- let sign = "";
- const cipher = crypto.createCipheriv("aes-128-cbc", key, iv); // createCipher在10.0.0已被废弃
- sign += cipher.update(src, "utf8", "hex");
- sign += cipher.final("hex");
- return sign;
- }
解密的算法:
- function decode(sign, key, iv) {
- let src = "";
- const cipher = crypto.createDecipheriv("aes-128-cbc", key, iv);
- src += cipher.update(sign, "hex", "utf8");
- src += cipher.final("utf8");
- return src;
- }
使用方法:
- const key = "37725295ea78b626"; // Buffer.from('37725295ea78b626', 'utf8');
- const iv = "efcf77768be478cb"; // Buffer.from('efcf77768be478cb', 'utf8');
- // console.log(key, iv);
- const src = "hello, my name is wenzi! my password is `etu^&&*(^123)`";
- const sign = encode(src, key, iv);
- const _src = decode(sign, key, iv);
-
- console.log("key: ", key, "iv: ", iv);
- console.log("原文:", src);
- console.log("加密后: ", sign);
- console.log("解密后: ", _src);
-
- // key: 37725295ea78b626 iv: efcf77768be478cb
- // 原文: hello, my name is wenzi! my password is `etu^&&*(^123)`
- // 加密后: ce6dc873bfd5a5ae6fe0b2bb3f3de46fb9fc15e0ffc75d12286871dbfa3ed185b3ebf60b8e16dd0057eb0750e897347abeddf5a2741944d5a307ceb25c181276
- // 解密后: hello, my name is wenzi! my password is `etu^&&*(^123)`
刚才了解了下对称加密,即加密和解密用的都是相同的密钥。非对称加密相对来说,比对称加密更安全,用公钥加密的内容,必须通过对应的私钥才能解密。双方传输信息时,可以使用先使用对方的公钥进行加密,然后对方再使用自己的私钥解开即可。
先用创建一个私钥:
openssl genrsa -out rsa_private.key 1024
然后根据私钥创建对应的公钥:
openssl rsa -in rsa_private.key -pubout -out rsa_public.key
这里就可以进行非对称的加密和解密了:
- const crypto = require("crypto");
- const fs = require("fs");
-
- const pub_key = fs.readFileSync("./rsa_public.key");
- const priv_key = fs.readFileSync("./rsa_private.key");
-
- const text = "hello, my name is 蚊子";
-
- const secret = crypto.publicEncrypt(pub_key, Buffer.from(text));
- const result = crypto.privateDecrypt(priv_key, secret);
-
- console.log(secret); // buffer格式
- console.log(result.toString()); // hello, my name is 蚊子
使用publicEncrypt
进行公钥的加密过程,使用privateDecrypt
进行私钥的解密过程。
在网络中传输的数据,除可使用 Cipher 类进行数据加密外,还可以对数据生成数字签名,以防止在传输过程中对数据进行修改。
签名的过程与非对称加密的过程正好相反,是使用私钥进行加密签名,然后使用公钥进行解密的签名验证。
- const crypto = require("crypto");
- const fs = require("fs");
-
- const pub_key = fs.readFileSync("./rsa_public.key");
- const priv_key = fs.readFileSync("./rsa_private.key");
-
- const text = "hello, my name is 蚊子";
-
- // 生成签名
- const sign = crypto.createSign("RSA-SHA256");
- sign.update(text);
- const signed = sign.sign(priv_key, "hex");
-
- // 验证签名
- const verify = crypto.createVerify("RSA-SHA256");
- verify.update(text);
- const verifyResult = verify.verify(pub_key, signed, "hex");
-
- console.log("sign", signed); // ca364a6e31c1f540737ba3efb1ddf7fa2a087c5c11efe52a9e1f2c88b1fd1e0e50f12da4f22362fdfc3d77f3f538995a27a8206d250dba3572510dfcb33064f48685b96f2b2393f56de4958448cec92a4299434aa3318efe418e166b38100bc3a1d1a9310a510087021da0f66a817043ddfd2fb88db76eb2ace480c17a7f732f
- console.log("verifyResult", verifyResult); // true
生成签名的 sign 方法有两个参数,第一个参数为私钥,第二个参数为生成签名的格式,最后返回的 signed 为生成的签名(字符串)。
验证签名的 verify 方法有三个参数,第一个参数为公钥,第二个参数为被验证的签名,第三个参数为生成签名时的格式,返回为布尔值,即是否通过验证。
本文主要介绍了结合使用对称加密算法(AES)和非对称加密算法(RSA)对消息交换双方消息加密传输的典型方案以及从简单的 hash 算法,到对称加密,最后到非对称加密和签名,都有个大致的了解。后续我们也会对 node 的其他模块进行深入的理解。文章中使用 nodejs 代码演示了主要流程的实现方式。在基于的浏览器/服务器(B/S)的服务中,浏览器端没有 nodejs
接口能力,可借助第三方库 crypto-js
、jsencrypt
等提供的 API 实现相关加密和解密算法。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。