Node.js 加密 Node.js 模块 Node.js 流 Node.js 加密 稳定性: 2 - 不稳定; 正在讨论未来版本的 API 改进,会尽量减少重大变化。详见后文。 顾名思义,Node.js加密模块允许你使用加密的功能,Node.js加密模块通过使用require('crypto')来访问。 Node.js加密模块提供了HTTP或HTTPS连接过程中封装安全凭证的方法。 Node.js加密模块还提供了OpenSSL的哈希,hmac、加密(cipher)、解密(decipher)、签名(sign)和验证(verify)方法的封装。 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 ciphers = crypto.getCiphers(); console.log(ciphers); // ['AES-128-CBC', 'AES-128-CBC-HMAC-SHA1', ...] crypto.getHashes() 返回支持的哈希算法名数组。 例如: var hashes = crypto.getHashes(); console.log(hashes); // ['sha', 'sha1', 'sha1WithRSAEncryption', ...] crypto.createCredentials(details) 稳定性: 0 - 抛弃。用 [tls.createSecureContext][] 替换 根据参数details,创建一个加密凭证对象。参数为字典,key包括: pfx: 字符串或者buffer对象,表示经PFX或PKCS12编码产生的私钥、证书以及CA证书 key: 进过 PEM 编码的私钥 passphrase: 私钥或pfx的密码 cert: PEM编码的证书 ca: 字符串或字符串数组,PEM编码的可信任的CA证书。 crl: 字符串或字符串数组,PEM编码的CRLs(证书吊销列表Certificate Revocation List)。 ciphers: 字符串,使用或者排除的加密算法。参见http://www.openssl.org/docs/apps/ciphers.html#CIPHER_LIST_FORMAT。 如果没有指定'ca',Node.js将会使用下面列表中的CA http://mxr.mozilla.org/mozilla/source/security/nss/lib/ckfw/builtins/certdata.txt。 crypto.createHash(algorithm) 创建并返回一个哈希对象,使用指定的算法来生成哈希摘要。 参数algorithm取决于平台的OpenSSL版本所支持的算法。例如,'sha1'、'md5'、'sha256'、'sha512'等等。在最近的版本中,openssllist-message-digest-algorithms会显示所有算法。 例如: 这个程序会计算文件的sha1的和。 var filename = process.argv[2]; var crypto = require('crypto'); var fs = require('fs'); var shasum = crypto.createHash('sha1'); var s = fs.ReadStream(filename); s.on('data', function(d) { shasum.update(d); }); s.on('end', function() { var d = shasum.digest('hex'); console.log(d + ' ' + filename); }); 类:Hash Hase用来生成数据的哈希值。 它是可读写的流stream。写入的数据来用计算哈希值。当写入流结束后,使用read()方法来获取计算后的哈希值。也支持旧的update和digest方法。 通过crypto.createHash返回。 hash.update(data[, input_encoding]) 根据data来更新哈希内容,编码方式根据input_encoding来定,有'utf8'、'ascii'或'binary'。如果没有传入值,默认编码方式是'binary'。如果 data是Buffer,则input_encoding将会被忽略。 因为它是流式数据,所以可以使用不同的数据调用很多次。 hash.digest([encoding]) 计算传入的数据的哈希摘要。 encoding可以是'hex'、'binary'或'base64',如果没有指定encoding,将返回buffer。 注意:调用digest()后不能再用hash对象。 crypto.createHmac(algorithm, key) 创建并返回一个hmac对象,用指定的算法和秘钥生成hmac图谱。 它是可读写的流stream。写入的数据来用计算hmac。当写入流结束后,使用read()方法来获取计算后的值。也支持旧的update和digest方法。 参数algorithm取决于平台上OpenSSL版本所支持的算法,参见前面的createHash。key是hmac算法中用的key。 类:Hmac 用来创建hmac加密图谱。 通过crypto.createHmac返回。 hmac.update(data) 根据data更新hmac对象。因为它是流式数据,所以可以使用新数据调用多次。 hmac.digest([encoding]) 计算传入数据的hmac值。encoding可以是'hex'、'binary'或'base64',如果没有指定encoding,将返回buffer。 注意:调用digest()后不能再用hmac对象。 crypto.createCipher(algorithm, password) 使用传入的算法和秘钥来生成并返回加密对象。 algorithm取决于OpenSSL,例如'aes192'等。最近发布的版本中,openssl list-cipher-algorithms将会展示可用的加密算法。password用来派生key 和IV,它必须是一个'binary'编码的字符串或者一个buffer。 它是可读写的stream流。写入的数据来用计算hmac。当写入流结束后,使用read()方法来获取计算后的值。也支持老的update和digest方法。 注意,OpenSSL函数EVP_BytesToKey摘要算法如果是一次迭代(one iteration),无需盐值(no salt)的MD5时,createCipher为它派生秘钥。缺少盐值使得字典攻击,相同的密码总是生成相同的key,低迭代次数和非加密的哈希算法,使得密码测试非常迅速。 OpenSSL推荐使用pbkdf2来替换EVP_BytesToKey,推荐使用crypto.pbkdf2来派生key和iv ,推荐使用createCipheriv()来创建加密流。 crypto.createCipheriv(algorithm, key, iv) 创建并返回一个加密对象,用指定的算法,key和iv。 algorithm参数和createCipher()一致。key在算法中用到。iv是一个initialization vector. key和iv必须是'binary'的编码字符串或buffers. 类: Cipher 加密数据的类。. 通过crypto.createCipher和crypto.createCipheriv返回。 它是可读写的stream流。写入的数据来用计算hmac。当写入流结束后,使用read()方法来获取计算后的值。也支持旧的update和digest方法。 cipher.update(data[, input_encoding][, output_encoding]) 根据data来更新哈希内容,编码方式根据input_encoding来定,有'utf8'、'ascii'或者'binary'。如果没有传入值,默认编码方式是'binary'。如果data是Buffer,input_encoding将会被忽略。 output_encoding指定了输出的加密数据的编码格式,它可用是'binary'、'base64'或'hex'。如果没有提供编码,将返回buffer。 返回加密后的内容,因为它是流式数据,所以可以使用不同的数据调用很多次。 cipher.final([output_encoding]) 返回加密后的内容,编码方式是由output_encoding指定,可以是'binary'、'base64'或'hex'。如果没有传入值,将返回buffer。 注意:cipher对象不能在final()方法之后调用。 cipher.setAutoPadding(auto_padding=true) 你可以禁用输入数据自动填充到块大小的功能。如果auto_padding是false, 那么输入数据的长度必须是加密器块大小的整倍数,否则final会失败。这对非标准的填充很有用,例如使用0x0而不是PKCS的填充。这个函数必须在cipher.final之前调用。 cipher.getAuthTag() 加密认证模式(目前支持:GCM),这个方法返回经过计算的认证标志Buffer。必须使用final方法完全加密后调用。 cipher.setAAD(buffer) 加密认证模式(目前支持:GCM),这个方法设置附加认证数据( AAD )。 crypto.createDecipher(algorithm, password) 根据传入的算法和密钥,创建并返回一个解密对象。这是createCipher()的镜像。 crypto.createDecipheriv(algorithm, key, iv) 根据传入的算法,密钥和iv,创建并返回一个解密对象。这是createCipheriv()的镜像。 类:Decipher 解密数据类。 通过crypto.createDecipher和crypto.createDecipheriv返回。 解密对象是可读写的streams流。用写入的加密数据生成可读的纯文本数据。也支持老的update和digest方法。 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()方法之后使用。 decipher.setAutoPadding(auto_padding=true) 如果加密的数据是非标准块,可以禁止其自动填充,防止decipher.final检查并移除。仅在输入数据长度是加密块长度的整数倍的时才有效。你必须在 decipher.update前调用。 decipher.setAuthTag(buffer) 对于加密认证模式(目前支持:GCM),必须用这个方法来传递接收到的认证标志。如果没有提供标志,或者密文被篡改,将会抛出final标志,认证失败,密文会被抛弃。 decipher.setAAD(buffer) 对于加密认证模式(目前支持:GCM),用这个方法设置附加认证数据( AAD )。 crypto.createSign(algorithm) 根据传入的算法创建并返回一个签名数据。 OpenSSL的最近版本里,openssl list-public-key-algorithms会列出所有算法,比如'RSA-SHA256'。 类:Sign 生成数字签名的类。 通过crypto.createSign返回。 签名对象是可读写的streams流。可写数据用来生成签名。当所有的数据写完,sign签名方法会返回签名。也支持老的update和digest方法。 sign.update(data) 用参数data来更新签名对象。因为是流式数据,它可以被多次调用。 sign.sign(private_key[, output_format]) 根据传送给sign的数据来计算电子签名。 private_key可以是一个对象或者字符串。如果是字符串,将会被当做没有密码的key。 private_key: key: 包含 PEM 编码的私钥 passphrase: 私钥的密码 返回值output_format包含数字签名, 格式是'binary'、'hex'或'base64'之一。如果没有指定encoding,将返回buffer。 注意:sign对象不能在sign()方法之后调用。 crypto.createVerify(algorithm) 根据传入的算法,创建并返回验证对象。是签名对象(signing object)的镜像。 类: Verify 用来验证签名的类。 通过crypto.createVerify返回。 是可写streams流。可写数据用来验证签名。一旦所有数据写完后,如签名正确verify方法会返回true。 也支持老的update方法。 verifier.update(data) 用参数data来更新验证对象。因为是流式数据,它可以被多次调用。 verifier.verify(object, signature[, signature_format]) 使用object和signature验证签名数据。参数object是包含了PEM编码对象的字符串,它可以是RSA公钥,DSA公钥,或X.509证书。signature是之前计算出来的数字签名。signature_format可以是'binary'、'hex'或'base64'之一,如果没有指定编码方式 ,则默认是buffer对象。 根据数据和公钥验证签名有效性,来返回true或false。 注意:verifier对象不能在verify()方法之后调用。 crypto.createDiffieHellman(prime_length[, generator]) 创建一个Diffie-Hellman密钥交换(Diffie-Hellman key exchange)对象,并根据给定的位长度生成一个质数。如果没有指定参数generator,默认为2。 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 创建Diffie-Hellman秘钥交换的类。 通过crypto.createDiffieHellman返回。 diffieHellman.verifyError 在初始化的时候,如果有警告或错误,将会反应到这。它是以下值(定义在constants模块): DH_CHECK_P_NOT_SAFE_PRIME DH_CHECK_P_NOT_PRIME DH_UNABLE_TO_CHECK_GENERATOR DH_NOT_SUITABLE_GENERATOR diffieHellman.generateKeys([encoding]) 生成秘钥和公钥,并返回指定格式的公钥。这个值必须传给其他部分。编码方式:'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。 diffieHellman.getPrime([encoding]) 用参数encoding指明的编码方式返回Diffie-Hellman质数,编码方式为: 'binary'、'hex'或'base64'。如果没有指定编码方式,将返回buffer。 diffieHellman.getGenerator([encoding]) 用参数encoding指明的编码方式返回Diffie-Hellman生成器,编码方式为: 'binary'、'hex'或'base64'。如果没有指定编码方式 ,将返回buffer。 diffieHellman.getPublicKey([encoding]) 用参数encoding指明的编码方式返回Diffie-Hellman公钥,编码方式为: 'binary'、'hex', 或'base64'。如果没有指定编码方式 ,将返回buffer。 diffieHellman.getPrivateKey([encoding]) 用参数encoding指明的编码方式返回Diffie-Hellman私钥,编码方式为: 'binary'、'hex'或'base64'。如果没有指定编码方式 ,将返回buffer。 diffieHellman.setPublicKey(public_key[, encoding]) 设置Diffie-Hellman的公钥,编码方式为: 'binary'、'hex'或'base64',如果没有指定编码方式 ,默认为buffer。 diffieHellman.setPrivateKey(private_key[, encoding]) 设置Diffie-Hellman的私钥,编码方式为: 'binary'、'hex'或'base64',如果没有指定编码方式 ,默认为buffer。 crypto.getDiffieHellman(group_name) 创建一个预定义的Diffie-Hellman秘钥交换对象。支持的组: 'modp1'、'modp2'、'modp5'(定义于RFC 2412),并且'modp14'、'modp15'、'modp16'、'modp17'、'modp18'(定义于RFC 3526)。返回对象模仿了上述创建的crypto.createDiffieHellman()对象,但是不允许修改秘钥交换(例如,diffieHellman.setPublicKey())。使用这套流程的好处是,双方不需要生成或交换组组余数,节省了计算和通讯时间。 例如 (获取一个共享秘密): var crypto = require('crypto'); var alice = crypto.getDiffieHellman('modp5'); var bob = crypto.getDiffieHellman('modp5'); alice.generateKeys(); bob.generateKeys(); var alice_secret = alice.computeSecret(bob.getPublicKey(), null, 'hex'); var bob_secret = bob.computeSecret(alice.getPublicKey(), null, 'hex'); /* alice_secret and bob_secret should be the same */ console.log(alice_secret == bob_secret); crypto.createECDH(curve_name) 使用传入的参数curve_name,创建一个Elliptic Curve (EC) Diffie-Hellman秘钥交换对象。 类:ECDH 这个类用来创建EC Diffie-Hellman秘钥交换。 通过crypto.createECDH返回。 ECDH.generateKeys([encoding[, format]]) 生成EC Diffie-Hellman的秘钥和公钥,并返回指定格式和编码的公钥,它会传递给第三方。 参数format是'compressed'、 'uncompressed'或 'hybrid'。如果没有指定,将返回'uncompressed'格式. 参数encoding是'binary'、'hex'或'base64'。如果没有指定编码方式,将返回buffer。 ECDH.computeSecret(other_public_key[, input_encoding][, output_encoding]) 以other_public_key作为第三方公钥计算共享秘密,并返回。秘钥会以input_encoding来解读。编码是:'binary'、'hex'或'base64'。如果没有指定编码方式,默认为buffer。 如果没有指定编码方式,将返回buffer。 ECDH.getPublicKey([encoding[, format]]) 用参数encoding指明的编码方式返回EC Diffie-Hellman公钥,编码方式为: 'compressed'、'uncompressed'或'hybrid'。如果没有指定编码方式 ,将返回'uncompressed'。 编码是:'binary'、'hex'或'base64'。如果没有指定编码方式 ,默认为buffer。 ECDH.getPrivateKey([encoding]) 用参数encoding指明的编码方式返回EC Diffie-Hellman私钥,编码是:'binary'、'hex'或'base64'。如果没有指定编码方式 ,默认为buffer。 ECDH.setPublicKey(public_key[, encoding]) 设置EC Diffie-Hellman的公钥,编码方式为: 'binary'、'hex'或'base64',如果没有指定编码方式,默认为buffer。 ECDH.setPrivateKey(private_key[, encoding]) 设置EC Diffie-Hellman的私钥,编码方式为: 'binary'、'hex'或'base64',如果没有指定编码方式,默认为buffer。 例如 (包含一个共享秘密): var crypto = require('crypto'); var alice = crypto.createECDH('secp256k1'); var bob = crypto.createECDH('secp256k1'); alice.generateKeys(); bob.generateKeys(); var alice_secret = alice.computeSecret(bob.getPublicKey(), null, 'hex'); var bob_secret = bob.computeSecret(alice.getPublicKey(), null, 'hex'); /* alice_secret and bob_secret should be the same */ console.log(alice_secret == bob_secret); crypto.pbkdf2(password, salt, iterations, keylen[, digest], callback) 异步PBKDF2提供了一个伪随机函数HMAC-SHA1,根据给定密码的长度,salt和iterations来得出一个密钥。回调函数得到两个参数 (err, derivedKey)。 例如: crypto.pbkdf2('secret', 'salt', 4096, 512, 'sha256', function(err, key) { if (err) throw err; console.log(key.toString('hex')); // 'c5e478d...1469e50' }); 在crypto.getHashes()里有支持的摘要函数列表。 crypto.pbkdf2Sync(password, salt, iterations, keylen[, digest]) 异步PBKDF2函数, 返回derivedKey或抛出错误。 crypto.randomBytes(size[, callback]) 生成一个密码强度随机的数据: // async crypto.randomBytes(256, function(ex, buf) { if (ex) throw ex; console.log('Have %d bytes of random data: %s', buf.length, buf); }); // sync try { var buf = crypto.randomBytes(256); console.log('Have %d bytes of random data: %s', buf.length, buf); } catch (ex) { // handle error // most likely, entropy sources are drained } 注意:如果没有足够积累的熵来生成随机强度的密码,将会抛出错误,或调用回调函数返回错误。换句话说,没有回调函数的crypto.randomBytes不会阻塞,即使耗尽所有的熵。 crypto.pseudoRandomBytes(size[, callback]) 生成非密码学强度的伪随机数据。如果数据足够长会返回一个唯一数据,但是这个数可能是可以预期的。因此,当不可预期很重要的时候,不要用这个函数。例如,在生成加密的秘钥时。 用法和crypto.randomBytes相同。 类: Certificate 这个类和签过名的公钥打交道。最重要的场景是处理<keygen>元素,http://www.openssl.org/docs/apps/spkac.html。 通过crypto.Certificate返回. Certificate.verifySpkac(spkac) 根据SPKAC返回true或false。 Certificate.exportChallenge(spkac) 根据提供的SPKAC,返回加密的公钥。 Certificate.exportPublicKey(spkac) 输出和SPKAC关联的编码challenge。 crypto.publicEncrypt(public_key, buffer) 使用public_key加密buffer。目前仅支持RSA。 public_key可以是对象或字符串。如果public_key是一个字符串,将会当做没有密码的key,并会用RSA_PKCS1_OAEP_PADDING。 public_key: key: 包含有PEM编码的私钥。 padding: 填充值,如下 constants.RSA_NO_PADDING constants.RSA_PKCS1_PADDING constants.RSA_PKCS1_OAEP_PADDING 注意:所有的填充值定义在constants模块. crypto.privateDecrypt(private_key, buffer) 使用private_key来解密buffer. private_key: key: 包含有 PEM 编码的私钥 passphrase: 私钥的密码 padding: 填充值,如下: constants.RSA_NO_PADDING constants.RSA_PKCS1_PADDING constants.RSA_PKCS1_OAEP_PADDING 注意:所有的填充值定义于constants模块. crypto.DEFAULT_ENCODING 函数所用的编码方式可以是字符串或buffer ,默认值是'buffer'。这是为了加密模块兼容默认'binary'为编码方式的遗留程序。 注意:新程序希望用buffer对象,所以这是暂时手段。 Recent API Changes 在统一的流API概念出现前,在引入Buffer对象来处理二进制数据之前,Crypto模块就已经添加到Node。 因此,流相关的类里没有其他的Node类里的典型方法,并且很多方法接收并返回二级制编码的字符串,而不是Buffers。在最近的版本中,这些函数改成默认使用 Buffers。 对于一些场景来说这是重大变化。 例如,如果你使用默认参数给签名类,将结果返回给认证类,中间没有验证数据,程序会正常工作。之前你会得到二进制编码的字符串,并传递给验证类,现在则是 Buffer。 如果你之前使用的字符串数据在Buffers对象不能正常工作(比如,连接数据,并存储在数据库里 )。或者你传递了二进制字符串给加密函数,但是没有指定编码方式,现在就需要提供编码参数。如果想切换回原来的风格,将crypto.DEFAULT_ENCODING设置为'binary'。注意,新的程序希望是buffers,所以之前的方法只能作为临时的办法。 Node.js 模块 Node.js 流