从节点脚本读取Ruby主动记录加密

发布于 2025-02-07 09:07:36 字数 5548 浏览 0 评论 0原文

我正在将Ruby放置在Rails Application到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中的值时,传达消息“未支撑状态或无法验证数据”,

我使用的来自数据库的数据的数据是从数据库中导出的,并且通过在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)

I'm porting a ruby on rails application to node.js, and need to be able to read the data encrypted via active record encryption.

It seems that the algorithm used is aes-256-gcm, and I've got a node.js script here to encrypt and decrypt data:

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')
);

This works fine and I get the result:

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'
}

However when I try and use the values from ruby it throws the message "Unsupported state or unable to authenticate data"

The data from rails I'm using is exported from the database, with the key exported via running the following in ruby:

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

And from looking through the ruby source code this appears to be how it encrypts/decrypts the data:

  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 -------------"

Which gives the values:

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 -------------

However running these values on node gives me the same error:

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)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

じ违心 2025-02-14 09:07:36

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

  • 不使用键推导(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

The NodeJS code returns the same results as the Ruby code if

  • no key derivation (SHA256) is used
  • for the ciphertext a Base64 encoding is applied instead of a hex encoding

Full NodeJS code:

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);

Output:

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
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文