我有一个 Java 代码,它使用 AES-256-GCM 加密消息。我想用 Ruby 编写一段代码,执行相同的加密和解密操作。
Java代码:
import java.util.Base64;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.SecretKeyFactory;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import javax.crypto.Cipher;
import javax.crypto.spec.GCMParameterSpec;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.security.spec.KeySpec;
import java.security.SecureRandom;
// ----------------------
String plainMessage = "Hello Java";
String password = "password";
byte[] salt = new byte[16];
new SecureRandom().nextBytes(salt);
KeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 65536, 256);
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
SecretKey secretKey = SecretKeySpec(factory.generateSecret(spec).getEncoded(), "AES");
byte[] iv = new byte[12];
new SecureRandom().nextBytes(iv);
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, secretKey, new GCMParameterSpec(128, iv));
byte[] encryptedMessageByte = cipher.doFinal(plainMessage.getBytes(StandardCharsets.UTF_8));
byte[] cipherByte = ByteBuffer.allocate(iv.length + salt.length + encryptedMessageByte.length)
.put(iv)
.put(salt)
.put(encryptedMessageByte)
.array();
String encryptedMessageBase64 = Base64.getEncoder().encodeToString(cipherByte);
请帮我用 Ruby 编写相同的实现(加密和解密)
我尝试了下面的 Ruby 解密代码。这给出了错误,
(irb):99:in
最终':OpenSSL::Cipher::CipherError`
Ruby代码解密:
require 'base64'
require 'openssl'
cipher_message = "encrypted-message-base64"
password = "password"
decoded_cipher_byte = Base64.decode64(cipher_message)
byte_buffer = StringIO.new(decoded_cipher_byte)
iv = byte_buffer.read(12)
salt = byte_buffer.read(16)
encrypted_byte = byte_buffer.read
spec = OpenSSL::KDF.pbkdf2_hmac(password, salt: salt, iterations: 65536, length: 32, hash: 'sha256')
secret_key = OpenSSL::Cipher.new('AES').tap { |c| c.key = spec }.random_key
cipher = OpenSSL::Cipher.new('AES-GCM')
cipher.decrypt
cipher.iv = iv
cipher.key = secret_key
decrypted_message_byte = cipher.update(encrypted_byte) + cipher.final
msg = decrypted_message_byte.force_encoding('UTF-8')
正如评论中所解释的,Ruby 代码使用了错误的密钥并且缺少标签的分离(与 Java 不同,Ruby/OpenSSL 分别处理标签和密文):
require 'base64'
require 'openssl'
require 'stringio'
cipher_message = "ngii4DerThzOaBfFt93zNgxY4kJVC7V+m92buC++OQC+OcKHA/FP1u0mwTIAOlexDJM/+xA0LRarIfXE7Qr7IkEfy/V8m37qqR3twrtKghexlV0oNOab" # generated with the Java code
password = "password"
decoded_cipher_byte = Base64.decode64(cipher_message)
# separate IV, salt, ciphertext and tag
byte_buffer = StringIO.new(decoded_cipher_byte)
ivSize = 12
saltSize = 16
tagSize = 16
ciphertextSize = decoded_cipher_byte.length - ivSize - saltSize - tagSize
iv = byte_buffer.read(ivSize)
salt = byte_buffer.read(saltSize)
encrypted_byte = byte_buffer.read(ciphertextSize)
tag = byte_buffer.read
# derive key
secret_key = OpenSSL::KDF.pbkdf2_hmac(password, salt: salt, iterations: 65536, length: 32, hash: 'sha256')
# decrypt with AES-256, GCM
cipher = OpenSSL::Cipher::AES.new(256, :GCM).decrypt
cipher.iv = iv
cipher.key = secret_key # Fix 1: apply derived key
cipher.auth_tag = tag # Fix 2: apply detached tag
#cipher.auth_data = auth_data # no AAD
decrypted_message_byte = cipher.update(encrypted_byte) + cipher.final
puts decrypted_message_byte.force_encoding('UTF-8') # The quick brown fox jumps over the lazy dog
在这里您可以从 Ruby/OpenSSL 文档中找到使用 AES/GCM 进行身份验证加密的示例。