zoukankan      html  css  js  c++  java
  • node.js_crypto 模块

     

    crypto 模块提供了加密功能,实现了包括对 OpenSSL 的哈希、HMAC、加密、解密、签名、以及验证功能的一整套封装。

    Hash 算法

    Hash 类是用于创建数据哈希值的工具类。

    查看 crypto 模块支持的 hash 函数:crypto.getHashes()

    [ 'RSA-MD4',
      'RSA-MD5',
      'RSA-MDC2',
      'RSA-RIPEMD160',
      'RSA-SHA1',
      'RSA-SHA1-2',
      'RSA-SHA224',
      'RSA-SHA256',
      'RSA-SHA384',
      'RSA-SHA512',
      'blake2b512',
      'blake2s256',
      'md4',
      'md4WithRSAEncryption',
      'md5',
      'md5-sha1',
      'md5WithRSAEncryption',
      'mdc2',
      'mdc2WithRSA',
      'ripemd',
      'ripemd160',
      'ripemd160WithRSA',
      'rmd160',
      'sha1',
      'sha1WithRSAEncryption',
      'sha224',
      'sha224WithRSAEncryption',
      'sha256',
      'sha256WithRSAEncryption',
      'sha384',
      'sha384WithRSAEncryption',
      'sha512',
      'sha512WithRSAEncryption',
      'ssl3-md5',
      'ssl3-sha1',
      'whirlpool' ]
    

    SHA-256

    使用 hash.update() 方法将要计算的数据以流(stream)的方式写入,流输入结束后,使用 hash.digest() 方法计算数据的 hash 值。

    const crypto = require('crypto');
    // 创建哈希函数 sha256
    const hash = crypto.createHash('sha256'); 
    
    // 输入流编码:utf8、ascii、binary(默认)
    hash.update('some data to hash', 'utf8');
    // 输出编码:hex、binary、base64
    console.log(hash.digest('hex'));
    
    // 输出
    // 6a2da20943931e9834fc12cfe5bb47bbd9ae43489a30726962b576f4e3993e50
    

    PBKDF2

    PBKDF2 是 Node.js 的 crypto 模块原生支持的标准方法。

    const crypto = require('crypto');
    
    function pbkdf2_encrypt(username, password) {
      // crypto.randomBytes()方法生成 32 字节的随机数 - 这里作为盐值
      crypto.randomBytes(32, (err, salt) => {
        if (err) throw err;
    
        // 参数列表:(密码,盐值,迭代次数,密钥长度,摘要函数)
        crypto.pbkdf2(password, salt, 4096, 512, 'sha256', (err, key) => {
          if (err) throw err;
          // 将用户名、密码哈希值和盐值存入数据库
          console.log(username, key.toString('hex'), salt.toString('hex'));
        });
      });
    }
    
    pbkdf2_encrypt('zhangSan', '123456');
    

    同步函数示例:

    const salt = crypto.randomBytes(32);
    const result = crypto.pbkdf2Sync(password, salt, 4096, 512, 'sha256');
    

    Hmac 算法

    Hmac 类是用于创建加密 Hmac 摘要的工具。

    示例一:

    const crypto = require('crypto');
    const key = 'BDvDYUmfdykkBLgX';
    const hmac = crypto.createHmac('sha1', key.toString('ascii'));
    
    hmac.update('data to crypt');
    console.log(hmac.digest('hex')); 
    
    // 输出
    // a43bfb9f12b6f69ad9fcd4338a981efbed2569ae
    

    示例二:

    const crypto = require('crypto');
    
    const secret = 'abcdefg';
    const hash = crypto.createHmac('sha256', secret)
                       .update('I love cupcakes')
                       .digest('hex');
    console.log(hash);
    

    加盐(salt)

    盐值就是随机数值,用于在计算密码的哈希值时加强数据的安全性,可以有效抵御诸如字典攻击、彩虹表攻击等密码攻击媒介。

    常见的 Hash 算法使用示例:

    const crypto = require('crypto');
    const md5 = crypto.createHash('md5');
    
    let password = '123456';
    // 直接对密码原文进行 Hash
    md5.update(password);
    console.log(md5.digest('hex')); 
    
    // 输出
    // e10adc3949ba59abbe56e057f20f883e
    // 可以通过 <https://www.cmd5.com/> 反向查询得到密码,不够安全。
    

    加“盐”的 Hash 算法:

    const crypto = require('crypto');
    const md5 = crypto.createHash('md5');
    
    let password = '123456';
    let salt = 'hhug6dcKyCNBQ5sUC0i6hja5dCTqdSzV'; // 盐值
    // 将密码拼接上任意长度的随机字符串后,再进行 Hash
    md5.update(password+salt);
    console.log(md5.digest('hex'));
    
    // 输出
    // b2d23b0443c319e574f1ea3f8bddc6e0
    

    crypto.randomBytes() 方法可以生成随机数,用于作为密钥或者盐值:

    const crypto = require('crypto');
    
    // crypto.randomBytes() 生成强加密的伪随机数
    crypto.randomBytes(16, (err, buf) => {
      if (err) throw err;
      console.log(`${buf.length} bytes of random data: ${buf.toString('hex')}`);
    });
    
    // 打印
    // 16 bytes of random data: 411fd0a79188f2fde58a91ea0302f148
    

    使用 Node crypto 模块为哈希函数生成随机的盐值:

    const crypto = require('crypto');
    
    // 1. 异步方法
    crypto.randomBytes(32, (err, salt) => {
      if (err) throw err;
    
      // 记录盐值可读的字符串版本
      console.log('salt: ' + salt.toString('hex'));
    
      // 后续步骤:使用盐值
    });
    
    // 2. 同步方法
    const buf = crypto.randomBytes(256);
    

    加密(Cipher)和解密(Decipher)算法

    对称加密算法

    查看 Openssl 对称加密算法列表:

    $ openssl list -cipher-algorithms
    $ openssl list-cipher-algorithms # 旧版本命令
    

    AES-256-GCM 示例1:

    'use strict';
    
    const crypto = require('crypto');
    
    // 初始化参数
    const text = 'Encryption Testing AES GCM mode'; // 要加密和解密的数据
    const key = crypto.randomBytes(32); // 256 位的共享密钥
    const iv = crypto.randomBytes(16); // 初始向量,16 字节
    const algorithm = 'aes-256-gcm'; // 加密算法和操作模式
    
    // 加密
    const cipher = crypto.createCipheriv(algorithm, key, iv); // 初始化加密算法
    let encrypted = cipher.update(text, 'utf8', 'hex');
    encrypted += cipher.final('hex');
    const tag = cipher.getAuthTag(); // 生成标签,用于验证密文的来源
    
    // 解密
    const decipher = crypto.createDecipheriv(algorithm, key, iv); // 初始化解密算法
    decipher.setAuthTag(tag); // 传入验证标签,验证密文的来源
    let decrypted = decipher.update(encrypted, 'hex', 'utf8');
    decrypted += decipher.final('utf8');
    
    console.log(decrypted); // Encryption Testing AES GCM mode
    

    AES-256-GCM 示例2:

    'use strict';
    
    const crypto = require('crypto');
    
    const aes256gcm = key => {
      const ALGO = 'aes-256-gcm'; // 加密算法和操作模式
    
      // encrypt returns base64-encoded ciphertext
      const encrypt = str => {
        // Hint: the 'iv' should be unique (but not necessarily random).
        // 'randomBytes' here are (relatively) slow but convenient for demonstration
        const iv = new Buffer.from(crypto.randomBytes(16), 'utf8'); // 初始向量,16 字节
        const cipher = crypto.createCipheriv(ALGO, key, iv); // 初始化加密算法
    
        // Hint: Larger inputs (it's GCM, after all!) should use the stream API
        let enc = cipher.update(str, 'utf8', 'base64');
        enc += cipher.final('base64');
        return [ enc, iv, cipher.getAuthTag() ];
      };
    
      // decrypt decodes base64-encoded ciphertext into a utf8-encoded string
      const decrpt = (enc, iv, authTag) => {
        const decipher = crypto.createDecipheriv(ALGO, key, iv);
        decipher.setAuthTag(authTag);
        let str = decipher.update(enc, 'base64', 'utf8');
        str += decipher.final('utf8');
        return str;
      };
    
      return { encrypt, decrpt };
    };
    
    // 加密密钥
    // Note: a key size of 16 bytes will use AES-128, 24 => AES-192, 32 => AES-256
    const KEY = new Buffer.from(crypto.randomBytes(32), 'utf8');
    
    const aesCipher = aes256gcm(KEY);
    
    // 加密
    const [ encrypted, iv, authTag ] = aesCipher.encrypt('hello world');
    // 解密
    const decrypted = aesCipher.decrpt(encrypted, iv, authTag);
    
    console.log(decrypted); // hello world
    

    非对称加密算法的使用,1. 公钥加密-私钥解密;2. 私钥加密-公钥解密。

    • crypto.publicEncrypt(key, buffer)
    • crypto.publicDecrypt(key, buffer)
    • crypto.privateEncrypt(privateKey, buffer)
    • crypto.privateDecrypt(privateKey, buffer)

    生成 RSA 密钥对

    方法:crypto.generateKeyPair(type, options, callback) 生成给定类型的新非对称密钥对,目前仅支持的算法类型:RSA,DSA 和 EC。

    参数:

    • type:<string> 类型,要生成的算法名称,必须是 'rsa''dsa' 或者 'ec' 之一;
    • options:<Object> 类型:
      • modulusLength:<number> 类型,密钥长度(RSA,DSA);
      • publicExponent:<number> 类型,公共指数(RSA),默认值:0x10001
      • divisorLength:<number> 类型,以比特为单位的 q 的大小(DSA);
      • namedCurve:<string> 类型,要使用的椭圆曲线名称(EC);
      • publicKeyEncoding:<Object> 类型,公钥编码格式:
        • type:<string> 类型,必须是 'pkcs1'(仅限 RSA)或 'spki' 之一;
        • format:<string> 类型,公钥输出格式,必须是 'pem' 或 'der' 之一;
      • privateKeyEncoding:<Object> 类型,私钥编码格式:
        • type:<string> 类型,必须是 'pkcs1'(仅限 RSA)或 'pkcs8' 或 'sec1'(仅限 EC) 之一;
        • format:<string> 类型,公钥输出格式,必须是 'pem' 或 'der' 之一;
        • cipher:<string> 类型,如果指定算法,将使用基于 PKCS#5 v2.0 规范的算法和密钥加密,使用给定的算法和密钥加密私钥;
        • passphrase:<string> 类型,用于加密的密钥;
    • callback:<Function> 类型:
      • err:<Error> 类型
      • publicKey:<string> 类型 | <Buffer> 类型,设置为 PEM 编码时返回 String 类型,DER 编码则返回 Buffer 类型
      • privateKey:<string> 类型 | <Buffer> 类型

    建议将公钥编码为 'spki' 格式,将私钥编码为 'pkcs8' 格式并加密保存:

    const { generateKeyPair } = require('crypto');
    generateKeyPair('rsa', {
      modulusLength: 4096,
      publicKeyEncoding: {
        type: 'spki', // pkcs1、spki
        format: 'pem'
      },
      privateKeyEncoding: {
        type: 'pkcs8', // pkcs1、pkcs8、sec1
        format: 'pem',
        cipher: 'aes-256-cbc', // 对私钥使用 aes-256-cbc 算法加密后返回
        passphrase: 'top secret' // aes-256-cbc 算法密钥
      }
    }, (err, publicKey, privateKey) => {
      // Handle errors and use the generated key pair.
    });
    

    使用 RSA 2048 进行公钥加密,私钥解密

    使用 OpenSSL 生成 2048位 RSA 密钥:

    # 使用 genrsa 命令生成密钥长度是 2048 比特的 RSA 密钥对
    $ openssl genrsa -out mykey.pem 2048
    
    # 从密钥对中分离出公钥
    $ openssl rsa -in mykey.pem -pubout -out mypubkey.pem
    

    使用公钥加密数据,再使用私钥解密数据:

    const crypto = require('crypto');
    const fs = require('fs');
    const path = require('path');
    const root = __dirname;
    
    const publicKey = fs.readFileSync(path.join(root, './mypubkey.pem')).toString('ascii');
    const privateKey = fs.readFileSync(path.join(root, './ mykey.pem')).toString('ascii');
    const data = 'data to crypt';
    
    // 公钥加密
    const encryptData = crypto.publicEncrypt(publicKey, Buffer.from(data)).toString('base64');
    console.log('encode', encryptData);
    
    // 私钥解密
    const decryptData = crypto.privateDecrypt(privateKey, Buffer.from(encryptData.toString('base64'), 'base64'));
    console.log('decode', decryptData.toString());
    

    签名(Sign)和验证(Verify)算法

    签名算法示例:

    const crypto = require('crypto');
    const fs = require('fs');
    const path = require('path');
    const root = __dirname;
    
    // 加载私钥文件
    const pem = fs.readFileSync(path.join(root, './rsa_private_key.pem'));
    const key = pem.toString('ascii');
    
    const sign = crypto.createSign('RSA-SHA256'); // 创建签名算法
    sign.update('data to sign'); // 更新待签名内容
    const result = sign.sign(key, 'hex'); // 生成并返回签名
    console.log('result:', result);
    
    // 打印
    // 3a26e0c3ec5ad1f517aad27f0f3a48cc0db156c2221800e4c2f995220f62a9077b4507a72ac6b0f1f50ee72386191d5ada4013945ff2afdb1ea6aa4e8d51a94f186089a67c2d6f4236150d59a1db134286fd7ba8deab0fc28f97eba7ad35820113f24dc6e30c6d11f0773527c4d81582c2b42016c9f972b5f4809e6d1f9115c2fb6db3168c68af3cfca986a458e3ea1c15606e7d33ee0bf5400ca460cf5371a51e800abe7ac943f16d9546239949bf7eec77488212d815b6a013e68fc2f456c4f2eb9d38c551e3f94168b076c3f761341afa3b724d8e73f486aacbace5a09412ef16c9b88182fcaeaaf97d96fdb402832530708a915d34c42131d7a26e4f04d4
    

    验证算法示例:

    const crypto = require('crypto');
    const fs = require('fs');
    const path = require('path');
    const root = __dirname;
    
    // 加载公钥文件
    const publicKey = fs.readFileSync(path.join(root, './rsa_public_key.pem')).toString('ascii');
    
    const verify = crypto.createVerify('RSA-SHA256'); // 创建验证算法
    verify.update('data to sign');
    console.log(verify.verify(publicKey, result, 'hex'));
    
    // 打印
    // true
    

    DH 密钥交换算法

    DiffieHellman 类是一个用来创建 Diffie-Hellman 键交换的工具。 DiffieHellman 类的实例可以使用 crypto.createDiffieHellman() 方法。

    const crypto = require('crypto');
    const assert = require('assert');
    
    // Generate Alice's keys...
    const alice = crypto.createDiffieHellman(2048);
    const aliceKey = alice.generateKeys();
    
    // Generate Bob's keys...
    const bob = crypto.createDiffieHellman(alice.getPrime(), alice.getGenerator());
    const bobKey = bob.generateKeys();
    
    // Exchange and generate the secret...
    const aliceSecret = alice.computeSecret(bobKey);
    const bobSecret = bob.computeSecret(aliceKey);
    
    // OK
    assert.strictEqual(aliceSecret.toString('hex'), bobSecret.toString('hex'));
    

    ECDH 密钥交换算法

    ECDH 类是创建椭圆曲线 Diffie-Hellman(Elliptic Curve Diffie-Hellman (ECDH))键交换的实用工具。 ECDH 类的实例可以使用 crypto.createECDH() 方法。

    crypto.getCurves() 方法会返回一个数组,其中包含支持的椭圆曲线的名称。

    const crypto = require('crypto');
    const assert = require('assert');
    
    // Generate Alice's keys...
    const alice = crypto.createECDH('secp521r1');
    const aliceKey = alice.generateKeys();
    
    // Generate Bob's keys...
    const bob = crypto.createECDH('secp521r1');
    const bobKey = bob.generateKeys();
    
    // Exchange and generate the secret...
    const aliceSecret = alice.computeSecret(bobKey);
    const bobSecret = bob.computeSecret(aliceKey);
    
    assert.strictEqual(aliceSecret.toString('hex'), bobSecret.toString('hex'));
    // OK
    

    参考

    相关第三方库

  • 相关阅读:
    前后端分离项目本地测试跨域问题配置
    STS没有找到Dynamic Web Project
    java常见面试题
    log4jdbc 使用
    Thymeleaf 入门
    集成maven和Spring boot的profile 专题
    ftp命令大全
    windows下安装redis
    linux安装环境
    atmega328 熔丝
  • 原文地址:https://www.cnblogs.com/onesea/p/15745902.html
Copyright © 2011-2022 走看看