nodejs AES/GCM/NoPadding 加密

nodejs AES/GCM/NoPadding encryption

我参考了 中提到的 nodejs 加密,我可以加密并在解密时收到错误“错误:不支持的状态或无法验证数据”

var crypto = require('crypto');

console.log('AES GCMC 256 String encryption with PBKDF2 derived key');

var plaintext = 'The quick brown fox jumps over the lazy dog';
console.log('plaintext:  ', plaintext);

const cryptoConfig = {
    cipherAlgorithm: 'aes-256-gcm',
    masterKey: 'somekey',
    iterations: 65535,
    keyLength: 32,
    saltLength: 16,
    ivLength: 12,
    tagLength: 16,
    digest: 'sha512'
}

var ciphertext = encrypt(plaintext);
console.log('ciphertext: ', ciphertext);
decrypt(ciphertext)
function encrypt(content) {
    const salt = crypto.randomBytes(cryptoConfig.saltLength);
    console.log("salt : ", salt)
    const iv = crypto.randomBytes(cryptoConfig.ivLength);
    console.log("iv : ", iv)
    const key = crypto.pbkdf2Sync(cryptoConfig.masterKey, salt, cryptoConfig.iterations,
    cryptoConfig.keyLength, cryptoConfig.digest);
    const cipher = crypto.createCipheriv(cryptoConfig.cipherAlgorithm, key, iv);
    const encrypted = Buffer.concat([cipher.update(content, 'utf8'), cipher.final()]);
    const tag = cipher.getAuthTag();
    console.log("tag : ", tag)
    // ### put the auth tag at the end of encrypted
    //const encdata = Buffer.concat([salt, iv, tag, encrypted]).toString('base64');
    const encdata = Buffer.concat([salt, iv, encrypted, tag]).toString('base64');
    
    return encdata;
}
    
function decrypt(encdata){
    ///decrypt
    // base64 decoding
    const bData = Buffer.from(encdata, 'base64');

    // convert data to buffers
    const salt1 = bData.slice(0, 16);
    const iv1 = bData.slice(16, 32);
    const tag1 = bData.slice(32, 48);
    const text1 = bData.slice(48);

    // derive key using; 32 byte key length
    // const key = crypto.pbkdf2Sync(cryptoConfig.masterkey, salt , 2145, 32, 'sha512');
    const key1 = crypto.pbkdf2Sync(cryptoConfig.masterKey, salt1, cryptoConfig.iterations,
    cryptoConfig.keyLength, cryptoConfig.digest)

    // AES 256 GCM Mode
    const decipher = crypto.createDecipheriv('aes-256-gcm', key1, iv1);
    decipher.setAuthTag(tag1);

    // encrypt the given text
    const decrypted = decipher.update(text1, 'binary', 'utf8') + decipher.final('utf8');

    console.log(decrypted)
}

从错误来看,我在从加密数据中分离 IV、salt 时搞砸了,因此与加密时使用的内容不匹配。

这是我为我的跨平台加密博客编写的示例程序 - 这应该可以帮助您分离和传送 salt、iv 和 gcm 标签。

可在此处找到实时 运行 版本:https://replit.com/@javacrypto/CpcNodeJsCryptoAesGcm256Pbkdf2StringEncryption#index.js/

var crypto = require('crypto');

console.log('AES GCM 256 String encryption with PBKDF2 derived key');

var plaintext = 'The quick brown fox jumps over the lazy dog';
console.log('plaintext: ', plaintext);
var password = "secret password";

console.log('\n* * * Encryption * * *');

var ciphertextBase64 = aesGcmPbkdf2EncryptToBase64(password, plaintext);
console.log('ciphertext (Base64): ' + ciphertextBase64);
console.log('output is (Base64) salt : (Base64) nonce : (Base64) ciphertext : (Base64) gcmTag');

console.log('\n* * * Decryption * * *');
var ciphertextDecryptionBase64 = ciphertextBase64;
console.log('ciphertext (Base64): ', ciphertextDecryptionBase64);
console.log('input is (Base64) salt : (Base64) nonce : (Base64) ciphertext : (Base64) gcmTag');
var decryptedtext = aesGcmPbkdf2DecryptFromBase64(password, ciphertextBase64);
console.log('plaintext: ', decryptedtext);

function aesGcmPbkdf2EncryptToBase64(password, data) {
  var PBKDF2_ITERATIONS = 15000;
  var salt = generateSalt32Byte();
  var key = crypto.pbkdf2Sync(password, salt, PBKDF2_ITERATIONS, 32, 'sha256');
  var nonce = generateRandomNonce();
  const cipher = crypto.createCipheriv('aes-256-gcm', key, nonce);
  let encryptedBase64 = '';
  cipher.setEncoding('base64');
  cipher.on('data', (chunk) => encryptedBase64 += chunk);
  cipher.on('end', () => {
  // do nothing console.log(encryptedBase64);
  // Prints: some clear text data
  });
  cipher.write(data);
  cipher.end();
  var saltBase64 = base64Encoding(salt);
  var nonceBase64 = base64Encoding(nonce);
  var gcmTagBase64 = base64Encoding(cipher.getAuthTag());
  return saltBase64 + ':' + nonceBase64 + ':' + encryptedBase64 + ':' + gcmTagBase64;
}

function aesGcmPbkdf2DecryptFromBase64(password, data) {
  var PBKDF2_ITERATIONS = 15000;
  var dataSplit = data.split(":");
  var salt = base64Decoding(dataSplit[0]);
  var gcmTag = base64Decoding(dataSplit[3]);
  var key = crypto.pbkdf2Sync(password, salt, PBKDF2_ITERATIONS, 32, 'sha256');
  var nonce = base64Decoding(dataSplit[1]);
  var ciphertext = dataSplit[2];
  const decipher = crypto.createDecipheriv('aes-256-gcm', key, nonce);
  decipher.setAuthTag(gcmTag);
  let decrypted = '';
  decipher.on('readable', () => {
    while (null !== (chunk = decipher.read())) {
      decrypted += chunk.toString('utf8');
    }
  });
  decipher.on('end', () => {
  // do nothing console.log(decrypted);
  });
  decipher.write(ciphertext, 'base64');
  decipher.end();
  return decrypted;
}

function generateSalt32Byte() {
  return crypto.randomBytes(32);
}

function generateRandomNonce() {
  return crypto.randomBytes(12);
}

function base64Encoding(input) {
  return input.toString('base64');
}

function base64Decoding(input) {
  return Buffer.from(input, 'base64')
}