如何在Ruby中加密数据,与SecretKeySpec相同?

3
我将使用AES算法,使用Ruby中的Cipher对字符串进行加密。我有一个在Java中编写的示例,并参考此示例编写了Java代码,但无法获得与Java相同的输出。
以下是Java代码:
import java.util.Base64;
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.security.Key;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.X509Certificate;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import java.util.Arrays;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

public class HelloWorld {
    public static final String PHONENUMBER_PARAM = "phoneNumber";
    public static final String PIN_PARAM ="pin";

    public static final String MERCHANTID_PARAM = "merchantId";

    public static void main(String args[]) throws Exception {

    String phoneNumber ="+917738995286";
    String pin ="5577";

    String merchantId ="527425858";
    String encodedKey ="vPDkdTDrcygLPROzd1829A==";

    String payLoad = PHONENUMBER_PARAM + "=" +         URLEncoder.encode(phoneNumber, "UTF-8")+ "&" + PIN_PARAM + "=" + URLEncoder.encode(pin, "UTF-8") ;

    byte[] decodedKey = Base64.getDecoder().decode(encodedKey.getBytes());

    Key encryptionKey = new SecretKeySpec(decodedKey, "AES");

    byte[] utf8Bytes = payLoad.getBytes("utf-8");

    byte[] encryptedBody = encrypt(encryptionKey, utf8Bytes);
    String encryptedData = new  String(Base64.getEncoder().encode(encryptedBody));

    System.out.println("encryptedData:" + encryptedData);
 }
private static byte[] encrypt(Key encryptionKey, byte[] data) throws Exception {
    Cipher c = Cipher.getInstance("AES");
    c.init(1, encryptionKey);
    return c.doFinal(data);
}
}

这段代码的输出结果是

encryptedData:lE40HlECbxU/mWRivF/+Szm3PprMoLW+Y7x911GczunakbG8l+A2JVEEP8gTw6xy

我尝试用Ruby编写相同的代码。Ruby代码如下:

payLoad = "phoneNumber=%2B917738995286&pin=5577"

encodedKey = "vPDkdTDrcygLPROzd1829A=="

decodedKey = Base64.decode64(encodedKey)

dKey = decodedKey.each_byte.map { |b| b.to_s(16) }.join

cipher = OpenSSL::Cipher.new('aes128').encrypt
encryptionKey  = cipher.update(dKey) 
encryptionKey<< cipher.final


utf8Bytes = payLoad.bytes
uKey = utf8Bytes.map { |b| b.to_s(16) }.join

scipher = OpenSSL::Cipher.new('aes128').encrypt
scipher.key = encryptionKey  
encryptedBody = scipher.update(uKey) 
encryptedBody<< scipher.final

encryptedData  = Base64.encode64(encryptedBody)

以下是代码的输出结果:

CqFmCKJ004PsoXi2tDCTBmx7/iTHVyDsFH9y8NWNrEP3k3bOQp7h8uyl/a7Z\nYi9ZmcXSspo6FCyCo6fJIwPohg==\n

不知道哪里出了问题。我已经工作了两天,但是没有任何答案。如果有帮助会很棒。先谢谢了。


请确保不要发布您的生产密钥,这可能存在安全风险(如果您已经发布,请立即更改)。 - Vasfed
我会使用 puts some_value 来调试每一步并找出差异。 - joewoodward
1
一般建议:始终使用完全限定的加密字符串。 Cipher.getInstance("AES"); 可能会根据默认安全提供程序而产生不同的加密方式。它很可能会产生 "AES/ECB/PKCS5Padding",但不一定如此。如果发生更改,则会失去不同 JVM 之间的兼容性。 - Artjom B.
1
永远不要使用ECB模式。它是确定性的,因此不是语义安全的。你应该至少使用像CBCCTR这样的随机模式。最好对你的密文进行身份验证,以使像填充预言攻击这样的攻击变得不可能。这可以通过使用GCM或EAX这样的身份验证模式,或者使用加密-然后-MAC方案来完成。 - Artjom B.
1个回答

2
以下版本输出与您的Java代码相同的结果:
# crypt.rb
require 'base64'
require 'openssl'

payLoad = "phoneNumber=%2B917738995286&pin=5577"
encodedKey = "vPDkdTDrcygLPROzd1829A=="

decodedKey = Base64.decode64(encodedKey)

scipher = OpenSSL::Cipher.new('aes-128-ecb').encrypt
scipher.key = decodedKey
encryptedBody = scipher.update(payLoad)
encryptedBody << scipher.final

encryptedData  = Base64.encode64(encryptedBody)
puts encryptedData


$ ruby crypt.rb
# => lE40HlECbxU/mWRivF/+Szm3PprMoLW+Y7x911GczunakbG8l+A2JVEEP8gT
#    w6xy

与您的 Ruby 脚本版本相比,有一些值得注意的差异:

  • 必须指定密码模式。问题在于 Java 默认使用 ECB 模式,而 Ruby 默认使用 CBC 模式。顺便说一下,ECB 模式今天被认为不够安全,您实际上不应该使用它

  • 在 Ruby 中,您尝试重新加密已解码为 Base64 的加密密钥版本,但在 Java 版本中不需要这样做。

  • 在 Ruby 中,无需进行字符串到字节的转换,您可以立即对字符串进行加密。

因此,虽然两个脚本现在输出相同的加密数据,但我强烈建议更改 AES 运行模式,以确保安全。


嗨@Yogesh,如果这个或任何答案解决了你的问题,请考虑通过点击复选框接受它。这向更广泛的社区表明你已经找到了解决方案,并为回答者和你自己赢得了一些声誉。没有义务这样做。 - Matouš Borák
嗨@BoraMa,我已经接受并投票支持了。可能是因为声望点数较少,所以没有显示出来。 - Yogesh Gupta
啊哈,我认为你应该能够接受答案,而不管声望(不是点赞,在声望达到15时启用)。无妨,Yogesh,很高兴它对你有用。 - Matouš Borák

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接