Crypto (加密)#

稳定性:2 - 稳定

源代码: lib/crypto.js

node:crypto 模块提供了加密功能,其中包括一组对 OpenSSL 的哈希、HMAC、加密、解密、签名和验证函数的封装。

const { createHmac } = await import('node:crypto');

const secret = 'abcdefg';
const hash = createHmac('sha256', secret)
               .update('I love cupcakes')
               .digest('hex');
console.log(hash);
// Prints:
//   c0fa1bc00531bd78ef38c628449c5102aeabd49b5dc3a2a516ea6ea959d6658econst { createHmac } = require('node:crypto');

const secret = 'abcdefg';
const hash = createHmac('sha256', secret)
               .update('I love cupcakes')
               .digest('hex');
console.log(hash);
// Prints:
//   c0fa1bc00531bd78ef38c628449c5102aeabd49b5dc3a2a516ea6ea959d6658e

确定加密支持是否不可用#

Node.js 在构建时可能不包含对 node:crypto 模块的支持。在这种情况下,尝试从 crypto import 或调用 require('node:crypto') 将导致抛出错误。

使用 CommonJS 时,抛出的错误可以使用 try/catch 捕获。

let crypto;
try {
  crypto = require('node:crypto');
} catch (err) {
  console.error('crypto support is disabled!');
} 

当使用词法 ESM import 关键字时,只有在尝试加载模块*之前*注册了 process.on('uncaughtException') 的处理程序(例如,使用预加载模块),才能捕获该错误。

使用 ESM 时,如果代码有可能在未启用加密支持的 Node.js 版本上运行,请考虑使用 import() 函数,而不是词法 import 关键字。

let crypto;
try {
  crypto = await import('node:crypto');
} catch (err) {
  console.error('crypto support is disabled!');
} 

非对称密钥类型#

下表列出了 KeyObject API 识别的非对称密钥类型

密钥类型描述OID
'dh'Diffie-Hellman1.2.840.113549.1.3.1
'dsa'DSA1.2.840.10040.4.1
'ec'椭圆曲线1.2.840.10045.2.1
'ed25519'Ed255191.3.101.112
'ed448'Ed4481.3.101.113
'ml-dsa-44'1ML-DSA-442.16.840.1.101.3.4.3.17
'ml-dsa-65'1ML-DSA-652.16.840.1.101.3.4.3.18
'ml-dsa-87'1ML-DSA-872.16.840.1.101.3.4.3.19
'ml-kem-512'1ML-KEM-5122.16.840.1.101.3.4.4.1
'ml-kem-768'1ML-KEM-7682.16.840.1.101.3.4.4.2
'ml-kem-1024'1ML-KEM-10242.16.840.1.101.3.4.4.3
'rsa-pss'RSA PSS1.2.840.113549.1.1.10
'rsa'RSA1.2.840.113549.1.1.1
'slh-dsa-sha2-128f'1SLH-DSA-SHA2-128f2.16.840.1.101.3.4.3.21
'slh-dsa-sha2-128s'1SLH-DSA-SHA2-128s2.16.840.1.101.3.4.3.22
'slh-dsa-sha2-192f'1SLH-DSA-SHA2-192f2.16.840.1.101.3.4.3.23
'slh-dsa-sha2-192s'1SLH-DSA-SHA2-192s2.16.840.1.101.3.4.3.24
'slh-dsa-sha2-256f'1SLH-DSA-SHA2-256f2.16.840.1.101.3.4.3.25
'slh-dsa-sha2-256s'1SLH-DSA-SHA2-256s2.16.840.1.101.3.4.3.26
'slh-dsa-shake-128f'1SLH-DSA-SHAKE-128f2.16.840.1.101.3.4.3.27
'slh-dsa-shake-128s'1SLH-DSA-SHAKE-128s2.16.840.1.101.3.4.3.28
'slh-dsa-shake-192f'1SLH-DSA-SHAKE-192f2.16.840.1.101.3.4.3.29
'slh-dsa-shake-192s'1SLH-DSA-SHAKE-192s2.16.840.1.101.3.4.3.30
'slh-dsa-shake-256f'1SLH-DSA-SHAKE-256f2.16.840.1.101.3.4.3.31
'slh-dsa-shake-256s'1SLH-DSA-SHAKE-256s2.16.840.1.101.3.4.3.32
'x25519'X255191.3.101.110
'x448'X4481.3.101.111

类:Certificate#

SPKAC 是一种证书签名请求机制,最初由 Netscape 实现,并作为 HTML5 keygen 元素的一部分正式指定。

<keygen>HTML 5.2 起已被弃用,新项目不应再使用此元素。

node:crypto 模块提供了 Certificate 类来处理 SPKAC 数据。最常见的用法是处理由 HTML5 <keygen> 元素生成的输出。Node.js 内部使用 OpenSSL 的 SPKAC 实现

静态方法:Certificate.exportChallenge(spkac[, encoding])#

const { Certificate } = await import('node:crypto');
const spkac = getSpkacSomehow();
const challenge = Certificate.exportChallenge(spkac);
console.log(challenge.toString('utf8'));
// Prints: the challenge as a UTF8 stringconst { Certificate } = require('node:crypto');
const spkac = getSpkacSomehow();
const challenge = Certificate.exportChallenge(spkac);
console.log(challenge.toString('utf8'));
// Prints: the challenge as a UTF8 string

静态方法:Certificate.exportPublicKey(spkac[, encoding])#

const { Certificate } = await import('node:crypto');
const spkac = getSpkacSomehow();
const publicKey = Certificate.exportPublicKey(spkac);
console.log(publicKey);
// Prints: the public key as <Buffer ...>const { Certificate } = require('node:crypto');
const spkac = getSpkacSomehow();
const publicKey = Certificate.exportPublicKey(spkac);
console.log(publicKey);
// Prints: the public key as <Buffer ...>

静态方法:Certificate.verifySpkac(spkac[, encoding])#

import { Buffer } from 'node:buffer';
const { Certificate } = await import('node:crypto');

const spkac = getSpkacSomehow();
console.log(Certificate.verifySpkac(Buffer.from(spkac)));
// Prints: true or falseconst { Buffer } = require('node:buffer');
const { Certificate } = require('node:crypto');

const spkac = getSpkacSomehow();
console.log(Certificate.verifySpkac(Buffer.from(spkac)));
// Prints: true or false

旧版 API#

稳定性: 0 - 废弃

作为旧版接口,可以创建 crypto.Certificate 类的新实例,如下例所示。

new crypto.Certificate()#

Certificate 类的实例可以使用 new 关键字创建,或者通过调用 crypto.Certificate() 作为函数来创建。

const { Certificate } = await import('node:crypto');

const cert1 = new Certificate();
const cert2 = Certificate();const { Certificate } = require('node:crypto');

const cert1 = new Certificate();
const cert2 = Certificate();
certificate.exportChallenge(spkac[, encoding])#
const { Certificate } = await import('node:crypto');
const cert = Certificate();
const spkac = getSpkacSomehow();
const challenge = cert.exportChallenge(spkac);
console.log(challenge.toString('utf8'));
// Prints: the challenge as a UTF8 stringconst { Certificate } = require('node:crypto');
const cert = Certificate();
const spkac = getSpkacSomehow();
const challenge = cert.exportChallenge(spkac);
console.log(challenge.toString('utf8'));
// Prints: the challenge as a UTF8 string
certificate.exportPublicKey(spkac[, encoding])#
const { Certificate } = await import('node:crypto');
const cert = Certificate();
const spkac = getSpkacSomehow();
const publicKey = cert.exportPublicKey(spkac);
console.log(publicKey);
// Prints: the public key as <Buffer ...>const { Certificate } = require('node:crypto');
const cert = Certificate();
const spkac = getSpkacSomehow();
const publicKey = cert.exportPublicKey(spkac);
console.log(publicKey);
// Prints: the public key as <Buffer ...>
certificate.verifySpkac(spkac[, encoding])#
import { Buffer } from 'node:buffer';
const { Certificate } = await import('node:crypto');

const cert = Certificate();
const spkac = getSpkacSomehow();
console.log(cert.verifySpkac(Buffer.from(spkac)));
// Prints: true or falseconst { Buffer } = require('node:buffer');
const { Certificate } = require('node:crypto');

const cert = Certificate();
const spkac = getSpkacSomehow();
console.log(cert.verifySpkac(Buffer.from(spkac)));
// Prints: true or false

类:Cipheriv#

Cipheriv 类的实例用于加密数据。该类可以通过以下两种方式之一使用:

  • 作为可读可写的 ,将未加密的纯文本数据写入,以在可读端产生加密数据,或者
  • 使用 cipher.update()cipher.final() 方法来生成加密数据。

crypto.createCipheriv() 方法用于创建 Cipheriv 实例。不应直接使用 new 关键字创建 Cipheriv 对象。

示例:将 Cipheriv 对象用作流

const {
  scrypt,
  randomFill,
  createCipheriv,
} = await import('node:crypto');

const algorithm = 'aes-192-cbc';
const password = 'Password used to generate key';

// First, we'll generate the key. The key length is dependent on the algorithm.
// In this case for aes192, it is 24 bytes (192 bits).
scrypt(password, 'salt', 24, (err, key) => {
  if (err) throw err;
  // Then, we'll generate a random initialization vector
  randomFill(new Uint8Array(16), (err, iv) => {
    if (err) throw err;

    // Once we have the key and iv, we can create and use the cipher...
    const cipher = createCipheriv(algorithm, key, iv);

    let encrypted = '';
    cipher.setEncoding('hex');

    cipher.on('data', (chunk) => encrypted += chunk);
    cipher.on('end', () => console.log(encrypted));

    cipher.write('some clear text data');
    cipher.end();
  });
});const {
  scrypt,
  randomFill,
  createCipheriv,
} = require('node:crypto');

const algorithm = 'aes-192-cbc';
const password = 'Password used to generate key';

// First, we'll generate the key. The key length is dependent on the algorithm.
// In this case for aes192, it is 24 bytes (192 bits).
scrypt(password, 'salt', 24, (err, key) => {
  if (err) throw err;
  // Then, we'll generate a random initialization vector
  randomFill(new Uint8Array(16), (err, iv) => {
    if (err) throw err;

    // Once we have the key and iv, we can create and use the cipher...
    const cipher = createCipheriv(algorithm, key, iv);

    let encrypted = '';
    cipher.setEncoding('hex');

    cipher.on('data', (chunk) => encrypted += chunk);
    cipher.on('end', () => console.log(encrypted));

    cipher.write('some clear text data');
    cipher.end();
  });
});

示例:使用 Cipheriv 和管道流

import {
  createReadStream,
  createWriteStream,
} from 'node:fs';

import {
  pipeline,
} from 'node:stream';

const {
  scrypt,
  randomFill,
  createCipheriv,
} = await import('node:crypto');

const algorithm = 'aes-192-cbc';
const password = 'Password used to generate key';

// First, we'll generate the key. The key length is dependent on the algorithm.
// In this case for aes192, it is 24 bytes (192 bits).
scrypt(password, 'salt', 24, (err, key) => {
  if (err) throw err;
  // Then, we'll generate a random initialization vector
  randomFill(new Uint8Array(16), (err, iv) => {
    if (err) throw err;

    const cipher = createCipheriv(algorithm, key, iv);

    const input = createReadStream('test.js');
    const output = createWriteStream('test.enc');

    pipeline(input, cipher, output, (err) => {
      if (err) throw err;
    });
  });
});const {
  createReadStream,
  createWriteStream,
} = require('node:fs');

const {
  pipeline,
} = require('node:stream');

const {
  scrypt,
  randomFill,
  createCipheriv,
} = require('node:crypto');

const algorithm = 'aes-192-cbc';
const password = 'Password used to generate key';

// First, we'll generate the key. The key length is dependent on the algorithm.
// In this case for aes192, it is 24 bytes (192 bits).
scrypt(password, 'salt', 24, (err, key) => {
  if (err) throw err;
  // Then, we'll generate a random initialization vector
  randomFill(new Uint8Array(16), (err, iv) => {
    if (err) throw err;

    const cipher = createCipheriv(algorithm, key, iv);

    const input = createReadStream('test.js');
    const output = createWriteStream('test.enc');

    pipeline(input, cipher, output, (err) => {
      if (err) throw err;
    });
  });
});

示例:使用 cipher.update()cipher.final() 方法

const {
  scrypt,
  randomFill,
  createCipheriv,
} = await import('node:crypto');

const algorithm = 'aes-192-cbc';
const password = 'Password used to generate key';

// First, we'll generate the key. The key length is dependent on the algorithm.
// In this case for aes192, it is 24 bytes (192 bits).
scrypt(password, 'salt', 24, (err, key) => {
  if (err) throw err;
  // Then, we'll generate a random initialization vector
  randomFill(new Uint8Array(16), (err, iv) => {
    if (err) throw err;

    const cipher = createCipheriv(algorithm, key, iv);

    let encrypted = cipher.update('some clear text data', 'utf8', 'hex');
    encrypted += cipher.final('hex');
    console.log(encrypted);
  });
});const {
  scrypt,
  randomFill,
  createCipheriv,
} = require('node:crypto');

const algorithm = 'aes-192-cbc';
const password = 'Password used to generate key';

// First, we'll generate the key. The key length is dependent on the algorithm.
// In this case for aes192, it is 24 bytes (192 bits).
scrypt(password, 'salt', 24, (err, key) => {
  if (err) throw err;
  // Then, we'll generate a random initialization vector
  randomFill(new Uint8Array(16), (err, iv) => {
    if (err) throw err;

    const cipher = createCipheriv(algorithm, key, iv);

    let encrypted = cipher.update('some clear text data', 'utf8', 'hex');
    encrypted += cipher.final('hex');
    console.log(encrypted);
  });
});

cipher.final([outputEncoding])#

  • outputEncoding <string> 返回值的编码
  • 返回:<Buffer> | <string> 任何剩余的加密内容。如果指定了 outputEncoding,则返回一个字符串。如果未提供 outputEncoding,则返回一个 Buffer

一旦调用了 cipher.final() 方法,Cipheriv 对象就不能再用于加密数据。多次尝试调用 cipher.final() 将导致抛出错误。

cipher.getAuthTag()#

  • 返回:<Buffer> 当使用认证加密模式(目前支持 GCMCCMOCBchacha20-poly1305)时,cipher.getAuthTag() 方法返回一个 Buffer,其中包含从给定数据计算出的*认证标签*。

cipher.getAuthTag() 方法只应在使用 cipher.final() 方法完成加密后调用。

如果在 cipher 实例创建期间设置了 authTagLength 选项,此函数将返回确切的 authTagLength 字节。

cipher.setAAD(buffer[, options])#

当使用认证加密模式(目前支持 GCMCCMOCBchacha20-poly1305)时,cipher.setAAD() 方法设置用于*附加认证数据*(AAD)输入参数的值。

对于 GCMOCBplaintextLength 选项是可选的。使用 CCM 时,必须指定 plaintextLength 选项,并且其值必须与明文的字节长度匹配。请参见 CCM 模式

cipher.setAAD() 方法必须在 cipher.update() 之前调用。

cipher.setAutoPadding([autoPadding])#

  • autoPadding <boolean> 默认值: true
  • 返回:<Cipheriv> 同一个 Cipheriv 实例,用于方法链式调用。

当使用分组加密算法时,Cipheriv 类会自动向输入数据添加填充,使其达到适当的分组大小。要禁用默认填充,请调用 cipher.setAutoPadding(false)

autoPaddingfalse 时,整个输入数据的长度必须是密码分组大小的倍数,否则 cipher.final() 会抛出错误。禁用自动填充对于非标准填充很有用,例如使用 0x0 而不是 PKCS 填充。

cipher.setAutoPadding() 方法必须在 cipher.final() 之前调用。

cipher.update(data[, inputEncoding][, outputEncoding])#

data 更新密码器。如果给定了 inputEncoding 参数,则 data 参数是使用指定编码的字符串。如果未给定 inputEncoding 参数,data 必须是 BufferTypedArrayDataView。如果 dataBufferTypedArrayDataView,则忽略 inputEncoding

outputEncoding 指定加密数据的输出格式。如果指定了 outputEncoding,则返回使用指定编码的字符串。如果没有提供 outputEncoding,则返回一个 Buffer

cipher.update() 方法可以多次调用以处理新数据,直到调用 cipher.final() 为止。在调用 cipher.final() 之后再调用 cipher.update() 将导致抛出错误。

类:Decipheriv#

Decipheriv 类的实例用于解密数据。该类可以通过以下两种方式之一使用:

  • 作为一个可读可写的 ,将加密的纯文本数据写入,以在可读端产生未加密的数据,或者
  • 使用 decipher.update()decipher.final() 方法来生成未加密的数据。

crypto.createDecipheriv() 方法用于创建 Decipheriv 实例。不应直接使用 new 关键字创建 Decipheriv 对象。

示例:将 Decipheriv 对象用作流

import { Buffer } from 'node:buffer';
const {
  scryptSync,
  createDecipheriv,
} = await import('node:crypto');

const algorithm = 'aes-192-cbc';
const password = 'Password used to generate key';
// Key length is dependent on the algorithm. In this case for aes192, it is
// 24 bytes (192 bits).
// Use the async `crypto.scrypt()` instead.
const key = scryptSync(password, 'salt', 24);
// The IV is usually passed along with the ciphertext.
const iv = Buffer.alloc(16, 0); // Initialization vector.

const decipher = createDecipheriv(algorithm, key, iv);

let decrypted = '';
decipher.on('readable', () => {
  let chunk;
  while (null !== (chunk = decipher.read())) {
    decrypted += chunk.toString('utf8');
  }
});
decipher.on('end', () => {
  console.log(decrypted);
  // Prints: some clear text data
});

// Encrypted with same algorithm, key and iv.
const encrypted =
  'e5f79c5915c02171eec6b212d5520d44480993d7d622a7c4c2da32f6efda0ffa';
decipher.write(encrypted, 'hex');
decipher.end();const {
  scryptSync,
  createDecipheriv,
} = require('node:crypto');
const { Buffer } = require('node:buffer');

const algorithm = 'aes-192-cbc';
const password = 'Password used to generate key';
// Key length is dependent on the algorithm. In this case for aes192, it is
// 24 bytes (192 bits).
// Use the async `crypto.scrypt()` instead.
const key = scryptSync(password, 'salt', 24);
// The IV is usually passed along with the ciphertext.
const iv = Buffer.alloc(16, 0); // Initialization vector.

const decipher = createDecipheriv(algorithm, key, iv);

let decrypted = '';
decipher.on('readable', () => {
  let chunk;
  while (null !== (chunk = decipher.read())) {
    decrypted += chunk.toString('utf8');
  }
});
decipher.on('end', () => {
  console.log(decrypted);
  // Prints: some clear text data
});

// Encrypted with same algorithm, key and iv.
const encrypted =
  'e5f79c5915c02171eec6b212d5520d44480993d7d622a7c4c2da32f6efda0ffa';
decipher.write(encrypted, 'hex');
decipher.end();

示例:使用 Decipheriv 和管道流

import {
  createReadStream,
  createWriteStream,
} from 'node:fs';
import { Buffer } from 'node:buffer';
const {
  scryptSync,
  createDecipheriv,
} = await import('node:crypto');

const algorithm = 'aes-192-cbc';
const password = 'Password used to generate key';
// Use the async `crypto.scrypt()` instead.
const key = scryptSync(password, 'salt', 24);
// The IV is usually passed along with the ciphertext.
const iv = Buffer.alloc(16, 0); // Initialization vector.

const decipher = createDecipheriv(algorithm, key, iv);

const input = createReadStream('test.enc');
const output = createWriteStream('test.js');

input.pipe(decipher).pipe(output);const {
  createReadStream,
  createWriteStream,
} = require('node:fs');
const {
  scryptSync,
  createDecipheriv,
} = require('node:crypto');
const { Buffer } = require('node:buffer');

const algorithm = 'aes-192-cbc';
const password = 'Password used to generate key';
// Use the async `crypto.scrypt()` instead.
const key = scryptSync(password, 'salt', 24);
// The IV is usually passed along with the ciphertext.
const iv = Buffer.alloc(16, 0); // Initialization vector.

const decipher = createDecipheriv(algorithm, key, iv);

const input = createReadStream('test.enc');
const output = createWriteStream('test.js');

input.pipe(decipher).pipe(output);

示例:使用 decipher.update()decipher.final() 方法

import { Buffer } from 'node:buffer';
const {
  scryptSync,
  createDecipheriv,
} = await import('node:crypto');

const algorithm = 'aes-192-cbc';
const password = 'Password used to generate key';
// Use the async `crypto.scrypt()` instead.
const key = scryptSync(password, 'salt', 24);
// The IV is usually passed along with the ciphertext.
const iv = Buffer.alloc(16, 0); // Initialization vector.

const decipher = createDecipheriv(algorithm, key, iv);

// Encrypted using same algorithm, key and iv.
const encrypted =
  'e5f79c5915c02171eec6b212d5520d44480993d7d622a7c4c2da32f6efda0ffa';
let decrypted = decipher.update(encrypted, 'hex', 'utf8');
decrypted += decipher.final('utf8');
console.log(decrypted);
// Prints: some clear text dataconst {
  scryptSync,
  createDecipheriv,
} = require('node:crypto');
const { Buffer } = require('node:buffer');

const algorithm = 'aes-192-cbc';
const password = 'Password used to generate key';
// Use the async `crypto.scrypt()` instead.
const key = scryptSync(password, 'salt', 24);
// The IV is usually passed along with the ciphertext.
const iv = Buffer.alloc(16, 0); // Initialization vector.

const decipher = createDecipheriv(algorithm, key, iv);

// Encrypted using same algorithm, key and iv.
const encrypted =
  'e5f79c5915c02171eec6b212d5520d44480993d7d622a7c4c2da32f6efda0ffa';
let decrypted = decipher.update(encrypted, 'hex', 'utf8');
decrypted += decipher.final('utf8');
console.log(decrypted);
// Prints: some clear text data

decipher.final([outputEncoding])#

  • outputEncoding <string> 返回值的编码
  • 返回:<Buffer> | <string> 任何剩余的解密内容。如果指定了 outputEncoding,则返回一个字符串。如果没有提供 outputEncoding,则返回一个 Buffer

一旦调用了 decipher.final() 方法,Decipheriv 对象就不能再用于解密数据。多次尝试调用 decipher.final() 将导致抛出错误。

decipher.setAAD(buffer[, options])#

当使用认证加密模式(目前支持 GCMCCMOCBchacha20-poly1305)时,decipher.setAAD() 方法设置用于*附加认证数据*(AAD)输入参数的值。

对于 GCMoptions 参数是可选的。使用 CCM 时,必须指定 plaintextLength 选项,并且其值必须与密文的字节长度匹配。请参见 CCM 模式

decipher.setAAD() 方法必须在 decipher.update() 之前调用。

当将字符串作为 buffer 传递时,请考虑将字符串用作加密 API 输入的注意事项

decipher.setAuthTag(buffer[, encoding])#

当使用认证加密模式(目前支持 GCMCCMOCBchacha20-poly1305)时,decipher.setAuthTag() 方法用于传入接收到的*认证标签*。如果没有提供标签,或者密文被篡改,decipher.final() 将会抛出异常,表明由于认证失败,应丢弃该密文。如果标签长度根据 NIST SP 800-38D 无效,或者与 authTagLength 选项的值不匹配,decipher.setAuthTag() 将抛出错误。

对于 CCM 模式,decipher.setAuthTag() 必须在 decipher.update() 之前调用;对于 GCMOCB 模式和 chacha20-poly1305,则必须在 decipher.final() 之前调用。decipher.setAuthTag() 只能调用一次。

当将字符串作为认证标签传递时,请考虑将字符串用作加密 API 输入的注意事项

decipher.setAutoPadding([autoPadding])#

  • autoPadding <boolean> 默认值: true
  • 返回:<Decipheriv> 同一个 Decipher 实例,用于方法链式调用。

当数据在没有标准块填充的情况下被加密时,调用 decipher.setAutoPadding(false) 将禁用自动填充,以防止 decipher.final() 检查和移除填充。

关闭自动填充仅在输入数据的长度是密码器块大小的倍数时才有效。

decipher.setAutoPadding() 方法必须在 decipher.final() 之前调用。

decipher.update(data[, inputEncoding][, outputEncoding])#

data 更新解密器。如果给定了 inputEncoding 参数,则 data 参数是使用指定编码的字符串。如果未给定 inputEncoding 参数,data 必须是 Buffer。如果 dataBuffer,则忽略 inputEncoding

outputEncoding 指定加密数据的输出格式。如果指定了 outputEncoding,则返回使用指定编码的字符串。如果没有提供 outputEncoding,则返回一个 Buffer

decipher.update() 方法可以多次调用以处理新数据,直到调用 decipher.final() 为止。在调用 decipher.final() 之后再调用 decipher.update() 将导致抛出错误。

即使底层密码器实现了认证,此函数返回的明文的真实性和完整性此时可能不确定。对于认证加密算法,真实性通常只有在应用程序调用 decipher.final() 时才能确定。

类:DiffieHellman#

DiffieHellman 类是用于创建 Diffie-Hellman 密钥交换的实用工具。

DiffieHellman 类的实例可以使用 crypto.createDiffieHellman() 函数创建。

import assert from 'node:assert';

const {
  createDiffieHellman,
} = await import('node:crypto');

// Generate Alice's keys...
const alice = createDiffieHellman(2048);
const aliceKey = alice.generateKeys();

// Generate Bob's keys...
const bob = 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'));const assert = require('node:assert');

const {
  createDiffieHellman,
} = require('node:crypto');

// Generate Alice's keys...
const alice = createDiffieHellman(2048);
const aliceKey = alice.generateKeys();

// Generate Bob's keys...
const bob = 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'));

diffieHellman.computeSecret(otherPublicKey[, inputEncoding][, outputEncoding])#

使用 otherPublicKey 作为另一方的公钥计算共享密钥,并返回计算出的共享密钥。提供的密钥使用指定的 inputEncoding 进行解释,密钥使用指定的 outputEncoding 进行编码。如果未提供 inputEncoding,则 otherPublicKey 应为 BufferTypedArrayDataView

如果指定了 outputEncoding,则返回一个字符串;否则,返回一个 Buffer

diffieHellman.generateKeys([encoding])#

生成私有和公共 Diffie-Hellman 密钥值,除非它们已经被生成或计算过,并以指定的 encoding 返回公钥。此密钥应传输给另一方。如果提供了 encoding,则返回一个字符串;否则返回一个 Buffer

此函数是 DH_generate_key() 的一个薄封装。特别地,一旦生成或设置了私钥,调用此函数仅更新公钥,而不会生成新的私钥。

diffieHellman.getGenerator([encoding])#

以指定的 encoding 返回 Diffie-Hellman 生成器。如果提供了 encoding,则返回一个字符串;否则返回一个 Buffer

diffieHellman.getPrime([encoding])#

以指定的 encoding 返回 Diffie-Hellman 素数。如果提供了 encoding,则返回一个字符串;否则返回一个 Buffer

diffieHellman.getPrivateKey([encoding])#

以指定的 encoding 返回 Diffie-Hellman 私钥。如果提供了 encoding,则返回一个字符串;否则返回一个 Buffer

diffieHellman.getPublicKey([encoding])#

以指定的 encoding 返回 Diffie-Hellman 公钥。如果提供了 encoding,则返回一个字符串;否则返回一个 Buffer

diffieHellman.setPrivateKey(privateKey[, encoding])#

设置 Diffie-Hellman 私钥。如果提供了 encoding 参数,privateKey 应为一个字符串。如果没有提供 encodingprivateKey 应为一个 BufferTypedArrayDataView

此函数不会自动计算相关的公钥。可以使用 diffieHellman.setPublicKey()diffieHellman.generateKeys() 来手动提供公钥或自动推导它。

diffieHellman.setPublicKey(publicKey[, encoding])#

设置 Diffie-Hellman 公钥。如果提供了 encoding 参数,publicKey 应为一个字符串。如果没有提供 encodingpublicKey 应为一个 BufferTypedArrayDataView

diffieHellman.verifyError#

一个位字段,包含在 DiffieHellman 对象初始化期间执行检查时产生的任何警告和/或错误。

以下值对此属性有效(定义在 node:constants 模块中):

  • DH_CHECK_P_NOT_SAFE_PRIME
  • DH_CHECK_P_NOT_PRIME
  • DH_UNABLE_TO_CHECK_GENERATOR
  • DH_NOT_SUITABLE_GENERATOR

类:DiffieHellmanGroup#

DiffieHellmanGroup 类以一个著名的 modp 组作为其参数。它的工作方式与 DiffieHellman 相同,只是不允许在创建后更改其密钥。换句话说,它没有实现 setPublicKey()setPrivateKey() 方法。

const { createDiffieHellmanGroup } = await import('node:crypto');
const dh = createDiffieHellmanGroup('modp16');const { createDiffieHellmanGroup } = require('node:crypto');
const dh = createDiffieHellmanGroup('modp16');

支持以下组:

  • 'modp14' (2048 位, RFC 3526 第 3 节)
  • 'modp15' (3072 位, RFC 3526 第 4 节)
  • 'modp16' (4096 位, RFC 3526 第 5 节)
  • 'modp17' (6144 位, RFC 3526 第 6 节)
  • 'modp18' (8192 位, RFC 3526 第 7 节)

以下组仍然支持但已弃用(参见注意事项):

  • 'modp1' (768 位, RFC 2409 第 6.1 节)
  • 'modp2' (1024 位, RFC 2409 第 6.2 节)
  • 'modp5' (1536 位, RFC 3526 第 2 节)

这些已弃用的组可能会在未来版本的 Node.js 中被移除。

类:ECDH#

ECDH 类是用于创建椭圆曲线 Diffie-Hellman (ECDH) 密钥交换的实用工具。

ECDH 类的实例可以使用 crypto.createECDH() 函数创建。

import assert from 'node:assert';

const {
  createECDH,
} = await import('node:crypto');

// Generate Alice's keys...
const alice = createECDH('secp521r1');
const aliceKey = alice.generateKeys();

// Generate Bob's keys...
const bob = 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'));
// OKconst assert = require('node:assert');

const {
  createECDH,
} = require('node:crypto');

// Generate Alice's keys...
const alice = createECDH('secp521r1');
const aliceKey = alice.generateKeys();

// Generate Bob's keys...
const bob = 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

静态方法:ECDH.convertKey(key, curve[, inputEncoding[, outputEncoding[, format]]])#

将由 keycurve 指定的 EC Diffie-Hellman 公钥转换为由 format 指定的格式。format 参数指定点编码,可以是 'compressed''uncompressed''hybrid'。提供的密钥使用指定的 inputEncoding 进行解释,返回的密钥使用指定的 outputEncoding 进行编码。

使用 crypto.getCurves() 获取可用曲线名称的列表。在最近的 OpenSSL 版本中,openssl ecparam -list_curves 也会显示每个可用椭圆曲线的名称和描述。

如果未指定 format,则点将以 'uncompressed' 格式返回。

如果未提供 inputEncoding,则 key 应为一个 BufferTypedArrayDataView

示例(解压缩一个密钥)

const {
  createECDH,
  ECDH,
} = await import('node:crypto');

const ecdh = createECDH('secp256k1');
ecdh.generateKeys();

const compressedKey = ecdh.getPublicKey('hex', 'compressed');

const uncompressedKey = ECDH.convertKey(compressedKey,
                                        'secp256k1',
                                        'hex',
                                        'hex',
                                        'uncompressed');

// The converted key and the uncompressed public key should be the same
console.log(uncompressedKey === ecdh.getPublicKey('hex'));const {
  createECDH,
  ECDH,
} = require('node:crypto');

const ecdh = createECDH('secp256k1');
ecdh.generateKeys();

const compressedKey = ecdh.getPublicKey('hex', 'compressed');

const uncompressedKey = ECDH.convertKey(compressedKey,
                                        'secp256k1',
                                        'hex',
                                        'hex',
                                        'uncompressed');

// The converted key and the uncompressed public key should be the same
console.log(uncompressedKey === ecdh.getPublicKey('hex'));

ecdh.computeSecret(otherPublicKey[, inputEncoding][, outputEncoding])#

使用 otherPublicKey 作为另一方的公钥计算共享密钥,并返回计算出的共享密钥。提供的密钥使用指定的 inputEncoding 进行解释,返回的密钥使用指定的 outputEncoding 进行编码。如果未提供 inputEncoding,则 otherPublicKey 应为一个 BufferTypedArrayDataView

如果指定了 outputEncoding,将返回一个字符串;否则返回一个 Buffer

otherPublicKey 不在椭圆曲线上时,ecdh.computeSecret 会抛出 ERR_CRYPTO_ECDH_INVALID_PUBLIC_KEY 错误。由于 otherPublicKey 通常由远程用户通过不安全的网络提供,请务必相应地处理此异常。

ecdh.generateKeys([encoding[, format]])#

生成私有和公共 EC Diffie-Hellman 密钥值,并以指定的 formatencoding 返回公钥。此密钥应传输给另一方。

format 参数指定点编码,可以是 'compressed''uncompressed'。如果未指定 format,则点将以 'uncompressed' 格式返回。

如果提供了 encoding,则返回一个字符串;否则返回一个 Buffer

ecdh.getPrivateKey([encoding])#

如果指定了 encoding,则返回一个字符串;否则返回一个 Buffer

ecdh.getPublicKey([encoding][, format])#

format 参数指定点编码,可以是 'compressed''uncompressed'。如果未指定 format,则点将以 'uncompressed' 格式返回。

如果指定了 encoding,则返回一个字符串;否则返回一个 Buffer

ecdh.setPrivateKey(privateKey[, encoding])#

设置 EC Diffie-Hellman 私钥。如果提供了 encoding,则 privateKey 应为一个字符串;否则 privateKey 应为一个 BufferTypedArrayDataView

如果 privateKey 对创建 ECDH 对象时指定的曲线无效,则会抛出错误。设置私钥后,相关的公钥点(密钥)也会生成并设置在 ECDH 对象中。

ecdh.setPublicKey(publicKey[, encoding])#

稳定性: 0 - 废弃

设置 EC Diffie-Hellman 公钥。如果提供了 encoding,则 publicKey 应为一个字符串;否则应为一个 BufferTypedArrayDataView

通常没有理由调用此方法,因为 ECDH 只需要一个私钥和另一方的公钥来计算共享密钥。通常会调用 ecdh.generateKeys()ecdh.setPrivateKey()ecdh.setPrivateKey() 方法会尝试生成与正在设置的私钥相关的公钥点/密钥。

示例(获取共享密钥)

const {
  createECDH,
  createHash,
} = await import('node:crypto');

const alice = createECDH('secp256k1');
const bob = createECDH('secp256k1');

// This is a shortcut way of specifying one of Alice's previous private
// keys. It would be unwise to use such a predictable private key in a real
// application.
alice.setPrivateKey(
  createHash('sha256').update('alice', 'utf8').digest(),
);

// Bob uses a newly generated cryptographically strong
// pseudorandom key pair
bob.generateKeys();

const aliceSecret = alice.computeSecret(bob.getPublicKey(), null, 'hex');
const bobSecret = bob.computeSecret(alice.getPublicKey(), null, 'hex');

// aliceSecret and bobSecret should be the same shared secret value
console.log(aliceSecret === bobSecret);const {
  createECDH,
  createHash,
} = require('node:crypto');

const alice = createECDH('secp256k1');
const bob = createECDH('secp256k1');

// This is a shortcut way of specifying one of Alice's previous private
// keys. It would be unwise to use such a predictable private key in a real
// application.
alice.setPrivateKey(
  createHash('sha256').update('alice', 'utf8').digest(),
);

// Bob uses a newly generated cryptographically strong
// pseudorandom key pair
bob.generateKeys();

const aliceSecret = alice.computeSecret(bob.getPublicKey(), null, 'hex');
const bobSecret = bob.computeSecret(alice.getPublicKey(), null, 'hex');

// aliceSecret and bobSecret should be the same shared secret value
console.log(aliceSecret === bobSecret);

类:Hash#

Hash 类是用于创建数据哈希摘要的实用工具。它可以通过以下两种方式之一使用:

  • 作为一个可读可写的 ,将数据写入以在可读端产生计算出的哈希摘要,或者
  • 使用 hash.update()hash.digest() 方法来生成计算出的哈希。

crypto.createHash() 方法用于创建 Hash 实例。不应直接使用 new 关键字创建 Hash 对象。

示例:将 Hash 对象用作流

const {
  createHash,
} = await import('node:crypto');

const hash = createHash('sha256');

hash.on('readable', () => {
  // Only one element is going to be produced by the
  // hash stream.
  const data = hash.read();
  if (data) {
    console.log(data.toString('hex'));
    // Prints:
    //   6a2da20943931e9834fc12cfe5bb47bbd9ae43489a30726962b576f4e3993e50
  }
});

hash.write('some data to hash');
hash.end();const {
  createHash,
} = require('node:crypto');

const hash = createHash('sha256');

hash.on('readable', () => {
  // Only one element is going to be produced by the
  // hash stream.
  const data = hash.read();
  if (data) {
    console.log(data.toString('hex'));
    // Prints:
    //   6a2da20943931e9834fc12cfe5bb47bbd9ae43489a30726962b576f4e3993e50
  }
});

hash.write('some data to hash');
hash.end();

示例:使用 Hash 和管道流

import { createReadStream } from 'node:fs';
import { stdout } from 'node:process';
const { createHash } = await import('node:crypto');

const hash = createHash('sha256');

const input = createReadStream('test.js');
input.pipe(hash).setEncoding('hex').pipe(stdout);const { createReadStream } = require('node:fs');
const { createHash } = require('node:crypto');
const { stdout } = require('node:process');

const hash = createHash('sha256');

const input = createReadStream('test.js');
input.pipe(hash).setEncoding('hex').pipe(stdout);

示例:使用 hash.update()hash.digest() 方法

const {
  createHash,
} = await import('node:crypto');

const hash = createHash('sha256');

hash.update('some data to hash');
console.log(hash.digest('hex'));
// Prints:
//   6a2da20943931e9834fc12cfe5bb47bbd9ae43489a30726962b576f4e3993e50const {
  createHash,
} = require('node:crypto');

const hash = createHash('sha256');

hash.update('some data to hash');
console.log(hash.digest('hex'));
// Prints:
//   6a2da20943931e9834fc12cfe5bb47bbd9ae43489a30726962b576f4e3993e50

hash.copy([options])#

创建一个新的 Hash 对象,其中包含当前 Hash 对象内部状态的深拷贝。

可选的 options 参数控制流的行为。对于 XOF 哈希函数(如 'shake256'),可以使用 outputLength 选项来指定所需的输出长度(以字节为单位)。

当在其 hash.digest() 方法被调用后尝试复制 Hash 对象时,会抛出一个错误。

// Calculate a rolling hash.
const {
  createHash,
} = await import('node:crypto');

const hash = createHash('sha256');

hash.update('one');
console.log(hash.copy().digest('hex'));

hash.update('two');
console.log(hash.copy().digest('hex'));

hash.update('three');
console.log(hash.copy().digest('hex'));

// Etc.// Calculate a rolling hash.
const {
  createHash,
} = require('node:crypto');

const hash = createHash('sha256');

hash.update('one');
console.log(hash.copy().digest('hex'));

hash.update('two');
console.log(hash.copy().digest('hex'));

hash.update('three');
console.log(hash.copy().digest('hex'));

// Etc.

hash.digest([encoding])#

计算所有被传入进行哈希处理的数据的摘要(使用 hash.update() 方法)。如果提供了 encoding,将返回一个字符串;否则返回一个 Buffer

hash.digest() 方法被调用后,Hash 对象不能再次使用。多次调用会引发错误。

hash.update(data[, inputEncoding])#

用给定的 data 更新哈希内容,其编码在 inputEncoding 中给出。如果未提供 encoding,且 data 是一个字符串,则强制使用 'utf8' 编码。如果 data 是一个 BufferTypedArrayDataView,则忽略 inputEncoding

当新数据以流式传输时,可以多次调用此方法。

类:Hmac#

Hmac 类是用于创建加密 HMAC 摘要的实用工具。它可以通过以下两种方式之一使用:

  • 作为一个可读可写的 ,将数据写入以在可读端产生计算出的 HMAC 摘要,或者
  • 使用 hmac.update()hmac.digest() 方法来生成计算出的 HMAC 摘要。

crypto.createHmac() 方法用于创建 Hmac 实例。不应直接使用 new 关键字创建 Hmac 对象。

示例:将 Hmac 对象用作流

const {
  createHmac,
} = await import('node:crypto');

const hmac = createHmac('sha256', 'a secret');

hmac.on('readable', () => {
  // Only one element is going to be produced by the
  // hash stream.
  const data = hmac.read();
  if (data) {
    console.log(data.toString('hex'));
    // Prints:
    //   7fd04df92f636fd450bc841c9418e5825c17f33ad9c87c518115a45971f7f77e
  }
});

hmac.write('some data to hash');
hmac.end();const {
  createHmac,
} = require('node:crypto');

const hmac = createHmac('sha256', 'a secret');

hmac.on('readable', () => {
  // Only one element is going to be produced by the
  // hash stream.
  const data = hmac.read();
  if (data) {
    console.log(data.toString('hex'));
    // Prints:
    //   7fd04df92f636fd450bc841c9418e5825c17f33ad9c87c518115a45971f7f77e
  }
});

hmac.write('some data to hash');
hmac.end();

示例:使用 Hmac 和管道流

import { createReadStream } from 'node:fs';
import { stdout } from 'node:process';
const {
  createHmac,
} = await import('node:crypto');

const hmac = createHmac('sha256', 'a secret');

const input = createReadStream('test.js');
input.pipe(hmac).pipe(stdout);const {
  createReadStream,
} = require('node:fs');
const {
  createHmac,
} = require('node:crypto');
const { stdout } = require('node:process');

const hmac = createHmac('sha256', 'a secret');

const input = createReadStream('test.js');
input.pipe(hmac).pipe(stdout);

示例:使用 hmac.update()hmac.digest() 方法

const {
  createHmac,
} = await import('node:crypto');

const hmac = createHmac('sha256', 'a secret');

hmac.update('some data to hash');
console.log(hmac.digest('hex'));
// Prints:
//   7fd04df92f636fd450bc841c9418e5825c17f33ad9c87c518115a45971f7f77econst {
  createHmac,
} = require('node:crypto');

const hmac = createHmac('sha256', 'a secret');

hmac.update('some data to hash');
console.log(hmac.digest('hex'));
// Prints:
//   7fd04df92f636fd450bc841c9418e5825c17f33ad9c87c518115a45971f7f77e

hmac.digest([encoding])#

计算使用 hmac.update() 传入的所有数据的 HMAC 摘要。如果提供了 encoding,则返回一个字符串;否则返回一个 Buffer

hmac.digest() 被调用后,Hmac 对象不能再次使用。多次调用 hmac.digest() 将导致抛出错误。

hmac.update(data[, inputEncoding])#

用给定的 data 更新 Hmac 内容,其编码在 inputEncoding 中给出。如果未提供 encoding,且 data 是一个字符串,则强制使用 'utf8' 编码。如果 data 是一个 BufferTypedArrayDataView,则忽略 inputEncoding

当新数据以流式传输时,可以多次调用此方法。

类:KeyObject#

Node.js 使用 KeyObject 类来表示对称或非对称密钥,每种密钥都公开不同的函数。crypto.createSecretKey()crypto.createPublicKey()crypto.createPrivateKey() 方法用于创建 KeyObject 实例。不应直接使用 new 关键字创建 KeyObject 对象。

由于具有改进的安全特性,大多数应用程序应考虑使用新的 KeyObject API,而不是将密钥作为字符串或 Buffer 传递。

KeyObject 实例可以通过 postMessage() 传递给其他线程。接收方会获得一个克隆的 KeyObject,且该 KeyObject 不需要列在 transferList 参数中。

静态方法:KeyObject.from(key)#

示例:将 CryptoKey 实例转换为 KeyObject

const { KeyObject } = await import('node:crypto');
const { subtle } = globalThis.crypto;

const key = await subtle.generateKey({
  name: 'HMAC',
  hash: 'SHA-256',
  length: 256,
}, true, ['sign', 'verify']);

const keyObject = KeyObject.from(key);
console.log(keyObject.symmetricKeySize);
// Prints: 32 (symmetric key size in bytes)const { KeyObject } = require('node:crypto');
const { subtle } = globalThis.crypto;

(async function() {
  const key = await subtle.generateKey({
    name: 'HMAC',
    hash: 'SHA-256',
    length: 256,
  }, true, ['sign', 'verify']);

  const keyObject = KeyObject.from(key);
  console.log(keyObject.symmetricKeySize);
  // Prints: 32 (symmetric key size in bytes)
})();

keyObject.asymmetricKeyDetails#

  • 类型:<Object>
    • modulusLength <number> 密钥大小(以位为单位)(RSA, DSA)。
    • publicExponent <bigint> 公共指数(RSA)。
    • hashAlgorithm <string> 消息摘要的名称(RSA-PSS)。
    • mgf1HashAlgorithm <string> MGF1 使用的消息摘要的名称(RSA-PSS)。
    • saltLength <number> 最小盐长度(以字节为单位)(RSA-PSS)。
    • divisorLength <number> q 的大小(以位为单位)(DSA)。
    • namedCurve <string> 曲线名称(EC)。

此属性仅存在于非对称密钥上。根据密钥的类型,此对象包含有关密钥的信息。通过此属性获得的任何信息都不能用于唯一标识密钥或危及密钥的安全性。

对于 RSA-PSS 密钥,如果密钥材料包含 RSASSA-PSS-params 序列,则将设置 hashAlgorithmmgf1HashAlgorithmsaltLength 属性。

其他密钥详细信息可能会通过此 API 使用其他属性公开。

keyObject.asymmetricKeyType#

对于非对称密钥,此属性表示密钥的类型。请参见支持的非对称密钥类型

对于无法识别的 KeyObject 类型和对称密钥,此属性为 undefined

keyObject.equals(otherKeyObject)#

根据密钥是否具有完全相同的类型、值和参数,返回 truefalse。此方法不是时间恒定的

keyObject.export([options])#

对于对称密钥,可以使用以下编码选项:

  • format <string> 必须是 'buffer' (默认) 或 'jwk'

对于公钥,可以使用以下编码选项:

  • type <string> 必须是 'pkcs1' (仅限 RSA) 或 'spki' 之一。
  • format <string> 必须是 'pem''der''jwk'

对于私钥,可以使用以下编码选项:

  • type <string> 必须是 'pkcs1' (仅限 RSA)、'pkcs8''sec1' (仅限 EC) 之一。
  • format <string> 必须是 'pem''der''jwk'
  • cipher <string> 如果指定,私钥将使用给定的 cipherpassphrase 通过 PKCS#5 v2.0 基于密码的加密进行加密。
  • passphrase <string> | <Buffer> 用于加密的密码短语,参见 cipher

结果类型取决于所选的编码格式,当为 PEM 时,结果为字符串;当为 DER 时,它将是一个包含 DER 编码数据的缓冲区;当为 JWK 时,它将是一个对象。

当选择 JWK 编码格式时,所有其他编码选项都将被忽略。

PKCS#1、SEC1 和 PKCS#8 类型的密钥可以通过结合使用 cipherformat 选项来加密。PKCS#8 type 可以与任何 format 一起使用,通过指定 cipher 来加密任何密钥算法(RSA、EC 或 DH)。PKCS#1 和 SEC1 只能在指定 cipher 且使用 PEM format 时进行加密。为了获得最大的兼容性,请使用 PKCS#8 来处理加密的私钥。由于 PKCS#8 定义了自己的加密机制,因此在加密 PKCS#8 密钥时不支持 PEM 级别的加密。有关 PKCS#8 加密,请参见 RFC 5208;有关 PKCS#1 和 SEC1 加密,请参见 RFC 1421

keyObject.symmetricKeySize#

对于密钥,此属性表示密钥的大小(以字节为单位)。对于非对称密钥,此属性为 undefined

keyObject.toCryptoKey(algorithm, extractable, keyUsages)#

KeyObject 实例转换为 CryptoKey

keyObject.type#

根据此 KeyObject 的类型,此属性对于密钥(对称密钥)为 'secret',对于公钥(非对称密钥)为 'public',或对于私钥(非对称密钥)为 'private'

类:Sign#

Sign 类是用于生成签名的实用工具。它可以通过以下两种方式之一使用:

crypto.createSign() 方法用于创建 Sign 实例。参数是所用哈希函数的字符串名称。不应直接使用 new 关键字创建 Sign 对象。

示例:将 SignVerify 对象用作流

const {
  generateKeyPairSync,
  createSign,
  createVerify,
} = await import('node:crypto');

const { privateKey, publicKey } = generateKeyPairSync('ec', {
  namedCurve: 'sect239k1',
});

const sign = createSign('SHA256');
sign.write('some data to sign');
sign.end();
const signature = sign.sign(privateKey, 'hex');

const verify = createVerify('SHA256');
verify.write('some data to sign');
verify.end();
console.log(verify.verify(publicKey, signature, 'hex'));
// Prints: trueconst {
  generateKeyPairSync,
  createSign,
  createVerify,
} = require('node:crypto');

const { privateKey, publicKey } = generateKeyPairSync('ec', {
  namedCurve: 'sect239k1',
});

const sign = createSign('SHA256');
sign.write('some data to sign');
sign.end();
const signature = sign.sign(privateKey, 'hex');

const verify = createVerify('SHA256');
verify.write('some data to sign');
verify.end();
console.log(verify.verify(publicKey, signature, 'hex'));
// Prints: true

示例:使用 sign.update()verify.update() 方法

const {
  generateKeyPairSync,
  createSign,
  createVerify,
} = await import('node:crypto');

const { privateKey, publicKey } = generateKeyPairSync('rsa', {
  modulusLength: 2048,
});

const sign = createSign('SHA256');
sign.update('some data to sign');
sign.end();
const signature = sign.sign(privateKey);

const verify = createVerify('SHA256');
verify.update('some data to sign');
verify.end();
console.log(verify.verify(publicKey, signature));
// Prints: trueconst {
  generateKeyPairSync,
  createSign,
  createVerify,
} = require('node:crypto');

const { privateKey, publicKey } = generateKeyPairSync('rsa', {
  modulusLength: 2048,
});

const sign = createSign('SHA256');
sign.update('some data to sign');
sign.end();
const signature = sign.sign(privateKey);

const verify = createVerify('SHA256');
verify.update('some data to sign');
verify.end();
console.log(verify.verify(publicKey, signature));
// Prints: true

sign.sign(privateKey[, outputEncoding])#

计算通过 sign.update()sign.write() 传入的所有数据的签名。

如果 privateKey 不是一个 KeyObject,此函数的行为就好像 privateKey 已被传递给 crypto.createPrivateKey()。如果它是一个对象,可以传递以下附加属性:

  • dsaEncoding <string> 对于 DSA 和 ECDSA,此选项指定生成签名的格式。它可以是以下之一:

    • 'der' (默认): DER 编码的 ASN.1 签名结构,编码为 (r, s)
    • 'ieee-p1363':IEEE-P1363 中提议的签名格式 r || s
  • padding <integer> RSA 的可选填充值,为以下之一:

    • crypto.constants.RSA_PKCS1_PADDING (默认)
    • crypto.constants.RSA_PKCS1_PSS_PADDING

    RSA_PKCS1_PSS_PADDING 将使用 MGF1,其哈希函数与用于签署消息的哈希函数相同,如 RFC 4055 第 3.1 节所述,除非已根据 RFC 4055 第 3.3 节的规定,在密钥中指定了 MGF1 哈希函数。

  • saltLength <integer> 当填充为 RSA_PKCS1_PSS_PADDING 时的盐长度。特殊值 crypto.constants.RSA_PSS_SALTLEN_DIGEST 将盐长度设置为摘要大小,crypto.constants.RSA_PSS_SALTLEN_MAX_SIGN (默认) 将其设置为最大允许值。

如果提供了 outputEncoding,则返回一个字符串;否则返回一个 Buffer

sign.sign() 方法被调用后,Sign 对象不能再次使用。多次调用 sign.sign() 将导致抛出错误。

sign.update(data[, inputEncoding])#

用给定的 data 更新 Sign 内容,其编码在 inputEncoding 中给出。如果未提供 encoding,且 data 是一个字符串,则强制使用 'utf8' 编码。如果 data 是一个 BufferTypedArrayDataView,则忽略 inputEncoding

当新数据以流式传输时,可以多次调用此方法。

类:Verify#

Verify 类是用于验证签名的实用工具。它可以通过以下两种方式之一使用:

crypto.createVerify() 方法用于创建 Verify 实例。不应直接使用 new 关键字创建 Verify 对象。

有关示例,请参见 Sign

verify.update(data[, inputEncoding])#

用给定的 data 更新 Verify 内容,其编码在 inputEncoding 中给出。如果未提供 inputEncoding,且 data 是一个字符串,则强制使用 'utf8' 编码。如果 data 是一个 BufferTypedArrayDataView,则忽略 inputEncoding

当新数据以流式传输时,可以多次调用此方法。

verify.verify(object, signature[, signatureEncoding])#

使用给定的 objectsignature 验证所提供的数据。

如果 object 不是一个 KeyObject,此函数的行为就好像 object 已被传递给 crypto.createPublicKey()。如果它是一个对象,可以传递以下附加属性:

  • dsaEncoding <string> 对于 DSA 和 ECDSA,此选项指定签名的格式。它可以是以下之一:

    • 'der' (默认): DER 编码的 ASN.1 签名结构,编码为 (r, s)
    • 'ieee-p1363':IEEE-P1363 中提议的签名格式 r || s
  • padding <integer> RSA 的可选填充值,为以下之一:

    • crypto.constants.RSA_PKCS1_PADDING (默认)
    • crypto.constants.RSA_PKCS1_PSS_PADDING

    RSA_PKCS1_PSS_PADDING 将使用 MGF1,其哈希函数与用于验证消息的哈希函数相同,如 RFC 4055 第 3.1 节所述,除非已根据 RFC 4055 第 3.3 节的规定,在密钥中指定了 MGF1 哈希函数。

  • saltLength <integer> 当填充为 RSA_PKCS1_PSS_PADDING 时的盐长度。特殊值 crypto.constants.RSA_PSS_SALTLEN_DIGEST 将盐长度设置为摘要大小,crypto.constants.RSA_PSS_SALTLEN_AUTO (默认) 使其自动确定。

signature 参数是先前为数据计算的签名,其编码为 signatureEncoding。如果指定了 signatureEncoding,则 signature 应为一个字符串;否则 signature 应为一个 BufferTypedArrayDataView

verify.verify() 被调用后,verify 对象不能再次使用。多次调用 verify.verify() 将导致抛出错误。

因为公钥可以从私钥派生,所以可以传递私钥来代替公钥。

类:X509Certificate#

封装 X509 证书并提供对其信息的只读访问。

const { X509Certificate } = await import('node:crypto');

const x509 = new X509Certificate('{... pem encoded cert ...}');

console.log(x509.subject);const { X509Certificate } = require('node:crypto');

const x509 = new X509Certificate('{... pem encoded cert ...}');

console.log(x509.subject);

new X509Certificate(buffer)#

x509.ca#

  • 类型:<boolean> 如果这是证书颁发机构 (CA) 证书,则为 true

x509.checkEmail(email[, options])#

检查证书是否与给定的电子邮件地址匹配。

如果 'subject' 选项未定义或设置为 'default',则仅当主题备用名称扩展不存在或不包含任何电子邮件地址时,才会考虑证书主题。

如果 'subject' 选项设置为 'always',并且主题备用名称扩展不存在或不包含匹配的电子邮件地址,则会考虑证书主题。

如果 'subject' 选项设置为 'never',则永远不会考虑证书主题,即使证书不包含主题备用名称。

x509.checkHost(name[, options])#

  • name <string>
  • options <Object>
    • subject <string> 'default''always''never'默认值: 'default'
    • wildcards <boolean> 默认值: true
    • partialWildcards <boolean> 默认值: true
    • multiLabelWildcards <boolean> 默认值: false
    • singleLabelSubdomains <boolean> 默认值: false
  • 返回:<string> | <undefined> 返回与 name 匹配的主题名称,如果没有主题名称匹配 name,则返回 undefined

检查证书是否与给定的主机名匹配。

如果证书与给定的主机名匹配,则返回匹配的主题名称。返回的名称可能是精确匹配(例如 foo.example.com),也可能包含通配符(例如 *.example.com)。因为主机名比较不区分大小写,所以返回的主题名称的大小写也可能与给定的 name 不同。

如果 'subject' 选项未定义或设置为 'default',则仅当主题备用名称扩展不存在或不包含任何 DNS 名称时,才会考虑证书主题。此行为与 RFC 2818(“HTTP Over TLS”)一致。

如果 'subject' 选项设置为 'always',并且主题备用名称扩展不存在或不包含匹配的 DNS 名称,则会考虑证书主题。

如果 'subject' 选项设置为 'never',则永远不会考虑证书主题,即使证书不包含主题备用名称。

x509.checkIP(ip)#

检查证书是否与给定的 IP 地址(IPv4 或 IPv6)匹配。

仅考虑 RFC 5280 iPAddress 主题备用名称,并且它们必须与给定的 ip 地址完全匹配。证书的其他主题备用名称以及主题字段将被忽略。

x509.checkIssued(otherCert)#

通过比较证书元数据,检查此证书是否可能由给定的 otherCert 颁发。

这对于修剪可能的颁发者证书列表非常有用,这些证书是使用更基本的过滤例程(即仅基于主题和颁发者名称)选择的。

最后,要验证此证书的签名是由与 otherCert 的公钥对应的私钥生成的,请使用 x509.verify(publicKey),其中 otherCert 的公钥表示为一个 KeyObject,如下所示

if (!x509.verify(otherCert.publicKey)) {
  throw new Error('otherCert did not issue x509');
} 

x509.checkPrivateKey(privateKey)#

检查此证书的公钥是否与给定的私钥一致。

x509.fingerprint#

此证书的 SHA-1 指纹。

由于 SHA-1 在密码学上已被破解,并且 SHA-1 的安全性明显低于通常用于签署证书的算法,因此请考虑改用 x509.fingerprint256

x509.fingerprint256#

此证书的 SHA-256 指纹。

x509.fingerprint512#

此证书的 SHA-512 指纹。

由于计算 SHA-256 指纹通常更快,并且其大小仅为 SHA-512 指纹的一半,因此 x509.fingerprint256 可能是更好的选择。虽然 SHA-512 通常被认为提供了更高的安全级别,但 SHA-256 的安全性与大多数常用于签署证书的算法相匹配。

x509.infoAccess#

证书的颁发机构信息访问扩展的文本表示。

这是一个以换行符分隔的访问描述列表。每行以访问方法和访问位置的类型开头,后跟一个冒号和与访问位置关联的值。

在表示访问方法和访问位置类型的前缀之后,每行剩余的部分可能会用引号括起来,以表明该值是一个 JSON 字符串字面量。为了向后兼容,Node.js 仅在为避免歧义而必要时才在此属性内使用 JSON 字符串字面量。第三方代码应准备好处理这两种可能的条目格式。

x509.issuer#

此证书中包含的颁发者标识。

x509.issuerCertificate#

颁发者证书,如果颁发者证书不可用,则为 undefined

x509.keyUsage#

一个详细说明此证书密钥扩展用途的数组。

x509.publicKey#

此证书的公钥 <KeyObject>

x509.raw#

一个包含此证书 DER 编码的 Buffer

x509.serialNumber#

此证书的序列号。

序列号由证书颁发机构分配,不能唯一标识证书。请考虑改用 x509.fingerprint256 作为唯一标识符。

x509.subject#

此证书的完整主题。

x509.subjectAltName#

为此证书指定的主题备用名称。

这是一个以逗号分隔的主题备用名称列表。每个条目以一个标识主题备用名称类型的字符串开头,后跟一个冒号和与该条目关联的值。

早期版本的 Node.js 错误地假设在此属性上按双字符序列 ', ' 进行分割是安全的(参见 CVE-2021-44532)。然而,无论是恶意证书还是合法证书,在表示为字符串时,其主题备用名称都可能包含此序列。

在表示条目类型的前缀之后,每个条目的剩余部分可能会用引号括起来,以表明该值是一个 JSON 字符串字面量。为了向后兼容,Node.js 仅在为避免歧义而必要时才在此属性内使用 JSON 字符串字面量。第三方代码应准备好处理这两种可能的条目格式。

x509.toJSON()#

没有针对 X509 证书的标准 JSON 编码。toJSON() 方法返回一个包含 PEM 编码证书的字符串。

x509.toLegacyObject()#

使用旧的 证书对象 编码返回有关此证书的信息。

x509.toString()#

返回 PEM 编码的证书。

x509.validFrom#

此证书的有效起始日期/时间。

x509.validFromDate#

此证书的有效起始日期/时间,封装在一个 Date 对象中。

x509.validTo#

此证书的有效截止日期/时间。

x509.validToDate#

此证书的有效截止日期/时间,封装在一个 Date 对象中。

x509.signatureAlgorithm#

用于签署证书的算法,如果 OpenSSL 不知道该签名算法,则为 undefined

x509.signatureAlgorithmOid#

用于签署证书的算法的 OID。

x509.verify(publicKey)#

验证此证书是否由给定的公钥签名。不对证书执行任何其他验证检查。

node:crypto 模块方法和属性#

crypto.argon2(algorithm, parameters, callback)#

稳定性:1.2 - 候选发布

提供一个异步的 Argon2 实现。Argon2 是一种基于密码的密钥派生函数,旨在使计算和内存成本高昂,从而使暴力攻击变得不划算。

nonce 应该尽可能唯一。建议 nonce 是随机的且至少 16 字节长。详情请参阅 NIST SP 800-132

当为 messagenoncesecretassociatedData 传递字符串时,请考虑将字符串用作加密 API 输入时的注意事项

callback 函数被调用时带有两个参数:errderivedKey。当密钥派生失败时,err 是一个异常对象,否则 errnullderivedKey 作为 Buffer 传递给回调函数。

当任何输入参数指定了无效的值或类型时,会抛出异常。

const { argon2, randomBytes } = await import('node:crypto');

const parameters = {
  message: 'password',
  nonce: randomBytes(16),
  parallelism: 4,
  tagLength: 64,
  memory: 65536,
  passes: 3,
};

argon2('argon2id', parameters, (err, derivedKey) => {
  if (err) throw err;
  console.log(derivedKey.toString('hex'));  // 'af91dad...9520f15'
});const { argon2, randomBytes } = require('node:crypto');

const parameters = {
  message: 'password',
  nonce: randomBytes(16),
  parallelism: 4,
  tagLength: 64,
  memory: 65536,
  passes: 3,
};

argon2('argon2id', parameters, (err, derivedKey) => {
  if (err) throw err;
  console.log(derivedKey.toString('hex'));  // 'af91dad...9520f15'
});

crypto.argon2Sync(algorithm, parameters)#

稳定性:1.2 - 候选发布

  • algorithm <string> Argon2 的变体,为 "argon2d""argon2i""argon2id" 之一。
  • parameters <Object>
    • message <string> | <ArrayBuffer> | <Buffer> | <TypedArray> | <DataView> 必需,这是 Argon2 密码哈希应用的密码。
    • nonce <string> | <ArrayBuffer> | <Buffer> | <TypedArray> | <DataView> 必需,必须至少为 8 字节长。这是 Argon2 密码哈希应用的盐。
    • parallelism <number> 必需,并行度决定了可以运行多少个计算链(通道)。必须大于 1 且小于 2**24-1
    • tagLength <number> 必需,要生成的密钥长度。必须大于 4 且小于 2**32-1
    • memory <number> 必需,以 1KiB 块为单位的内存成本。必须大于 8 * parallelism 且小于 2**32-1。实际块数会向下取整到最接近 4 * parallelism 的倍数。
    • passes <number> 必需,传递次数(迭代次数)。必须大于 1 且小于 2**32-1
    • secret <string> | <ArrayBuffer> | <Buffer> | <TypedArray> | <DataView> | <undefined> 可选,随机的附加输入,类似于盐,应与派生密钥一起存储。这在密码哈希应用中被称为“胡椒”(pepper)。如果使用,长度不得超过 2**32-1 字节。
    • associatedData <string> | <ArrayBuffer> | <Buffer> | <TypedArray> | <DataView> | <undefined> 可选,要添加到哈希中的附加数据,功能上等同于盐或秘密,但用于非随机数据。如果使用,长度不得超过 2**32-1 字节。
  • 返回:<Buffer>

提供一个同步的 Argon2 实现。Argon2 是一种基于密码的密钥派生函数,旨在使计算和内存成本高昂,从而使暴力攻击变得不划算。

nonce 应该尽可能唯一。建议 nonce 是随机的且至少 16 字节长。详情请参阅 NIST SP 800-132

当为 messagenoncesecretassociatedData 传递字符串时,请考虑将字符串用作加密 API 输入时的注意事项

当密钥派生失败时,会抛出异常,否则派生的密钥将作为 Buffer 返回。

当任何输入参数指定了无效的值或类型时,会抛出异常。

const { argon2Sync, randomBytes } = await import('node:crypto');

const parameters = {
  message: 'password',
  nonce: randomBytes(16),
  parallelism: 4,
  tagLength: 64,
  memory: 65536,
  passes: 3,
};

const derivedKey = argon2Sync('argon2id', parameters);
console.log(derivedKey.toString('hex'));  // 'af91dad...9520f15'const { argon2Sync, randomBytes } = require('node:crypto');

const parameters = {
  message: 'password',
  nonce: randomBytes(16),
  parallelism: 4,
  tagLength: 64,
  memory: 65536,
  passes: 3,
};

const derivedKey = argon2Sync('argon2id', parameters);
console.log(derivedKey.toString('hex'));  // 'af91dad...9520f15'

crypto.checkPrime(candidate[, options], callback)#

  • candidate <ArrayBuffer> | <SharedArrayBuffer> | <TypedArray> | <Buffer> | <DataView> | <bigint> 一个可能的素数,编码为任意长度的大端字节序列。
  • options <Object>
    • checks <number> 要执行的 Miller-Rabin 概率性素性测试的迭代次数。当值为 0(零)时,将使用一定数量的检查,使得对于随机输入,假阳性率最多为 2-64。选择检查次数时必须谨慎。有关更多详细信息,请参阅 OpenSSL 文档中关于 BN_is_prime_ex 函数的 nchecks 选项。默认值: 0
  • callback <Function>
    • err <Error> 如果在检查期间发生错误,则设置为一个 <Error> 对象。
    • result <boolean> 如果候选数是素数且错误概率小于 0.25 ** options.checks,则为 true

检查 candidate 的素性。

crypto.checkPrimeSync(candidate[, options])#

  • candidate <ArrayBuffer> | <SharedArrayBuffer> | <TypedArray> | <Buffer> | <DataView> | <bigint> 一个可能的素数,编码为任意长度的大端字节序列。
  • options <Object>
    • checks <number> 要执行的 Miller-Rabin 概率性素性测试的迭代次数。当值为 0(零)时,将使用一定数量的检查,使得对于随机输入,假阳性率最多为 2-64。选择检查次数时必须谨慎。有关更多详细信息,请参阅 OpenSSL 文档中关于 BN_is_prime_ex 函数的 nchecks 选项。默认值: 0
  • 返回:<boolean> 如果候选数是素数且错误概率小于 0.25 ** options.checks,则为 true

检查 candidate 的素性。

crypto.constants#

一个包含与加密和安全相关操作常用常量的对象。当前定义的具体常量在 加密常量 中有描述。

crypto.createCipheriv(algorithm, key, iv[, options])#

创建并返回一个 Cipheriv 对象,使用给定的 algorithmkey 和初始化向量 (iv)。

options 参数控制流行为,并且是可选的,除非使用 CCM 或 OCB 模式的密码(例如 'aes-128-ccm')。在这种情况下,authTagLength 选项是必需的,并指定身份验证标签的长度(以字节为单位),请参阅 CCM 模式。在 GCM 模式下,authTagLength 选项不是必需的,但可用于设置将由 getAuthTag() 返回的身份验证标签的长度,默认为 16 字节。对于 chacha20-poly1305authTagLength 选项默认为 16 字节。

algorithm 取决于 OpenSSL,例如 'aes192' 等。在最新的 OpenSSL 版本中,openssl list -cipher-algorithms 将显示可用的密码算法。

keyalgorithm 使用的原始密钥,iv初始化向量。两个参数都必须是 'utf8' 编码的字符串、BuffersTypedArrayDataViewkey 也可以是类型为 secretKeyObject。如果密码不需要初始化向量,iv 可以是 null

当为 keyiv 传递字符串时,请考虑将字符串用作加密 API 输入时的注意事项

初始化向量应该是不可预测且唯一的;理想情况下,它们应该是加密安全的随机数。它们不必是机密的:IV 通常只是以未加密的形式添加到密文消息中。某些事物必须是不可预测且唯一的,但又不必是机密的,这听起来可能有些矛盾;请记住,攻击者必须不能提前预测出给定的 IV 将是什么。

crypto.createDecipheriv(algorithm, key, iv[, options])#

创建并返回一个使用给定 algorithmkey 和初始化向量 (iv) 的 Decipheriv 对象。

options 参数控制流行为,并且是可选的,除非使用 CCM 或 OCB 模式的密码(例如 'aes-128-ccm')。在这种情况下,authTagLength 选项是必需的,并指定身份验证标签的长度(以字节为单位),请参阅 CCM 模式。对于 AES-GCM 和 chacha20-poly1305authTagLength 选项默认为 16 字节,如果使用不同长度,则必须设置为不同的值。

algorithm 取决于 OpenSSL,例如 'aes192' 等。在最新的 OpenSSL 版本中,openssl list -cipher-algorithms 将显示可用的密码算法。

keyalgorithm 使用的原始密钥,iv初始化向量。两个参数都必须是 'utf8' 编码的字符串、BuffersTypedArrayDataViewkey 也可以是类型为 secretKeyObject。如果密码不需要初始化向量,iv 可以是 null

当为 keyiv 传递字符串时,请考虑将字符串用作加密 API 输入时的注意事项

初始化向量应该是不可预测且唯一的;理想情况下,它们应该是加密安全的随机数。它们不必是机密的:IV 通常只是以未加密的形式添加到密文消息中。某些事物必须是不可预测且唯一的,但又不必是机密的,这听起来可能有些矛盾;请记住,攻击者必须不能提前预测出给定的 IV 将是什么。

crypto.createDiffieHellman(prime[, primeEncoding][, generator][, generatorEncoding])#

使用提供的 prime 和一个可选的特定 generator 创建一个 DiffieHellman 密钥交换对象。

generator 参数可以是一个数字、字符串或 Buffer。如果未指定 generator,则使用值 2

如果指定了 primeEncoding,则 prime 应为字符串;否则应为 BufferTypedArrayDataView

如果指定了 generatorEncoding,则 generator 应为字符串;否则应为数字、BufferTypedArrayDataView

crypto.createDiffieHellman(primeLength[, generator])#

创建一个 DiffieHellman 密钥交换对象,并使用可选的特定数值 generator 生成一个 primeLength 位的素数。如果未指定 generator,则使用值 2

crypto.createDiffieHellmanGroup(name)#

crypto.getDiffieHellman() 的别名

crypto.createECDH(curveName)#

使用由 curveName 字符串指定的预定义曲线创建一个椭圆曲线迪菲-赫尔曼 (ECDH) 密钥交换对象。使用 crypto.getCurves() 获取可用曲线名称的列表。在最新的 OpenSSL 版本中,openssl ecparam -list_curves 也会显示每个可用椭圆曲线的名称和描述。

crypto.createHash(algorithm[, options])#

创建并返回一个 Hash 对象,该对象可用于使用给定的 algorithm 生成哈希摘要。可选的 options 参数控制流行为。对于 XOF 哈希函数(如 'shake256'),可以使用 outputLength 选项来指定所需的输出长度(以字节为单位)。

algorithm 取决于平台上 OpenSSL 版本支持的可用算法。例如 'sha256''sha512' 等。在最新的 OpenSSL 版本中,openssl list -digest-algorithms 将显示可用的摘要算法。

示例:生成文件的 sha256 校验和

import {
  createReadStream,
} from 'node:fs';
import { argv } from 'node:process';
const {
  createHash,
} = await import('node:crypto');

const filename = argv[2];

const hash = createHash('sha256');

const input = createReadStream(filename);
input.on('readable', () => {
  // Only one element is going to be produced by the
  // hash stream.
  const data = input.read();
  if (data)
    hash.update(data);
  else {
    console.log(`${hash.digest('hex')} ${filename}`);
  }
});const {
  createReadStream,
} = require('node:fs');
const {
  createHash,
} = require('node:crypto');
const { argv } = require('node:process');

const filename = argv[2];

const hash = createHash('sha256');

const input = createReadStream(filename);
input.on('readable', () => {
  // Only one element is going to be produced by the
  // hash stream.
  const data = input.read();
  if (data)
    hash.update(data);
  else {
    console.log(`${hash.digest('hex')} ${filename}`);
  }
});

crypto.createHmac(algorithm, key[, options])#

创建并返回一个使用给定 algorithmkeyHmac 对象。可选的 options 参数控制流行为。

algorithm 取决于平台上 OpenSSL 版本支持的可用算法。例如 'sha256''sha512' 等。在最新的 OpenSSL 版本中,openssl list -digest-algorithms 将显示可用的摘要算法。

key 是用于生成加密 HMAC 哈希的 HMAC 密钥。如果它是一个 KeyObject,其类型必须是 secret。如果它是一个字符串,请考虑将字符串用作加密 API 输入时的注意事项。如果它是从加密安全的熵源(如 crypto.randomBytes()crypto.generateKey())获得的,其长度不应超过 algorithm 的块大小(例如,对于 SHA-256 为 512 位)。

示例:生成文件的 sha256 HMAC

import {
  createReadStream,
} from 'node:fs';
import { argv } from 'node:process';
const {
  createHmac,
} = await import('node:crypto');

const filename = argv[2];

const hmac = createHmac('sha256', 'a secret');

const input = createReadStream(filename);
input.on('readable', () => {
  // Only one element is going to be produced by the
  // hash stream.
  const data = input.read();
  if (data)
    hmac.update(data);
  else {
    console.log(`${hmac.digest('hex')} ${filename}`);
  }
});const {
  createReadStream,
} = require('node:fs');
const {
  createHmac,
} = require('node:crypto');
const { argv } = require('node:process');

const filename = argv[2];

const hmac = createHmac('sha256', 'a secret');

const input = createReadStream(filename);
input.on('readable', () => {
  // Only one element is going to be produced by the
  // hash stream.
  const data = input.read();
  if (data)
    hmac.update(data);
  else {
    console.log(`${hmac.digest('hex')} ${filename}`);
  }
});

crypto.createPrivateKey(key)#

创建并返回一个包含私钥的新密钥对象。如果 key 是字符串或 Buffer,则假定 format'pem';否则,key 必须是具有上述属性的对象。

如果私钥已加密,则必须指定 passphrase。密码短语的长度限制为 1024 字节。

crypto.createPublicKey(key)#

创建并返回一个包含公钥的新密钥对象。如果 key 是字符串或 Buffer,则假定 format'pem';如果 key 是一个类型为 'private'KeyObject,则公钥从给定的私钥派生;否则,key 必须是具有上述属性的对象。

如果格式是 'pem''key' 也可以是 X.509 证书。

因为公钥可以从私钥派生,所以可以传递私钥来代替公钥。在这种情况下,此函数的行为就好像调用了 crypto.createPrivateKey() 一样,只是返回的 KeyObject 的类型将是 'public',并且无法从返回的 KeyObject 中提取私钥。类似地,如果给定一个类型为 'private'KeyObject,将返回一个类型为 'public' 的新 KeyObject,并且无法从返回的对象中提取私钥。

crypto.createSecretKey(key[, encoding])#

创建并返回一个包含用于对称加密或 Hmac 的密钥的新密钥对象。

crypto.createSign(algorithm[, options])#

创建并返回一个使用给定 algorithmSign 对象。使用 crypto.getHashes() 获取可用摘要算法的名称。可选的 options 参数控制 stream.Writable 行为。

在某些情况下,可以使用签名算法的名称(如 'RSA-SHA256')来创建 Sign 实例,而不是摘要算法的名称。这将使用相应的摘要算法。但这不适用于所有签名算法,例如 'ecdsa-with-SHA256',所以最好总是使用摘要算法的名称。

crypto.createVerify(algorithm[, options])#

创建并返回一个使用给定算法的 Verify 对象。使用 crypto.getHashes() 获取可用签名算法名称的数组。可选的 options 参数控制 stream.Writable 行为。

在某些情况下,可以使用签名算法的名称(如 'RSA-SHA256')来创建 Verify 实例,而不是摘要算法的名称。这将使用相应的摘要算法。但这不适用于所有签名算法,例如 'ecdsa-with-SHA256',所以最好总是使用摘要算法的名称。

crypto.decapsulate(key, ciphertext[, callback])#

稳定性:1.2 - 候选发布

使用 KEM 算法和私钥进行密钥解封装。

支持的密钥类型及其 KEM 算法如下

  • 'rsa'2 RSA 秘密值封装
  • 'ec'3 DHKEM(P-256, HKDF-SHA256), DHKEM(P-384, HKDF-SHA256), DHKEM(P-521, HKDF-SHA256)
  • 'x25519'3 DHKEM(X25519, HKDF-SHA256)
  • 'x448'3 DHKEM(X448, HKDF-SHA512)
  • 'ml-kem-512'1 ML-KEM
  • 'ml-kem-768'1 ML-KEM
  • 'ml-kem-1024'1 ML-KEM

如果 key 不是 KeyObject,此函数的行为就好像将 key 传递给了 crypto.createPrivateKey() 一样。

如果提供了 callback 函数,此函数将使用 libuv 的线程池。

crypto.diffieHellman(options[, callback])#

根据 privateKeypublicKey 计算 Diffie-Hellman 共享密钥。两个密钥必须具有相同的 asymmetricKeyType,并且必须支持 DH 或 ECDH 操作。

如果提供了 callback 函数,此函数将使用 libuv 的线程池。

crypto.encapsulate(key[, callback])#

稳定性:1.2 - 候选发布

使用 KEM 算法和公钥进行密钥封装。

支持的密钥类型及其 KEM 算法如下

  • 'rsa'2 RSA 秘密值封装
  • 'ec'3 DHKEM(P-256, HKDF-SHA256), DHKEM(P-384, HKDF-SHA256), DHKEM(P-521, HKDF-SHA256)
  • 'x25519'3 DHKEM(X25519, HKDF-SHA256)
  • 'x448'3 DHKEM(X448, HKDF-SHA512)
  • 'ml-kem-512'1 ML-KEM
  • 'ml-kem-768'1 ML-KEM
  • 'ml-kem-1024'1 ML-KEM

如果 key 不是 KeyObject,此函数的行为就好像将 key 传递给了 crypto.createPublicKey() 一样。

如果提供了 callback 函数,此函数将使用 libuv 的线程池。

crypto.fips#

稳定性: 0 - 废弃

用于检查和控制当前是否正在使用符合 FIPS 的加密提供程序的属性。设置为 true 需要 Node.js 的 FIPS 构建版本。

此属性已弃用。请改用 crypto.setFips()crypto.getFips()

crypto.generateKey(type, options, callback)#

  • type <string> 生成的密钥的预期用途。当前接受的值是 'hmac''aes'
  • options <Object>
    • length <number> 要生成的密钥的位长度。此值必须大于 0。
      • 如果 type'hmac',最小值为 8,最大长度为 231-1。如果该值不是 8 的倍数,生成的密钥将被截断为 Math.floor(length / 8)
      • 如果 type'aes',长度必须是 128192256 之一。
  • callback <Function>

异步生成一个给定 length 的新随机密钥。type 将决定对 length 执行哪些验证。

const {
  generateKey,
} = await import('node:crypto');

generateKey('hmac', { length: 512 }, (err, key) => {
  if (err) throw err;
  console.log(key.export().toString('hex'));  // 46e..........620
});const {
  generateKey,
} = require('node:crypto');

generateKey('hmac', { length: 512 }, (err, key) => {
  if (err) throw err;
  console.log(key.export().toString('hex'));  // 46e..........620
});

生成的 HMAC 密钥的大小不应超过底层哈希函数的块大小。有关更多信息,请参阅 crypto.createHmac()

crypto.generateKeyPair(type, options, callback)#

生成给定 type 的新非对称密钥对。目前支持 RSA、RSA-PSS、DSA、EC、Ed25519、Ed448、X25519、X448 和 DH。

如果指定了 publicKeyEncodingprivateKeyEncoding,此函数的行为就好像在其结果上调用了 keyObject.export()。否则,密钥的相应部分将作为 KeyObject 返回。

建议将公钥编码为 'spki',将私钥编码为 'pkcs8' 并进行加密以供长期存储。

const {
  generateKeyPair,
} = await import('node:crypto');

generateKeyPair('rsa', {
  modulusLength: 4096,
  publicKeyEncoding: {
    type: 'spki',
    format: 'pem',
  },
  privateKeyEncoding: {
    type: 'pkcs8',
    format: 'pem',
    cipher: 'aes-256-cbc',
    passphrase: 'top secret',
  },
}, (err, publicKey, privateKey) => {
  // Handle errors and use the generated key pair.
});const {
  generateKeyPair,
} = require('node:crypto');

generateKeyPair('rsa', {
  modulusLength: 4096,
  publicKeyEncoding: {
    type: 'spki',
    format: 'pem',
  },
  privateKeyEncoding: {
    type: 'pkcs8',
    format: 'pem',
    cipher: 'aes-256-cbc',
    passphrase: 'top secret',
  },
}, (err, publicKey, privateKey) => {
  // Handle errors and use the generated key pair.
});

完成后,将调用 callback,其中 err 设置为 undefinedpublicKey / privateKey 表示生成的密钥对。

如果此方法作为其 util.promisify() 版本被调用,它将返回一个 Promise,该 Promise 解析为一个具有 publicKeyprivateKey 属性的对象。

crypto.generateKeyPairSync(type, options)#

生成给定 type 的新非对称密钥对。目前支持 RSA、RSA-PSS、DSA、EC、Ed25519、Ed448、X25519、X448、DH 和 ML-DSA1

如果指定了 publicKeyEncodingprivateKeyEncoding,此函数的行为就好像在其结果上调用了 keyObject.export()。否则,密钥的相应部分将作为 KeyObject 返回。

编码公钥时,建议使用 'spki'。编码私钥时,建议使用 'pkcs8' 和强密码,并对密码保密。

const {
  generateKeyPairSync,
} = await import('node:crypto');

const {
  publicKey,
  privateKey,
} = generateKeyPairSync('rsa', {
  modulusLength: 4096,
  publicKeyEncoding: {
    type: 'spki',
    format: 'pem',
  },
  privateKeyEncoding: {
    type: 'pkcs8',
    format: 'pem',
    cipher: 'aes-256-cbc',
    passphrase: 'top secret',
  },
});const {
  generateKeyPairSync,
} = require('node:crypto');

const {
  publicKey,
  privateKey,
} = generateKeyPairSync('rsa', {
  modulusLength: 4096,
  publicKeyEncoding: {
    type: 'spki',
    format: 'pem',
  },
  privateKeyEncoding: {
    type: 'pkcs8',
    format: 'pem',
    cipher: 'aes-256-cbc',
    passphrase: 'top secret',
  },
});

返回值 { publicKey, privateKey } 表示生成的密钥对。当选择 PEM 编码时,相应的密钥将是一个字符串,否则它将是一个包含 DER 编码数据的缓冲区。

crypto.generateKeySync(type, options)#

  • type <string> 生成的密钥的预期用途。当前接受的值是 'hmac''aes'
  • options <Object>
    • length <number> 要生成的密钥的位长度。
      • 如果 type'hmac',最小值为 8,最大长度为 231-1。如果该值不是 8 的倍数,生成的密钥将被截断为 Math.floor(length / 8)
      • 如果 type'aes',长度必须是 128192256 之一。
  • 返回:<KeyObject>

同步生成一个给定 length 的新随机密钥。type 将决定对 length 执行哪些验证。

const {
  generateKeySync,
} = await import('node:crypto');

const key = generateKeySync('hmac', { length: 512 });
console.log(key.export().toString('hex'));  // e89..........41econst {
  generateKeySync,
} = require('node:crypto');

const key = generateKeySync('hmac', { length: 512 });
console.log(key.export().toString('hex'));  // e89..........41e

生成的 HMAC 密钥的大小不应超过底层哈希函数的块大小。有关更多信息,请参阅 crypto.createHmac()

crypto.generatePrime(size[, options], callback)#

生成一个 size 位的伪随机素数。

如果 options.safetrue,则该素数将是一个安全素数——也就是说,(prime - 1) / 2 也将是一个素数。

options.addoptions.rem 参数可用于强制执行附加要求,例如用于 Diffie-Hellman

  • 如果 options.addoptions.rem 都被设置,素数将满足条件 prime % add = rem
  • 如果仅设置了 options.addoptions.safe 不为 true,则素数将满足条件 prime % add = 1
  • 如果仅设置了 options.addoptions.safe 设置为 true,则素数将改为满足条件 prime % add = 3。这是必要的,因为对于 options.add > 2prime % add = 1 会与 options.safe 强制执行的条件相矛盾。
  • 如果未给出 options.add,则忽略 options.rem

如果 options.addoptions.remArrayBufferSharedArrayBufferTypedArrayBufferDataView 的形式给出,则它们必须编码为大端序列。

默认情况下,素数被编码为 <ArrayBuffer> 中的大端八位字节序列。如果 bigint 选项为 true,则提供一个 <bigint>

素数的 size 会直接影响生成素数所需的时间。size 越大,所需时间越长。因为我们使用 OpenSSL 的 BN_generate_prime_ex 函数,该函数对我们中断生成过程的能力控制很小,所以不建议生成过大的素数,因为这样做可能会使进程无响应。

crypto.generatePrimeSync(size[, options])#

生成一个 size 位的伪随机素数。

如果 options.safetrue,则该素数将是一个安全素数——也就是说,(prime - 1) / 2 也将是一个素数。

options.addoptions.rem 参数可用于强制执行附加要求,例如用于 Diffie-Hellman

  • 如果 options.addoptions.rem 都被设置,素数将满足条件 prime % add = rem
  • 如果仅设置了 options.addoptions.safe 不为 true,则素数将满足条件 prime % add = 1
  • 如果仅设置了 options.addoptions.safe 设置为 true,则素数将改为满足条件 prime % add = 3。这是必要的,因为对于 options.add > 2prime % add = 1 会与 options.safe 强制执行的条件相矛盾。
  • 如果未给出 options.add,则忽略 options.rem

如果 options.addoptions.remArrayBufferSharedArrayBufferTypedArrayBufferDataView 的形式给出,则它们必须编码为大端序列。

默认情况下,素数被编码为 <ArrayBuffer> 中的大端八位字节序列。如果 bigint 选项为 true,则提供一个 <bigint>

素数的 size 会直接影响生成素数所需的时间。size 越大,所需时间越长。因为我们使用 OpenSSL 的 BN_generate_prime_ex 函数,该函数对我们中断生成过程的能力控制很小,所以不建议生成过大的素数,因为这样做可能会使进程无响应。

crypto.getCipherInfo(nameOrNid[, options])#

  • nameOrNid <string> | <number> 要查询的密码的名称或 nid。
  • options <Object>
    • keyLength <number> 一个测试密钥长度。
    • ivLength <number> 一个测试 IV 长度。
  • 返回:<Object>
    • name <string> 密码的名称
    • nid <number> 密码的 nid
    • blockSize <number> 密码的块大小(以字节为单位)。当 mode'stream' 时,此属性被省略。
    • ivLength <number> 预期的或默认的初始化向量长度(以字节为单位)。如果密码不使用初始化向量,则省略此属性。
    • keyLength <number> 预期的或默认的密钥长度(以字节为单位)。
    • mode <string> 密码模式。为 'cbc''ccm''cfb''ctr''ecb''gcm''ocb''ofb''stream''wrap''xts' 之一。

返回有关给定密码的信息。

一些密码接受可变长度的密钥和初始化向量。默认情况下,crypto.getCipherInfo() 方法将返回这些密码的默认值。要测试给定的密钥长度或 IV 长度是否适用于给定的密码,请使用 keyLengthivLength 选项。如果给定的值不可接受,将返回 undefined

crypto.getCiphers()#

  • 返回:<string[]> 一个包含支持的密码算法名称的数组。
const {
  getCiphers,
} = await import('node:crypto');

console.log(getCiphers()); // ['aes-128-cbc', 'aes-128-ccm', ...]const {
  getCiphers,
} = require('node:crypto');

console.log(getCiphers()); // ['aes-128-cbc', 'aes-128-ccm', ...]

crypto.getCurves()#

  • 返回:<string[]> 一个包含支持的椭圆曲线名称的数组。
const {
  getCurves,
} = await import('node:crypto');

console.log(getCurves()); // ['Oakley-EC2N-3', 'Oakley-EC2N-4', ...]const {
  getCurves,
} = require('node:crypto');

console.log(getCurves()); // ['Oakley-EC2N-3', 'Oakley-EC2N-4', ...]

crypto.getDiffieHellman(groupName)#

创建一个预定义的 DiffieHellmanGroup 密钥交换对象。支持的组在 DiffieHellmanGroup 的文档中列出。

返回的对象模仿由 crypto.createDiffieHellman() 创建的对象的接口,但不允许更改密钥(例如,使用 diffieHellman.setPublicKey())。使用此方法的优点是各方不必事先生成或交换组模数,从而节省了处理器和通信时间。

示例(获取共享密钥)

const {
  getDiffieHellman,
} = await import('node:crypto');
const alice = getDiffieHellman('modp14');
const bob = getDiffieHellman('modp14');

alice.generateKeys();
bob.generateKeys();

const aliceSecret = alice.computeSecret(bob.getPublicKey(), null, 'hex');
const bobSecret = bob.computeSecret(alice.getPublicKey(), null, 'hex');

/* aliceSecret and bobSecret should be the same */
console.log(aliceSecret === bobSecret);const {
  getDiffieHellman,
} = require('node:crypto');

const alice = getDiffieHellman('modp14');
const bob = getDiffieHellman('modp14');

alice.generateKeys();
bob.generateKeys();

const aliceSecret = alice.computeSecret(bob.getPublicKey(), null, 'hex');
const bobSecret = bob.computeSecret(alice.getPublicKey(), null, 'hex');

/* aliceSecret and bobSecret should be the same */
console.log(aliceSecret === bobSecret);

crypto.getFips()#

  • 返回:<number> 当且仅当当前正在使用符合 FIPS 的加密提供程序时返回 1,否则返回 0。未来的 semver-major 版本可能会将此 API 的返回类型更改为 <boolean>

crypto.getHashes()#

  • 返回:<string[]> 一个包含支持的哈希算法名称的数组,例如 'RSA-SHA256'。哈希算法也称为“摘要”算法。
const {
  getHashes,
} = await import('node:crypto');

console.log(getHashes()); // ['DSA', 'DSA-SHA', 'DSA-SHA1', ...]const {
  getHashes,
} = require('node:crypto');

console.log(getHashes()); // ['DSA', 'DSA-SHA', 'DSA-SHA1', ...]

crypto.getRandomValues(typedArray)#

crypto.webcrypto.getRandomValues() 的一个方便别名。此实现不符合 Web Crypto 规范,要编写与 Web 兼容的代码,请改用 crypto.webcrypto.getRandomValues()

crypto.hash(algorithm, data[, options])#

稳定性:1.2 - 候选发布

  • algorithm <string> | <undefined>
  • data <string> | <Buffer> | <TypedArray> | <DataView>data 是字符串时,它将在哈希前被编码为 UTF-8。如果希望对字符串输入使用不同的输入编码,用户可以使用 TextEncoderBuffer.from() 将字符串编码为 TypedArray,然后将编码后的 TypedArray 传递给此 API。
  • options <Object> | <string>
    • outputEncoding <string> 用于编码返回的摘要的编码默认值: 'hex'
    • outputLength <number> 对于 XOF 哈希函数(如 'shake256'),可以使用 outputLength 选项来指定所需的输出长度(以字节为单位)。
  • 返回:<string> | <Buffer>

一个用于创建数据的一次性哈希摘要的实用工具。在对较小量(<= 5MB)且已准备好的数据进行哈希时,它可能比基于对象的 crypto.createHash() 更快。如果数据可能很大或是流式数据,仍建议使用 crypto.createHash()

algorithm 取决于平台上 OpenSSL 版本支持的可用算法。例如 'sha256''sha512' 等。在最新的 OpenSSL 版本中,openssl list -digest-algorithms 将显示可用的摘要算法。

如果 options 是一个字符串,则它指定了 outputEncoding

示例

const crypto = require('node:crypto');
const { Buffer } = require('node:buffer');

// Hashing a string and return the result as a hex-encoded string.
const string = 'Node.js';
// 10b3493287f831e81a438811a1ffba01f8cec4b7
console.log(crypto.hash('sha1', string));

// Encode a base64-encoded string into a Buffer, hash it and return
// the result as a buffer.
const base64 = 'Tm9kZS5qcw==';
// <Buffer 10 b3 49 32 87 f8 31 e8 1a 43 88 11 a1 ff ba 01 f8 ce c4 b7>
console.log(crypto.hash('sha1', Buffer.from(base64, 'base64'), 'buffer'));import crypto from 'node:crypto';
import { Buffer } from 'node:buffer';

// Hashing a string and return the result as a hex-encoded string.
const string = 'Node.js';
// 10b3493287f831e81a438811a1ffba01f8cec4b7
console.log(crypto.hash('sha1', string));

// Encode a base64-encoded string into a Buffer, hash it and return
// the result as a buffer.
const base64 = 'Tm9kZS5qcw==';
// <Buffer 10 b3 49 32 87 f8 31 e8 1a 43 88 11 a1 ff ba 01 f8 ce c4 b7>
console.log(crypto.hash('sha1', Buffer.from(base64, 'base64'), 'buffer'));

crypto.hkdf(digest, ikm, salt, info, keylen, callback)#

HKDF 是 RFC 5869 中定义的一个简单的密钥派生函数。给定的 ikmsaltinfodigest 一起用于派生一个长度为 keylen 字节的密钥。

提供的 callback 函数被调用时带有两个参数:errderivedKey。如果在派生密钥时发生错误,err 将被设置;否则 err 将为 null。成功生成的 derivedKey 将作为 <ArrayBuffer> 传递给回调函数。如果任何输入参数指定了无效的值或类型,将抛出错误。

import { Buffer } from 'node:buffer';
const {
  hkdf,
} = await import('node:crypto');

hkdf('sha512', 'key', 'salt', 'info', 64, (err, derivedKey) => {
  if (err) throw err;
  console.log(Buffer.from(derivedKey).toString('hex'));  // '24156e2...5391653'
});const {
  hkdf,
} = require('node:crypto');
const { Buffer } = require('node:buffer');

hkdf('sha512', 'key', 'salt', 'info', 64, (err, derivedKey) => {
  if (err) throw err;
  console.log(Buffer.from(derivedKey).toString('hex'));  // '24156e2...5391653'
});

crypto.hkdfSync(digest, ikm, salt, info, keylen)#

提供一个同步的 HKDF 密钥派生函数,如 RFC 5869 中定义。给定的 ikmsaltinfodigest 一起用于派生一个长度为 keylen 字节的密钥。

成功生成的 derivedKey 将作为 <ArrayBuffer> 返回。

如果任何输入参数指定了无效的值或类型,或者无法生成派生密钥,则将抛出错误。

import { Buffer } from 'node:buffer';
const {
  hkdfSync,
} = await import('node:crypto');

const derivedKey = hkdfSync('sha512', 'key', 'salt', 'info', 64);
console.log(Buffer.from(derivedKey).toString('hex'));  // '24156e2...5391653'const {
  hkdfSync,
} = require('node:crypto');
const { Buffer } = require('node:buffer');

const derivedKey = hkdfSync('sha512', 'key', 'salt', 'info', 64);
console.log(Buffer.from(derivedKey).toString('hex'));  // '24156e2...5391653'

crypto.pbkdf2(password, salt, iterations, keylen, digest, callback)#

提供一个异步的基于密码的密钥派生函数 2 (PBKDF2) 的实现。它应用由 digest 指定的 HMAC 摘要算法,从 passwordsaltiterations 中派生出所需字节长度 (keylen) 的密钥。

提供的 callback 函数被调用时会带有两个参数:errderivedKey。如果在派生密钥时发生错误,err 将被设置;否则 err 将为 null。默认情况下,成功生成的 derivedKey 将作为 Buffer 传递给回调函数。如果任何输入参数指定了无效的值或类型,则会抛出错误。

iterations 参数必须是一个数字,并且设置得越高越好。迭代次数越多,派生出的密钥就越安全,但完成所需的时间也越长。

salt 应尽可能唯一。建议 salt 是随机的,并且长度至少为 16 字节。详情请参阅 NIST SP 800-132

当为 passwordsalt 传递字符串时,请考虑将字符串用作加密 API 输入时的注意事项

const {
  pbkdf2,
} = await import('node:crypto');

pbkdf2('secret', 'salt', 100000, 64, 'sha512', (err, derivedKey) => {
  if (err) throw err;
  console.log(derivedKey.toString('hex'));  // '3745e48...08d59ae'
});const {
  pbkdf2,
} = require('node:crypto');

pbkdf2('secret', 'salt', 100000, 64, 'sha512', (err, derivedKey) => {
  if (err) throw err;
  console.log(derivedKey.toString('hex'));  // '3745e48...08d59ae'
});

可以使用 crypto.getHashes() 获取支持的摘要函数数组。

此 API 使用 libuv 的线程池,这可能对某些应用程序产生意想不到的负面性能影响;更多信息请参阅 UV_THREADPOOL_SIZE 文档。

crypto.pbkdf2Sync(password, salt, iterations, keylen, digest)#

提供一个同步的基于密码的密钥派生函数 2 (PBKDF2) 的实现。它应用由 digest 指定的 HMAC 摘要算法,从 passwordsaltiterations 中派生出所需字节长度 (keylen) 的密钥。

如果发生错误,将抛出 Error,否则派生的密钥将作为 Buffer 返回。

iterations 参数必须是一个数字,并且设置得越高越好。迭代次数越多,派生出的密钥就越安全,但完成所需的时间也越长。

salt 应尽可能唯一。建议 salt 是随机的,并且长度至少为 16 字节。详情请参阅 NIST SP 800-132

当为 passwordsalt 传递字符串时,请考虑将字符串用作加密 API 输入时的注意事项

const {
  pbkdf2Sync,
} = await import('node:crypto');

const key = pbkdf2Sync('secret', 'salt', 100000, 64, 'sha512');
console.log(key.toString('hex'));  // '3745e48...08d59ae'const {
  pbkdf2Sync,
} = require('node:crypto');

const key = pbkdf2Sync('secret', 'salt', 100000, 64, 'sha512');
console.log(key.toString('hex'));  // '3745e48...08d59ae'

可以使用 crypto.getHashes() 获取支持的摘要函数数组。

crypto.privateDecrypt(privateKey, buffer)#

使用 privateKey 解密 bufferbuffer 之前是使用相应的公钥加密的,例如使用 crypto.publicEncrypt()

如果 privateKey 不是 KeyObject,此函数的行为就如同将 privateKey 传递给了 crypto.createPrivateKey()。如果它是一个对象,则可以传递 padding 属性。否则,此函数使用 RSA_PKCS1_OAEP_PADDING

crypto.privateDecrypt() 中使用 crypto.constants.RSA_PKCS1_PADDING 要求 OpenSSL 支持隐式拒绝(rsa_pkcs1_implicit_rejection)。如果 Node.js 使用的 OpenSSL 版本不支持此功能,尝试使用 RSA_PKCS1_PADDING 将会失败。

crypto.privateEncrypt(privateKey, buffer)#

使用 privateKey 加密 buffer。返回的数据可以使用相应的公钥解密,例如使用 crypto.publicDecrypt()

如果 privateKey 不是 KeyObject,此函数的行为就如同将 privateKey 传递给了 crypto.createPrivateKey()。如果它是一个对象,则可以传递 padding 属性。否则,此函数使用 RSA_PKCS1_PADDING

crypto.publicDecrypt(key, buffer)#

使用 key 解密 bufferbuffer 之前是使用相应的私钥加密的,例如使用 crypto.privateEncrypt()

如果 key 不是 KeyObject,此函数的行为就如同将 key 传递给了 crypto.createPublicKey()。如果它是一个对象,则可以传递 padding 属性。否则,此函数使用 RSA_PKCS1_PADDING

因为 RSA 公钥可以从私钥派生,所以可以传递私钥而不是公钥。

crypto.publicEncrypt(key, buffer)#

使用 key 加密 buffer 的内容,并返回一个包含加密内容的新 Buffer。返回的数据可以使用相应的私钥解密,例如使用 crypto.privateDecrypt()

如果 key 不是 KeyObject,此函数的行为就如同将 key 传递给了 crypto.createPublicKey()。如果它是一个对象,则可以传递 padding 属性。否则,此函数使用 RSA_PKCS1_OAEP_PADDING

因为 RSA 公钥可以从私钥派生,所以可以传递私钥而不是公钥。

crypto.randomBytes(size[, callback])#

生成密码学上强伪随机数据。size 参数是一个数字,指示要生成的字节数。

如果提供了 callback 函数,则字节会异步生成,并且 callback 函数会被调用并带有两个参数:errbuf。如果发生错误,err 将是一个 Error 对象;否则为 nullbuf 参数是一个包含所生成字节的 Buffer

// Asynchronous
const {
  randomBytes,
} = await import('node:crypto');

randomBytes(256, (err, buf) => {
  if (err) throw err;
  console.log(`${buf.length} bytes of random data: ${buf.toString('hex')}`);
});// Asynchronous
const {
  randomBytes,
} = require('node:crypto');

randomBytes(256, (err, buf) => {
  if (err) throw err;
  console.log(`${buf.length} bytes of random data: ${buf.toString('hex')}`);
});

如果没有提供 callback 函数,则随机字节会同步生成并作为 Buffer 返回。如果在生成字节时出现问题,将抛出错误。

// Synchronous
const {
  randomBytes,
} = await import('node:crypto');

const buf = randomBytes(256);
console.log(
  `${buf.length} bytes of random data: ${buf.toString('hex')}`);// Synchronous
const {
  randomBytes,
} = require('node:crypto');

const buf = randomBytes(256);
console.log(
  `${buf.length} bytes of random data: ${buf.toString('hex')}`);

crypto.randomBytes() 方法直到有足够的可用熵时才会完成。这通常不应超过几毫秒。唯一可能导致生成随机字节阻塞较长时间的情况是在系统启动后不久,此时整个系统熵较低。

此 API 使用 libuv 的线程池,这可能对某些应用程序产生意想不到的负面性能影响;更多信息请参阅 UV_THREADPOOL_SIZE 文档。

crypto.randomBytes() 的异步版本在一个线程池请求中执行。为了最小化线程池任务长度的变化,在作为满足客户端请求的一部分执行此操作时,请将大的 randomBytes 请求进行分区。

crypto.randomFill(buffer[, offset][, size], callback)#

此函数类似于 crypto.randomBytes(),但要求第一个参数是一个将被填充的 Buffer。它还要求传入一个回调函数。

如果没有提供 callback 函数,将抛出错误。

import { Buffer } from 'node:buffer';
const { randomFill } = await import('node:crypto');

const buf = Buffer.alloc(10);
randomFill(buf, (err, buf) => {
  if (err) throw err;
  console.log(buf.toString('hex'));
});

randomFill(buf, 5, (err, buf) => {
  if (err) throw err;
  console.log(buf.toString('hex'));
});

// The above is equivalent to the following:
randomFill(buf, 5, 5, (err, buf) => {
  if (err) throw err;
  console.log(buf.toString('hex'));
});const { randomFill } = require('node:crypto');
const { Buffer } = require('node:buffer');

const buf = Buffer.alloc(10);
randomFill(buf, (err, buf) => {
  if (err) throw err;
  console.log(buf.toString('hex'));
});

randomFill(buf, 5, (err, buf) => {
  if (err) throw err;
  console.log(buf.toString('hex'));
});

// The above is equivalent to the following:
randomFill(buf, 5, 5, (err, buf) => {
  if (err) throw err;
  console.log(buf.toString('hex'));
});

任何 ArrayBufferTypedArrayDataView 实例都可以作为 buffer 传递。

虽然这包括 Float32ArrayFloat64Array 的实例,但不应使用此函数生成随机浮点数。结果可能包含 +Infinity-InfinityNaN,即使数组仅包含有限的数字,它们也不是从均匀随机分布中抽取的,并且没有有意义的下限或上限。

import { Buffer } from 'node:buffer';
const { randomFill } = await import('node:crypto');

const a = new Uint32Array(10);
randomFill(a, (err, buf) => {
  if (err) throw err;
  console.log(Buffer.from(buf.buffer, buf.byteOffset, buf.byteLength)
    .toString('hex'));
});

const b = new DataView(new ArrayBuffer(10));
randomFill(b, (err, buf) => {
  if (err) throw err;
  console.log(Buffer.from(buf.buffer, buf.byteOffset, buf.byteLength)
    .toString('hex'));
});

const c = new ArrayBuffer(10);
randomFill(c, (err, buf) => {
  if (err) throw err;
  console.log(Buffer.from(buf).toString('hex'));
});const { randomFill } = require('node:crypto');
const { Buffer } = require('node:buffer');

const a = new Uint32Array(10);
randomFill(a, (err, buf) => {
  if (err) throw err;
  console.log(Buffer.from(buf.buffer, buf.byteOffset, buf.byteLength)
    .toString('hex'));
});

const b = new DataView(new ArrayBuffer(10));
randomFill(b, (err, buf) => {
  if (err) throw err;
  console.log(Buffer.from(buf.buffer, buf.byteOffset, buf.byteLength)
    .toString('hex'));
});

const c = new ArrayBuffer(10);
randomFill(c, (err, buf) => {
  if (err) throw err;
  console.log(Buffer.from(buf).toString('hex'));
});

此 API 使用 libuv 的线程池,这可能对某些应用程序产生意想不到的负面性能影响;更多信息请参阅 UV_THREADPOOL_SIZE 文档。

crypto.randomFill() 的异步版本在一个线程池请求中执行。为了最小化线程池任务长度的变化,在作为满足客户端请求的一部分执行此操作时,请将大的 randomFill 请求进行分区。

crypto.randomFillSync(buffer[, offset][, size])#

crypto.randomFill() 的同步版本。

import { Buffer } from 'node:buffer';
const { randomFillSync } = await import('node:crypto');

const buf = Buffer.alloc(10);
console.log(randomFillSync(buf).toString('hex'));

randomFillSync(buf, 5);
console.log(buf.toString('hex'));

// The above is equivalent to the following:
randomFillSync(buf, 5, 5);
console.log(buf.toString('hex'));const { randomFillSync } = require('node:crypto');
const { Buffer } = require('node:buffer');

const buf = Buffer.alloc(10);
console.log(randomFillSync(buf).toString('hex'));

randomFillSync(buf, 5);
console.log(buf.toString('hex'));

// The above is equivalent to the following:
randomFillSync(buf, 5, 5);
console.log(buf.toString('hex'));

任何 ArrayBufferTypedArrayDataView 实例都可以作为 buffer 传递。

import { Buffer } from 'node:buffer';
const { randomFillSync } = await import('node:crypto');

const a = new Uint32Array(10);
console.log(Buffer.from(randomFillSync(a).buffer,
                        a.byteOffset, a.byteLength).toString('hex'));

const b = new DataView(new ArrayBuffer(10));
console.log(Buffer.from(randomFillSync(b).buffer,
                        b.byteOffset, b.byteLength).toString('hex'));

const c = new ArrayBuffer(10);
console.log(Buffer.from(randomFillSync(c)).toString('hex'));const { randomFillSync } = require('node:crypto');
const { Buffer } = require('node:buffer');

const a = new Uint32Array(10);
console.log(Buffer.from(randomFillSync(a).buffer,
                        a.byteOffset, a.byteLength).toString('hex'));

const b = new DataView(new ArrayBuffer(10));
console.log(Buffer.from(randomFillSync(b).buffer,
                        b.byteOffset, b.byteLength).toString('hex'));

const c = new ArrayBuffer(10);
console.log(Buffer.from(randomFillSync(c)).toString('hex'));

crypto.randomInt([min, ]max[, callback])#

  • min <integer> 随机范围的起始值(包含)。默认值: 0
  • max <integer> 随机范围的结束值(不包含)。
  • callback <Function> function(err, n) {}

返回一个随机整数 n,使得 min <= n < max。此实现避免了模偏差

范围 (max - min) 必须小于 248minmax 必须是安全整数

如果没有提供 callback 函数,则随机整数会同步生成。

// Asynchronous
const {
  randomInt,
} = await import('node:crypto');

randomInt(3, (err, n) => {
  if (err) throw err;
  console.log(`Random number chosen from (0, 1, 2): ${n}`);
});// Asynchronous
const {
  randomInt,
} = require('node:crypto');

randomInt(3, (err, n) => {
  if (err) throw err;
  console.log(`Random number chosen from (0, 1, 2): ${n}`);
});
// Synchronous
const {
  randomInt,
} = await import('node:crypto');

const n = randomInt(3);
console.log(`Random number chosen from (0, 1, 2): ${n}`);// Synchronous
const {
  randomInt,
} = require('node:crypto');

const n = randomInt(3);
console.log(`Random number chosen from (0, 1, 2): ${n}`);
// With `min` argument
const {
  randomInt,
} = await import('node:crypto');

const n = randomInt(1, 7);
console.log(`The dice rolled: ${n}`);// With `min` argument
const {
  randomInt,
} = require('node:crypto');

const n = randomInt(1, 7);
console.log(`The dice rolled: ${n}`);

crypto.randomUUID([options])#

  • options <Object>
    • disableEntropyCache <boolean> 默认情况下,为提高性能,Node.js 会生成并缓存足够的随机数据,以生成多达 128 个随机 UUID。要生成一个不使用缓存的 UUID,请将 disableEntropyCache 设置为 true默认值: false
  • 返回: <string>

生成一个随机的 RFC 4122 版本 4 UUID。该 UUID 使用密码学伪随机数生成器生成。

crypto.scrypt(password, salt, keylen[, options], callback)#

提供一个异步的 scrypt 实现。Scrypt 是一种基于密码的密钥派生函数,其设计旨在计算和内存上都非常昂贵,以使暴力攻击得不偿失。

salt 应尽可能唯一。建议 salt 是随机的,并且长度至少为 16 字节。详情请参阅 NIST SP 800-132

当为 passwordsalt 传递字符串时,请考虑将字符串用作加密 API 输入时的注意事项

callback 函数被调用时带有两个参数:errderivedKey。当密钥派生失败时,err 是一个异常对象,否则 errnullderivedKey 作为 Buffer 传递给回调函数。

当任何输入参数指定了无效的值或类型时,会抛出异常。

const {
  scrypt,
} = await import('node:crypto');

// Using the factory defaults.
scrypt('password', 'salt', 64, (err, derivedKey) => {
  if (err) throw err;
  console.log(derivedKey.toString('hex'));  // '3745e48...08d59ae'
});
// Using a custom N parameter. Must be a power of two.
scrypt('password', 'salt', 64, { N: 1024 }, (err, derivedKey) => {
  if (err) throw err;
  console.log(derivedKey.toString('hex'));  // '3745e48...aa39b34'
});const {
  scrypt,
} = require('node:crypto');

// Using the factory defaults.
scrypt('password', 'salt', 64, (err, derivedKey) => {
  if (err) throw err;
  console.log(derivedKey.toString('hex'));  // '3745e48...08d59ae'
});
// Using a custom N parameter. Must be a power of two.
scrypt('password', 'salt', 64, { N: 1024 }, (err, derivedKey) => {
  if (err) throw err;
  console.log(derivedKey.toString('hex'));  // '3745e48...aa39b34'
});

crypto.scryptSync(password, salt, keylen[, options])#

提供一个同步的 scrypt 实现。Scrypt 是一种基于密码的密钥派生函数,其设计旨在计算和内存上都非常昂贵,以使暴力攻击得不偿失。

salt 应尽可能唯一。建议 salt 是随机的,并且长度至少为 16 字节。详情请参阅 NIST SP 800-132

当为 passwordsalt 传递字符串时,请考虑将字符串用作加密 API 输入时的注意事项

当密钥派生失败时,会抛出异常,否则派生的密钥将作为 Buffer 返回。

当任何输入参数指定了无效的值或类型时,会抛出异常。

const {
  scryptSync,
} = await import('node:crypto');
// Using the factory defaults.

const key1 = scryptSync('password', 'salt', 64);
console.log(key1.toString('hex'));  // '3745e48...08d59ae'
// Using a custom N parameter. Must be a power of two.
const key2 = scryptSync('password', 'salt', 64, { N: 1024 });
console.log(key2.toString('hex'));  // '3745e48...aa39b34'const {
  scryptSync,
} = require('node:crypto');
// Using the factory defaults.

const key1 = scryptSync('password', 'salt', 64);
console.log(key1.toString('hex'));  // '3745e48...08d59ae'
// Using a custom N parameter. Must be a power of two.
const key2 = scryptSync('password', 'salt', 64, { N: 1024 });
console.log(key2.toString('hex'));  // '3745e48...aa39b34'

crypto.secureHeapUsed()#

  • 返回:<Object>
    • total <number> 使用 --secure-heap=n 命令行标志指定的已分配安全堆的总大小。
    • min <number> 使用 --secure-heap-min 命令行标志指定的安全堆的最小分配量。
    • used <number> 当前从安全堆中分配的总字节数。
    • utilization <number> usedtotal 已分配字节数的计算比率。

crypto.setEngine(engine[, flags])#

为部分或全部 OpenSSL 函数(由标志选择)加载并设置 engine。OpenSSL 3 起已弃用对自定义引擎的支持。

engine 可以是引擎的 id 或其共享库的路径。

可选的 flags 参数默认使用 ENGINE_METHOD_ALLflags 是一个位字段,可以接受以下一个或多个标志的组合(定义在 crypto.constants 中):

  • crypto.constants.ENGINE_METHOD_RSA
  • crypto.constants.ENGINE_METHOD_DSA
  • crypto.constants.ENGINE_METHOD_DH
  • crypto.constants.ENGINE_METHOD_RAND
  • crypto.constants.ENGINE_METHOD_EC
  • crypto.constants.ENGINE_METHOD_CIPHERS
  • crypto.constants.ENGINE_METHOD_DIGESTS
  • crypto.constants.ENGINE_METHOD_PKEY_METHS
  • crypto.constants.ENGINE_METHOD_PKEY_ASN1_METHS
  • crypto.constants.ENGINE_METHOD_ALL
  • crypto.constants.ENGINE_METHOD_NONE

crypto.setFips(bool)#

  • bool <boolean> true 表示启用 FIPS 模式。

在一个启用了 FIPS 的 Node.js 构建中,启用符合 FIPS 的加密提供程序。如果 FIPS 模式不可用,则抛出错误。

crypto.sign(algorithm, data, key[, callback])#

使用给定的私钥和算法计算并返回 data 的签名。如果 algorithmnullundefined,则算法依赖于密钥类型。

对于 Ed25519、Ed448 和 ML-DSA,algorithm 必须为 nullundefined

如果 key 不是 KeyObject,此函数的行为就如同将 key 传递给了 crypto.createPrivateKey()。如果它是一个对象,可以传递以下附加属性:

  • dsaEncoding <string> 对于 DSA 和 ECDSA,此选项指定生成签名的格式。它可以是以下之一:

    • 'der' (默认): DER 编码的 ASN.1 签名结构,编码为 (r, s)
    • 'ieee-p1363':IEEE-P1363 中提议的签名格式 r || s
  • padding <integer> RSA 的可选填充值,为以下之一:

    • crypto.constants.RSA_PKCS1_PADDING (默认)
    • crypto.constants.RSA_PKCS1_PSS_PADDING

    RSA_PKCS1_PSS_PADDING 将使用 MGF1,其哈希函数与 RFC 4055 第 3.1 节中指定的用于签署消息的哈希函数相同。

  • saltLength <integer> 当填充为 RSA_PKCS1_PSS_PADDING 时的盐长度。特殊值 crypto.constants.RSA_PSS_SALTLEN_DIGEST 将盐长度设置为摘要大小,crypto.constants.RSA_PSS_SALTLEN_MAX_SIGN (默认) 将其设置为最大允许值。

  • context <ArrayBuffer> | <Buffer> | <TypedArray> | <DataView> 对于 Ed448、ML-DSA 和 SLH-DSA,此选项指定可选的上下文,以区分使用相同密钥为不同目的生成的签名。

如果提供了 callback 函数,此函数将使用 libuv 的线程池。

crypto.subtle#

crypto.webcrypto.subtle 的一个方便别名。

crypto.timingSafeEqual(a, b)#

此函数使用恒定时间算法比较表示给定 ArrayBufferTypedArrayDataView 实例的底层字节。

此函数不会泄露计时信息,从而避免攻击者猜测其中一个值。这适用于比较 HMAC 摘要或机密值,如身份验证 Cookie 或能力 URL

ab 必须都是 BufferTypedArrayDataView,并且它们必须具有相同的字节长度。如果 ab 的字节长度不同,则会抛出错误。

如果 ab 中至少有一个是每个条目超过一个字节的 TypedArray(例如 Uint16Array),则结果将使用平台字节序进行计算。

当两个输入都是 Float32ArrayFloat64Array 时,由于 IEEE 754 浮点数编码,此函数可能会返回意外结果。特别是,x === yObject.is(x, y) 都不意味着两个浮点数 xy 的字节表示是相等的。

使用 crypto.timingSafeEqual 并不能保证*周边*代码是时间安全的。应注意确保周边代码不会引入时间漏洞。

crypto.verify(algorithm, data, key, signature[, callback])#

使用给定的密钥和算法验证 data 的签名。如果 algorithmnullundefined,则算法依赖于密钥类型。

对于 Ed25519、Ed448 和 ML-DSA,algorithm 必须为 nullundefined

如果 key 不是 KeyObject,此函数的行为就如同将 key 传递给了 crypto.createPublicKey()。如果它是一个对象,可以传递以下附加属性:

  • dsaEncoding <string> 对于 DSA 和 ECDSA,此选项指定签名的格式。它可以是以下之一:

    • 'der' (默认): DER 编码的 ASN.1 签名结构,编码为 (r, s)
    • 'ieee-p1363':IEEE-P1363 中提议的签名格式 r || s
  • padding <integer> RSA 的可选填充值,为以下之一:

    • crypto.constants.RSA_PKCS1_PADDING (默认)
    • crypto.constants.RSA_PKCS1_PSS_PADDING

    RSA_PKCS1_PSS_PADDING 将使用 MGF1,其哈希函数与 RFC 4055 第 3.1 节中指定的用于签署消息的哈希函数相同。

  • saltLength <integer> 当填充为 RSA_PKCS1_PSS_PADDING 时的盐长度。特殊值 crypto.constants.RSA_PSS_SALTLEN_DIGEST 将盐长度设置为摘要大小,crypto.constants.RSA_PSS_SALTLEN_MAX_SIGN (默认) 将其设置为最大允许值。

  • context <ArrayBuffer> | <Buffer> | <TypedArray> | <DataView> 对于 Ed448、ML-DSA 和 SLH-DSA,此选项指定可选的上下文,以区分使用相同密钥为不同目的生成的签名。

signature 参数是先前为 data 计算的签名。

由于公钥可以从私钥派生,因此可以为 key 传递私钥或公钥。

如果提供了 callback 函数,此函数将使用 libuv 的线程池。

crypto.webcrypto#

类型: <Crypto> Web Crypto API 标准的实现。

详情请参阅 Web Crypto API 文档

注意事项#

将字符串用作加密 API 的输入#

由于历史原因,Node.js 提供的许多加密 API 接受字符串作为输入,而底层加密算法处理的是字节序列。这些情况包括明文、密文、对称密钥、初始化向量、密码短语、盐值、认证标签和附加认证数据。

将字符串传递给加密 API 时,请考虑以下因素。

  • 并非所有字节序列都是有效的 UTF-8 字符串。因此,当从字符串派生出长度为 n 的字节序列时,其熵通常低于随机或伪随机 n 字节序列的熵。例如,没有 UTF-8 字符串会产生字节序列 c0 af。密钥几乎都应该是随机或伪随机的字节序列。

  • 同样,当将随机或伪随机字节序列转换为 UTF-8 字符串时,不代表有效码点的子序列可能会被 Unicode 替换字符 (U+FFFD) 替换。因此,生成的 Unicode 字符串的字节表示可能不等于创建该字符串的字节序列。

    const original = [0xc0, 0xaf];
    const bytesAsString = Buffer.from(original).toString('utf8');
    const stringAsBytes = Buffer.from(bytesAsString, 'utf8');
    console.log(stringAsBytes);
    // Prints '<Buffer ef bf bd ef bf bd>'. 

    密码、哈希函数、签名算法和密钥派生函数的输出是伪随机字节序列,不应作为 Unicode 字符串使用。

  • 当从用户输入中获取字符串时,一些 Unicode 字符可以用多种等效方式表示,从而导致不同的字节序列。例如,当将用户密码短语传递给密钥派生函数(如 PBKDF2 或 scrypt)时,密钥派生函数的结果取决于字符串是使用组合字符还是分解字符。Node.js 不会规范化字符表示。开发者应考虑在将用户输入传递给加密 API 之前使用 String.prototype.normalize()

旧版流 API(Node.js 0.10 之前)#

Crypto 模块是在统一的 Stream API 概念出现之前以及在用于处理二进制数据的 Buffer 对象出现之前添加到 Node.js 中的。因此,许多 crypto 类具有通常在其他实现 streams API 的 Node.js 类中找不到的方法(例如 update()final()digest())。此外,许多方法默认接受并返回 'latin1' 编码的字符串,而不是 Buffer。这个默认值在 Node.js v0.8 之后被更改为默认使用 Buffer 对象。

对弱算法或已受损算法的支持#

node:crypto 模块仍然支持一些已经受到威胁且不推荐使用的算法。该 API 还允许使用密钥大小过小、安全性不足的密码和哈希。

用户应根据其安全需求,对选择加密算法和密钥大小负全部责任。

根据 NIST SP 800-131A 的建议

  • 在需要抗碰撞性的场合(如数字签名),MD5 和 SHA-1 已不再可接受。
  • 建议与 RSA、DSA 和 DH 算法一起使用的密钥至少有 2048 位,而 ECDSA 和 ECDH 的曲线至少有 224 位,以便在未来几年内安全使用。
  • modp1modp2modp5 的 DH 组的密钥大小小于 2048 位,不推荐使用。

有关其他建议和详细信息,请参阅参考文献。

一些已知存在弱点且在实践中关联性不大的算法只能通过旧版提供程序获得,该提供程序默认不启用。

CCM 模式#

CCM 是受支持的AEAD 算法之一。使用此模式的应用程序在使用 cipher API 时必须遵守某些限制:

  • 身份验证标签的长度必须在创建密码时通过设置 authTagLength 选项来指定,并且必须是 4、6、8、10、12、14 或 16 字节之一。
  • 初始化向量 (nonce) N 的长度必须在 7 到 13 字节之间 (7 ≤ N ≤ 13)。
  • 明文的长度限制为 2 ** (8 * (15 - N)) 字节。
  • 解密时,必须在调用 update() 之前通过 setAuthTag() 设置身份验证标签。否则,解密将失败,并且 final() 将根据 RFC 3610 第 2.6 节抛出错误。
  • 在 CCM 模式下使用流方法(如 write(data)end(data)pipe())可能会失败,因为 CCM 每个实例只能处理一个数据块。
  • 当传递附加认证数据 (AAD) 时,实际消息的字节长度必须通过 plaintextLength 选项传递给 setAAD()。许多加密库将身份验证标签包含在密文中,这意味着它们生成的密文长度为 plaintextLength + authTagLength。Node.js 不包含身份验证标签,所以密文长度始终为 plaintextLength。如果未使用 AAD,则此项不是必需的。
  • 由于 CCM 一次处理整个消息,update() 必须只调用一次。
  • 尽管调用 update() 足以加密/解密消息,但应用程序*必须*调用 final() 来计算或验证身份验证标签。
import { Buffer } from 'node:buffer';
const {
  createCipheriv,
  createDecipheriv,
  randomBytes,
} = await import('node:crypto');

const key = 'keykeykeykeykeykeykeykey';
const nonce = randomBytes(12);

const aad = Buffer.from('0123456789', 'hex');

const cipher = createCipheriv('aes-192-ccm', key, nonce, {
  authTagLength: 16,
});
const plaintext = 'Hello world';
cipher.setAAD(aad, {
  plaintextLength: Buffer.byteLength(plaintext),
});
const ciphertext = cipher.update(plaintext, 'utf8');
cipher.final();
const tag = cipher.getAuthTag();

// Now transmit { ciphertext, nonce, tag }.

const decipher = createDecipheriv('aes-192-ccm', key, nonce, {
  authTagLength: 16,
});
decipher.setAuthTag(tag);
decipher.setAAD(aad, {
  plaintextLength: ciphertext.length,
});
const receivedPlaintext = decipher.update(ciphertext, null, 'utf8');

try {
  decipher.final();
} catch (err) {
  throw new Error('Authentication failed!', { cause: err });
}

console.log(receivedPlaintext);const { Buffer } = require('node:buffer');
const {
  createCipheriv,
  createDecipheriv,
  randomBytes,
} = require('node:crypto');

const key = 'keykeykeykeykeykeykeykey';
const nonce = randomBytes(12);

const aad = Buffer.from('0123456789', 'hex');

const cipher = createCipheriv('aes-192-ccm', key, nonce, {
  authTagLength: 16,
});
const plaintext = 'Hello world';
cipher.setAAD(aad, {
  plaintextLength: Buffer.byteLength(plaintext),
});
const ciphertext = cipher.update(plaintext, 'utf8');
cipher.final();
const tag = cipher.getAuthTag();

// Now transmit { ciphertext, nonce, tag }.

const decipher = createDecipheriv('aes-192-ccm', key, nonce, {
  authTagLength: 16,
});
decipher.setAuthTag(tag);
decipher.setAAD(aad, {
  plaintextLength: ciphertext.length,
});
const receivedPlaintext = decipher.update(ciphertext, null, 'utf8');

try {
  decipher.final();
} catch (err) {
  throw new Error('Authentication failed!', { cause: err });
}

console.log(receivedPlaintext);

FIPS 模式#

当使用 OpenSSL 3 时,Node.js 在与适当的 OpenSSL 3 提供程序(例如来自 OpenSSL 3 的 FIPS 提供程序)一起使用时支持 FIPS 140-2,该提供程序可以按照 OpenSSL 的 FIPS README 文件中的说明进行安装。

要在 Node.js 中支持 FIPS,您需要:

  • 一个正确安装的 OpenSSL 3 FIPS 提供程序。
  • 一个 OpenSSL 3 FIPS 模块配置文件
  • 一个引用 FIPS 模块配置文件的 OpenSSL 3 配置文件。

Node.js 需要使用一个指向 FIPS 提供程序的 OpenSSL 配置文件进行配置。一个示例配置文件如下:

nodejs_conf = nodejs_init

.include /<absolute path>/fipsmodule.cnf

[nodejs_init]
providers = provider_sect

[provider_sect]
default = default_sect
# The fips section name should match the section name inside the
# included fipsmodule.cnf.
fips = fips_sect

[default_sect]
activate = 1 

其中 fipsmodule.cnf 是从 FIPS 提供程序安装步骤生成的 FIPS 模块配置文件:

openssl fipsinstall 

OPENSSL_CONF 环境变量设置为指向您的配置文件,并将 OPENSSL_MODULES 设置为 FIPS 提供程序动态库的位置。例如:

export OPENSSL_CONF=/<path to configuration file>/nodejs.cnf
export OPENSSL_MODULES=/<path to openssl lib>/ossl-modules 

然后可以通过以下方式在 Node.js 中启用 FIPS 模式:

  • 使用 --enable-fips--force-fips 命令行标志启动 Node.js。
  • 以编程方式调用 crypto.setFips(true)

或者,可以通过 OpenSSL 配置文件在 Node.js 中启用 FIPS 模式。例如:

nodejs_conf = nodejs_init

.include /<absolute path>/fipsmodule.cnf

[nodejs_init]
providers = provider_sect
alg_section = algorithm_sect

[provider_sect]
default = default_sect
# The fips section name should match the section name inside the
# included fipsmodule.cnf.
fips = fips_sect

[default_sect]
activate = 1

[algorithm_sect]
default_properties = fips=yes 

加密常量#

crypto.constants 导出的以下常量适用于 node:cryptonode:tlsnode:https 模块的各种用途,并且通常特定于 OpenSSL。

OpenSSL 选项#

详情请参阅 SSL OP 标志列表

常量 描述
SSL_OP_ALL 在 OpenSSL 中应用多个错误修复。详情请参阅 https://www.openssl.org/docs/man3.0/man3/SSL_CTX_set_options.html
SSL_OP_ALLOW_NO_DHE_KEX 指示 OpenSSL 允许为 TLS v1.3 使用非 [EC]DHE 的密钥交换模式。
SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION 允许 OpenSSL 与未打补丁的客户端或服务器之间进行旧版不安全的重新协商。请参阅 https://www.openssl.org/docs/man3.0/man3/SSL_CTX_set_options.html
SSL_OP_CIPHER_SERVER_PREFERENCE 在选择密码时,尝试使用服务器的偏好而不是客户端的。行为取决于协议版本。请参阅 https://www.openssl.org/docs/man3.0/man3/SSL_CTX_set_options.html
SSL_OP_CISCO_ANYCONNECT 指示 OpenSSL 使用思科版本的 DTLS_BAD_VER 标识符。
SSL_OP_COOKIE_EXCHANGE 指示 OpenSSL 开启 cookie 交换。
SSL_OP_CRYPTOPRO_TLSEXT_BUG 指示 OpenSSL 添加来自早期版本 cryptopro 草案的 server-hello 扩展。
SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS 指示 OpenSSL 禁用 OpenSSL 0.9.6d 中添加的 SSL 3.0/TLS 1.0 漏洞修复。
SSL_OP_LEGACY_SERVER_CONNECT 允许初始连接到不支持 RI 的服务器。
SSL_OP_NO_COMPRESSION 指示 OpenSSL 禁用对 SSL/TLS 压缩的支持。
SSL_OP_NO_ENCRYPT_THEN_MAC 指示 OpenSSL 禁用 encrypt-then-MAC。
SSL_OP_NO_QUERY_MTU
SSL_OP_NO_RENEGOTIATION 指示 OpenSSL 禁用重新协商。
SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION 指示 OpenSSL 在执行重新协商时总是启动一个新会话。
SSL_OP_NO_SSLv2 指示 OpenSSL 关闭 SSL v2
SSL_OP_NO_SSLv3 指示 OpenSSL 关闭 SSL v3
SSL_OP_NO_TICKET 指示 OpenSSL 禁用 RFC4507bis 票据的使用。
SSL_OP_NO_TLSv1 指示 OpenSSL 关闭 TLS v1
SSL_OP_NO_TLSv1_1 指示 OpenSSL 关闭 TLS v1.1
SSL_OP_NO_TLSv1_2 指示 OpenSSL 关闭 TLS v1.2
SSL_OP_NO_TLSv1_3 指示 OpenSSL 关闭 TLS v1.3
SSL_OP_PRIORITIZE_CHACHA 指示 OpenSSL 服务器在客户端也这样做时优先选择 ChaCha20-Poly1305。如果未启用 SSL_OP_CIPHER_SERVER_PREFERENCE,此选项无效。
SSL_OP_TLS_ROLLBACK_BUG 指示 OpenSSL 禁用版本回滚攻击检测。

OpenSSL 引擎常量#

常量 描述
ENGINE_METHOD_RSA 限制引擎使用 RSA
ENGINE_METHOD_DSA 限制引擎使用 DSA
ENGINE_METHOD_DH 限制引擎使用 DH
ENGINE_METHOD_RAND 限制引擎使用 RAND
ENGINE_METHOD_EC 限制引擎使用 EC
ENGINE_METHOD_CIPHERS 限制引擎使用 CIPHERS
ENGINE_METHOD_DIGESTS 限制引擎使用 DIGESTS
ENGINE_METHOD_PKEY_METHS 限制引擎使用 PKEY_METHS
ENGINE_METHOD_PKEY_ASN1_METHS 限制引擎使用 PKEY_ASN1_METHS
ENGINE_METHOD_ALL
ENGINE_METHOD_NONE

其他 OpenSSL 常量#

常量 描述
DH_CHECK_P_NOT_SAFE_PRIME
DH_CHECK_P_NOT_PRIME
DH_UNABLE_TO_CHECK_GENERATOR
DH_NOT_SUITABLE_GENERATOR
RSA_PKCS1_PADDING
RSA_SSLV23_PADDING
RSA_NO_PADDING
RSA_PKCS1_OAEP_PADDING
RSA_X931_PADDING
RSA_PKCS1_PSS_PADDING
RSA_PSS_SALTLEN_DIGEST 在签名或验证时,将 RSA_PKCS1_PSS_PADDING 的盐长度设置为摘要大小。
RSA_PSS_SALTLEN_MAX_SIGN 在签署数据时,将 RSA_PKCS1_PSS_PADDING 的盐长度设置为最大允许值。
RSA_PSS_SALTLEN_AUTO 在验证签名时,使 RSA_PKCS1_PSS_PADDING 的盐长度自动确定。
POINT_CONVERSION_COMPRESSED
POINT_CONVERSION_UNCOMPRESSED
POINT_CONVERSION_HYBRID

Node.js 加密常量#

常量 描述
defaultCoreCipherList 指定 Node.js 使用的内置默认密码列表。
defaultCipherList 指定当前 Node.js 进程使用的活动默认密码列表。

脚注

  1. 需要 OpenSSL >= 3.5 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25

  2. 需要 OpenSSL >= 3.0 2

  3. 需要 OpenSSL >= 3.2 2 3 4 5 6