从节点脚本读取 Ruby Active Record 加密

史蒂夫约翰斯通

我正在将 ruby​​ on rails 应用程序移植到 node.js,并且需要能够读取通过活动记录加密加密的数据。

看来使用的算法是aes-256-gcm,我这里有一个node.js脚本来加密和解密数据:

import crypto from 'crypto';
import config from './config';

const algorithm = 'aes-256-gcm';
const encrypt = (secret: string, originalData: string) => {
  console.log('encrypting with ', {
    secret,
    originalData
  });
  const iv = crypto.randomBytes(16);
  var crypt = crypto.createCipheriv(algorithm, Buffer.from(secret, 'base64'), iv);

  var encoded = crypt.update(originalData, 'utf8', 'hex');
  encoded += crypt.final('hex');
  const at = crypt.getAuthTag();
  return { encrypted: encoded, iv, at };
};

const decrypt = (secret: string, encryptedData: string, iv: string, at: string) => {
  console.log('decrypting with ', {
    secret,
    encryptedData,
    iv,
    at
  });
  var crypt = crypto.createDecipheriv(algorithm, Buffer.from(secret, 'base64'), Buffer.from(iv, 'base64'));
  crypt.setAuthTag(Buffer.from(at, 'base64'));
  var decoded = crypt.update(encryptedData, 'hex', 'utf8');
  decoded += crypt.final('utf8');
  return decoded;
};

const secret = crypto.createHash('sha256').update('ThisIsASecretKey', 'ascii').digest();
const originalData = 'This is just a test to see what would happen';
const encryptionResult = encrypt(secret.toString('base64'), originalData);
const decryptionResult = decrypt(
  secret.toString('base64'),
  encryptionResult.encrypted,
  encryptionResult.iv.toString('base64'),
  encryptionResult.at.toString('base64')
);

这很好用,我得到了结果:

encrypting with  {
  secret: 'eKAyTQoTH0apq8+fsQYHSYelUy3mL34gl2M+rnvpXhs=',
  originalData: 'This is just a test to see what would happen'
}
decrypting with  {
  secret: 'eKAyTQoTH0apq8+fsQYHSYelUy3mL34gl2M+rnvpXhs=',
  encryptedData: '63eaf4dde9b1cc0e76d9d95725c85234ef2a742dd1c76a9579a8f03d20494cd85b658dcc3518b1ee9a5301b0',
  iv: 'eloq0RQOnaFejiJzkQ7ybw==',
  at: 'ylfqtCSRdMZLgnGC0wf3EA=='
}
Encryption data matches {
  originalData: 'This is just a test to see what would happen',
  encryptedData: '63eaf4dde9b1cc0e76d9d95725c85234ef2a742dd1c76a9579a8f03d20494cd85b658dcc3518b1ee9a5301b0',
  decryptionResult: 'This is just a test to see what would happen'
}

但是,当我尝试使用 ruby​​ 中的值时,它会抛出消息“不支持状态或无法验证数据”

我正在使用的 Rails 中的数据是从数据库中导出的,密钥是通过在 ruby​​ 中运行以下命令导出的:

key_provider = ActiveRecord::Encryption.key_provider
key = key_provider.encryption_key
puts Base64.encode64(key.secret)

通过查看 ruby​​ 源代码,这似乎是它加密/解密数据的方式:

  require "openssl"

  secret = Base64.strict_decode64("eKAyTQoTH0apq8+fsQYHSYelUy3mL34gl2M+rnvpXhs=")
  clear_text = "This is some debug text"

  puts "--------------- encrypt -------------"
  CIPHER_TYPE = "aes-256-gcm"

  cipher = OpenSSL::Cipher.new(CIPHER_TYPE)
  cipher.encrypt
  cipher.key = secret

  iv = cipher.random_iv
  cipher.iv = iv

  encrypted_data = clear_text.empty? ? clear_text.dup : cipher.update(clear_text)
  encrypted_data << cipher.final

  auth_tag = cipher.auth_tag

  puts "clear_text="+clear_text
  puts "secret="+Base64.strict_encode64(secret)
  puts "iv="+Base64.strict_encode64(iv)
  puts "auth_tag="+Base64.strict_encode64(auth_tag)
  puts "encrypted_data="+Base64.strict_encode64(encrypted_data)

  puts "--------------- /encrypt -------------"


  puts "--------------- decrypt -------------"
  begin
      # secret = key.secret
      # cipher = ActiveRecord::Encryption::Aes256Gcm.new(secret, deterministic: false)
      # decrypted_data = cipher.decrypt(encrypted_message)
      # puts decrypted_data
      CIPHER_TYPE = "aes-256-gcm"

      # encrypted_data = encrypted_message.payload
      puts "encrypted_data=" + Base64.strict_encode64(encrypted_data)
      # iv = encrypted_message.headers.iv
      puts "iv=" + Base64.strict_encode64(iv)
      # auth_tag = encrypted_message.headers.auth_tag
      puts "auth_tag=" +  Base64.strict_encode64(auth_tag)

      cipher = OpenSSL::Cipher.new(CIPHER_TYPE)

      puts cipher

      cipher.decrypt
      cipher.key = secret
      puts "secret=" +  Base64.strict_encode64(secret)
      cipher.iv = iv
      cipher.auth_tag = auth_tag
      cipher.auth_data = ""
      decrypted_data = encrypted_data.empty? ? encrypted_data : cipher.update(encrypted_data)
      decrypted_data << cipher.final
      puts "decrypted_data=" + decrypted_data
    end
  puts "--------------- /decrypt -------------"

这给出了值:

app_1  | --------------- encrypt -------------
app_1  | clear_text=This is some debug text
app_1  | secret=eKAyTQoTH0apq8+fsQYHSYelUy3mL34gl2M+rnvpXhs=
app_1  | iv=377YrtFzBASSjvol
app_1  | auth_tag=QqQcyOhGKPP0sPFOANToFw==
app_1  | encrypted_data=LtbIG7tnBVtbMUlnvG+qtmoK2NgL+18=
app_1  | --------------- /encrypt -------------
app_1  | --------------- decrypt -------------
app_1  | encrypted_data=LtbIG7tnBVtbMUlnvG+qtmoK2NgL+18=
app_1  | iv=377YrtFzBASSjvol
app_1  | auth_tag=QqQcyOhGKPP0sPFOANToFw==
app_1  | #<OpenSSL::Cipher:0x0000558adf4ae830>
app_1  | secret=eKAyTQoTH0apq8+fsQYHSYelUy3mL34gl2M+rnvpXhs=
app_1  | decrypted_data=This is some debug text
app_1  | --------------- /decrypt -------------

但是在节点上运行这些值会给我同样的错误:

const debug = decrypt(
  'eKAyTQoTH0apq8+fsQYHSYelUy3mL34gl2M+rnvpXhs=',
  'LtbIG7tnBVtbMUlnvG+qtmoK2NgL+18=',
  '377YrtFzBASSjvol',
  'qQcyOhGKPP0sPFOANToFw=='
);
Error: Unsupported state or unable to authenticate data
    at Decipheriv.final (node:internal/crypto/cipher:193:29)
托帕科

NodeJS 代码返回与 Ruby 代码相同的结果,如果

  • 不使用密钥派生 (SHA256)
  • 对于密文,应用 Base64 编码而不是十六进制编码

完整的 NodeJS 代码:

var crypto = require('crypto');

const algorithm = 'aes-256-gcm';

function encrypt(secret, originalData) {
  console.log('encrypting with ', {
    secret,
    originalData
  });
  const iv = Buffer.from('377YrtFzBASSjvol', 'base64'); // crypto.randomBytes(16); // disabled for testing (in the final solution a random nonce MUST be applied for security reasons)
  var crypt = crypto.createCipheriv(algorithm, Buffer.from(secret, 'base64'), iv);

  var encoded = crypt.update(originalData, 'utf8', 'base64'); // Fix: Base64 encoding
  encoded += crypt.final('base64'); // Fix: Base64 encoding
  const at = crypt.getAuthTag();
  return { encrypted: encoded, iv: iv.toString('base64'), at: at.toString('base64') };
};
function decrypt(secret, encryptedData, iv, at) {
  console.log('decrypting with ', {
    secret,
    encryptedData,
    iv,
    at
  });
  var crypt = crypto.createDecipheriv(algorithm, Buffer.from(secret, 'base64'), Buffer.from(iv, 'base64'));
  crypt.setAuthTag(Buffer.from(at, 'base64'));
  var decoded = crypt.update(encryptedData, 'base64', 'utf8'); // Fix: Base64 encoding 
  decoded += crypt.final('utf8');
  return decoded;
};

var originalData = 'This is some debug text';
var secret = 'eKAyTQoTH0apq8+fsQYHSYelUy3mL34gl2M+rnvpXhs='; // Fix: No key derivation
const encryptionResult = encrypt(
  secret,
  originalData,
);

console.log(encryptionResult);
var secret = 'eKAyTQoTH0apq8+fsQYHSYelUy3mL34gl2M+rnvpXhs='; // Fix: No key derivation
var encryptedData = 'LtbIG7tnBVtbMUlnvG+qtmoK2NgL+18=';
var iv = '377YrtFzBASSjvol';
var at = 'QqQcyOhGKPP0sPFOANToFw==';
const decryptionResult = decrypt(
  secret,
  encryptedData,
  iv,
  at
);
console.log(decryptionResult);

输出:

encrypting with  {
  secret: 'eKAyTQoTH0apq8+fsQYHSYelUy3mL34gl2M+rnvpXhs=',
  originalData: 'This is some debug text'
}
{
  encrypted: 'LtbIG7tnBVtbMUlnvG+qtmoK2NgL+18=',
  iv: '377YrtFzBASSjvol',
  at: 'QqQcyOhGKPP0sPFOANToFw=='
}
decrypting with  {
  secret: 'eKAyTQoTH0apq8+fsQYHSYelUy3mL34gl2M+rnvpXhs=',
  encryptedData: 'LtbIG7tnBVtbMUlnvG+qtmoK2NgL+18=',
  iv: '377YrtFzBASSjvol',
  at: 'QqQcyOhGKPP0sPFOANToFw=='
}
This is some debug text

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章