nodeJS之crypto加密

Posted 小火柴的蓝色理想

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了nodeJS之crypto加密相关的知识,希望对你有一定的参考价值。

前面的话

  加密模块提供了 HTTP 或 HTTPS 连接过程中封装安全凭证的方法。也提供了 OpenSSL 的哈希,hmac, 加密(cipher), 解密(decipher), 签名(sign) 和 验证(verify) 方法的封装。本文将详细介绍加密crypto

 

crypto

【crypto.setEngine(engine[, flags])】

  为某些/所有 OpenSSL 函数加载并设置引擎(根据参数 flags 来设置)。

  engine 可能是 id,或者是指向引擎共享库的路径。

  flags是可选参数,默认值是ENGINE_METHOD_ALL ,可以是以下一个或多个参数的组合(在constants里定义)

ENGINE_METHOD_RSA
ENGINE_METHOD_DSA
ENGINE_METHOD_DH
ENGINE_METHOD_RAND
ENGINE_METHOD_ECDH
ENGINE_METHOD_ECDSA
ENGINE_METHOD_CIPHERS
ENGINE_METHOD_DIGESTS
ENGINE_METHOD_STORE
ENGINE_METHOD_PKEY_METH
ENGINE_METHOD_PKEY_ASN1_METH
ENGINE_METHOD_ALL
ENGINE_METHOD_NONE

【crypto.getCiphers()】

  返回支持的加密算法名数组

var crypto = require(\'crypto\');
console.log(crypto.getCiphers());
//[ \'aes-128-cbc\',  \'aes-128-ccm\',  \'aes-128-cfb\',  \'aes-128-cfb1\',  \'aes-128-cfb8\',  \'aes-128-ctr\',  \'aes-128-ecb\',  \'aes-128-gcm\',  \'aes-128-ofb\',  \'aes-128-xts\',  \'aes-192-cbc\',  \'aes-192-ccm\',  \'aes-192-cfb\',  \'aes-192-cfb1\',  \'aes-192-cfb8\',  \'aes-192-ctr\',  \'aes-192-ecb\',  \'aes-192-gcm\',  \'aes-192-ofb\',  \'aes-256-cbc\',  \'aes-256-ccm\',  \'aes-256-cfb\',  \'aes-256-cfb1\',  \'aes-256-cfb8\',  \'aes-256-ctr\',  \'aes-256-ecb\',  \'aes-256-gcm\',  \'aes-256-ofb\',  \'aes-256-xts\',  \'aes128\',  \'aes192\',  \'aes256\',  \'bf\',  \'bf-cbc\',  \'bf-cfb\',  \'bf-ecb\',  \'bf-ofb\',  \'blowfish\',  \'camellia-128-cbc\',  \'camellia-128-cfb\',  \'camellia-128-cfb1\',  \'camellia-128-cfb8\',  \'camellia-128-ecb\',  \'camellia-128-ofb\',  \'camellia-192-cbc\',  \'camellia-192-cfb\',  \'camellia-192-cfb1\',  \'camellia-192-cfb8\',  \'camellia-192-ecb\',  \'camellia-192-ofb\',  \'camellia-256-cbc\',  \'camellia-256-cfb\',  \'camellia-256-cfb1\',  \'camellia-256-cfb8\',  \'camellia-256-ecb\',  \'camellia-256-ofb\',  \'camellia128\',  \'camellia192\',  \'camellia256\',  \'cast\',  \'cast-cbc\',  \'cast5-cbc\',  \'cast5-cfb\',  \'cast5-ecb\',  \'cast5-ofb\',  \'des\',  \'des-cbc\',  \'des-cfb\',  \'des-cfb1\',  \'des-cfb8\',  \'des-ecb\',  \'des-ede\',  \'des-ede-cbc\',  \'des-ede-cfb\',  \'des-ede-ofb\',  \'des-ede3\',  \'des-ede3-cbc\',  \'des-ede3-cfb\',  \'des-ede3-cfb1\',  \'des-ede3-cfb8\',  \'des-ede3-ofb\',  \'des-ofb\',  \'des3\',  \'desx\',  \'desx-cbc\',  \'id-aes128-CCM\',  \'id-aes128-GCM\',  \'id-aes128-wrap\',  \'id-aes192-CCM\',  \'id-aes192-GCM\',  \'id-aes192-wrap\',  \'id-aes256-CCM\',  \'id-aes256-GCM\',  \'id-aes256-wrap\',  \'id-smime-alg-CMS3DESwrap\',  \'idea\',  \'idea-cbc\',  \'idea-cfb\',  \'idea-ecb\',  \'idea-ofb\',  ... 15 more items ]

【crypto.getCiphers()】

  返回支持的哈希算法名数组。

var crypto = require(\'crypto\');
console.log(crypto.getHashes());
//[ \'DSA\',  \'DSA-SHA\',  \'DSA-SHA1\',  \'DSA-SHA1-old\',  \'RSA-MD4\',  \'RSA-MD5\',  \'RSA-MDC2\',  \'RSA-RIPEMD160\',  \'RSA-SHA\',  \'RSA-SHA1\',  \'RSA-SHA1-2\',  \'RSA-SHA224\',  \'RSA-SHA256\',  \'RSA-SHA384\',  \'RSA-SHA512\',  \'dsaEncryption\',  \'dsaWithSHA\',  \'dsaWithSHA1\',  \'dss1\',  \'ecdsa-with-SHA1\',  \'md4\',  \'md4WithRSAEncryption\',  \'md5\',  \'md5WithRSAEncryption\',  \'mdc2\',  \'mdc2WithRSA\',  \'ripemd\',  \'ripemd160\',  \'ripemd160WithRSA\',  \'rmd160\',  \'sha\',  \'sha1\',  \'sha1WithRSAEncryption\',  \'sha224\',  \'sha224WithRSAEncryption\',  \'sha256\',  \'sha256WithRSAEncryption\',  \'sha384\',  \'sha384WithRSAEncryption\',  \'sha512\',  \'sha512WithRSAEncryption\',  \'shaWithRSAEncryption\',  \'ssl2-md5\',  \'ssl3-md5\',  \'ssl3-sha1\',  \'whirlpool\' ]

【crypto.getCurves()】

  返回支持的椭圆曲线名数组。

var crypto = require(\'crypto\');
console.log(crypto.getCurves());
//[ \'Oakley-EC2N-3\',  \'Oakley-EC2N-4\',  \'brainpoolP160r1\',  \'brainpoolP160t1\',  \'brainpoolP192r1\',  \'brainpoolP192t1\',  \'brainpoolP224r1\',  \'brainpoolP224t1\',  \'brainpoolP256r1\',  \'brainpoolP256t1\',  \'brainpoolP320r1\',  \'brainpoolP320t1\',  \'brainpoolP384r1\',  \'brainpoolP384t1\',  \'brainpoolP512r1\',  \'brainpoolP512t1\',  \'c2pnb163v1\',  \'c2pnb163v2\',  \'c2pnb163v3\',  \'c2pnb176v1\',  \'c2pnb208w1\',  \'c2pnb272w1\',  \'c2pnb304w1\',  \'c2pnb368w1\',  \'c2tnb191v1\',  \'c2tnb191v2\',  \'c2tnb191v3\',  \'c2tnb239v1\',  \'c2tnb239v2\',  \'c2tnb239v3\',  \'c2tnb359v1\',  \'c2tnb431r1\',  \'prime192v1\',  \'prime192v2\',  \'prime192v3\',  \'prime239v1\',  \'prime239v2\',  \'prime239v3\',  \'prime256v1\',  \'secp112r1\',  \'secp112r2\',  \'secp128r1\',  \'secp128r2\',  \'secp160k1\',  \'secp160r1\',  \'secp160r2\',  \'secp192k1\',  \'secp224k1\',  \'secp224r1\',  \'secp256k1\',  \'secp384r1\',  \'secp521r1\',  \'sect113r1\',  \'sect113r2\',  \'sect131r1\',  \'sect131r2\',  \'sect163k1\',  \'sect163r1\',  \'sect163r2\',  \'sect193r1\',  \'sect193r2\',  \'sect233k1\',  \'sect233r1\',  \'sect239k1\',  \'sect283k1\',  \'sect283r1\',  \'sect409k1\',  \'sect409r1\',  \'sect571k1\',  \'sect571r1\',  \'wap-wsg-idm-ecid-wtls1\',  \'wap-wsg-idm-ecid-wtls10\',  \'wap-wsg-idm-ecid-wtls11\',  \'wap-wsg-idm-ecid-wtls12\',  \'wap-wsg-idm-ecid-wtls3\',  \'wap-wsg-idm-ecid-wtls4\',  \'wap-wsg-idm-ecid-wtls5\',  \'wap-wsg-idm-ecid-wtls6\',  \'wap-wsg-idm-ecid-wtls7\',  \'wap-wsg-idm-ecid-wtls8\',  \'wap-wsg-idm-ecid-wtls9\' ]

 

MD5

  MD5是一种常用的哈希算法,用于给任意数据一个“签名”。这个签名通常用一个十六进制的字符串表示:

【crypto.createHash(algorithm)】

  创建并返回一个哈希对象,使用指定的算法来生成哈希摘要。

  参数 algorithm 取决于平台上 OpenSSL 版本所支持的算法。例如,\'sha1\', \'md5\', \'sha256\', \'sha512\' 等等

【hash.update(data[, input_encoding])】

  根据 data 来更新哈希内容,编码方式根据 input_encoding 来定,有 \'utf8\', \'ascii\' 或 \'binary\'。如果没有传入值,默认编码方式是\'utf8\'。如果 data 是 Buffer, input_encoding 将会被忽略。

  因为它是流式数据,所以可以使用不同的数据调用很多次。

【hash.digest([encoding])】

  计算传入的数据的哈希摘要。encoding 可以是 \'hex\', \'binary\' 或 \'base64\',如果没有指定encoding ,将返回 buffer。

  [注意]调用 digest() 后不能再用 hash 对象。

var crypto = require(\'crypto\');
var hash = crypto.createHash(\'md5\');

// 可任意多次调用update():
hash.update(\'Hello, world!\');
hash.update(\'Hello, nodejs!\');

console.log(hash.digest(\'hex\')); // 7e1977739c748beac0c0fd14fd26a544

 

Hmac

  Hmac算法也是一种哈希算法,它可以利用MD5或SHA1等哈希算法。不同的是,Hmac还需要一个密钥:

【crypto.createHmac(algorithm, key)】

  创建并返回一个 hmac 对象,用指定的算法和秘钥生成 hmac 图谱。

  它是可读写的流 stream 。写入的数据来用计算 hmac。当写入流结束后,使用 read() 方法来获取计算后的值。也支持老的 update 和 digest 方法。

  参数 algorithm 取决于平台上 OpenSSL 版本所支持的算法,参见前面的 createHash。key是 hmac 算法中用的 key

【hmac.update(data)】

  根据 data 更新 hmac 对象。因为它是流式数据,所以可以使用新数据调用多次。

【hmac.digest([encoding])】

  计算传入数据的 hmac 值。encoding可以是 \'hex\', \'binary\' 或 \'base64\',如果没有指定encoding ,将返回 buffer。

  [注意]调用 digest() 后不能再用 hmac 对象

var crypto = require(\'crypto\');
var hmac = crypto.createHmac(\'sha256\', \'match\');

hmac.update(\'Hello, world!\');
hmac.update(\'Hello, nodejs!\');
//e82a58066cae2fae4f44e58be1d589b66a5d102c2e8846d796607f02a88c1649
console.log(hmac.digest(\'hex\')); 

 

AES

  AES是一种常用的对称加密算法,加解密都用同一个密钥。crypto模块提供了AES支持,但是需要自己封装好函数,便于使用:

【crypto.createCipher(algorithm, password)】

  使用传入的算法和秘钥来生成并返回加密对象。

  algorithm 取决于 OpenSSL,例如\'aes192\'等。password 用来派生 key 和 IV,它必须是一个\'binary\' 编码的字符串或者一个buffer。

  它是可读写的流 stream 。写入的数据来用计算 hmac。当写入流结束后,使用 read() 方法来获取计算后的值。也支持老的update 和 digest 方法。

【cipher.update(data[, input_encoding][, output_encoding])】

  根据 data 来更新哈希内容,编码方式根据 input_encoding 来定,有 \'utf8\', \'ascii\' or \'binary\'。如果没有传入值,默认编码方式是\'binary\'。如果data 是 Buffer,input_encoding 将会被忽略。

  output_encoding 指定了输出的加密数据的编码格式,它可用是 \'binary\', \'base64\' 或 \'hex\'。如果没有提供编码,将返回 buffer 。

  返回加密后的内容,因为它是流式数据,所以可以使用不同的数据调用很多次。

【cipher.final([output_encoding])】

  返回加密后的内容,编码方式是由 output_encoding 指定,可以是 \'binary\', \'base64\' 或 \'hex\'。如果没有传入值,将返回 buffer。

  [注意]cipher 对象不能在 final() 方法之后调用。

var crypto = require(\'crypto\');
function aesEncrypt(data, key) {
    const cipher = crypto.createCipher(\'aes192\', key);
    var crypted = cipher.update(data, \'utf8\', \'hex\');
    crypted += cipher.final(\'hex\');
    return crypted;
}
var data = \'Hello, this is a secret message!\';
var key = \'Password!\';
var encrypted = aesEncrypt(data, key);
//8a944d97bdabc157a5b7a40cb180e713f901d2eb454220d6aaa1984831e17231f87799ef334e3825123658c80e0e5d0c
console.log(encrypted);

【crypto.createDecipher(algorithm, password)】

  根据传入的算法和密钥,创建并返回一个解密对象。这是 createCipher() 的镜像

【decipher.update(data[, input_encoding][, output_encoding])】

  使用参数 data 更新需要解密的内容,其编码方式是 \'binary\',\'base64\' 或 \'hex\'。如果没有指定编码方式,则把 data 当成 buffer 对象。

  如果 data 是 Buffer,则忽略 input_encoding 参数。

  参数 output_decoding 指定返回文本的格式,是 \'binary\', \'ascii\' 或 \'utf8\' 之一。如果没有提供编码格式,则返回 buffer。

【decipher.final([output_encoding])】

  返回剩余的解密过的内容,参数 output_encoding 是 \'binary\', \'ascii\' 或 \'utf8\',如果没有指定编码方式,返回 buffer。

  [注意]decipher对象不能在 final() 方法之后使用。

var crypto = require(\'crypto\');
function aesDecrypt(encrypted, key) {
    const decipher = crypto.createDecipher(\'aes192\', key);
    var decrypted = decipher.update(encrypted, \'hex\', \'utf8\');
    decrypted += decipher.final(\'utf8\');
    return decrypted;
}
var data = \'Hello, this is a secret message!\';
var key = \'Password!\';
var encrypted = \'8a944d97bdabc157a5b7a40cb180e713f901d2eb454220d6aaa1984831e17231f87799ef334e3825123658c80e0e5d0c\';
var decrypted = aesDecrypt(encrypted, key);
console.log(decrypted);//Hello, this is a secret message!

  可以看出,加密后的字符串通过解密又得到了原始内容。

  注意到AES有很多不同的算法,如aes192aes-128-ecbaes-256-cbc等,AES除了密钥外还可以指定IV(Initial Vector),不同的系统只要IV不同,用相同的密钥加密相同的数据得到的加密结果也是不同的。加密结果通常有两种表示方法:hex和base64,这些功能Nodejs全部都支持,但是在应用中要注意,如果加解密双方一方用Nodejs,另一方用Java、php等其它语言,需要仔细测试。如果无法正确解密,要确认双方是否遵循同样的AES算法,字符串密钥和IV是否相同,加密后的数据是否统一为hex或base64格式

【crypto.createCipheriv(algorithm, key, iv)】

  创建并返回一个加密对象,用指定的算法,key 和 iv。

  algorithm 参数和 createCipher() 一致。key 在算法中用到.iv 是一个initialization vector.

  key 和 iv 必须是 \'binary\' 的编码字符串或buffers.

【crypto.createDecipheriv(algorithm, key, iv)】

  根据传入的算法,密钥和 iv,创建并返回一个解密对象。这是 createCipheriv() 的镜像。

const crypto = require(\'crypto\');

function aesEncryptiv(data, key,iv) {
    const cipher = crypto.createCipher(\'aes192\', key, iv);
    var crypted = cipher.update(data, \'utf8\', \'hex\');
    crypted += cipher.final(\'hex\');
    return crypted;
}

function aesDecryptiv(encrypted, key,iv) {
    const decipher = crypto.createDecipher(\'aes192\', key, iv);
    var decrypted = decipher.update(encrypted, \'hex\', \'utf8\');
    decrypted += decipher.final(\'utf8\');
    return decrypted;
}

var data = \'Hello, this is a secret message!\';
var key = \'Password!\';
var iv = \'match\';
var encrypted = aesEncryptiv(data, key, iv);
var decrypted = aesDecryptiv(encrypted, key, iv);
//Hello, this is a secret message!
console.log(data);
//8a944d97bdabc157a5b7a40cb180e713f901d2eb454220d6aaa1984831e17231f87799ef334e3825123658c80e0e5d0c
console.log(encrypted);
//Hello, this is a secret message!
console.log(decrypted);

 

Diffie-Hellman

【crypto.createDiffieHellman(prime[, prime_encoding][, generator][, generator_encoding])】

  使用传入的 prime 和 generator 创建 Diffie-Hellman 秘钥交互对象。

  generator 可以是数字,字符串或Buffer。如果没有指定 generator,使用 2

  prime_encoding 和 generator_encoding 可以是 \'binary\', \'hex\', 或 \'base64\'。

  如果没有指定 prime_encoding, 则 Buffer 为 prime。如果没有指定 generator_encoding ,则 Buffer 为 generator。

【diffieHellman.generateKeys([encoding])】

  生成秘钥和公钥,并返回指定格式的公钥。这个值必须传给其他部分。编码方式: \'binary\', \'hex\', 或 \'base64\'。如果没有指定编码方式,将返回 buffer。

【diffieHellman.getPrime([encoding])】

  用参数 encoding 指明的编码方式返回 Diffie-Hellman 质数,编码方式为: \'binary\', \'hex\', 或 \'base64\'。 如果没有指定编码方式,将返回 buffer。

【diffieHellman.getGenerator([encoding])】

  用参数 encoding 指明的编码方式返回 Diffie-Hellman 生成器,编码方式为: \'binary\', \'hex\', 或 \'base64\'. 如果没有指定编码方式 ,将返回 buffer。

【diffieHellman.computeSecret(other_public_key[, input_encoding][, output_encoding])】

  使用 other_public_key 作为第三方公钥来计算并返回共享秘密(shared secret)。秘钥用input_encoding 编码。编码方式为:\'binary\', \'hex\', 或 \'base64\'。如果没有指定编码方式 ,默认为 buffer。

  如果没有指定返回编码方式,将返回 buffer。

DH算法

  DH算法是一种密钥交换协议,它可以让双方在不泄漏密钥的情况下协商出一个密钥来。DH算法基于数学原理,比如小明和小红想要协商一个密钥,可以这么做:

  1、小明先选一个素数和一个底数,例如,素数p=23,底数g=5(底数可以任选),再选择一个秘密整数a=6,计算A=g^a mod p=8,然后大声告诉小红:p=23,g=5,A=8;

  2、小红收到小明发来的p,g,A后,也选一个秘密整数b=15,然后计算B=g^b mod p=19,并大声告诉小明:B=19;

  3、小明自己计算出s=B^a mod p=2,小红也自己计算出s=A^b mod p=2,因此,最终协商的密钥s为2。

  在这个过程中,密钥2并不是小明告诉小红的,也不是小红告诉小明的,而是双方协商计算出来的。第三方只能知道p=23,g=5,A=8,B=19,由于不知道双方选的秘密整数a=6和b=15,因此无法计算出密钥2。

  用crypto模块实现DH算法如下:

var crypto = require(\'crypto\');

// xiaoming\'s keys:
var ming = crypto.createDiffieHellman(512);
var ming_keys = ming.generateKeys();

var prime = ming.getPrime();
var generator = ming.getGenerator();

//Prime: 8df777257625c66821af697652f28e93af05b9f779af919111b89816faa11c36fcf9df04c76811471a6099800213c4fe8e3fbec8d2f90bd00795e4b7fd241603
console.log(\'Prime: \' + prime.toString(\'hex\'));
//Generator: 02
console.log(\'Generator: \' + generator.toString(\'hex\'));

// xiaohong\'s keys:
var hong = crypto.createDiffieHellman(prime, generator);
var hong_keys = hong.generateKeys();

// exchange and generate secret:
var ming_secret = ming.computeSecret(hong_keys);
var hong_secret = hong.computeSecret(ming_keys);

//Secret of Xiao Ming: 4237157ab4c9211f78ffdb67d127d749cec91780d594b81a7e75f1fb591fecb84f33ae6591e1edda4bc9685b503010fe8f9928c6ed69e4ff9fdb44adb9ba1539
console.log(\'Secret of Xiao Ming: \' + ming_secret.toString(\'hex\'));
//Secret of Xiao Hong: 4237157ab4c9211f78ffdb67d127d749cec91780d594b81a7e75f1fb591fecb84f33ae6591e1edda4bc9685b503010fe8f9928c6ed69e4ff9fdb44adb9ba1539
console.log(\'Secret of Xiao Hong: \' + hong_secret.toString(\'hex\'))

   [注意]每次输出都不一样,因为素数的选择是随机的。

以上是关于nodeJS之crypto加密的主要内容,如果未能解决你的问题,请参考以下文章

nodeJS之crypto模块公钥加密及解密

nodeJS之crypto模块md5和Hmac加密

nodeJS之crypto模块公钥加密及解密

NodeJS Crypto RS-SHA256 和 JWT 承载

关于nodejs 怎么实现 crypto des加密

Chapter Six:常用的编码与加密介绍(python和js实现)