【问题标题】:nodejs AES/GCM/NoPadding encryptionnodejs AES/GCM/NoPadding 加密
【发布时间】:2021-09-24 00:19:19
【问题描述】:

我提到了https://stackoverflow.com/a/65072352/4910936 中提到的 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 时搞砸了,因此与加密时使用的不匹配。

【问题讨论】:

  • 你做Buffer.concat([salt, iv, encrypted, tag]),其中 iv(又名 nonce)是 12 个字节,但(在 de-base64 之后)你拆分为 salt、iv、tag,用 iv 加密为 16,最后两个交换。为什么不使用与加密时相同的 config. 字段进行解密?
  • @dave_thompson_085 跨平台共享相同的加密和解密。在Nodejs中加密,在java中解密,反之亦然,所以不能使用config。感谢您发现加密和标签已交换。我在加密版本中修复了它,并按照下面的 Micahel 回答并工作。
  • 您的代码可以通过const iv1 = bData.slice(16, 28); const text1 = bData.slice(28, bData.length - 16); const tag1 = bData.slice(bData.length - 16);轻松修复>
  • @user9014097 是的,我看到现在使用多个 Base64 编码并删除了分隔符并使用了您的建议。谢谢

标签: node.js encryption aes-gcm


【解决方案1】:

这是我为我的跨平台加密博客编写的示例程序 - 这应该可以帮助您分离和运输 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')
}

【讨论】:

    猜你喜欢
    • 2016-11-14
    • 2018-02-19
    • 1970-01-01
    • 2019-05-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-20
    • 1970-01-01
    相关资源
    最近更新 更多