源代码: 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);
// 打印:
// c0fa1bc00531bd78ef38c628449c5102aeabd49b5dc3a2a516ea6ea959d6658e
const 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 string
const { 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 或 false
const { 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 string
const { 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 或 false
const { 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 =