On this page

加密

History
Source Code: lib/crypto.js

稳定性:2 - 稳定

node:crypto 模块提供加密功能,其中包含一组封装器,用于调用 OpenSSL 的哈希、消息认证码(MAC)、加密、解密、签名、验证和密钥封装机制(KEM)函数。

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

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

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

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

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

let crypto;
try {
  crypto = require('node:crypto');
} catch (err) {
  console.error('加密支持已禁用!');
}

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

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

let crypto;
try {
  crypto = await import('node:crypto');
} catch (err) {
  console.error('加密支持已禁用!');
}

以下内容按照导入和导出每种类型所支持的完整格式集合,列出了 KeyObject API 所识别的非对称密钥类型。

格式: 'pem'、'der'

  • 'dh'(Diffie-Hellman) — OID 1.2.840.113549.1.3.1
  • 'dsa' — OID 1.2.840.10040.4.1
  • 'rsa-pss' — OID 1.2.840.113549.1.1.10

格式: 'pem'、'der'、'jwk'

  • 'rsa' — OID 1.2.840.113549.1.1.1

格式: 'pem'、'der'、'jwk'、'raw-public'、'raw-private'

  • 'ec'(椭圆曲线) — OID 1.2.840.10045.2.1
  • 'ed25519' — OID 1.3.101.112
  • 'ed448' — OID 1.3.101.113
  • 'slh-dsa-sha2-128f'1 — OID 2.16.840.1.101.3.4.3.21
  • 'slh-dsa-sha2-128s'1 — OID 2.16.840.1.101.3.4.3.20
  • 'slh-dsa-sha2-192f'1 — OID 2.16.840.1.101.3.4.3.23
  • 'slh-dsa-sha2-192s'1 — OID 2.16.840.1.101.3.4.3.22
  • 'slh-dsa-sha2-256f'1 — OID 2.16.840.1.101.3.4.3.25
  • 'slh-dsa-sha2-256s'1 — OID 2.16.840.1.101.3.4.3.24
  • 'slh-dsa-shake-128f'1 — OID 2.16.840.1.101.3.4.3.27
  • 'slh-dsa-shake-128s'1 — OID 2.16.840.1.101.3.4.3.26
  • 'slh-dsa-shake-192f'1 — OID 2.16.840.1.101.3.4.3.29
  • 'slh-dsa-shake-192s'1 — OID 2.16.840.1.101.3.4.3.28
  • 'slh-dsa-shake-256f'1 — OID 2.16.840.1.101.3.4.3.31
  • 'slh-dsa-shake-256s'1 — OID 2.16.840.1.101.3.4.3.30
  • 'x25519' — OID 1.3.101.110
  • 'x448' — OID 1.3.101.111

格式: 'pem'、'der'、'jwk'、'raw-public'、'raw-seed'

  • 'ml-dsa-44'1 — OID 2.16.840.1.101.3.4.3.17
  • 'ml-dsa-65'1 — OID 2.16.840.1.101.3.4.3.18
  • 'ml-dsa-87'1 — OID 2.16.840.1.101.3.4.3.19
  • 'ml-kem-512'1 — OID 2.16.840.1.101.3.4.4.1
  • 'ml-kem-768'1 — OID 2.16.840.1.101.3.4.4.2
  • 'ml-kem-1024'1 — OID 2.16.840.1.101.3.4.4.3

非对称密钥可以用几种格式表示。推荐的方法是将密钥材料导入 KeyObject 一次,并在所有后续操作中重用它,因为这避免了重复解析并提供最佳性能。

当 KeyObject 不切实际时——例如,当密钥材料出现在协议消息中且仅使用一次时——大多数加密函数也接受 PEM 字符串或直接指定格式和密钥材料的对象。有关每种格式接受的完整选项,请参阅 crypto.createPublicKey()、crypto.createPrivateKey() 和 keyObject.export()。

KeyObject 是解析后的密钥的内存表示。它由 crypto.createPublicKey()、crypto.createPrivateKey()、crypto.createSecretKey() 或密钥生成函数(如 crypto.generateKeyPair())创建。使用给定 KeyObject 进行的第一个加密操作可能比后续操作慢,因为 OpenSSL 会在首次使用时延迟初始化内部缓存。

PEM 和 DER 是基于 ASN.1 结构的非对称密钥的传统编码格式。

  • PEM 是一种文本编码,它将 Base64 编码的 DER 数据包装在页眉和页脚行之间(例如 -----BEGIN PUBLIC KEY-----)。PEM 字符串可以直接传递给大多数加密操作。
  • DER 是相同 ASN.1 结构的二进制编码。提供 DER 输入时,必须明确指定 type(通常为 'spki' 或 'pkcs8')。

JSON Web Key (JWK) 是 RFC 7517 中定义的基于 JSON 的密钥表示。JWK 将每个密钥组件编码为 JSON 对象内的单个 Base64url 编码值。对于 RSA 密钥,JWK 避免了 ASN.1 解析开销,是最快的序列化导入格式。

稳定性:1.1 - 积极开发中

'raw-public'、'raw-private' 和 'raw-seed' 密钥格式允许导入和导出原始密钥材料而无需任何编码包装器。有关使用详情,请参阅 keyObject.export()、crypto.createPublicKey() 和 crypto.createPrivateKey()。

'raw-public' 通常是导入公钥的最快方式。'raw-private' 和 'raw-seed' 并不总是比其他格式快,因为它们仅包含私钥标量或种子——导入它们需要派生公钥组件(例如,椭圆曲线点乘法或种子扩展),这可能很昂贵。其他格式包括私钥和公钥组件,避免了该计算。

始终优先使用 KeyObject——从你拥有的任何格式创建一个并重用它。下面的指南仅适用于在选择序列化格式时,无论是导入到 KeyObject 还是在 KeyObject 不切实际时内联传递密钥材料。

当创建 KeyObject 以供重复使用时,导入成本只支付一次,因此选择更快的格式可以减少启动延迟。

导入成本分为两部分:解析开销(解码序列化包装器)和密钥计算(重建完整密钥所需的任何数学工作,例如从私钥标量派生公钥或扩展种子)。哪部分占主导地位取决于密钥类型。例如:

  • 公钥——'raw-public' 是最快的序列化格式,因为原始格式跳过了所有 ASN.1 和 Base64 解码。
  • EC 私钥——'raw-private' 比 PEM 或 DER 快,因为它避免了 ASN.1 解析。但是,对于较大的曲线(例如 P-384、P-521),从私钥标量派生公钥点所需的计算变得昂贵,减少了优势。
  • RSA 密钥——'jwk' 是最快的序列化格式。JWK 将 RSA 密钥组件表示为单个 Base64url 编码的整数,完全避免了 ASN.1 解析的开销。

当无法重用 KeyObject 时(例如,密钥作为原始字节出现在协议消息中且仅使用一次),大多数加密函数也接受 PEM 字符串或直接指定格式和密钥材料的对象。在这种情况下,总成本是密钥导入和加密计算本身的总和。

对于加密计算占主导地位的操作——例如使用 RSA 签名或使用 P-384 或 P-521 进行 ECDH 密钥协商——序列化格式对整体吞吐量的影响可以忽略不计,因此选择最方便的格式。对于轻量级操作,如 Ed25519 签名或验证,导入成本占总成本的较大比例,因此更快的格式(如 'raw-public' 或 'raw-private')可以显著提高吞吐量。

即使相同的密钥材料只使用几次,将其导入到 KeyObject 也比重复传递原始或 PEM 表示更值得。

示例:在签名和验证操作中重用 KeyObject:

import { promisify } from 'node:util';
const { generateKeyPair, sign, verify } = await import('node:crypto');

const { publicKey, privateKey } = await promisify(generateKeyPair)('ed25519');

// KeyObject 将解析后的密钥保存在内存中,可以复用
// 跨多个操作而无需重新解析。
const data = new TextEncoder().encode('message to sign');
const signature = sign(null, data, privateKey);
verify(null, data, publicKey, signature);

示例:将各种格式的密钥导入到 KeyObject 中:

import { promisify } from 'node:util';
const {
  createPrivateKey, createPublicKey, generateKeyPair,
} = await import('node:crypto');

const generated = await promisify(generateKeyPair)('ed25519');

// PEM
const privatePem = generated.privateKey.export({ format: 'pem', type: 'pkcs8' });
const publicPem = generated.publicKey.export({ format: 'pem', type: 'spki' });
createPrivateKey(privatePem);
createPublicKey(publicPem);

// DER - 需要明确指定类型
const privateDer = generated.privateKey.export({ format: 'der', type: 'pkcs8' });
const publicDer = generated.publicKey.export({ format: 'der', type: 'spki' });
createPrivateKey({ key: privateDer, format: 'der', type: 'pkcs8' });
createPublicKey({ key: publicDer, format: 'der', type: 'spki' });

// JWK
const privateJwk = generated.privateKey.export({ format: 'jwk' });
const publicJwk = generated.publicKey.export({ format: 'jwk' });
createPrivateKey({ key: privateJwk, format: 'jwk' });
createPublicKey({ key: publicJwk, format: 'jwk' });

// 原始格式
const rawPriv = generated.privateKey.export({ format: 'raw-private' });
const rawPub = generated.publicKey.export({ format: 'raw-public' });
createPrivateKey({ key: rawPriv, format: 'raw-private', asymmetricKeyType: 'ed25519' });
createPublicKey({ key: rawPub, format: 'raw-public', asymmetricKeyType: 'ed25519' });

示例:直接将密钥材料传递给 crypto.sign() 和 crypto.verify() 而无需先创建 KeyObject:

import { promisify } from 'node:util';
const { generateKeyPair, sign, verify } = await import('node:crypto');

const generated = await promisify(generateKeyPair)('ed25519');

const data = new TextEncoder().encode('message to sign');

// PEM 字符串
const privatePem = generated.privateKey.export({ format: 'pem', type: 'pkcs8' });
const publicPem = generated.publicKey.export({ format: 'pem', type: 'spki' });
const sig1 = sign(null, data, privatePem);
verify(null, data, publicPem, sig1);

// JWK 对象
const privateJwk = generated.privateKey.export({ format: 'jwk' });
const publicJwk = generated.publicKey.export({ format: 'jwk' });
const sig2 = sign(null, data, { key: privateJwk, format: 'jwk' });
verify(null, data, { key: publicJwk, format: 'jwk' }, sig2);

// 原始密钥字节
const rawPriv = generated.privateKey.export({ format: 'raw-private' });
const rawPub = generated.publicKey.export({ format: 'raw-public' });
const sig3 = sign(null, data, {
  key: rawPriv, format: 'raw-private', asymmetricKeyType: 'ed25519',
});
verify(null, data, {
  key: rawPub, format: 'raw-public', asymmetricKeyType: 'ed25519',
}, sig3);

示例:对于 EC 密钥,导入原始密钥时需要 namedCurve 选项:

import { promisify } from 'node:util';
const {
  createPrivateKey, createPublicKey, generateKeyPair, sign, verify,
} = await import('node:crypto');

const generated = await promisify(generateKeyPair)('ec', {
  namedCurve: 'P-256',
});

// 导出原始 EC 公钥(默认未压缩)。
const rawPublicKey = generated.publicKey.export({ format: 'raw-public' });

// 以下等效。
const rawPublicKeyUncompressed = generated.publicKey.export({
  format: 'raw-public',
  type: 'uncompressed',
});

// 导出压缩点格式。
const rawPublicKeyCompressed = generated.publicKey.export({
  format: 'raw-public',
  type: 'compressed',
});

// 导出原始 EC 私钥。
const rawPrivateKey = generated.privateKey.export({ format: 'raw-private' });

// 导入原始 EC 密钥。
// 接受压缩和未压缩的点格式。
const publicKey = createPublicKey({
  key: rawPublicKey,
  format: 'raw-public',
  asymmetricKeyType: 'ec',
  namedCurve: 'P-256',
});
const privateKey = createPrivateKey({
  key: rawPrivateKey,
  format: 'raw-private',
  asymmetricKeyType: 'ec',
  namedCurve: 'P-256',
});

const data = new TextEncoder().encode('message to sign');
const signature = sign('sha256', data, privateKey);
verify('sha256', data, publicKey, signature);

示例:导出原始种子并导入它们:

import { promisify } from 'node:util';
const {
  createPrivateKey, decapsulate, encapsulate, generateKeyPair,
} = await import('node:crypto');

const generated = await promisify(generateKeyPair)('ml-kem-768');

// 导出原始种子(ML-KEM 为 64 字节)。
const seed = generated.privateKey.export({ format: 'raw-seed' });

// 导入原始种子。
const privateKey = createPrivateKey({
  key: seed,
  format: 'raw-seed',
  asymmetricKeyType: 'ml-kem-768',
});

const { ciphertext } = encapsulate(generated.publicKey);
decapsulate(privateKey, ciphertext);

类:Certificate

History

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

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

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

Attributes
encoding:string
spkac 字符串的 编码 。
const { Certificate } = await import('node:crypto');
const spkac = getSpkacSomehow();
const challenge = Certificate.exportChallenge(spkac);
console.log(challenge.toString('utf8'));
// 打印:挑战作为 UTF8 字符串
const { Certificate } = require('node:crypto');
const spkac = getSpkacSomehow();
const challenge = Certificate.exportChallenge(spkac);
console.log(challenge.toString('utf8'));
// 打印:挑战作为 UTF8 字符串
Attributes
encoding:string
spkac 字符串的 编码 。
const { Certificate } = await import('node:crypto');
const spkac = getSpkacSomehow();
const publicKey = Certificate.exportPublicKey(spkac);
console.log(publicKey);
// 打印:公钥为 <Buffer ...>
const { Certificate } = require('node:crypto');
const spkac = getSpkacSomehow();
const publicKey = Certificate.exportPublicKey(spkac);
console.log(publicKey);
// 打印:公钥为 <Buffer ...>
Attributes
encoding:string
spkac 字符串的 编码 。
import { Buffer } from 'node:buffer';
const { Certificate } = await import('node:crypto');

const spkac = getSpkacSomehow();
console.log(Certificate.verifySpkac(Buffer.from(spkac)));
// 打印:true 或 false
const { Buffer } = require('node:buffer');
const { Certificate } = require('node:crypto');

const spkac = getSpkacSomehow();
console.log(Certificate.verifySpkac(Buffer.from(spkac)));
// 打印:true 或 false

稳定性:0 - 已弃用

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

new crypto.Certificate(): void

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();
M

certificate.exportChallenge

History
certificate.exportChallenge(spkac, encoding?): void
Attributes
encoding:string
spkac 字符串的 编码 。
const { Certificate } = await import('node:crypto');
const cert = Certificate();
const spkac = getSpkacSomehow();
const challenge = cert.exportChallenge(spkac);
console.log(challenge.toString('utf8'));
// 打印:挑战作为 UTF8 字符串
const { Certificate } = require('node:crypto');
const cert = Certificate();
const spkac = getSpkacSomehow();
const challenge = cert.exportChallenge(spkac);
console.log(challenge.toString('utf8'));
// 打印:挑战作为 UTF8 字符串
M

certificate.exportPublicKey

History
certificate.exportPublicKey(spkac, encoding?): void
Attributes
encoding:string
spkac 字符串的 编码 。
const { Certificate } = await import('node:crypto');
const cert = Certificate();
const spkac = getSpkacSomehow();
const publicKey = cert.exportPublicKey(spkac);
console.log(publicKey);
// 打印:公钥为 <Buffer ...>
const { Certificate } = require('node:crypto');
const cert = Certificate();
const spkac = getSpkacSomehow();
const publicKey = cert.exportPublicKey(spkac);
console.log(publicKey);
// 打印:公钥为 <Buffer ...>
M

certificate.verifySpkac

History
certificate.verifySpkac(spkac, encoding?): void
Attributes
encoding:string
spkac 字符串的 编码 。
import { Buffer } from 'node:buffer';
const { Certificate } = await import('node:crypto');

const cert = Certificate();
const spkac = getSpkacSomehow();
console.log(cert.verifySpkac(Buffer.from(spkac)));
// 打印:true 或 false
const { Buffer } = require('node:buffer');
const { Certificate } = require('node:crypto');

const cert = Certificate();
const spkac = getSpkacSomehow();
console.log(cert.verifySpkac(Buffer.from(spkac)));
// 打印:true 或 false

类:Cipheriv

History

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

  • 作为一个既可读又可写的 [stream][],将明文未加密数据写入以在可读侧产生加密数据,或
  • 使用 cipher.update() 和 cipher.final() 方法来生成加密数据。

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

所选算法可能会对流式处理以及对 cipher.update() 的调用施加其他限制。请参阅 CCM mode、CBC-CTS mode、XTS mode、AES key wrap modes 和 SIV and GCM-SIV modes。

示例:将 Cipheriv 对象用作流:

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

const algorithm = 'aes-192-cbc';
const password = '用于生成密钥的密码';

// 首先,我们将生成密钥。密钥长度取决于算法。
// 在这种情况下,对于 aes192,它是 24 字节(192 位)。
scrypt(password, 'salt', 24, (err, key) => {
  if (err) throw err;
  // 然后,我们将生成一个随机初始化向量
  randomFill(new Uint8Array(16), (err, iv) => {
    if (err) throw err;

    // 一旦我们有了密钥和 iv,我们就可以创建并使用密码...
    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 = '用于生成密钥的密码';

// 首先,我们将生成密钥。密钥长度取决于算法。
// 在这种情况下,对于 aes192,它是 24 字节(192 位)。
scrypt(password, 'salt', 24, (err, key) => {
  if (err) throw err;
  // 然后,我们将生成一个随机初始化向量
  randomFill(new Uint8Array(16), (err, iv) => {
    if (err) throw err;

    // 一旦我们有了密钥和 iv,我们就可以创建并使用密码...
    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 = '用于生成密钥的密码';

// 首先,我们将生成密钥。密钥长度取决于算法。
// 在这种情况下,对于 aes192,它是 24 字节(192 位)。
scrypt(password, 'salt', 24, (err, key) => {
  if (err) throw err;
  // 然后,我们将生成一个随机初始化向量
  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 = '用于生成密钥的密码';

// 首先,我们将生成密钥。密钥长度取决于算法。
// 在这种情况下,对于 aes192,它是 24 字节(192 位)。
scrypt(password, 'salt', 24, (err, key) => {
  if (err) throw err;
  // 然后,我们将生成一个随机初始化向量
  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 = '用于生成密钥的密码';

// 首先,我们将生成密钥。密钥长度取决于算法。
// 在这种情况下,对于 aes192,它是 24 字节(192 位)。
scrypt(password, 'salt', 24, (err, key) => {
  if (err) throw err;
  // 然后,我们将生成一个随机初始化向量
  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 = '用于生成密钥的密码';

// 首先,我们将生成密钥。密钥长度取决于算法。
// 在这种情况下,对于 aes192,它是 24 字节(192 位)。
scrypt(password, 'salt', 24, (err, key) => {
  if (err) throw err;
  // 然后,我们将生成一个随机初始化向量
  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);
  });
});
M

cipher.final

History
cipher.final(outputEncoding?): void
Attributes
outputEncoding:string
返回值的 编码 。

如果在之前调用 cipher.update() 时指定了输出编码, 则 outputEncoding 必须使用相同的编码。

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

M

cipher.getAuthTag

History
cipher.getAuthTag(): void
  • 返回:Buffer 使用认证加密模式时(目前支持 GCM、CCM、OCB、SIV、GCM-SIV 和 chacha20-poly1305),cipher.getAuthTag() 方法会返回一个 Buffer,其中包含根据给定数据计算得出的 认证标签。

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

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

M

cipher.setAAD

History
cipher.setAAD(buffer, options?): void
Attributes
options:Object
[ stream.transform 选项][]
plaintextLength:number
encoding:string
当 buffer 是字符串时使用的字符串编码。

使用认证加密模式时(目前支持 GCM、CCM、OCB、SIV、GCM-SIV 和 chacha20-poly1305),cipher.setAAD() 方法会设置用于 附加认证数据(AAD)输入参数的值。

对于 GCM、OCB、SIV 和 GCM-SIV,plaintextLength 选项是可选的。使用 CCM 时,必须指定 plaintextLength 选项,且其值必须与明文的字节长度相符。请参阅 CCM mode。

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

M

cipher.setAutoPadding

History
cipher.setAutoPadding(autoPadding?): void
Attributes
autoPadding:boolean
默认: true

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

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

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

cipher.update(data, inputEncoding?, outputEncoding?): void
Attributes
inputEncoding:string
数据的 编码 。
outputEncoding:string
返回值的 编码 。

使用 data 更新密码。如果给出了 inputEncoding 参数, 则 data 参数是使用指定编码的字符串。如果未给出 inputEncoding 参数,data 必须是 Buffer、TypedArray 或 DataView。如果 data 是 Buffer、TypedArray 或 DataView,则 忽略 inputEncoding。

outputEncoding 指定加密数据的输出格式。如果指定了 outputEncoding,则返回使用指定编码的字符串。如果未提供 outputEncoding,则返回 Buffer。 指定 outputEncoding 时,必须使用与之前调用 cipher.update() 时相同的编码。

对于大多数算法,可以使用新数据多次调用 cipher.update(),直到调用 cipher.final()。某些算法会限制对 cipher.update() 的调用。例如,CCM mode、CBC-CTS mode、XTS mode、AES key wrap modes 和 SIV and GCM-SIV modes 要求在单次调用中提供完整消息。在调用 cipher.final() 后再调用 cipher.update() 将导致抛出错误。

类:Decipheriv

History

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

  • 作为一个既可读又可写的 流,将纯加密数据写入以在可读侧产生未加密数据,或
  • 使用 decipher.update() 和 decipher.final() 方法来产生未加密数据。

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

所选算法可能会对流式处理以及对 decipher.update() 的调用施加其他限制。请参阅 CCM mode、CBC-CTS mode、XTS mode、AES key wrap modes 和 SIV and GCM-SIV modes。

示例:将 Decipheriv 对象用作流:

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

const algorithm = 'aes-192-cbc';
const password = '用于生成密钥的密码';
// 密钥长度取决于算法。在这种情况下,对于 aes192,它是
// 24 字节(192 位)。
// 请改用异步的 `crypto.scrypt()`。
const key = scryptSync(password, 'salt', 24);
// IV 通常与密文一起传递。
const iv = Buffer.alloc(16, 0); // 初始化向量。

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);
  // 输出:一些明文数据
});

// 使用相同的算法、密钥和 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 = '用于生成密钥的密码';
// 密钥长度取决于算法。在这种情况下,对于 aes192,它是
// 24 字节(192 位)。
// 请改用异步的 `crypto.scrypt()`。
const key = scryptSync(password, 'salt', 24);
// IV 通常与密文一起传递。
const iv = Buffer.alloc(16, 0); // 初始化向量。

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);
  // 输出:一些明文数据
});

// 使用相同的算法、密钥和 iv 进行加密。
const encrypted =
  'e5f79c5915c02171eec6b212d5520d44480993d7d622a7c4c2da32f6efda0ffa';
decipher.write(encrypted, 'hex');
decipher.end();

示例:将 Decipheriv 对象用作流:

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

const algorithm = 'aes-192-cbc';
const password = '用于生成密钥的密码';
// 密钥长度取决于算法。在这种情况下,对于 aes192,它是
// 24 字节(192 位)。
// 请改用异步的 `crypto.scrypt()`。
const key = scryptSync(password, 'salt', 24);
// IV 通常与密文一起传递。
const iv = Buffer.alloc(16, 0); // 初始化向量。

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);
  // 输出:一些明文数据
});

// 使用相同的算法、密钥和 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 = '用于生成密钥的密码';
// 密钥长度取决于算法。在这种情况下,对于 aes192,它是
// 24 字节(192 位)。
// 请改用异步的 `crypto.scrypt()`。
const key = scryptSync(password, 'salt', 24);
// IV 通常与密文一起传递。
const iv = Buffer.alloc(16, 0); // 初始化向量。

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);
  // 输出:一些明文数据
});

// 使用相同的算法、密钥和 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 = '用于生成密钥的密码';
// 请改用异步的 `crypto.scrypt()`。
const key = scryptSync(password, 'salt', 24);
// IV 通常与密文一起传递。
const iv = Buffer.alloc(16, 0); // 初始化向量。

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 = '用于生成密钥的密码';
// 请改用异步的 `crypto.scrypt()`。
const key = scryptSync(password, 'salt', 24);
// IV 通常与密文一起传递。
const iv = Buffer.alloc(16, 0); // 初始化向量。

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 = '用于生成密钥的密码';
// 请改用异步的 `crypto.scrypt()`。
const key = scryptSync(password, 'salt', 24);
// IV 通常与密文一起传递。
const iv = Buffer.alloc(16, 0); // 初始化向量。

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

// 使用相同的算法、密钥和 iv 进行加密。
const encrypted =
  'e5f79c5915c02171eec6b212d5520d44480993d7d622a7c4c2da32f6efda0ffa';
let decrypted = decipher.update(encrypted, 'hex', 'utf8');
decrypted += decipher.final('utf8');
console.log(decrypted);
// 输出:一些明文数据
const {
  scryptSync,
  createDecipheriv,
} = require('node:crypto');
const { Buffer } = require('node:buffer');

const algorithm = 'aes-192-cbc';
const password = '用于生成密钥的密码';
// 请改用异步的 `crypto.scrypt()`。
const key = scryptSync(password, 'salt', 24);
// IV 通常与密文一起传递。
const iv = Buffer.alloc(16, 0); // 初始化向量。

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

// 使用相同的算法、密钥和 iv 进行加密。
const encrypted =
  'e5f79c5915c02171eec6b212d5520d44480993d7d622a7c4c2da32f6efda0ffa';
let decrypted = decipher.update(encrypted, 'hex', 'utf8');
decrypted += decipher.final('utf8');
console.log(decrypted);
// 输出:一些明文数据
M

decipher.final

History
decipher.final(outputEncoding?): void
Attributes
outputEncoding:string
返回值的 编码 。

如果在之前调用 decipher.update() 时指定了输出编码,outputEncoding 必须使用相同的编码。

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

decipher.setAAD(buffer, options?): void
Attributes
options:Object
[ stream.transform 选项][]
plaintextLength:number
encoding:string
当 buffer 为字符串时使用的字符串编码。

使用认证加密模式时(目前支持 GCM、CCM、OCB、SIV、GCM-SIV 和 chacha20-poly1305),decipher.setAAD() 方法会设置用作_附加认证数据_(AAD)输入参数的值。

对于 GCM、OCB、SIV 和 GCM-SIV,options 参数是可选的。使用 CCM 时,必须指定 plaintextLength 选项,且其值必须与密文的字节长度相同。参见 [CCM 模式][]。

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

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

decipher.setAuthTag(buffer, encoding?): void
Attributes
encoding:string
当 buffer 为字符串时使用的字符串编码。

使用认证加密模式时(目前支持 GCM、CCM、OCB、SIV、GCM-SIV 和 chacha20-poly1305),decipher.setAuthTag() 方法用于传入收到的_认证标签_。如果未提供标签,或者密文遭到篡改,decipher.final() 将抛出错误,表明由于认证失败,应丢弃密文。如果标签长度不符合 NIST SP 800-38D 的规定,或与 authTagLength 选项的值不匹配,decipher.setAuthTag() 将抛出错误。

对于 CCM、SIV 和 GCM-SIV 模式,必须在 decipher.update() 之前调用 decipher.setAuthTag() 方法;对于 GCM 和 OCB 模式以及 chacha20-poly1305,则必须在 decipher.final() 之前调用。decipher.setAuthTag() 只能调用一次。

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

M

decipher.setAutoPadding

History
decipher.setAutoPadding(autoPadding?): void
Attributes
autoPadding:boolean
默认值: true

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

对于使用标准块填充的分组密码,禁用填充后,输入数据的长度必须是密码块大小的倍数。

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

decipher.update(data, inputEncoding?, outputEncoding?): void
Attributes
inputEncoding:string
data 字符串的 编码 。
outputEncoding:string
返回值的 编码 。

使用 data 更新解密器。如果提供了 inputEncoding 参数,data 参数就是使用指定编码的字符串。如果未提供 inputEncoding 参数,data 必须是 Buffer、TypedArray 或 DataView。如果 data 是 Buffer、TypedArray 或 DataView,则会忽略 inputEncoding。

outputEncoding 指定解密数据的输出格式。如果指定了 outputEncoding,则返回使用指定编码的字符串。如果未提供 outputEncoding,则返回 Buffer。指定 outputEncoding 时,它必须与之前调用 decipher.update() 时使用相同的编码。

对于大多数算法,可以使用新数据多次调用 decipher.update(),直到调用 decipher.final()。某些算法会限制对 decipher.update() 的调用。例如,[CCM 模式][]、[CBC-CTS 模式][]、[XTS 模式][]、[AES 密钥包装模式][]以及 [SIV 和 GCM-SIV 模式][]要求在一次调用中提供完整消息。在 decipher.final() 之后调用 decipher.update() 将导致抛出错误。

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

类:DiffieHellman

History

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

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

import assert from 'node:assert';

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

// 生成 Alice 的密钥...
const alice = createDiffieHellman(2048);
const aliceKey = alice.generateKeys();

// 生成 Bob 的密钥...
const bob = createDiffieHellman(alice.getPrime(), alice.getGenerator());
const bobKey = bob.generateKeys();

// 交换并生成秘密...
const aliceSecret = alice.computeSecret(bobKey);
const bobSecret = bob.computeSecret(aliceKey);

// 正常
assert.strictEqual(aliceSecret.toString('hex'), bobSecret.toString('hex'));
const assert = require('node:assert');

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

// 生成 Alice 的密钥...
const alice = createDiffieHellman(2048);
const aliceKey = alice.generateKeys();

// 生成 Bob 的密钥...
const bob = createDiffieHellman(alice.getPrime(), alice.getGenerator());
const bobKey = bob.generateKeys();

// 交换并生成秘密...
const aliceSecret = alice.computeSecret(bobKey);
const bobSecret = bob.computeSecret(aliceKey);

// 正常
assert.strictEqual(aliceSecret.toString('hex'), bobSecret.toString('hex'));
M

diffieHellman.computeSecret

History
diffieHellman.computeSecret(otherPublicKey, inputEncoding?, outputEncoding?): void
Attributes
inputEncoding:string
otherPublicKey 字符串的 编码 。
outputEncoding:string
返回值的 编码 。

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

如果给出了 outputEncoding,则返回字符串;否则返回 Buffer。

M

diffieHellman.generateKeys

History
diffieHellman.generateKeys(encoding?): void
Attributes
encoding:string
返回值的 编码 。

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

此函数是对 DH_generate_key() 的轻量封装。特别是,一旦私钥已生成或已设置,调用此函数只会根据现有私钥重新计算公钥。由于公钥是由私钥决定的,除非通过 diffieHellman.setPrivateKey() 更改了私钥,否则结果将保持不变。

M

diffieHellman.getGenerator

History
diffieHellman.getGenerator(encoding?): void
Attributes
encoding:string
返回值的 编码 。

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

M

diffieHellman.getPrime

History
diffieHellman.getPrime(encoding?): void
Attributes
encoding:string
返回值的 编码 。

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

M

diffieHellman.getPrivateKey

History
diffieHellman.getPrivateKey(encoding?): void
Attributes
encoding:string
返回值的 编码 。

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

M

diffieHellman.getPublicKey

History
diffieHellman.getPublicKey(encoding?): void
Attributes
encoding:string
返回值的 编码 。

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

M

diffieHellman.setPrivateKey

History
diffieHellman.setPrivateKey(privateKey, encoding?): void
Attributes
encoding:string
privateKey 字符串的 编码 。

设置 Diffie-Hellman 私钥。如果提供了 encoding 参数,privateKey 应为字符串。如果未提供 encoding,privateKey 应为 Buffer、TypedArray 或 DataView。

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

M

diffieHellman.setPublicKey

History
diffieHellman.setPublicKey(publicKey, encoding?): void
Attributes
encoding:string
publicKey 字符串的 编码 。

设置 Diffie-Hellman 公钥。如果提供了 encoding 参数,publicKey 应为字符串。如果未提供 encoding,publicKey 应为 Buffer、TypedArray 或 DataView。

P

diffieHellman.verifyError

History

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

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

  • DH_CHECK_P_NOT_SAFE_PRIME
  • DH_CHECK_P_NOT_PRIME
  • DH_UNABLE_TO_CHECK_GENERATOR
  • DH_NOT_SUITABLE_GENERATOR

类:DiffieHellmanGroup

History

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

History

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

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

import assert from 'node:assert';

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

// 生成 Alice 的密钥...
const alice = createECDH('secp521r1');
const aliceKey = alice.generateKeys();

// 生成 Bob 的密钥...
const bob = createECDH('secp521r1');
const bobKey = bob.generateKeys();

// 交换并生成秘密...
const aliceSecret = alice.computeSecret(bobKey);
const bobSecret = bob.computeSecret(aliceKey);

assert.strictEqual(aliceSecret.toString('hex'), bobSecret.toString('hex'));
// 正常
const assert = require('node:assert');

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

// 生成 Alice 的密钥...
const alice = createECDH('secp521r1');
const aliceKey = alice.generateKeys();

// 生成 Bob 的密钥...
const bob = createECDH('secp521r1');
const bobKey = bob.generateKeys();

// 交换并生成秘密...
const aliceSecret = alice.computeSecret(bobKey);
const bobSecret = bob.computeSecret(aliceKey);

assert.strictEqual(aliceSecret.toString('hex'), bobSecret.toString('hex'));
// 正常
Attributes
curve:string
inputEncoding:string
key 字符串的 编码 。
outputEncoding:string
返回值的 编码 。
format:string
默认: 'uncompressed'

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

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

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

如果未提供 inputEncoding,key 应为 Buffer、TypedArray 或 DataView。

示例(解压缩密钥):

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');

// 转换后的密钥和未压缩的公钥应该相同
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');

// 转换后的密钥和未压缩的公钥应该相同
console.log(uncompressedKey === ecdh.getPublicKey('hex'));
ecdh.computeSecret(otherPublicKey, inputEncoding?, outputEncoding?): void
Attributes
inputEncoding:string
otherPublicKey 字符串的 编码 。
outputEncoding:string
返回值的 编码 。

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

如果给出了 outputEncoding,则返回字符串;否则返回 Buffer。

当 otherPublicKey 位于椭圆曲线之外时,ecdh.computeSecret 将抛出 ERR_CRYPTO_ECDH_INVALID_PUBLIC_KEY 错误。由于 otherPublicKey 通常是通过不安全网络从远程用户提供的,因此请务必相应地处理此异常。

M

ecdh.generateKeys

History
ecdh.generateKeys(encoding?, format?): void
Attributes
encoding:string
返回值的 编码 。
format:string
默认: 'uncompressed'

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

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

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

M

ecdh.getPrivateKey

History
ecdh.getPrivateKey(encoding?): void
Attributes
encoding:string
返回值的 编码 。

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

M

ecdh.getPublicKey

History
ecdh.getPublicKey(encoding?, format?): void
Attributes
encoding:string
返回值的 编码 。
format:string
默认: 'uncompressed'

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

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

M

ecdh.setPrivateKey

History
ecdh.setPrivateKey(privateKey, encoding?): void
Attributes
encoding:string
privateKey 字符串的 编码 。

设置 EC Diffie-Hellman 私钥。 如果提供了 encoding,privateKey 应为字符串;否则 privateKey 应为 Buffer、TypedArray 或 DataView。

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

M

ecdh.setPublicKey

History
ecdh.setPublicKey(publicKey, encoding?): void

稳定性:0 - 已弃用

Attributes
encoding:string
publicKey 字符串的 编码 。

设置 EC Diffie-Hellman 公钥。 如果提供了 encoding,publicKey 应为字符串;否则应为 Buffer、TypedArray 或 DataView。

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

示例(获取共享秘密):

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

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

// 这是一种指定 Alice 之前的私钥之一的快捷方式。
// 在实际应用程序中使用如此可预测的私钥是不明智的。
alice.setPrivateKey(
  createHash('sha256').update('alice', 'utf8').digest(),
);

// Bob 使用新生成的加密强度高的
// 伪随机密钥对
bob.generateKeys();

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

// aliceSecret 和 bobSecret 应该是相同的共享秘密值
console.log(aliceSecret === bobSecret);
const {
  createECDH,
  createHash,
} = require('node:crypto');

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

// 这是一种指定 Alice 之前的私钥之一的快捷方式。
// 在实际应用程序中使用如此可预测的私钥是不明智的。
alice.setPrivateKey(
  createHash('sha256').update('alice', 'utf8').digest(),
);

// Bob 使用新生成的加密强度高的
// 伪随机密钥对
bob.generateKeys();

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

// aliceSecret 和 bobSecret 应该是相同的共享秘密值
console.log(aliceSecret === bobSecret);

类:Hash

History

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

  • 作为一个既可读又可写的 流,数据被写入以在可读侧产生计算出的哈希摘要,或
  • 使用 hash.update() 和 hash.digest() 方法来产生计算出的哈希。

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

示例:将 Hash 对象用作流:

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

const hash = createHash('sha256');

hash.on('readable', () => {
  // 哈希流只会产生一个元素。
  const data = hash.read();
  if (data) {
    console.log(data.toString('hex'));
    // 输出:
    //   6a2da20943931e9834fc12cfe5bb47bbd9ae43489a30726962b576f4e3993e50
  }
});

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

const hash = createHash('sha256');

hash.on('readable', () => {
  // 哈希流只会产生一个元素。
  const data = hash.read();
  if (data) {
    console.log(data.toString('hex'));
    // 输出:
    //   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'));
// 输出:
//   6a2da20943931e9834fc12cfe5bb47bbd9ae43489a30726962b576f4e3993e50
const {
  createHash,
} = require('node:crypto');

const hash = createHash('sha256');

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

hash.copy

History
hash.copy(options?): void
Attributes
options:Object
[ stream.transform 选项][]

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

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

如果在调用 hash.digest() 方法后尝试复制 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'));

// 等等。
// 计算滚动哈希。
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'));

// 等等。
M

hash.digest

History
hash.digest(encoding?): void
Attributes
encoding:string
返回值的 编码 。

计算传递给哈希的所有数据的摘要(使用 hash.update() 方法)。 如果提供了 encoding,则返回字符串;否则返回 Buffer。

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

hash.update(data, inputEncoding?): void
Attributes
inputEncoding:string
data 字符串的 编码 。

使用给定的 data 更新哈希内容,其编码由 inputEncoding 给出。 如果未提供 encoding,且 data 是字符串,则强制使用 'utf8' 编码。如果 data 是 Buffer、TypedArray 或 DataView,则忽略 inputEncoding。

随着数据流式传输,可以多次调用此方法并传入新数据。

类:Hmac

History

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

  • 作为一个既可读又可写的 流,数据被写入以在可读侧产生计算出的 HMAC 摘要,或
  • 使用 hmac.update() 和 hmac.digest() 方法来产生计算出的 HMAC 摘要。

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

示例:将 Hmac 对象用作流:

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

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

hmac.on('readable', () => {
  // 哈希流只会产生一个元素。
  const data = hmac.read();
  if (data) {
    console.log(data.toString('hex'));
    // 输出:
    //   7fd04df92f636fd450bc841c9418e5825c17f33ad9c87c518115a45971f7f77e
  }
});

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

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

hmac.on('readable', () => {
  // 哈希流只会产生一个元素。
  const data = hmac.read();
  if (data) {
    console.log(data.toString('hex'));
    // 输出:
    //   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'));
// 输出:
//   7fd04df92f636fd450bc841c9418e5825c17f33ad9c87c518115a45971f7f77e
const {
  createHmac,
} = require('node:crypto');

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

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

hmac.digest

History
hmac.digest(encoding?): void
Attributes
encoding:string
返回值的 编码 。

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

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

hmac.update(data, inputEncoding?): void
Attributes
inputEncoding:string
data 字符串的 编码 。

使用给定的 data 更新 Hmac 内容,其编码由 inputEncoding 给出。 如果未提供 encoding,且 data 是字符串,则强制使用 'utf8' 编码。如果 data 是 Buffer、TypedArray 或 DataView,则忽略 inputEncoding。

随着数据流式传输,可以多次调用此方法并传入新数据。

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

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

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

Attributes

返回可提取的 CryptoKey 的底层密钥材料的 KeyObject 表示。 返回的 KeyObject 不会保留 Web Crypto API 对原始 CryptoKey 施加的任何限制,例如允许的密钥用途、算法或哈希算法绑定。

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);
// 输出:32(对称密钥大小,单位字节)
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);
  // 输出:32(对称密钥大小,单位字节)
})();
  • 类型:Object
    Attributes
    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 序列,则将设置 hashAlgorithm、mgf1HashAlgorithm 和 saltLength 属性。

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

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

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

生成相等性检查

History
Attributes
otherKeyObject:KeyObject
用于与 keyObject 比较的 KeyObject 。

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

Attributes
options:Object

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

Attributes
format:string
必须是 'buffer' (默认)或 'jwk' 。

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

Attributes
format:string
必须是 'pem' 、 'der' 、 'jwk' 或 'raw-public' 。请参阅 非对称密钥类型 了解格式支持。
type:string
当 format 为 'pem' 或 'der' 时,必须是 'pkcs1' (仅 RSA)或 'spki' 。对于使用 'raw-public' 格式的 EC 密钥,可以是 'uncompressed' (默认)或 'compressed' 。当 format 为 'jwk' 时忽略。

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

Attributes
format:string
必须是 'pem' 、 'der' 、 'jwk' 、 'raw-private' 或 'raw-seed' 。请参阅 非对称密钥类型 了解格式支持。
type:string
当 format 为 'pem' 或 'der' 时,必须是 'pkcs1' (仅 RSA)、 'pkcs8' 或 'sec1' (仅 EC)。当 format 为 'jwk' 、 'raw-private' 或 'raw-seed' 时忽略。
cipher:string
如果指定,私钥将使用给定的 cipher 和 passphrase 通过 PKCS#5 v2.0 基于密码的加密进行加密。当 format 为 'jwk' 、 'raw-private' 或 'raw-seed' 时忽略。
passphrase:string | Buffer
用于加密的密码短语。指定 cipher 时必需。

结果类型取决于所选的编码格式,当为 PEM 时结果是字符串,当为 DER 时将是包含 DER 编码数据的 buffer,当为 JWK 时将是对象。原始格式返回包含原始密钥材料的 Buffer。

可以通过指定 cipher 和 passphrase 来加密私钥。 PKCS#8 type 支持任何密钥算法的 PEM 和 DER format 加密。 PKCS#1 和 SEC1 仅在使用 PEM format 时可以加密。 为了最大兼容性,对加密私钥使用 PKCS#8。 由于 PKCS#8 定义了自己的加密机制,加密 PKCS#8 密钥时不支持 PEM 级加密。 请参阅 RFC 5208 了解 PKCS#8 加密,RFC 1421 了解 PKCS#1 和 SEC1 加密。

密钥大小

History

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

导入密钥

History
Attributes
Attributes
extractable:boolean
keyUsages:string[]
请参阅 密钥用法 。

将 KeyObject 实例转换为 CryptoKey。

密钥用途

History

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

C

Mac

History

Mac 类使用 OpenSSL 提供程序提供的 MAC 实现来计算消息认证码。它可以通过以下两种方式之一使用:

  • 作为可读写的 流,将数据写入其中,并在可写端结束时从可读端生成一个认证标签;或者
  • 调用一次或多次 mac.update(),然后调用 mac.final()。

Mac 实例使用 crypto.createMac() 创建。Mac 类不会由 node:crypto 模块直接导出。

如果未先写入数据就调用 mac.end(),则会计算空消息的认证标签。如果所选 MAC 生成零字节标签(例如,提供程序接受 outputLength: 0),则可读端会在不发出数据块的情况下结束,因为 Node.js 流不会发出零长度数据块。改用 mac.final() 时,它会返回零长度的 Buffer 或空编码字符串。

mac.end() 和 mac.final() 是互斥的终结操作,不得在同一个对象上同时调用。尝试执行任一终结操作,或底层 MAC 更新失败后,都不能再次使用 Mac 对象。

示例:使用 mac.update() 和 mac.final():

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

const key = randomBytes(16);
const mac = createMac('CMAC', key, {
  cipher: 'AES-128-CBC',
});

mac.update('some data to authenticate');
console.log(mac.final('hex'));
M

mac.final

History
mac.final(outputEncoding?): void
Attributes
outputEncoding:string
返回值的 编码 。

完成 MAC 计算并返回认证标签。如果省略 outputEncoding 或其值为 'buffer',则返回 Buffer。否则返回字符串。

要验证认证标签,请使用 crypto.timingSafeEqual() 比较长度相同的 Buffer 值。

尝试终结后(包括终结失败时),都不能再次使用 Mac 对象。之后调用 mac.update() 或 mac.final() 会抛出 ERR_CRYPTO_MAC_FINALIZED。

M

mac.update

History
mac.update(data, inputEncoding?): void
Attributes
inputEncoding:string
data 字符串的 编码 。

使用 data 更新 MAC,并返回 Mac 对象,以便链式调用。当 data 是字符串时,inputEncoding 默认为 'utf8'。当 data 是 Buffer、TypedArray 或 DataView 时,会忽略 inputEncoding。

在终结之前可以多次调用此方法。如果底层 MAC 更新失败,则不能再次使用 Mac 对象。在先前的底层 MAC 更新失败后或终结后调用此方法会抛出 ERR_CRYPTO_MAC_FINALIZED。

C

Sign

History

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

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

示例:将 Sign 和 Verify 对象作为流使用:

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'));
// 输出:true
const {
  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'));
// 输出: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));
// 输出:true
const {
  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));
// 输出:true
sign(privateKey, outputEncoding?): void
Attributes
dsaEncoding:string
padding:integer
saltLength:integer
outputEncoding:string
返回值的 编码 。

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

如果 privateKey 不是 KeyObject,此函数的行为就像将 privateKey 传入 crypto.createPrivateKey() 一样。当 privateKey 是字符串、ArrayBuffer、Buffer、TypedArray 或 DataView 时,它必须包含 PEM 编码的密钥材料。如果它是一个对象,则可以传入以下附加属性:

Attributes
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:
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?): void
Attributes
inputEncoding:string
data 字符串的 编码 。

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

随着数据流式传输,可以多次调用此方法并传入新数据。

类:Verify

History

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

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

示例请参阅 Sign。

verify.update(data, inputEncoding?): void
Attributes
inputEncoding:string
data 字符串的 编码 。

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

随着数据流式传输,可以多次调用此方法并传入新数据。

verify(key, signature, signatureEncoding?): void
Attributes
dsaEncoding:string
padding:integer
saltLength:integer
signatureEncoding:string
signature 字符串的 编码 。

使用给定的 key 和 signature 来验证所提供的数据。

如果 key 不是 KeyObject,此函数的行为就像将 key 传递给了 crypto.createPublicKey() 一样。当 key 是字符串、ArrayBuffer、Buffer、TypedArray 或 DataView 时,它必须包含 PEM 编码的密钥材料。如果它是一个对象,则可以传入以下附加属性:

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

signature 参数是之前在 signatureEncoding 中计算出的数据签名。 如果指定了 signatureEncoding,则 signature 预期为字符串;否则 signature 预期为 Buffer、TypedArray 或 DataView。

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

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

类:X509Certificate

History

封装一个 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);

颁发者证书

History
Attributes
一个 PEM 或 DER 编码的 X509 证书。

主题备用名称

History
  • 类型:boolean 如果这是一个证书颁发机构 (CA) 证书,则为 true。
Attributes
email:string
options:Object
subject:string
'default' 、 'always' 或 'never' 。 默认值: 'default' 。

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

如果 'subject' 选项未定义或设置为 'default',则根据 OpenSSL 的默认行为考虑证书主题。

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

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

Attributes
name:string
options:Object
subject:string
'default' 、 'always' 或 'never' 。 默认值: 'default' 。
wildcards:boolean
默认值: true 。
partialWildcards:boolean
默认值: true 。
multiLabelWildcards:boolean
默认值: false 。
singleLabelSubdomains:boolean
默认值: false 。

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

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

如果 'subject' 选项未定义或设置为 'default',则根据 OpenSSL 的默认行为考虑证书主题。

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

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

Attributes

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

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

颁发者检查

History
Attributes
otherCert:X509Certificate

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

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

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

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

x509.checkPrivateKey

History
x509.checkPrivateKey(privateKey): void
Attributes
privateKey:KeyObject
一个私钥。

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

P

x509.fingerprint

History

此证书的 SHA-1 指纹。

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

P

x509.fingerprint256

History

此证书的 SHA-256 指纹。

P

x509.fingerprint512

History

此证书的 SHA-512 指纹。

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

证书授权信息访问扩展的文本表示。

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

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

P

x509.issuer

History

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

P

x509.issuerCertificate

History

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

P

x509.keyUsage

History

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

P

x509.publicKey

History

此证书的公钥 KeyObject。

P

x509.raw

History

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

P

x509.serialNumber

History

此证书的序列号。

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

P

x509.subject

History

此证书的完整主题。

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

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

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

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

M

x509.toJSON

History
x509.toJSON(): void

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

M

x509.toLegacyObject

History
x509.toLegacyObject(): void

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

M

x509.toString

History
x509.toString(): void

返回 PEM 编码的证书。

P

x509.validFrom

History

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

P

x509.validFromDate

History

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

P

x509.validTo

History

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

P

x509.validToDate

History

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

P

x509.signatureAlgorithm

History

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

P

x509.signatureAlgorithmOid

History

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

M

x509.verify

History
x509.verify(publicKey): void
Attributes
publicKey:KeyObject
一个公钥。

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

argon2

History
  • 类型 string Argon2 的变体,取值为 argon2d、argon2i 或 argon2id。
  • 选项 Object
    • 密码短语 string | ArrayBuffer | Buffer | TypedArray | DataView 必需,这是 Argon2 密码哈希应用中的密码。
    • 盐值 string | ArrayBuffer | Buffer | TypedArray | DataView 必需,长度必须至少为 8 字节。这是 Argon2 密码哈希应用中的盐值。
    • 并行度 number 必需,并行度决定可运行多少条计算链(lane)。必须至少为 1,且至多为 4。
    • 密钥长度 number 必需,要生成的密钥长度。必须至少为 4,且至多为 4294967295。
    • 内存 number 必需,以 1KiB 块为单位的内存成本。必须至少为 8192,且至多为 4294967295。实际块数会向下取整到最接近的 4 的倍数。
    • 迭代次数 number 必需,遍数(迭代次数)。必须至少为 1,且至多为 4294967295。
    • secret string | ArrayBuffer | Buffer | TypedArray | DataView | undefined 可选,随机附加输入,类似于盐值,但不应与派生密钥一起存储。在密码哈希应用中这称为 pepper。如果使用,其长度不得超过 4294967295 字节。
    • associatedData string | ArrayBuffer | Buffer | TypedArray | DataView | undefined 可选,要添加到哈希中的附加数据,功能上等同于盐值或 secret,但用于非随机数据。如果使用,其长度不得超过 4294967295 字节。
  • 回调函数 Function

提供异步 Argon2 实现。Argon2 是一种基于密码的密钥派生函数,旨在在计算和内存方面都很昂贵,以使暴力破解攻击无利可图。

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

当为 passphrase、salt、secret 或 associatedData 传递字符串时,请考虑 使用字符串作为加密 API 输入时的注意事项。

callback 函数带有两个参数:error 和 result。如果密钥派生失败,error 是一个异常对象,否则 result 为 Buffer。error 作为 [err][] 传递给回调。

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

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'
});
M

crypto.argon2Sync

History
crypto.argon2Sync(algorithm, parameters): void
Attributes
algorithm:string
Argon2 的变体,取值可以是 "argon2d" 、 "argon2i" 或 "argon2id" 。
parameters:Object
必需,这是 Argon2 密码哈希应用中的密码。
必需,长度必须至少为 8 字节。这是 Argon2 密码哈希应用中的盐值。
parallelism:number
必需,并行度决定可运行多少条计算链(lane)。必须至少为 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 。
可选,随机附加输入,类似于盐值,但 不应 与派生密钥一起存储。在密码哈希应用中这称为 pepper。如果使用,其长度不得超过 2**32-1 字节。
可选,要添加到哈希中的附加数据,功能上等同于盐值或 secret,但用于非随机数据。如果使用,其长度不得超过 2**32-1 字节。

提供同步 Argon2 实现。Argon2 是一种基于密码的密钥派生函数,旨在在计算和内存方面都很昂贵,以使暴力破解攻击无利可图。

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

当为 message、nonce、secret 或 associatedData 传递字符串时,请考虑 使用字符串作为加密 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): void
Attributes
一个可能的素数,编码为任意长度的大端字节序列。
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 的素性。

M

crypto.checkPrimeSync

History
crypto.checkPrimeSync(candidate, options?): void
Attributes
一个可能的素数,编码为任意长度的大端字节序列。
options:Object
checks:number
要执行的 Miller-Rabin 概率素性迭代次数。当值为 0 (零)时,使用的检查次数产生的随机输入假阳性率最多为 2 -64 。选择检查次数时必须小心。有关更多详细信息,请参阅 OpenSSL 文档中的 BN_is_prime_ex 函数 nchecks 选项。 默认: 0

检查 candidate 的素性。

P

crypto.constants

History

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

M

crypto.createCipheriv

crypto.createCipheriv(algorithm, key, iv, options?): void
Attributes
algorithm:string
options:Object
[ stream.transform 选项][],包含以下附加属性:
authTagLength:number
认证标签的长度,单位为字节。其要求和默认值取决于经过认证的密码,如下所述。
ctsMode:string
[CBC-CTS 模式][]变体。可选值为 'CS1' 、 'CS2' 或 'CS3' 。这些值区分大小写。 默认值: 'CS1' 。
encoding:string
当 key 为字符串时使用的 编码 。此选项不会影响字符串 iv ,后者始终按 UTF-8 解释。 默认值: 'utf8' 。
xtsStandard:string
sm4-xts 使用的标准。可选值为 'GB' 或 'IEEE' 。这些值区分大小写。 默认值: 'GB' 。

创建并返回一个 Cipheriv 对象,带有给定的 algorithm、key 和初始化向量 (iv)。

options 参数控制特定于密码的设置和流行为。除非使用 CCM 或 OCB 模式下的密码(例如 'aes-128-ccm'),否则该参数是可选的。在这种情况下,必须设置 authTagLength 选项,以指定认证标签的长度(单位为字节),参见 [CCM 模式][]。在 GCM 模式下,authTagLength 选项不是必需的,但可用于设置 getAuthTag() 返回的认证标签长度,默认值为 16 字节。 对于 SIV、GCM-SIV 和 chacha20-poly1305,authTagLength 选项默认为 16 字节。SIV 和 GCM-SIV 仅支持 16 字节的认证标签。

ctsMode 和 xtsStandard 选项用于配置 OpenSSL providers 暴露的参数。只有 OpenSSL 3.0 或更高版本,以及支持相应参数的 provider 才能使用这些选项。ctsMode 仅适用于 CBC-CTS 密码,而 xtsStandard 仅适用于 sm4-xts。如果为不支持该选项的可用密码实现提供其中任一选项,则会抛出 ERR_CRYPTO_UNSUPPORTED_OPERATION 错误。详情请参阅 [CBC-CTS 模式][]和 [XTS 模式][]。

可用算法取决于 OpenSSL。crypto.getCiphers() 列出 Node.js 暴露的算法。在较新的 OpenSSL 版本中,openssl list -cipher-algorithms 会显示 OpenSSL 可用的算法,其中可能包含 Node.js 未暴露的算法。

key 是 algorithm 使用的原始密钥,iv 是初始化向量。二者都可以是字符串、ArrayBuffer、Buffer、TypedArray 或 DataView。字符串形式的 key 使用 options.encoding 解码,该选项默认为 'utf8';字符串形式的 iv 始终按 UTF-8 解码。key 也可以是类型为 secret 的 KeyObject。如果密码不需要初始化向量,iv 可以是 null。

当为 key 或 iv 传递字符串时,请考虑 使用字符串作为加密 API 输入时的注意事项。

初始化向量的要求取决于算法。对于某些算法,IV 必须不可预测且唯一;对于其他算法,仅需唯一即可、必须使用固定值,或不使用 IV。请遵循所选算法的要求。IV 通常不必保密,可以与密文一起传输。

M

crypto.createDecipheriv

crypto.createDecipheriv(algorithm, key, iv, options?): void
Attributes
algorithm:string
options:Object
[ stream.transform 选项][],包含以下附加属性:
authTagLength:number
认证标签的长度,单位为字节。其要求和默认值取决于经过认证的密码,如下所述。
ctsMode:string
[CBC-CTS 模式][]变体。可选值为 'CS1' 、 'CS2' 或 'CS3' 。这些值区分大小写。 默认值: 'CS1' 。
encoding:string
当 key 为字符串时使用的 编码 。此选项不会影响字符串 iv ,后者始终按 UTF-8 解释。 默认值: 'utf8' 。
xtsStandard:string
sm4-xts 使用的标准。可选值为 'GB' 或 'IEEE' 。这些值区分大小写。 默认值: 'GB' 。

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

options 参数控制特定于密码的设置和流行为。除非使用 CCM 或 OCB 模式下的密码(例如 'aes-128-ccm'),否则该参数是可选的。在这种情况下,必须设置 authTagLength 选项,以指定认证标签的长度(单位为字节),参见 [CCM 模式][]。对于 GCM 和 chacha20-poly1305,authTagLength 选项默认为 16 字节;如果使用不同的长度,则必须设置该选项。对于 SIV 和 GCM-SIV,authTagLength 选项默认为 16 字节,并且仅支持 16 字节的认证标签。

ctsMode 和 xtsStandard 选项用于配置 OpenSSL providers 暴露的参数。只有 OpenSSL 3.0 或更高版本,以及支持相应参数的 provider 才能使用这些选项。ctsMode 仅适用于 CBC-CTS 密码,而 xtsStandard 仅适用于 sm4-xts。如果为不支持该选项的可用密码实现提供其中任一选项,则会抛出 ERR_CRYPTO_UNSUPPORTED_OPERATION 错误。详情请参阅 [CBC-CTS 模式][]和 [XTS 模式][]。

可用算法取决于 OpenSSL。crypto.getCiphers() 列出 Node.js 暴露的算法。在较新的 OpenSSL 版本中,openssl list -cipher-algorithms 会显示 OpenSSL 可用的算法,其中可能包含 Node.js 未暴露的算法。

key 是 algorithm 使用的原始密钥,iv 是初始化向量。二者都可以是字符串、ArrayBuffer、Buffer、TypedArray 或 DataView。字符串形式的 key 使用 options.encoding 解码,该选项默认为 'utf8';字符串形式的 iv 始终按 UTF-8 解码。key 也可以是类型为 secret 的 KeyObject。如果密码不需要初始化向量,iv 可以是 null。

当为 key 或 iv 传递字符串时,请考虑 使用字符串作为加密 API 输入时的注意事项。

初始化向量的要求取决于算法。对于某些算法,IV 必须不可预测且唯一;对于其他算法,仅需唯一即可、必须使用固定值,或不使用 IV。请遵循所选算法的要求。IV 通常不必保密,可以与密文一起传输。

crypto.createDiffieHellman(prime, primeEncoding?, generator?, generatorEncoding?): void
Attributes
primeEncoding:string
prime 字符串的 编码 。
默认值: 2
generatorEncoding:string
generator 字符串的 编码 。

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

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

如果指定了 primeEncoding,则 prime 预期为字符串;否则预期为 Buffer、TypedArray 或 DataView。

如果指定了 generatorEncoding,则 generator 预期为字符串;否则预期为数字、Buffer、TypedArray 或 DataView。

M

crypto.createDiffieHellman

History
crypto.createDiffieHellman(primeLength, generator?): void
Attributes
primeLength:number
generator:number
默认: 2

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

M

crypto.createDiffieHellmanGroup

History
crypto.createDiffieHellmanGroup(name): void
Attributes
name:string

crypto.getDiffieHellman() 的别名。

M

crypto.createECDH

History
crypto.createECDH(curveName): void
Attributes
curveName:string

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

crypto.createHash(algorithm, options?): void
Attributes
algorithm:string
options:Object
对于 cSHAKE 哈希函数,指定自定义字节字符串。**默认值:**空字节字符串。
对于 cSHAKE 哈希函数,指定 NIST 函数名称字节字符串。**默认值:**空字节字符串。
outputLength:number
对于 XOF 哈希函数,指定所需的输出长度(以字节为单位)。

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

functionName 和 customization 选项仅适用于 cSHAKE-128 和 cSHAKE-256。只有在使用 OpenSSL 4.0 或更高版本构建 Node.js,且所选提供程序支持相应摘要参数时,才支持这些选项。字符串会编码为 UTF-8,字符串和值都不得包含 NUL 字节。这两个选项的默认值均为空字节字符串。对于 OpenSSL 内置提供程序,functionName 区分大小写,且必须是 ''、'TupleHash'、'ParallelHash' 或 'KMAC'。其他提供程序可能会施加不同的限制。当这两个选项均为空时,对于相同的输出长度,cSHAKE 会产生与对应 SHAKE 函数相同的输出。cshake-128 和 cshake-256 的默认输出长度分别为 32 字节和 64 字节。

当数据量较小(< 5MB)且可随时获取时,通常 crypto.hash() 更快。

可用算法取决于平台上的 OpenSSL 版本和配置。例如 'sha256' 和 'sha512'。使用 crypto.getHashes() 获取 Node.js 进程可用的哈希算法列表。

示例:生成文件的 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', () => {
  // 哈希流只会产生一个元素。
  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', () => {
  // 哈希流只会产生一个元素。
  const data = input.read();
  if (data)
    hash.update(data);
  else {
    console.log(`${hash.digest('hex')} ${filename}`);
  }
});
crypto.createHmac(algorithm, key, options?): void
Attributes
algorithm:string
options:Object
encoding:string
当 key 是字符串时使用的字符串编码。

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

可用算法取决于平台上的 OpenSSL 版本和配置。例如 'sha256' 和 'sha512'。crypto.getHashes() 列出了哈希 API 可用的算法,但 HMAC 另有一些限制,因此并非列出的所有算法都适用。

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', () => {
  // 哈希流只会产生一个元素。
  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', () => {
  // 哈希流只会产生一个元素。
  const data = input.read();
  if (data)
    hmac.update(data);
  else {
    console.log(`${hmac.digest('hex')} ${filename}`);
  }
});
M

crypto.createMac

History
crypto.createMac(algorithm, key, options?): void

稳定性:1.2 - 候选发布版

Attributes
algorithm:string
MAC 算法的名称。
options:Object
digest:string
MAC(如 HMAC)使用的摘要算法。
cipher:string
MAC(如 CMAC 或 GMAC)使用的密码算法。
MAC(如 GMAC)的初始化向量。
支持自定义的 MAC(如 KMAC)使用的自定义字节字符串。
支持盐值的 MAC(如 BLAKE2 MAC)使用的盐值字节字符串。
outputLength:number
所请求的提供程序输出大小(以字节为单位)。必须是无符号 32 位整数。还需遵循提供程序特定的限制。

algorithm 必须是非空的提供程序 MAC 名称。上面列出的 MAC 特定属性是标准 stream.transform options 的扩展,只有在所选提供程序实现声明支持对应类型的参数时,才会传递这些属性。如果提供了所选实现不支持的 MAC 特定选项,则会导致错误。

下表总结了 OpenSSL 内置提供程序中的 MAC 实现所接受的 MAC 特定选项。每种 MAC 都必须提供 key 参数。该表仅列出 MAC 特定选项;每个系列仍可使用标准 stream.transform options。

MAC 系列必需选项可选选项备注
HMACdigest无
CMAC使用 CBC 模式的 cipher无
GMAC使用 GCM 模式的 cipher、非空 iv无对使用给定密钥进行身份验证的每条消息,都必须使用唯一的 IV。
KMAC无customization、outputLength
BLAKE2 MAC无customization、salt、outputLength
Poly1305无无每个密钥只能用于一条消息。
SipHash无outputLength

outputLength 用于配置提供程序 MAC 的输出大小。它绝不会通过计算更长的标签再截断来实现。值 0 会传递给提供程序,且只有在该提供程序能够以零字节输出初始化并完成 MAC 时才会被接受。如果省略 outputLength,则使用提供程序的默认输出大小,该值必须非零。

key 必须包含字节,或者是类型为 secret 的 KeyObject。密钥长度和其他密钥要求由所选提供程序实现决定。

可用算法及其接受的参数取决于 OpenSSL 版本、已加载的提供程序以及当前启用的默认属性查询。使用 crypto.getMacs() 列出可获取的 MAC 名称。列出的名称仍可能要求提供选项或具有提供程序特定属性的密钥。

crypto.createPrivateKey(key): void
Attributes
密钥材料,可以是 PEM、DER、JWK 或原始格式,也可以是引用 OpenSSL STORE 加载器对象的 URL 。
format:string
必须是 'pem' 、 'der' 、 'jwk' 、 'raw-private' 或 'raw-seed' 。 默认值: 'pem' 。
type:string
必须是 'pkcs1' 、 'pkcs8' 或 'sec1' 。仅当 format 为 'der' 时才需要此选项,否则将忽略该选项。
passphrase:string | Buffer
用于解密的密码短语。当 key 为 URL 时,这是传递给 STORE 加载器的可选 PIN/密码短语。
properties:string
获取 URL 密钥的 STORE 加载器时使用的可选 OpenSSL 属性查询。
encoding:string
当 key 为字符串时使用的字符串编码。
asymmetricKeyType:string
当 format 为 'raw-private' 或 'raw-seed' 时必须提供,否则将忽略该选项。 必须是[受支持的密钥类型][asymmetric key types]。
namedCurve:string
要使用的曲线名称。当 asymmetricKeyType 为 'ec' 时必须提供,否则将忽略该选项。

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

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

稳定性:1.1 - 活跃开发中

如果 key 是 URL(或 key 属性为 URL 的对象),则会通过 OpenSSL STORE 加载器加载私钥。该 URL 会作为 URI 传递给 OpenSSL,例如 file: URI 或由提供程序支持的方案(如 pkcs11:)。启用[权限模型][Permission Model]时,需要使用 --allow-openssl-store。

警告:URI 方案不会固定 OpenSSL STORE 加载器,也不能证明返回的密钥来自何处。Node.js 会将 URI 转发给 OpenSSL,由 OpenSSL 根据其版本和配置选择加载器。例如,OpenSSL 可能会在尝试 pkcs11 加载器之前,先将不透明的 URI(例如 pkcs11:object=...,即方案后没有 // 的 URI)提供给其 file 加载器。如果完整 URI 是有效的本地路径且该文件存在,则可能会加载该文件。 Node.js 不会验证是哪个加载器提供了密钥。不要依赖特定于提供程序的 URI 方案来证明密钥来自该提供程序或硬件设备。

已配置的 OpenSSL STORE 加载器拥有广泛的权限,可能会访问文件、设备、令牌或网络。加载器执行的访问不受 fs.read、fs.write 或 net 权限范围的限制。

使用 URL 时,即使 format、type、asymmetricKeyType 和 namedCurve 这些选项原本会相互依赖(例如 format: 'der' 时的 type,或 asymmetricKeyType: 'ec' 时的 namedCurve),也会忽略它们。输入会作为 URI 传递给 STORE 加载器,而不会作为 PEM、DER、JWK 或原始密钥材料处理。passphrase 仍会作为传递给加载器的可选 PIN/密码短语使用;如果该 passphrase 是字符串,encoding 也会生效。

请使用 passphrase,而不要将凭据嵌入传递给 STORE 加载器的 URI 中。Node.js 会从自身的权限拒绝资源和诊断信息中隐藏 URI。加载开始后,OpenSSL 或提供程序报告的错误可能会包含 URI。

当 properties 与 URL 密钥一同指定时,它会作为用于选择 STORE 加载器的属性查询传递给 OpenSSL。它不会追加到 URL 中,并且不同于特定于提供程序的 URI 参数。

crypto.createPublicKey(key): void
Attributes
密钥材料,PEM、DER、JWK 或 raw 格式。
format:string
必须是 'pem' 、 'der' 、 'jwk' 或 'raw-public' 。 默认: 'pem' 。
type:string
必须是 'pkcs1' 或 'spki' 。仅当 format 为 'der' 时需要此选项,否则忽略。
encoding:string
当 key 是字符串时使用的字符串编码。
asymmetricKeyType:string
当 format 为 'raw-public' 时需要,否则忽略。 必须是 支持的密钥类型 。
namedCurve:string
要使用的曲线名称。当 asymmetricKeyType 为 'ec' 时需要,否则忽略。

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

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

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

由存储支持的私钥可以先使用 crypto.createPrivateKey() 加载,然后作为公钥使用;不能直接将 URL 传递给 crypto.createPublicKey()。

crypto.createSecretKey(key, encoding?): void
Attributes
encoding:string
当 key 是字符串时的字符串编码。

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

M

crypto.createSign

History
crypto.createSign(algorithm, options?): void
Attributes
algorithm:string
options:Object
[ stream.Writable 选项][]

创建并返回一个使用给定 algorithm 的 Sign 对象。使用 crypto.getHashes() 获取哈希 API 可用的名称。密钥类型和签名方案可能会对可用摘要施加其他限制。可选的 options 参数用于控制 stream.Writable 的行为。

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

crypto.createVerify(algorithm)

History

创建并返回一个使用给定 algorithm 的 Verify 对象。 使用 crypto.getHashes() 获取哈希 API 可用的名称。 密钥类型和签名方案可能会对可用摘要施加其他限制。可选的 options 参数用于控制 stream.Writable 的行为。

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

crypto.createHash(algorithm)

History
Attributes

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

支持的密钥类型及其 KEM 算法有:

  • RSA-OAEP2 RSA 密钥封装
  • P-2563 DHKEM(P-256, HKDF-SHA256)、DHKEM(P-384, HKDF-SHA256)、DHKEM(P-521, HKDF-SHA256)
  • X255193 DHKEM(X25519, HKDF-SHA256)
  • X4483 DHKEM(X448, HKDF-SHA512)
  • ML-KEM-5121 ML-KEM
  • ML-KEM-7681 ML-KEM
  • ML-KEM-10241 ML-KEM

如果 ciphertext 不是 [Buffer][],此函数的行为就像已将 encoding 传递给 [Buffer.from][]。

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

Attributes

基于 privateKey 和 publicKey 计算 Diffie-Hellman 共享秘密。 两个密钥必须表示相同的非对称密钥类型,并且必须支持 DH 或 ECDH 操作。

如果 privateKey 不是 [KeyObject][],此函数的行为就像 encoding 已传递给 [Buffer.from][]。

如果 publicKey 不是 [KeyObject][],此函数的行为就像 encoding 已传递给 [Buffer.from][]。

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

crypto.encapsulate(publicKey)

History

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

支持的密钥类型及其 KEM 算法有:

  • RSA-OAEP2 RSA 密钥封装
  • P-2563 DHKEM(P-256, HKDF-SHA256)、DHKEM(P-384, HKDF-SHA256)、DHKEM(P-521, HKDF-SHA256)
  • X255193 DHKEM(X25519, HKDF-SHA256)
  • X4483 DHKEM(X448, HKDF-SHA512)
  • ML-KEM-5121 ML-KEM
  • ML-KEM-7681 ML-KEM
  • ML-KEM-10241 ML-KEM

如果 publicKey 不是 [KeyObject][],此函数的行为就像将 encoding 传递给了 [Buffer.from][]。

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

crypto.fips

History

稳定性:0 - 已弃用

用于检查和控制 FIPS 模式. 已弃用的属性。请改用 crypto.getFips() 和 crypto.setFips()。

  • type string 生成的密钥的预期用途。当前接受的值为 'hmac' 和 'aes'。
  • options Object
    • length number 要生成的密钥的位长度。这必须是大于 0 的值。
      • 如果 type 是 'hmac',最小值为 8,最大长度为 231-1。如果值不是 8 的倍数,生成的密钥将被截断为长度。
      • 如果 type 是 'aes',长度必须是 128、192 或 256 之一。
  • callback Function

异步生成给定类型的新随机密钥。options 将决定对 type 执行哪些验证。

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): void
Attributes
type:string
要生成的非对称密钥类型。参见支持的 非对称密钥类型 。
options:Object
modulusLength:number
密钥大小(位)(RSA, DSA)。
publicExponent:number
公钥指数 (RSA)。 默认: 0x10001 。
hashAlgorithm:string
消息摘要名称 (RSA-PSS)。
mgf1HashAlgorithm:string
MGF1 使用的消息摘要名称 (RSA-PSS)。
saltLength:number
最小盐长度(字节)(RSA-PSS)。
divisorLength:number
q 的大小(位)(DSA)。
namedCurve:string
要使用的曲线名称 (EC)。
prime:Buffer
素数参数 (DH)。
primeLength:number
素数长度(位)(DH)。
generator:number
自定义生成器 (DH)。 默认: 2 。
groupName:string
Diffie-Hellman 组名称 (DH)。参见 crypto.getDiffieHellman() 。
paramEncoding:string
必须是 'named' 或 'explicit' (EC)。 默认: 'named' 。
publicKeyEncoding:Object
privateKeyEncoding:Object
callback:Function
err:Error
publicKey:string | Buffer | KeyObject
privateKey:string | Buffer | KeyObject

生成给定 type 的新非对称密钥对。参见支持的 非对称密钥类型。

对于 RSA-PSS 密钥,crypto.getHashes() 会列出哈希 API 可用的算法,但并非所有列出的摘要都能编码到 RSA-PSS 参数中,或受到活动 RSA 实现的支持。

如果指定了 publicKeyEncoding 或 privateKeyEncoding,此函数的行为就像对其结果调用了 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) => {
  // 处理错误并使用生成的密钥对。
});
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) => {
  // 处理错误并使用生成的密钥对。
});

完成后,callback 将被调用,err 设置为 undefined,publicKey / privateKey 代表生成的密钥对。

如果此方法作为其 util.promisify() 版本调用,它返回一个 Promise,对象包含 publicKey 和 privateKey 属性。

crypto.generateKeyPairSync(type, options): void
Attributes
type:string
要生成的非对称密钥类型。参见支持的 非对称密钥类型 。
options:Object
modulusLength:number
密钥大小(位)(RSA, DSA)。
publicExponent:number
公钥指数 (RSA)。 默认: 0x10001 。
hashAlgorithm:string
消息摘要名称 (RSA-PSS)。
mgf1HashAlgorithm:string
MGF1 使用的消息摘要名称 (RSA-PSS)。
saltLength:number
最小盐长度(字节)(RSA-PSS)。
divisorLength:number
q 的大小(位)(DSA)。
namedCurve:string
要使用的曲线名称 (EC)。
prime:Buffer
素数参数 (DH)。
primeLength:number
素数长度(位)(DH)。
generator:number
自定义生成器 (DH)。 默认: 2 。
groupName:string
Diffie-Hellman 组名称 (DH)。参见 crypto.getDiffieHellman() 。
paramEncoding:string
必须是 'named' 或 'explicit' (EC)。 默认: 'named' 。
publicKeyEncoding:Object
privateKeyEncoding:Object
publicKey:string | Buffer | KeyObject
privateKey:string | Buffer | KeyObject

生成给定 type 的新非对称密钥对。参见支持的 非对称密钥类型。

对于 RSA-PSS 密钥,crypto.getHashes() 会列出哈希 API 可用的算法,但并非所有列出的摘要都能编码到 RSA-PSS 参数中,或受到活动 RSA 实现的支持。

如果指定了 publicKeyEncoding 或 privateKeyEncoding,此函数的行为就像对其结果调用了 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 的数据的缓冲区。

M

crypto.generateKeySync

History
crypto.generateKeySync(type, options): void
Attributes
type:string
生成的密钥的预期用途。当前接受的值为 'hmac' 和 'aes' 。
options:Object
length:number
要生成的密钥的位长度。

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

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

const key = generateKeySync('hmac', { length: 512 });
console.log(key.export().toString('hex'));  // e89..........41e
const {
  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): void
Attributes
size:number
要生成的素数的大小(位)。
options:Object
safe:boolean
默认: false 。
bigint:boolean
当为 true 时,生成的素数作为 bigint 返回。
callback:Function

生成 size 位的伪随机素数。

如果 options.safe 为 true,素数将是安全素数——即,(prime - 1) / 2 也将是素数。

options.add 和 options.rem 参数可用于强制执行额外要求,例如,对于 Diffie-Hellman:

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

如果 options.add 和 options.rem 作为 ArrayBuffer、SharedArrayBuffer、TypedArray、Buffer 或 DataView 给出,则必须编码为大端序列。

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

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

M

crypto.generatePrimeSync

History
crypto.generatePrimeSync(size, options?): void
Attributes
size:number
要生成的素数的大小(位)。
options:Object
safe:boolean
默认: false 。
bigint:boolean
当为 true 时,生成的素数作为 bigint 返回。

生成 size 位的伪随机素数。

如果 options.safe 为 true,素数将是安全素数——即,(prime - 1) / 2 也将是素数。

options.add 和 options.rem 参数可用于强制执行额外要求,例如,对于 Diffie-Hellman:

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

如果 options.add 和 options.rem 作为 ArrayBuffer、SharedArrayBuffer、TypedArray、Buffer 或 DataView 给出,则必须编码为大端序列。

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

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

M

crypto.getCipherInfo

History
crypto.getCipherInfo(nameOrNid, options?): void
Attributes
nameOrNid:string | number
要查询的密码名称或 nid。
options:Object
keyLength:number
测试密钥长度。
ivLength:number
测试 IV 长度。
name:string
密码的名称
密码的 nid。如果密码没有 OpenSSL nid,此属性为 undefined 。
blockSize:number | undefined
密码的块大小(字节)。当 mode 为 'stream' 时,此属性为 undefined 。
ivLength:number | undefined
预期或默认的初始化向量长度(字节)。如果密码不使用初始化向量,此属性为 undefined 。
keyLength:number
预期或默认的密钥长度(字节)。
mode:string
密码模式。取值之一: 'cbc' 、 'ccm' 、 'cfb' 、 'ctr' 、 'ecb' 、 'gcm' 、 'gcm-siv' 、 'ocb' 、 'ofb' 、 'siv' 、 'stream' 、 'wrap' 、 'xts' 。

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

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

M

crypto.getCiphers

History
crypto.getCiphers(): void
  • 返回: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', ...]
M

crypto.getCurves

History
crypto.getCurves(): void
  • 返回值: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', ...]
M

crypto.createDiffieHellmanGroup

History
crypto.createDiffieHellmanGroup(name): void
Attributes
name:string

创建一个预定义的 Diffie-Hellman 密钥交换对象。支持的组列在 [crypto.getDiffieHellman][] 文档中。

返回的对象模仿由 [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 和 bobSecret 应该相同 */
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 和 bobSecret 应该相同 */
console.log(aliceSecret === bobSecret);
M

crypto.getFips

History
crypto.getFips(): void
  • 返回:number 如果启用了 FIPS 模式,则为 1,否则为 0。未来的 semver-major 版本可能会将此 API 的返回类型更改为 boolean。

使用 OpenSSL 3 时,此 API 报告默认属性查询是否包含 fips=yes。它不能证明 FIPS 提供程序已加载或经过验证。即使请求的加密实现无法获取,因为没有已加载的提供程序为 fips=yes 提供匹配项,它也可能返回 1。请参阅 FIPS 模式。

crypto.getHashes(): void
  • 返回:string[] 支持的哈希算法名称数组,例如 'RSA-SHA256'。哈希算法也称为“摘要”算法。

这是当前进程中可供 crypto.createHash() 和 crypto.hash() 使用的权威 Node.js 哈希算法列表。对于 OpenSSL 3 或更高版本,该列表取决于已加载的提供程序,以及首次生成列表时生效的默认属性查询。部分列出的算法可能需要特定于 API 的选项,例如 XOF 哈希函数的 outputLength。

列出的哈希算法不一定受将摘要与另一项加密操作结合使用的 API 支持,例如 HMAC、密钥派生或签名。这些操作可能会施加额外限制。

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', ...]
M

crypto.getMacs

History
crypto.getMacs(): void

稳定性:1.2 - 候选发布版

  • 返回:string[] 一个新数组,包含可获取的 MAC 实现的小写名称及其别名,并按名称排序。

返回已加载的 OpenSSL 提供程序所公开且符合当前默认属性查询的 MAC 名称。重复名称和数字 OID 别名会被省略。在不支持 OpenSSL EVP_MAC 的构建版本中,此函数返回空数组。

返回的名称描述的是 OpenSSL 可以获取的实现。它们并不保证 crypto.createMac() 无需额外选项即可初始化 MAC。提供程序可能要求额外参数或具有特定于算法属性的密钥,也可能公开此 API 不支持的参数。

使用 crypto.setFips() 成功更改 FIPS 模式后,后续调用会反映新的模式,新建的 Mac 对象也会使用该模式。现有的 Mac 对象会继续使用创建时选定的提供程序实现。

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

console.log(getMacs());
// ['blake2bmac', 'blake2smac', 'cmac', 'gmac', 'hmac', ...]
M

crypto.getRandomValues

History
crypto.getRandomValues(typedArray): void
Attributes

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

crypto.hash(algorithm, data, options?): void
Attributes
algorithm:string | undefined
当 data 是字符串时,它将在被哈希之前编码为 UTF-8。如果希望字符串输入使用不同的输入编码,用户可以使用 TextEncoder 或 Buffer.from() 将字符串编码为 TypedArray ,并将编码后的 TypedArray 传递到此 API 中。
options:Object | string
对于 cSHAKE 哈希函数,指定自定义字节串。 默认值: 空字节串。
对于 cSHAKE 哈希函数,指定 NIST 函数名称字节串。 默认值: 空字节串。
outputEncoding:string
用于对返回的摘要进行编码的[编码方式][encoding]。 默认值: 'hex' 。
outputLength:number
对于 'shake256' 等 XOF 哈希函数,指定所需的输出长度(字节)。对于没有默认输出长度的 XOF 哈希函数,此选项为必需。

用于创建数据一次性哈希摘要的实用工具。当哈希少量现成数据(<= 5MB)时,它可能比基于对象的 crypto.createHash() 更快。如果数据可能很大或是流式的,仍建议使用 crypto.createHash()。

可用算法取决于平台上的 OpenSSL 版本和配置。示例包括 'sha256' 和 'sha512'。使用 crypto.getHashes() 获取 Node.js 进程可用的哈希算法列表。

functionName 和 customization 选项仅适用于 cSHAKE-128 和 cSHAKE-256。只有在使用 OpenSSL 4.0 或更高版本构建 Node.js,且所选提供程序支持相应摘要参数时,才支持这些选项。字符串会编码为 UTF-8,字符串和字节值均不得包含 NUL 字节。这两个选项的默认值都是空字节串。对于 OpenSSL 内置提供程序,functionName 区分大小写,且必须为 ''、'TupleHash'、'ParallelHash' 或 'KMAC'。其他提供程序可能施加不同限制。当这两个选项都为空时,对于相同的输出长度,cSHAKE 会产生与相应 SHAKE 函数相同的输出。cshake-128 和 cshake-256 的默认输出长度分别为 32 和 64 字节。

如果 options 是字符串,则它指定 outputEncoding。

示例:

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

// 对字符串进行哈希处理并将结果作为十六进制编码字符串返回。
const string = 'Node.js';
// 10b3493287f831e81a438811a1ffba01f8cec4b7
console.log(crypto.hash('sha1', string));

// 将 base64 编码的字符串编码为 Buffer,对其进行哈希处理并将
// 结果作为 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';

// 对字符串进行哈希处理并将结果作为十六进制编码字符串返回。
const string = 'Node.js';
// 10b3493287f831e81a438811a1ffba01f8cec4b7
console.log(crypto.hash('sha1', string));

// 将 base64 编码的字符串编码为 Buffer,对其进行哈希处理,并将
// 结果作为 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): void
Attributes
digest:string
要使用的摘要算法。
输入密钥材料。必须提供,但可以是零长度。
盐值。必须提供,但可以是零长度。
附加信息值。必须提供,但可以是零长度,且不能超过 1024 字节。
keylen:number
要生成的密钥长度。必须大于 0。最大允许值是所选摘要函数产生的字节数的 255 倍(例如 sha512 生成 64 字节哈希,使最大 HKDF 输出为 16320 字节)。
callback:Function
err:Error
derivedKey:ArrayBuffer

HKDF 是 RFC 5869 中定义的一种简单密钥派生函数。给定的 ikm、salt 和 info 与 digest 一起使用,以派生长度为 keylen 字节的密钥。可用的摘要算法取决于 OpenSSL 的版本和配置。HKDF 在内部使用 HMAC。crypto.getHashes() 列出了哈希 API 可用的算法,但列出的算法并非都适用于 HMAC 或 HKDF。

提供的 callback 函数接收两个参数:err 和 derivedKey。如果在派生密钥时发生错误,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): void
Attributes
digest:string
要使用的摘要算法。
输入密钥材料。必须提供但可以是零长度。
盐值。必须提供但可以是零长度。
附加信息值。必须提供但可以是零长度,且不能超过 1024 字节。
keylen:number
要生成的密钥长度。必须大于 0。最大允许值是所选摘要函数产生的字节数的 255 倍(例如 sha512 生成 64 字节哈希,使最大 HKDF 输出为 16320 字节)。

提供 RFC 5869 中定义的同步 HKDF 密钥派生函数。给定的 ikm、salt 和 info 与 digest 一起使用,以派生长度为 keylen 字节的密钥。可用的摘要算法取决于 OpenSSL 的版本和配置。HKDF 在内部使用 HMAC。crypto.getHashes() 列出了哈希 API 可用的算法,但列出的算法并非都适用于 HMAC 或 HKDF。

成功生成的 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'
M

crypto.parsePKCS12

History
crypto.parsePKCS12(bundle, options?): void
Attributes
DER 编码的 PKCS#12( .p12 或 .pfx )捆绑包。
options:Object
用于保护捆绑包的密码短语。省略此选项等同于传入 '' 。
privateKey:KeyObject | null
捆绑包中的第一个私钥,如果不存在,则为 null 。
certificate:X509Certificate | null
与 privateKey 匹配的证书,如果不存在匹配的证书,则为 null 。
additionalCertificates:X509Certificate[]
捆绑包中的所有其他证书。如果没有私钥,则此项包含所有证书。可以为空。

解析 PKCS#12 捆绑包(通常以 .p12 或 .pfx 为扩展名存储),并返回其中的私钥和证书。

import { parsePKCS12 } from 'node:crypto';
import { readFileSync } from 'node:fs';

const { privateKey, certificate, additionalCertificates } = parsePKCS12(
  readFileSync('bundle.p12'),
  { passphrase: 'secret' },
);
crypto.pbkdf2(password, salt, iterations, keylen, digest, callback): void
Attributes
iterations:number
keylen:number
digest:string
callback:Function
err:Error
derivedKey:Buffer

提供异步基于密码的密钥派生函数 2(PBKDF2)实现。应用由 digest 指定的选定 HMAC 摘要算法,从 password、salt 和 iterations 派生请求字节长度(keylen)的密钥。

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

iterations 参数必须是一个设置得尽可能高的数字。迭代次数越高,派生密钥就越安全,但完成所需的时间就越长。

salt 应尽可能唯一。建议 salt 是随机的且至少 16 字节长。详见 NIST SP 800-132。

当为 password 或 salt 传递字符串时,请考虑 [将字符串用作加密 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'
});

可用的摘要算法取决于 OpenSSL 的版本和配置。PBKDF2 在内部使用 HMAC。crypto.getHashes() 列出了哈希 API 可用的算法,但列出的算法并非都适用于 HMAC 或 PBKDF2。

此 API 使用 libuv 的线程池,这可能会对某些应用程序产生令人惊讶的负面性能影响;有关更多信息,请参阅 UV_THREADPOOL_SIZE 文档。

crypto.pbkdf2Sync(password, salt, iterations, keylen, digest): void
Attributes

提供同步基于密码的密钥派生函数 2 (PBKDF2) 实现。应用由 digest 指定的选定 HMAC 摘要算法,从 password、salt 和 iterations 派生请求字节长度 (keylen) 的密钥。

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

iterations 参数必须是一个设置得尽可能高的数字。迭代次数越高,派生密钥就越安全,但完成所需的时间就越长。

salt 应尽可能唯一。建议 salt 是随机的且至少 16 字节长。详见 NIST SP 800-132。

当为 password 或 salt 传递字符串时,请考虑 [将字符串用作加密 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'

可用的摘要算法取决于 OpenSSL 的版本和配置。PBKDF2 在内部使用 HMAC。crypto.getHashes() 列出了哈希 API 可用的算法,但列出的算法并非都适用于 HMAC 或 PBKDF2。

crypto.privateDecrypt(privateKey, buffer): void
Attributes
oaepHash:string
用于 OAEP 填充和 MGF1 的哈希函数,除非设置了 mgf1Hash 。 默认值: 'sha1'
mgf1Hash:string
用于 OAEP 填充的 MGF1 掩码生成函数的哈希函数。如果未指定,则使用 oaepHash 的值。这允许 OAEP 摘要和 MGF1 摘要不同。
用于 OAEP 填充的标签。如果未指定,则不使用标签。
在 crypto.constants 中定义的可选填充值,可以是: crypto.constants.RSA_NO_PADDING 、 crypto.constants.RSA_PKCS1_PADDING 或 crypto.constants.RSA_PKCS1_OAEP_PADDING 。

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

crypto.getHashes() 列出了哈希 API 可用的算法,但当前的 RSA 实现可能会对用于 OAEP 或 MGF1 的摘要施加额外限制。

如果 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): void
Attributes
私钥材料、 KeyObject ,或引用 OpenSSL STORE 加载器对象的 URL 。
私钥的可选密码短语。
在 crypto.constants 中定义的可选填充值,可以是: crypto.constants.RSA_NO_PADDING 或 crypto.constants.RSA_PKCS1_PADDING 。
encoding:string
当 buffer 、 key 或 passphrase 为字符串时使用的字符串编码。

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

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

crypto.publicDecrypt(key, buffer): void
Attributes
私钥的可选 密码短语。
在 crypto.constants 中定义的可选填充值,可能是: crypto.constants.RSA_NO_PADDING 或 crypto.constants.RSA_PKCS1_PADDING 。
encoding:string
当 buffer 、 key 、 或 passphrase 为字符串时使用的字符串编码。

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

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

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

crypto.publicEncrypt(key, buffer): void
Attributes
PEM 编码的公钥或私钥,或者 KeyObject 。
oaepHash:string
用于 OAEP 填充和 MGF1 的哈希函数,除非 设置了 mgf1Hash 。 默认值: 'sha1'
mgf1Hash:string
用于 OAEP 填充的 MGF1 掩码生成 函数的哈希函数。如果未指定,则使用 oaepHash 的值。 这样可以使 OAEP 摘要与 MGF1 摘要不同。
用于 OAEP 填充的标签。如果未指定,则不使用标签。
私钥的可选 密码短语。
在 crypto.constants 中定义的可选填充值,可能是: crypto.constants.RSA_NO_PADDING 、 crypto.constants.RSA_PKCS1_PADDING ,或 crypto.constants.RSA_PKCS1_OAEP_PADDING 。
encoding:string
当 buffer 、 key 、 oaepLabel 或 passphrase 为字符串时使用的字符串编码。

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

crypto.getHashes() 列出了哈希 API 可用的算法,但当前活动的 RSA 实现可能会对用于 OAEP 或 MGF1 的摘要施加额外限制。

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

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

crypto.randomBytes(size, callback?): void
Attributes
size:number
要生成的字节数。 size 不得大于 2**31 - 1 。
callback:Function
err:Error
buf:Buffer

生成在密码学上安全的伪随机数据。size 参数是一个数字,表示要生成的字节数。

如果提供了 callback 函数,字节将异步生成,并使用两个参数 err 和 buf 调用 callback 函数。如果发生错误,err 将是一个 Error 对象;否则它将为 null。buf 参数是一个包含所生成字节的 Buffer。

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

randomBytes(256, (err, buf) => {
  if (err) throw err;
  console.log(`${buf.length} 字节的随机数据:${buf.toString('hex')}`);
});
// Asynchronous
const {
  randomBytes,
} = require('node:crypto');

randomBytes(256, (err, buf) => {
  if (err) throw err;
  console.log(`${buf.length} 字节的随机数据:${buf.toString('hex')}`);
});

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

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

const buf = randomBytes(256);
console.log(
  `${buf.length} 字节的随机数据:${buf.toString('hex')}`);
// Synchronous
const {
  randomBytes,
} = require('node:crypto');

const buf = randomBytes(256);
console.log(
  `${buf.length} 字节的随机数据:${buf.toString('hex')}`);

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

此 API 使用 libuv 的线程池,这可能会对某些应用程序产生令人惊讶的负面性能影响;有关更多信息,请参阅 UV_THREADPOOL_SIZE 文档。

crypto.randomBytes() 的异步版本通过单个线程池请求执行。为了尽量减少线程池任务长度的变化,在处理客户端请求时,请将大型 randomBytes 请求拆分。

crypto.randomFill(buffer, offset?, size?, callback): void
Attributes
必须提供。所提供的 buffer 大小不得大于 2**31 - 1 。
offset:number
起始位置;对于 TypedArray ,单位为元素,对于 ArrayBuffer 或 DataView ,单位为字节。 默认值: 0
size:number
要填充的数量,单位与 offset 相同。 **默认值:**对于 TypedArray ,为 buffer.length - offset ;对于 ArrayBuffer 或 DataView ,为 buffer.byteLength - offset 。 size 不得大于 2**31 - 1 。
callback:Function
function(err, buf) {} 。

此函数类似于 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'));
});

// 上面等同于以下:
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'));
});

// 上面等同于以下:
randomFill(buf, 5, 5, (err, buf) => {
  if (err) throw err;
  console.log(buf.toString('hex'));
});

任何 ArrayBuffer、TypedArray 或 DataView 实例都可以作为 buffer 传递。

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

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?): void
Attributes
必须提供。所提供的 buffer 大小不得大于 2**31 - 1 。
offset:number
起始位置;对于 TypedArray ,单位为元素;对于 ArrayBuffer 或 DataView ,单位为字节。 默认值: 0
size:number
要填充的数量,单位与 offset 相同。 **默认值:**对于 TypedArray ,为 buffer.length - offset ;对于 ArrayBuffer 或 DataView ,为 buffer.byteLength - offset 。 size 不得大于 2**31 - 1 。

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'));

// 上面等同于以下:
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'));

// 上面等同于以下:
randomFillSync(buf, 5, 5);
console.log(buf.toString('hex'));

任何 ArrayBuffer、TypedArray 或 DataView 实例都可以作为 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?): void
Attributes
随机范围的起始值(包含)。 默认: 0 。
随机范围的结束值(不包含)。
callback:Function
function(err, n) {} 。

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

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

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

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

randomInt(3, (err, n) => {
  if (err) throw err;
  console.log(`从 (0, 1, 2) 中选择的随机数:${n}`);
});
// 异步
const {
  randomInt,
} = require('node:crypto');

randomInt(3, (err, n) => {
  if (err) throw err;
  console.log(`从 (0, 1, 2) 中选择的随机数:${n}`);
});
// 同步
const {
  randomInt,
} = await import('node:crypto');

const n = randomInt(3);
console.log(`从 (0, 1, 2) 中选择的随机数:${n}`);
// 同步
const {
  randomInt,
} = require('node:crypto');

const n = randomInt(3);
console.log(`从 (0, 1, 2) 中选择的随机数:${n}`);
// 带 `min` 参数
const {
  randomInt,
} = await import('node:crypto');

const n = randomInt(1, 7);
console.log(`掷出的骰子点数:${n}`);
// 带 `min` 参数
const {
  randomInt,
} = require('node:crypto');

const n = randomInt(1, 7);
console.log(`掷出的骰子点数:${n}`);
M

crypto.randomUUID

History
crypto.randomUUID(): void
Attributes
options:Object
disableEntropyCache:boolean
默认情况下,为了提高性能,Node.js 会生成并缓存足够的随机数据,以生成最多 128 个随机 UUID。要生成不使用缓存的 UUID,请将 disableEntropyCache 设置为 true 。 默认: false 。

生成一个随机 RFC 4122 版本 4 UUID。UUID 使用加密伪随机数生成器生成。

M

crypto.randomUUID

History
crypto.randomUUID(): void
Attributes
options:Object
disableEntropyCache:boolean
默认情况下,为了提高性能,Node.js 会生成并缓存足够的随机数据,以生成最多 128 个随机 UUID。要生成不使用缓存的 UUID,请将 disableEntropyCache 设置为 true 。 默认: false 。

生成一个随机的 RFC 9562 版本 7 UUID。该 UUID 在最高 48 位包含一个以毫秒为精度的 Unix 时间戳,随后在其余字段中包含加密安全的随机比特,因此适合作为带有基于时间排序能力的数据库键。嵌入的时间戳依赖于非单调时钟,并不保证严格递增。

crypto.scrypt(password, salt, keylen, options, callback): void
Attributes
keylen:number
options:Object
cost:number
CPU/内存成本参数。必须是大于 1 的 2 的幂。 默认: 16384 。
blockSize:number
块大小参数。 默认: 8 。
parallelization:number
并行化参数。 默认: 1 。
cost 的别名。只能指定其中之一。
blockSize 的别名。只能指定其中之一。
parallelization 的别名。只能指定其中之一。
maxmem:number
内存上限。当(大约) 128 * N * r > maxmem 时为错误。 默认: 32 * 1024 * 1024 。
callback:Function
err:Error
derivedKey:Buffer

提供异步 scrypt 实现。Scrypt 是一种基于密码的密钥派生函数,旨在在计算和内存方面都很昂贵,从而使暴力破解攻击无利可图。

salt 应尽可能唯一。建议 salt 是随机的且至少 16 字节长。详见 NIST SP 800-132。

当为 password 或 salt 传递字符串时,请考虑 [将字符串用作加密 API 输入时的注意事项][]。

callback 函数使用两个参数调用:err 和 derivedKey。err 是密钥派生失败时的异常对象,否则 err 为 null。derivedKey 作为 [callback][] 传递给回调。

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

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

// 使用工厂默认值。
scrypt('password', 'salt', 64, (err, derivedKey) => {
  if (err) throw err;
  console.log(derivedKey.toString('hex'));  // '3745e48...08d59ae'
});
// 使用自定义 N 参数。必须是 2 的幂。
scrypt('password', 'salt', 64, { N: 1024 }, (err, derivedKey) => {
  if (err) throw err;
  console.log(derivedKey.toString('hex'));  // '3745e48...aa39b34'
});
const {
  scrypt,
} = require('node:crypto');

// 使用工厂默认值。
scrypt('password', 'salt', 64, (err, derivedKey) => {
  if (err) throw err;
  console.log(derivedKey.toString('hex'));  // '3745e48...08d59ae'
});
// 使用自定义 N 参数。必须是 2 的幂。
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?): void
  • password string | Buffer | TypedArray | DataView
  • salt string | Buffer | TypedArray | DataView
  • keylen number
  • options Object
    • cost number CPU/内存成本参数。必须是大于 1 的 2 的幂。默认: 16384。
    • blockSize number 块大小参数。默认: 8。
    • parallelization number 并行化参数。默认: 1。
    • N number cost 的别名。只能指定其中之一。
    • r number blockSize 的别名。只能指定其中之一。
    • p number parallelization 的别名。只能指定其中之一。
    • maxmem number 内存上限。当(大约)1024 ** 3 * 32 时为错误。默认: 33554432。
  • 返回:Buffer

提供同步 scrypt 实现。Scrypt 是一种基于密码的密钥派生函数,旨在在计算和内存方面都很昂贵,从而使暴力破解攻击无利可图。

salt 应尽可能唯一。建议 salt 是随机的且至少 16 字节长。详见 NIST SP 800-132。

当为 password 或 salt 传递字符串时,请考虑 [将字符串用作加密 API 输入时的注意事项][]。

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

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

const {
  scryptSync,
} = await import('node:crypto');
// 使用工厂默认值。

const key1 = scryptSync('password', 'salt', 64);
console.log(key1.toString('hex'));  // '3745e48...08d59ae'
// 使用自定义 N 参数。必须是 2 的幂。
const key2 = scryptSync('password', 'salt', 64, { N: 1024 });
console.log(key2.toString('hex'));  // '3745e48...aa39b34'
const {
  scryptSync,
} = require('node:crypto');
// 使用工厂默认值。

const key1 = scryptSync('password', 'salt', 64);
console.log(key1.toString('hex'));  // '3745e48...08d59ae'
// 使用自定义 N 参数。必须是 2 的幂。
const key2 = scryptSync('password', 'salt', 64, { N: 1024 });
console.log(key2.toString('hex'));  // '3745e48...aa39b34'

安全堆统计信息

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

稳定性:0 - 已弃用

Attributes
engine:string
默认: crypto.constants.ENGINE_METHOD_ALL

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

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

可选的 flags 参数默认使用 ENGINE_METHOD_ALL。flags 是一个位字段,采用以下标志之一或组合(定义在 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

enableFips

History
Attributes
bool:boolean
true 启用 FIPS 模式, false 禁用 FIPS 模式。

更改 FIPS 模式。对于 OpenSSL 3,这只会在默认属性查询中添加或移除 fips=yes。它不会安装、加载、初始化或验证 FIPS 提供程序。要获得可用的 FIPS 配置,请安装该提供程序,并按照 FIPS 模式 中的说明配置 OpenSSL,使其在 Node.js 启动时加载该提供程序。

如果没有已加载的提供程序提供与 fips=yes 匹配的请求加密实现,则调用仍可能成功, 且 crypto.getFips() 仍可能返回 1,但获取该实现会失败。受影响的 node:crypto 操作通常会失败,并返回 ERR_OSSL_EVP_UNSUPPORTED。不需要重新获取实现的操作, 包括使用先前已获取的实现或已初始化操作上下文的操作,仍可能成功。请在应用初始化 期间调用此方法,并在应用代码使用其他基于 OpenSSL 的 API 之前调用。

此方法只会影响后续的算法获取。Node.js 会在应用代码运行前初始化部分 OpenSSL 状态。 当属性查询必须从进程启动时就处于活动状态时,请在 OpenSSL 配置中设置 default_properties = fips=yes,或使用 --enable-fips 或 --force-fips。此外,命令行标志还要求配置名为 fips 的提供程序,以便对其 进行初始化并通过其自检;否则 Node.js 将无法启动。

如果 OpenSSL 无法更改状态,则抛出错误。当 Node.js 使用 --force-fips 启动时, 无法禁用 FIPS 模式。对于 OpenSSL 1.1.1,启用 FIPS 模式要求使用支持 FIPS 的 OpenSSL 构建版本。

Attributes

使用给定的私钥和算法计算并返回 data 的签名。如果 algorithm 是 null 或 undefined,则算法取决于密钥类型。

对于 Ed25519、Ed448 和 ML-DSA,algorithm 必须是 null 或 undefined。

crypto.getHashes() 列出了哈希 API 可用的算法,但密钥类型和签名方案决定了列出的摘要算法是否可用于签名。

如果 key 不是 KeyObject,此函数的行为就像将 key 传递给了 crypto.createPrivateKey()。当 key 是字符串、ArrayBuffer、Buffer、TypedArray 或 DataView 时,它必须包含 PEM 编码的密钥材料。如果它是一个对象,则可以传入以下其他属性:

Attributes
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:
saltLength:integer
当填充为 RSA_PKCS1_PSS_PADDING 时的盐长度。特殊值 crypto.constants.RSA_PSS_SALTLEN_DIGEST 将盐长度设置为摘要大小, crypto.constants.RSA_PSS_SALTLEN_MAX_SIGN (默认)将其设置为最大允许值。
对于 Ed25519 1 (使用 RFC 8032 中的 Ed25519ctx)、Ed448、ML-DSA 和 SLH-DSA,此选项指定可选上下文,以区分使用相同密钥为不同目的生成的签名。

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

subtle

History

crypto.webcrypto.subtle 的便捷别名。

Attributes

此函数使用恒定时间算法比较给定 ArrayBuffer、TypedArray 或 DataView 实例所表示的底层字节。

此函数不会泄露可被攻击者用来猜测其中一个值的时间信息。这适用于 比较 HMAC 摘要或秘密值,例如身份验证 cookie 或 能力 URL。

a 和 b 必须都是 Buffer、TypedArray 或 DataView,并且它们 必须具有相同的字节长度。如果 a 和 b 具有 不同的字节长度,则会抛出错误。

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

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

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

crypto.verify(algorithm, data, key, signature, callback?): void
Attributes

使用给定的密钥和算法验证 data 的给定签名。如果 algorithm 是 null 或 undefined,则算法取决于 密钥类型。

对于 Ed25519、Ed448 和 ML-DSA,algorithm 必须为 null 或 undefined。

crypto.getHashes() 列出了哈希 API 可用的算法,但密钥类型和签名方案决定了列出的摘要算法是否可用于验证。

If key 不是 KeyObject,此函数的行为就像将 key 传递给了 crypto.createPublicKey()。当 key 是字符串、ArrayBuffer、Buffer、TypedArray 或 DataView 时,它必须包含 PEM 编码的密钥材料。如果它是一个对象,则可以传递以下附加属性:

Attributes
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:
saltLength:integer
当填充为 RSA_PKCS1_PSS_PADDING 时的盐长度。特殊值 crypto.constants.RSA_PSS_SALTLEN_DIGEST 将盐长度设置为摘要 大小, crypto.constants.RSA_PSS_SALTLEN_MAX_SIGN (默认)将其设置为 最大允许值。
对于 Ed25519 1 (使用 RFC 8032 中的 Ed25519ctx)、Ed448、ML-DSA 和 SLH-DSA, 此选项指定可选的上下文,以区分使用相同密钥 为不同目的生成的签名。

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

因为公钥可以从私钥派生,所以可以将私钥或公钥传递给 key。

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

P

crypto.webcrypto

History

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

详见 [Web Crypto API 文档][]。

Node.js 公开了所链接的 OpenSSL 库提供的 FIPS 支持。Node.js 本身并未通过 FIPS 验证。验证针对特定的 OpenSSL 模块或提供程序,并且仅在按照其安全策略部署时适用。供应商提供的 Node.js 或 OpenSSL 构建可能需要不同的配置;对于这些构建,请遵循供应商的文档。

使用 OpenSSL 1.1.1 时,Node.js 必须针对支持 FIPS 的 OpenSSL 库构建。

使用 OpenSSL 3 时,FIPS 支持采用 OpenSSL FIPS 模块指南 中描述的提供程序模型。使用经 FIPS 批准的实现需要:

  • 正确安装的 OpenSSL 3 FIPS 提供程序。

  • OpenSSL 3 FIPS 模块配置文件。

  • 将 FIPS 提供程序加载到 Node.js 使用的 OpenSSL 库上下文中,通常是在 Node.js 启动时通过 OpenSSL 配置文件激活该提供程序。

  • 在获取加密实现时,使默认属性查询包含 fips=yes。可以通过 OpenSSL 配置、--enable-fips 或 --force-fips 在进程启动时设置,也可以通过 crypto.setFips(true) 为后续获取操作设置。

    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()。

Crypto 模块在 Node.js 尚未有统一流 API 概念、也尚未有用于处理二进制数据的 Buffer 对象之前就已加入。因此,许多 crypto 类具有其他实现 [streams][stream] API 的 Node.js 类通常没有的方法(例如 update()、final() 或 digest())。此外,许多方法默认接受并返回 'latin1' 编码的字符串,而不是 Buffer。此默认设置在 Node.js 0.9.3 中更改为默认使用 Buffer 对象。

node:crypto 模块仍支持一些已经遭破解且不建议使用的算法。该 API 也允许使用密钥长度较短、无法安全使用的密码和哈希算法。

用户应全权负责根据自身安全要求选择加密算法和密钥长度。

根据 NIST SP 800-131A 的建议:

  • 在需要抗碰撞性的场景(例如数字签名)中,MD5 和 SHA-1 已不再适用。
  • 建议 RSA、DSA 和 DH 算法使用的密钥至少为 2048 位,ECDSA 和 ECDH 使用的曲线至少为 224 位,以确保未来数年内可安全使用。
  • modp1、modp2 和 modp5 DH 组的密钥长度小于 2048 位,不建议使用。

其他建议和详细信息请参阅参考资料。

一些已知存在弱点、且在实践中意义不大的算法仅可通过 [legacy provider][] 使用,该提供程序默认未启用。

CCM 是受支持的 [AEAD 算法][]之一。使用此模式的应用程序必须在使用密码 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);

对于 CBC 密文窃取(CBC-CTS)密码,crypto.createCipheriv() 或 crypto.createDecipheriv() 的 ctsMode 选项用于选择变体:

  • 'CS1' 是默认值。对于块对齐的输入,其输出与 CBC 模式相同。
  • 'CS2' 对于块对齐的输入也与 CBC 相同。对于末尾含有部分数据块的输入,它会相对于 CS1 交换末尾完整密文块和部分密文块的位置。
  • 'CS3' 是 Kerberos 5 变体。对于末尾含有部分数据块的输入,它使用 CS2 的顺序;对于块对齐的输入,它也会交换末尾两个密文块。

加密和解密必须使用相同的变体。此选项仅适用于 OpenSSL 3.0 或更高版本中的 CBC-CTS 提供程序密码。

使用此模式的应用程序必须遵守以下限制:

  • 明文或密文至少必须有一个数据块长。
  • 密文长度与明文长度相同。
  • 必须只调用一次 cipher.update() 或 decipher.update(),并传入所有输入数据。write(data)、end(data) 或 pipe() 等流方法可能会失败,因为 CBC-CTS 不接受多次数据更新。
  • 仍必须调用 cipher.final() 或 decipher.final() 来完成操作。
  • crypto.getCipherInfo() 会将基本模式报告为 'cbc'。

XTS 密码对可独立调整的数据单元进行操作。一个 Cipheriv 或 Decipheriv 实例表示一个完整的数据单元,其 iv 参数提供 16 字节的 tweak。对于每个不同的逻辑数据单元,请使用具有相应位置 tweak 的新实例。

使用 XTS 模式的应用程序必须遵守以下限制:

  • 明文或密文至少必须有一个 16 字节的数据块。其长度不必是 16 的倍数,因为 XTS 会对末尾的部分数据块使用密文窃取。
  • 密文长度与明文长度相同。
  • 必须只调用一次 cipher.update() 或 decipher.update(),并传入所有输入数据。write(data)、end(data) 或 pipe() 等流方法可能会失败,因为 XTS 不接受多次数据更新。
  • 仍必须调用 cipher.final() 或 decipher.final() 来完成操作。

对于 sm4-xts,crypto.createCipheriv() 或 crypto.createDecipheriv() 的 xtsStandard 选项用于选择 GB/T 17964-2021 中的默认 'GB' 变体或 IEEE Std 1619-2007 中的 'IEEE' 变体。加密和解密必须使用相同的变体。此选项仅适用于 sm4-xts;不适用于 AES-XTS 密码。OpenSSL 的默认提供程序在 OpenSSL 3.2 或更高版本中支持 sm4-xts。

AES 密钥封装(AES-WRAP)和带填充的 AES 密钥封装(AES-WRAP-PAD)密码针对完整的密钥数据值进行操作,而不是针对增量字节流进行操作。逆变换变体具有相同的处理限制。

使用 AES 密钥封装密码的应用程序必须遵守以下限制:

  • 必须只调用一次 cipher.update() 或 decipher.update(),并传入完整的非空输入值。
  • 不要将 AES 密钥封装密码用作通用的 stream.Transform 流。write(data)、end(data) 和 pipe() 等方法可能会将一个值拆分为多次更新,而每次更新都会被视为单独的封装或解封装操作。
  • 仍必须调用 cipher.final() 或 decipher.final() 来完成操作。

如果 OpenSSL 支持,SIV2 和 GCM-SIV3 是受支持的 [AEAD 算法][]。使用这些模式的应用程序必须在使用密码 API 时遵守某些限制:

  • 认证标签长度固定为 16 字节。
  • AES-SIV 密钥长度是所指定 AES 密钥长度的两倍:aes-128-siv 需要 32 字节密钥,aes-192-siv 需要 48 字节密钥,aes-256-siv 需要 64 字节密钥。
  • AES-SIV 密码不使用初始化向量。向 crypto.createCipheriv() 或 crypto.createDecipheriv() 传递 null 或长度为零的 iv。
  • 只有在 OpenSSL 3.5 或更高版本中,AES-SIV 和 AES-GCM-SIV 密码才支持零长度明文。
  • AES-SIV 没有单独的 nonce 或 IV 参数。RFC 5297 定义的 AES-SIV 以有序的关联数据输入列表为基础。每次调用 setAAD() 都会向该列表提供一个输入。如果某个协议在使用 AES-SIV 时使用 nonce,请在其他关联数据输入之后、update() 之前调用 setAAD(nonce)。最多可提供 126 个关联数据输入。
  • AES-GCM-SIV 密码需要 12 字节的初始化向量。
  • 解密时,必须在调用 update() 之前通过 setAuthTag() 设置认证标签。
  • 使用 write(data)、end(data) 或 pipe() 等流方法可能会失败,因为这些模式的每个实例无法处理多个数据块。
  • 由于这些模式会一次性处理整条消息,因此必须且只能调用一次 update()。
  • 尽管调用 update() 已足以加密或解密消息,但应用程序仍必须调用 final() 来计算或验证认证标签。

Node.js 公开了所链接的 OpenSSL 库提供的 FIPS 支持。Node.js 本身并未通过 FIPS 验证。验证针对特定的 OpenSSL 模块或提供程序,并且仅在按照其安全策略部署时适用。供应商提供的 Node.js 或 OpenSSL 构建可能需要不同的配置;对于这些构建,请遵循供应商的文档。

使用 OpenSSL 1.1.1 时,Node.js 必须针对支持 FIPS 的 OpenSSL 库构建。

使用 OpenSSL 3 时,FIPS 支持采用 OpenSSL FIPS 模块指南 中描述的提供程序模型。使用经 FIPS 批准的实现需要:

  • 正确安装的 OpenSSL 3 FIPS 提供程序。
  • OpenSSL 3 FIPS 模块配置文件。
  • 将 FIPS 提供程序加载到 Node.js 使用的 OpenSSL 库上下文中,通常是在 Node.js 启动时通过 OpenSSL 配置文件激活该提供程序。
  • 在获取加密实现时,使默认属性查询包含 fips=yes。可以通过 OpenSSL 配置、--enable-fips 或 --force-fips 在进程启动时设置,也可以通过 crypto.setFips(true) 为后续获取操作设置。

OpenSSL 3 配置文件示例如下:

nodejs_conf = nodejs_init
config_diagnostics = 1

.include /<absolute path>/fipsmodule.cnf

[nodejs_init]
providers = provider_sect
alg_section = algorithm_sect

[provider_sect]
# FIPS 节的名称应与所包含的 fipsmodule.cnf 内的节名称匹配。
fips = fips_sect
base = base_sect

[base_sect]
activate = 1

[algorithm_sect]
default_properties = fips=yes

fipsmodule.cnf 文件是在安装 FIPS 提供程序时生成的,其中包含模块完整性和自检信息。具体命令和参数因安装方式而异;请参阅 OpenSSL FIPS 配置 和 OpenSSL FIPS 模块指南。安装过程使用 openssl fipsinstall。

该示例会在 Node.js 启动时激活提供程序并启用 fips=yes 属性查询。若要在启动时激活提供程序,但稍后通过 crypto.setFips(true) 启用属性查询,请省略 alg_section = algorithm_sect 和 [algorithm_sect] 代码块。提供程序仍然必须被加载;使用此启动配置时,请保持其激活状态。应在应用程序代码使用其他基于 OpenSSL 的 API 之前调用 crypto.setFips(true)。它不等同于在进程启动时启用属性查询,因为 Node.js 会在运行应用程序代码之前初始化一些 OpenSSL 状态。当属性查询必须从进程启动时就处于活动状态时,请使用此处所示的示例、--enable-fips 或 --force-fips。

config_diagnostics 会使配置错误阻止启动,而不是被忽略。base 提供程序提供非加密的支持算法,例如编码器和解码器,这些算法通常需要与 FIPS 提供程序配合使用。default_properties = fips=yes 会将 OpenSSL 的默认算法选择限制为符合 fips=yes 的实现。

将 OPENSSL_CONF 设置为 OpenSSL 配置文件。对于动态加载的提供程序,可以使用 OPENSSL_MODULES 设置包含提供程序模块的目录。例如:

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

--openssl-config 命令行选项用于选择配置文件,其优先级高于 OPENSSL_CONF。如果两者均未设置,则使用 OpenSSL 的默认配置文件。

默认情况下,Node.js 会读取 nodejs_conf 节,而不是 OpenSSL 通常使用的 openssl_conf 节。使用 --openssl-shared-config 可读取 openssl_conf,或者使用 ./configure --openssl-conf-name=<name> 构建 Node.js,以更改默认节名称。

在 OpenSSL 3 上,上述配置会在启动时启用 fips=yes 属性查询。还可以使用以下控制项:

  • --enable-fips 和 --force-fips 会启用属性查询,并额外要求配置的、名为 fips 的提供程序完成初始化并通过自检。如果检查失败,Node.js 将退出。--force-fips 还会阻止通过脚本代码禁用 FIPS 模式。使用 --force-fips=strict 时,Node.js 还会拒绝通过 OpenSSL FIPS 指示器回调报告的非批准操作。
  • crypto.setFips() 会更改 FIPS/属性查询状态。在 OpenSSL 3 上,它不会安装、加载、初始化或验证提供程序。调用之前获取的实现不会发生变化。
  • crypto.getFips() 会报告 FIPS/属性查询状态。在 OpenSSL 3 上,返回值为 1 并不能证明 FIPS 提供程序已加载或已通过验证。
  • 使用 --enable-fips-indicator-events 时,'crypto.fips.indicator' 诊断通道会报告由配置为向后兼容的 OpenSSL 3.4 或更高版本 FIPS 提供程序允许的非批准操作。

使用 OpenSSL 1.1.1 时,这些控制项使用库提供的 FIPS 模式支持,并且要求使用支持 FIPS 的 OpenSSL 构建。

只能使用当前 FIPS 设置下可用的算法。在 OpenSSL 3 上,如果没有已加载的提供程序提供符合 fips=yes 的所请求加密实现,则获取该实现会失败,通常会产生 ERR_OSSL_EVP_UNSUPPORTED。对于 Node.js 支持、但在禁用 FIPS 模式时才可用且在当前 FIPS 设置下不可用的算法,也可能出现相同的错误。

OpenSSL 文档说明,同一个 FIPS 提供程序不能被一个进程中的多个 libcrypto 副本使用。这可能会影响加载另一个 libcrypto 副本的原生插件;OpenSSL 文档给出的解决方法是为每个 libcrypto 实例使用一个独立的提供程序副本。请参阅 OpenSSL FIPS 提供程序限制。

以下由 crypto.constants 导出的常量适用于 node:crypto、node:tls 和 node:https 模块的各种用途,通常特定于 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 使用 Cisco 的 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 禁用版本回滚攻击检测。
常量 描述
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
常量 描述
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
常量 描述
defaultCoreCipherList 指定 Node.js 使用的内置默认密码列表。
defaultCipherList 指定当前 Node.js 进程使用的活动默认密码列表。

Footnotes

  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

  2. 需要 OpenSSL >= 3.0 ↩ ↩2 ↩3

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