Home > Software design >  how do i reencrypt a aes encrypted private key generated using nodejs crypto
how do i reencrypt a aes encrypted private key generated using nodejs crypto

Time:01-27

I tried to decrypt a private AES encrypted RSA key and re-encrypt it with the same AES algorithm, but a different password. I used this method to create an RSA keypair:

crypto.generateKeyPairSync('rsa', {
        modulusLength: 2048,
        publicKeyEncoding: {
          type: 'pkcs1',
          format: 'pem',
        },
        privateKeyEncoding: {
            type: 'pkcs1',
            format: 'pem',
            cipher: 'aes-256-cbc',
            passphrase: secret
        }
      });

This is an example private key generated by this method:

-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-256-CBC,B25B895829F89685570676A6C53B4E9F

xb/IOI6S1rXqxo N1Aba1M4xCqDG8u5OGBiF/zvDnKzZ/rV7/TTVIhQouZKdq 26
XMQDWGlRx1YQqWajJUZzbvixdWR3pacRpR1Mli1WPIU3rtWwOlYVgL3TqtN19IK 
NEMMrGpbnUQmXM1M5ZExRrKYw9qPI3VJwmCuE0j19 FoYZJT/5v861RcNs18v8hz
[...]
-----END RSA PRIVATE KEY-----

I want to decrypt the private key that was encrypted using aes-256-cbc and a password and re-encrypt it using another password and the same AES algorithm.

I have already tried the following method but I did not manage to get it working:

const decryptRsaPrivateKey = (privateKey, oldPassword, newPassword) => {
    const keyLines = privateKey.split('\n');

    // Extract the DEK-Info line
    var dekInfoLine = keyLines[2]

    // Extract the encryption algorithm and IV from the DEK-Info line
    const [, algorithm, ivHex] = dekInfoLine.match(/DEK-Info: (. ),(. )/);
    const iv = Buffer.from(ivHex, 'hex');

    // Remove the DEK-Info line and the header/footer lines
    keyLines.splice(2, 1);
    keyLines.splice(0, 1);
    keyLines.splice(-1, 1);

    // Join the remaining lines and base64-decode the key
    const encodedKey = keyLines.join('');
    const decipher = crypto.createDecipheriv(algorithm, Buffer.from(oldPassword), iv);
    let decryptedKey = decipher.update(encodedKey, 'base64', 'binary');
    decryptedKey  = decipher.final('binary');

    // Encrypt the key with the new password
    const cipher = crypto.createCipheriv(algorithm, Buffer.from(newPassword), iv);
    let encryptedKey = cipher.update(decryptedKey, 'binary', 'base64');
    encryptedKey  = cipher.final('base64');

    // Add the DEK-Info line and the header/footer lines
    const dekInfo = `DEK-Info: ${algorithm},${iv.toString('hex')}`;
    return `-----BEGIN RSA PRIVATE KEY-----\n${dekInfo}\n\n${encryptedKey}\n-----END RSA PRIVATE KEY-----`;
}

This was the error I got from this method:

node:internal/crypto/cipher:116
    this[kHandle].initiv(cipher, credential, iv, authTagLength);
                  ^

RangeError: Invalid key length
    at Decipheriv.createCipherBase (node:internal/crypto/cipher:116:19)
    at Decipheriv.createCipherWithIV (node:internal/crypto/cipher:135:3)
    at new Decipheriv (node:internal/crypto/cipher:289:3)
    at Object.createDecipheriv (node:crypto:149:10)
    at Module._compile (node:internal/modules/cjs/loader:1159:14)
    at Module._extensions..js (node:internal/modules/cjs/loader:1213:10)
    at Module.load (node:internal/modules/cjs/loader:1037:32)
    at Module._load (node:internal/modules/cjs/loader:878:12) {
  code: 'ERR_CRYPTO_INVALID_KEYLEN'
}

I also read the documentation from node, but I found nothing that could help me.

Thank you in advance.

CodePudding user response:

A key is usually imported with crypto.createPrivateKey(). This is also true for an encrypted key, for which the password must be specified.
The export is possible with keyObject.export(). If the key is to be encrypted, the password and algorithm must be specified.

If a password of an existing encrypted key is to be changed, the procedure is analogous: Import with the old password, export with the new password.

Example:

var crypto = require('crypto');

// Generate keypair
var secret ='secret';
var keyPair = crypto.generateKeyPairSync(
    'rsa', 
    {
        modulusLength: 2048,
        publicKeyEncoding: {type: 'pkcs1', format: 'pem'},
        privateKeyEncoding: {type: 'pkcs1', format: 'pem', cipher: 'aes-256-cbc', passphrase: secret}
    }
);

var encPkcs1Pem = keyPair.privateKey;
console.log(encPkcs1Pem);

// Import
var privateKey = crypto.createPrivateKey({key: encPkcs1Pem, type: 'pkcs1', format: 'pem', passphrase: secret});
//var privateKey = crypto.createPrivateKey({key: encPkcs1Pem, passphrase: secret}); // works also

// Export with new passphrase
var secret_2 ='another secret';
var encPkcs1Pem_2 = privateKey.export({type: 'pkcs1', format: 'pem', cipher: 'aes-256-cbc', passphrase: secret_2});
console.log(encPkcs1Pem_2);
  • Related