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

时间:2016-05-12 23:05:04

标签: javascript node.js encryption openssl

我想将节点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'));

1 个答案:

答案 0 :(得分:0)

我意识到这个问题到目前为止是旧的AF,但是我在尝试自己解决该问题时遇到了这个问题,最终能够找到答案,所以我将分享给后代:

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

诀窍是,由于加密的私钥以纯文本存储,因此必须先将其重新加载到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 */ }

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

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

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

它镜像了私钥的初始创建,并产生了一个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');

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

CRYPTO.privateDecrypt()

然后可以将该字符串与注册时存储的哈希进行比较,以验证用户身份。如果您所做的只是签名,则可以不像我一样提供其他参数,但是如果您要通过字符串重新创建私钥,则可能需要提供更多的参数。另外,您也可以像我一样使用已编码的私钥作为字符串来提供密码短语,并且大多数解释都是不必要的,因为它是处理解密而不是签名。

无论哪种方式,我都记录了自己的挣扎。希望这对某人有帮助。