如何在 Node.JS 中加载加密的私钥

hof*_*n41 6 javascript encryption openssl node.js

我想使用带有加密私钥的节点Crypto:Sign模块。Crypto 模块中是否有允许我解密私钥的功能?

例如,在 Python 中有一个OpenSSL.crypto.load_privatekey函数,它使用密码来解密私钥。我希望实现相同的功能,但使用 Node.JS 库。

const crypto = require('crypto');
const sign = crypto.createSign('sha256');

sign.update('some data to sign');

let private_key = '-----BEGIN ENCRYPTED PRIVATE KEY-----\n' +
                    'ABCDEFGHIJKLMNOP\n' +  
                    '-----END ENCRYPTED PRIVATE KEY-----\n';

// Somehow decrypt private_key using passphrase.    
what_to_do(????);

console.log(sign.sign(private_key).toString('hex'));
Run Code Online (Sandbox Code Playgroud)

cit*_*ave 11

我意识到这个问题现在已经很老了,但是我在尝试自己解决时遇到了这个问题,最终找到了答案,所以我将分享给后代:

我正在开发一个应用程序,即使在没有 HTTPS 连接的情况下也试图保护它。我正在使用非对称加密处理登录。用户提供他们的用户名,这会导致在后端进行查找,以检索注册时计算的用户公钥。然后使用公钥使用 JSEncrypt 加密客户端上的密码,然后将其发送回后端进行解密和验证,私钥也在注册时以加密形式计算和存储。

诀窍是,由于加密的私钥以纯文本形式存储,因此必须将其重新加载到 a 中,KeyObject然后才能被加密方法使用。我的密钥创建如下:

CRYPTO.generateKeyPair('rsa', {
        'modulusLength': 4096,
        'publicKeyEncoding': {
            'type': 'spki',
            'format': 'pem',
        },
        'privateKeyEncoding': {
            'type': 'pkcs8',
            'format': 'pem',
            'cipher': 'aes-256-cbc',
            'passphrase': 'passphrase'
        }
    }, (err, publicKey, privateKey) => { /* store keys, return public key */ }
Run Code Online (Sandbox Code Playgroud)

根据文档,如果提供了编码选项,则该函数的行为就像KeyObject.export()已针对结果调用一样,因此您将KeyObject在回调中获得字符串而不是 a 。无论如何,这需要将密钥存储在数据库中,正如我正在做的那样,并且我认为 OP 正在做,因为他试图从字符串中加载密钥。

所以现在我们必须把它改回来。这是通过CRYPTO.createPrivateKey(). 我的函数调用如下所示:

var privateKey = CRYPTO.createPrivateKey({
    'key': encodedPrivateKeyString,
    'format': 'pem',
    'type': 'pkcs8',
    'cipher': 'aes-256-cbc',
    'passphrase': 'passphrase'
});
Run Code Online (Sandbox Code Playgroud)

它反映了私钥的初始创建并KeyObject生成可用于CRYPTO.privateDecrypt()或 的CRYPTO.Sign.sign()

我实际上在生成用于令牌验证的密钥后立即使用了 sign,并且能够让它与这个一起工作:

var signToken = CRYPTO.createSign('SHA256');
signToken.write('random text to sign');
signToken.end();
var token = signToken.sign({ 'key': privateKey, 'passphrase': 'passphrase' }, 'hex');
Run Code Online (Sandbox Code Playgroud)

但此时我在CRYPTO.generateKeyPair()回调中,所以原始变量仍然可用,无论它们的确切类型是什么。我假设此时privateKey并不是一个真正的字符串,即使我能够像它一样将它存储到数据库中,但是 a KeyObject,或者它以某种方式知道或默认了我没有提供的参数选项。但是,当我回到后端以在登录时验证用户密码时,我必须再次从纯文本创建私钥,所以当我调用时CRYPTO.privateDecrypt(),我必须KeyObject首先像上面那样重新创建私钥,然后CRYPTO.privateDecrypt()像这样调用:

var hash = CRYPTO.privateDecrypt({
    'key': privateKey,
    'passphrase': 'passphrase',
    'cipher': 'aes-256-cbc',
    'padding': CRYPTO.constants.RSA_PKCS1_PADDING
}, buffer).toString();
Run Code Online (Sandbox Code Playgroud)

然后可以将此字符串与注册时存储的散列进行比较以验证用户。如果您所做的只是签名,那么您可以像我一样不提供其他参数,但如果您要从字符串重新创建私钥,则可能需要提供更多参数。或者,您可以只提供您的密码短语,就像我将编码的私钥作为字符串所做的那样,并且大部分解释都是不必要的,因为它处理的是解密而不是签名。

无论哪种方式,我都记录了我的斗争。希望这可以帮助某人。

  • 就我而言,我想使用加密的私钥进行 JWT 签名。所以首先我使用 openssl genpkey -aes-256-ofb -algorithm EC -out key.private.pem -pkeyopt ec_paramgen_curve:P-256 -pkeyopt ec_param_enc:named_curve 创建 EC 密钥对 然后我使用以下命令来获取解密的私钥:` privateKeyObject = createPrivateKey({ 格式:'pem',密钥:encryptedPrivateKey,密码:'passphrase',类型:'pkcs8' });` `const privateKey = privateKeyObject.export({ 格式:'pem',类型:'pkcs8' }).toString();` (2认同)