源代码: lib/crypto.js
crypto 模块提供了加密功能,其中包括了用于 OpenSSL 散列、HMAC、加密、解密、签名、以及验证的函数的一整套封装。
const { createHmac } = await import('crypto');
const secret = 'abcdefg';
const hash = createHmac('sha256', secret)
.update('I love cupcakes')
.digest('hex');
console.log(hash);
// 打印:
// c0fa1bc00531bd78ef38c628449c5102aeabd49b5dc3a2a516ea6ea959d6658econst crypto = require('crypto');
const secret = 'abcdefg';
const hash = crypto.createHmac('sha256', secret)
.update('I love cupcakes')
.digest('hex');
console.log(hash);
// 打印:
// c0fa1bc00531bd78ef38c628449c5102aeabd49b5dc3a2a516ea6ea959d6658e
可以在不支持 crypto 模块的情况下构建 Node.js。
在这种情况下,尝试 import crypto 或调用 require('crypto') 将导致抛出错误。
使用 CommonJS 时,可以使用 try/catch 捕获抛出的错误:
let crypto;
try {
crypto = require('crypto');
} catch (err) {
console.log('crypto support is disabled!');
}
当使用词法 ESM import 关键字时,只有在尝试加载模块之前注册了 process.on('uncaughtException') 的句柄时,才能捕获错误(例如,使用预加载模块)。
使用 ESM 时,如果有可能在未启用加密支持的 Node.js 版本上运行代码,则考虑使用 import() 函数而不是 import 关键字:
let crypto;
try {
crypto = await import('crypto');
} catch (err) {
console.log('crypto support is disabled!');
}
Certificate 类#SPKAC 是最初由 Netscape 实现的证书签名请求机制,并被正式指定为 HTML5 的 keygen 元素的一部分。
<keygen> 已弃用,因为 HTML 5.2 和新项目不应再使用此元素。
crypto 模块提供了用于处理 SPKAC 数据的 Certificate 类。
最常见的用法是处理由 HTML5 <keygen> 元素生成的输出。
Node.js 在内部使用 OpenSSL 的 SPKAC 实现。
Certificate.exportChallenge(spkac[, encoding])#spkac <string> | <ArrayBuffer> | <Buffer> | <TypedArray> | <DataView>encoding <string> spkac 字符串的编码。spkac 数据结构的挑战组件,包括公钥和挑战。const { Certificate } = await import('crypto');
const spkac = getSpkacSomehow();
const challenge = Certificate.exportChallenge(spkac);
console.log(challenge.toString('utf8'));
// 打印: the challenge as a UTF8 stringconst { Certificate } = require('crypto');
const spkac = getSpkacSomehow();
const challenge = Certificate.exportChallenge(spkac);
console.log(challenge.toString('utf8'));
// 打印: the challenge as a UTF8 string
Certificate.exportPublicKey(spkac[, encoding])#spkac <string> | <ArrayBuffer> | <Buffer> | <TypedArray> | <DataView>encoding <string> spkac 字符串的编码。spkac 数据结构的公钥组件,包括公钥和挑战。const { Certificate } = await import('crypto');
const spkac = getSpkacSomehow();
const publicKey = Certificate.exportPublicKey(spkac);
console.log(publicKey);
// 打印: the public key as <Buffer ...>const { Certificate } = require('crypto');
const spkac = getSpkacSomehow();
const publicKey = Certificate.exportPublicKey(spkac);
console.log(publicKey);
// 打印: the public key as <Buffer ...>
Certificate.verifySpkac(spkac[, encoding])#spkac <string> | <ArrayBuffer> | <Buffer> | <TypedArray> | <DataView>encoding <string> spkac 字符串的编码。spkac 数据结构有效,则为 true,否则为 false。import { Buffer } from 'buffer';
const { Certificate } = await import('crypto');
const spkac = getSpkacSomehow();
console.log(Certificate.verifySpkac(Buffer.from(spkac)));
// 打印: true 或 falseconst { Certificate } = require('crypto');
const { Buffer } = require('buffer');
const spkac = getSpkacSomehow();
console.log(Certificate.verifySpkac(Buffer.from(spkac)));
// 打印: true 或 false
作为旧版接口,可以创建 crypto.Certificate 类的新实例,如下面的示例所示。
new crypto.Certificate()#可以使用 new 关键字或通过调用 crypto.Certificate() 作为函数来创建 Certificate 类的实例:
const { Certificate } = await import('crypto');
const cert1 = new Certificate();
const cert2 = Certificate();const { Certificate } = require('crypto');
const cert1 = new Certificate();
const cert2 = Certificate();
certificate.exportChallenge(spkac[, encoding])#spkac <string> | <ArrayBuffer> | <Buffer> | <TypedArray> | <DataView>encoding <string> spkac 字符串的编码。spkac 数据结构的挑战组件,包括公钥和挑战。const { Certificate } = await import('crypto');
const cert = Certificate();
const spkac = getSpkacSomehow();
const challenge = cert.exportChallenge(spkac);
console.log(challenge.toString('utf8'));
// 打印: the challenge as a UTF8 stringconst { Certificate } = require('crypto');
const cert = Certificate();
const spkac = getSpkacSomehow();
const challenge = cert.exportChallenge(spkac);
console.log(challenge.toString('utf8'));
// 打印: the challenge as a UTF8 string
certificate.exportPublicKey(spkac[, encoding])#spkac <string> | <ArrayBuffer> | <Buffer> | <TypedArray> | <DataView>encoding <string> spkac 字符串的编码。spkac 数据结构的公钥组件,包括公钥和挑战。const { Certificate } = await import('crypto');
const cert = Certificate();
const spkac = getSpkacSomehow();
const publicKey = cert.exportPublicKey(spkac);
console.log(publicKey);
// 打印: the public key as <Buffer ...>const { Certificate } = require('crypto');
const cert = Certificate();
const spkac = getSpkacSomehow();
const publicKey = cert.exportPublicKey(spkac);
console.log(publicKey);
// 打印: the public key as <Buffer ...>
certificate.verifySpkac(spkac[, encoding])#spkac <string> | <ArrayBuffer> | <Buffer> | <TypedArray> | <DataView>encoding <string> spkac 字符串的编码。spkac 数据结构有效,则为 true,否则为 false。import { Buffer } from 'buffer';
const { Certificate } = await import('crypto');
const cert = Certificate();
const spkac = getSpkacSomehow();
console.log(cert.verifySpkac(Buffer.from(spkac)));
// 打印: true 或 falseconst { Certificate } = require('crypto');
const { Buffer } = require('buffer');
const cert = Certificate();
const spkac = getSpkacSomehow();
console.log(cert.verifySpkac(Buffer.from(spkac)));
// 打印: true 或 false
Cipher 类#Cipher 类的实例用于加密数据。
可以通过以下两种方式之一使用该类:
cipher.update() 和 cipher.final() 方法生成加密的数据。crypto.createCipher() 或 crypto.createCipheriv() 方法用于创建 Cipher 实例。
Cipher 对象不能直接使用 new 关键字创建。
示例:使用 Cipher 对象作为流:
const {
scrypt,
randomFill,
createCipheriv
} = await import('crypto');
const algorithm = 'aes-192-cbc';
const password = 'Password used to generate key';
// 首先,将生成密钥。密钥长度取决于算法。
// 在此示例中,用于 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('crypto');
const algorithm = 'aes-192-cbc';
const password = 'Password used to generate key';
// 首先,将生成密钥。密钥长度取决于算法。
// 在此示例中,用于 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();
});
});
示例:使用 Cipher 和管道流:
import {
createReadStream,
createWriteStream,
} from 'fs';
import {
pipeline
} from 'stream';
const {
scrypt,
randomFill,
createCipheriv
} = await import('crypto');
const algorithm = 'aes-192-cbc';
const password = 'Password used to generate key';
// 首先,将生成密钥。密钥长度取决于算法。
// 在此示例中,用于 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('fs');
const {
pipeline
} = require('stream');
const {
scrypt,
randomFill,
createCipheriv,
} = require('crypto');
const algorithm = 'aes-192-cbc';
const password = 'Password used to generate key';
// 首先,将生成密钥。密钥长度取决于算法。
// 在此示例中,用于 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('crypto');
const algorithm = 'aes-192-cbc';
const password = 'Password used to generate key';
// 首先,将生成密钥。密钥长度取决于算法。
// 在此示例中,用于 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('crypto');
const algorithm = 'aes-192-cbc';
const password = 'Password used to generate key';
// 首先,将生成密钥。密钥长度取决于算法。
// 在此示例中,用于 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);
});
});
cipher.final([outputEncoding])#outputEncoding <string> 返回值的编码。outputEncoding,则返回字符串。
如果未提供 outputEncoding,则返回 Buffer。一旦调用了 cipher.final() 方法,则 Cipher 对象就不能再用于加密数据。
多次尝试调用 cipher.final() 将导致抛出错误。
cipher.getAuthTag()#只有在使用 cipher.final() 方法完成加密后才应调用 cipher.getAuthTag() 方法。
如果在创建 cipher 实例时设置了 authTagLength 选项,则此函数将准确返回 authTagLength 个字节。
cipher.setAAD(buffer[, options])#buffer <string> | <ArrayBuffer> | <Buffer> | <TypedArray> | <DataView>options <Object> stream.transform 选项
当使用认证的加密模式时(目前支持 GCM、CCM 和 OCB),则 cipher.setAAD() 方法设置用于额外的认证数据 (AAD) 输入参数的值。
plaintextLength 选项对于 GCM 和 OCB 是可选的。
使用 CCM 时,必须指定 plaintextLength 选项,其值必须与明文的字节长度匹配。
请参见 CCM 模式。
cipher.setAAD() 方法必须在 cipher.update() 之前调用。
cipher.setAutoPadding([autoPadding])#当使用块加密算法时,Cipher 类会自动向输入数据添加填充到适当的块大小。
要禁用默认填充调用 cipher.setAutoPadding(false)。
当 autoPadding 为 false 时,整个输入数据的长度必须是密码块大小的倍数,否则 cipher.final() 将抛出错误。
禁用自动填充对于非标准填充很有用,例如使用 0x0 而不是 PKCS 填充。
cipher.setAutoPadding() 方法必须在 cipher.final() 之前调用。
cipher.update(data[, inputEncoding][, outputEncoding])#data <string> | <Buffer> | <TypedArray> | <DataView>inputEncoding <string> 数据的编码。outputEncoding <string> 返回值的编码。使用 data 更新密码。
如果给定了 inputEncoding 参数,则 data 参数是使用指定编码的字符串。
如果未给定 inputEncoding 参数,则 data 必须是 Buffer、TypedArray 或 DataView。
如果 data 是 Buffer、TypedArray 或 DataView,则忽略 inputEncoding。
outputEncoding 指定加密数据的输出格式。
如果指定了 outputEncoding,则返回使用指定编码的字符串。
如果未提供 outputEncoding,则返回 Buffer。
可以使用新数据多次调用 cipher.update() 方法,直到调用 cipher.final()。
在 cipher.final() 之后调用 cipher.update() 将导致抛出错误。
Decipher 类#Decipher 类的实例用于解密数据。
可以通过以下两种方式之一使用该类:
decipher.update() 和 decipher.final() 方法生成未加密的数据。crypto.createDecipher() 或 crypto.createDecipheriv() 方法用于创建 Decipher 实例。
Decipher 对象不能直接使用 new 关键字创建。
示例:使用 Decipher 对象作为流:
import { Buffer } from 'buffer';
const {
scryptSync,
createDecipheriv
} = await import('crypto');
const algorithm = 'aes-192-cbc';
const password = 'Password used to generate key';
// 密钥长度取决于算法。
// 在这种情况下,对于 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', () => {
while (null !== (chunk = decipher.read())) {
decrypted += chunk.toString('utf8');
}
});
decipher.on('end', () => {
console.log(decrypted);
// 打印: some clear text data
});
// 使用相同的算法、密钥和 iv 加密。
const encrypted =
'e5f79c5915c02171eec6b212d5520d44480993d7d622a7c4c2da32f6efda0ffa';
decipher.write(encrypted, 'hex');
decipher.end();const {
scryptSync,
createDecipheriv,
} = require('crypto');
const { Buffer } = require('buffer');
const algorithm = 'aes-192-cbc';
const password = 'Password used to generate key';
// 密钥长度取决于算法。
// 在这种情况下,对于 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', () => {
while (null !== (chunk = decipher.read())) {
decrypted += chunk.toString('utf8');
}
});
decipher.on('end', () => {
console.log(decrypted);
// 打印: some clear text data
});
// 使用相同的算法、密钥和 iv 加密。
const encrypted =
'e5f79c5915c02171eec6b212d5520d44480993d7d622a7c4c2da32f6efda0ffa';
decipher.write(encrypted, 'hex');
decipher.end();
示例:使用 Decipher 和管道流:
import {
createReadStream,
createWriteStream,
} from 'fs';
import { Buffer } from 'buffer';
const {
scryptSync,
createDecipheriv
} = await import('crypto');
const algorithm = 'aes-192-cbc';
const password = 'Password used to generate key';
// 请改用异步的 `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('fs');
const {
scryptSync,
createDecipheriv,
} = require('crypto');
const { Buffer } = require('buffer');
const algorithm =