You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

求对应Java AES-256-GCM的Ruby加解密实现及错误修复

问题:Java AES-256-GCM加密对应的Ruby加解密实现修正

我有一段使用AES-256-GCM加密消息的Java代码,需要编写对应的Ruby加解密代码。尝试写了Ruby解密代码,但出现(irb):99:in final': OpenSSL::Cipher::CipherError错误,求正确的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 =  new 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解密代码

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代码有两个关键错误:

  1. 密钥生成错误:通过PBKDF2生成的spec已经是正确的AES密钥,但你用random_key重新生成了随机密钥,覆盖了正确密钥,导致解密时密钥不匹配。
  2. AES-GCM模式处理:Java中cipher.doFinal()返回的字节数组包含密文+认证标签,Ruby的OpenSSL::Cipher在GCM模式下会自动从输入中提取认证标签进行验证,无需额外处理。

正确的Ruby解密代码

require 'base64'
require 'openssl'
require 'stringio'

cipher_message = "替换为Java生成的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

# 直接使用PBKDF2生成的结果作为AES密钥
secret_key = OpenSSL::KDF.pbkdf2_hmac(password, 
                                      salt: salt, 
                                      iterations: 65536, 
                                      length: 32, 
                                      hash: 'sha256')

cipher = OpenSSL::Cipher.new('AES-256-GCM')
cipher.decrypt
cipher.iv = iv
cipher.key = secret_key

# GCM模式下自动验证标签
decrypted_message = cipher.update(encrypted_byte) + cipher.final
puts decrypted_message.force_encoding('UTF-8')

对应的Ruby加密代码

如果需要Ruby端生成兼容Java的加密串,可使用以下代码:

require 'base64'
require 'openssl'

plain_message = "Hello Ruby"
password = "password"

# 生成16字节salt
salt = OpenSSL::Random.random_bytes(16)
# 生成12字节IV(GCM推荐长度)
iv = OpenSSL::Random.random_bytes(12)

# 生成AES密钥
secret_key = OpenSSL::KDF.pbkdf2_hmac(password, 
                                      salt: salt, 
                                      iterations: 65536, 
                                      length: 32, 
                                      hash: 'sha256')

cipher = OpenSSL::Cipher.new('AES-256-GCM')
cipher.encrypt
cipher.iv = iv
cipher.key = secret_key

# 加密并获取认证标签
encrypted_data = cipher.update(plain_message) + cipher.final
tag = cipher.auth_tag

# 按Java格式拼接:IV + Salt + 密文 + 标签
cipher_byte = iv + salt + encrypted_data + tag
encrypted_message_base64 = Base64.strict_encode64(cipher_byte)

puts encrypted_message_base64

内容的提问来源于stack exchange,提问作者Nalinda Dulwala

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 17:29:58