Кроссплатформенный AES 256 GCM Javascript и Elixir

#javascript #react-native #cryptography #erlang #elixir

#javascript #react-native #криптография #erlang #elixir

Вопрос:

Я пытался зашифровать в Javascript и расшифровать в Elixir, используя AES 256 с помощью GCM. Я позаимствовал несколько примеров здесь и там и придумал следующее.

Шифрование в Javascript

 const _crypto = require('crypto');

function encrypt(message, secret) {
  // random initialization vector
  const iv = _crypto.randomBytes(16);

  // extract the auth tag
  const cipher = _crypto.createCipheriv('aes-256-gcm', secret, iv);

  // encrypt the given text
  const encrypted = Buffer.concat([cipher.update(message, 'utf8'), cipher.final()]);

  // extract the auth tag
  const tag = cipher.getAuthTag();

  const encrypted_message = Buffer.concat([iv, tag, encrypted]).toString('base64');
  return encrypted_message;
}

const secret = _crypto.randomBytes(32);
encrypt("secret message", secret);
  

Расшифровка в Elixir

 def decrypt(encrypted_message, secret) do
  secret_key = :base64.decode(secret)
  ciphertext = :base64.decode(encrypted_message)

  <<iv::binary-16, tag::binary-16, ciphertext::binary>> = ciphertext
  :crypto.block_decrypt(:aes_gcm, secret_key, iv, {"AES256GCM", ciphertext, tag})
end

# secret would be the secret from javascript encoded in base64
decrypt(encrypted_message, secret)
  

И мой результат на стороне Elixir всегда был :error
У меня такое чувство, что это как-то связано с кодированием и декодированием, но я не могу понять, где и что пошло не так.

Если кто-то может указать мне правильное направление, я был бы очень признателен.

Спасибо!

ОБНОВЛЕННАЯ рабочая версия

для тех, кто намерен использовать те же языки:

Шифрование Javascript

 const _crypto = require('crypto');

function encrypt(message, secret) {
  // random initialization vector
  const iv = _crypto.randomBytes(16);

  // extract the auth tag
  const cipher = _crypto.createCipheriv('aes-256-gcm', secret, iv);

  // add the following line if you want to include "AES256GCM" on the elixir side
  // cipher.setAAD(Buffer.from("AES256GCM", 'utf8'));

  // encrypt the given text
  const encrypted = Buffer.concat([cipher.update(message, 'utf8'), cipher.final()]);

  // extract the auth tag
  const tag = cipher.getAuthTag();

  const encrypted_message = Buffer.concat([iv, tag, encrypted]).toString('base64');
  return encrypted_message;
}

const secret = _crypto.randomBytes(32);
encrypt("secret message", secret);
  

Расшифровка Elixir

 def decrypt(encrypted_message, secret) do
  secret_key = :base64.decode(secret)
  ciphertext = :base64.decode(encrypted_message)

  <<iv::binary-16, tag::binary-16, ciphertext::binary>> = ciphertext

  // make sure _AAD is an empty string "" if you didn't set it during encryption
  :crypto.block_decrypt(:aes_gcm, secret_key, iv, {_AAD, ciphertext, tag})

  // otherwise, you would need to set _AAD to whatever you set during encryption, using "AES256GCM" as example
  // Note: AAD (Associated Authenticated Data) can be whatever string you want to my knowledge, just to make sure you have the same in both encryption and decryption process
  // :crypto.block_decrypt(:aes_gcm, secret_key, iv, {"AES256GCM", ciphertext, tag})
end

# secret would be the secret from javascript encoded in base64
decrypt(encrypted_message, secret)
  

Ответ №1:

Это очень просто: ваш "AES256GCM" не должен присутствовать (или null, я не настолько знаком с Erlang). Он представляет дополнительные аутентифицированные данные и включается в вычисление тега аутентификации, что, очевидно, отличает его от тега аутентификации, сгенерированного кодом шифрования.

:aes_gcm режим уже указан, а размер ключа, конечно, определяется размером secret_key , поэтому строка в любом случае была бы совершенно ненужной.

Комментарии:

1. Это работает! когда я установил «AES256GCM» в «», и он успешно декодировался. Я думал, что это было там, чтобы соответствовать aes-256-gcm на стороне javascript. Большое спасибо!!