简体   繁体   English

Java到ruby的AES / ECB / PKCS5Padding加密

[英]Java to ruby AES/ECB/PKCS5Padding encryption

I have an online eCommerce websites that uses a third party payment portal. 我有一个使用第三方付款门户的在线电子商务网站。 The payment portal was working fine until the third party payment portal have asked everyone to start using a hash key with other payment parameters. 在第三方支付门户要求所有人开始使用具有其他支付参数的哈希键之前,支付门户一直运行良好。

Now the problem is that the third party payment portal have only provided one page documentation for implementing the hash key. 现在的问题是,第三方支付门户网站仅提供了一页文档来实现哈希密钥。

This is the documentation provided:- 这是提供的文档:

Encryption Algorithm 加密演算法

In order to mitigate parameter tempering/modification while transfer and posting of data, merchant can encrypt the request using the hash key provided by Telenor POC. 为了减轻数据传输和发布过程中的参数调整/修改,商家可以使用Telenor POC提供的哈希密钥对请求进行加密。 This encrypted request is sent along with the main request, which then reconciled at OPS end to detect if parameter is changed or not. 该加密的请求与主请求一起发送,然后在OPS端进行协调以检测参数是否已更改。 The encryption can be done using following algorithm: 可以使用以下算法进行加密:

  1. Create map of all the fields that are part of the request Map fields = new HashMap(); 创建属于请求的所有字段的映射。Map fields = new HashMap();

    fields.put("amount", "10"); fields.put(“ amount”,“ 10”);

    fields.put("storeId", "28"); fields.put(“ storeId”,“ 28”);

    fields.put("orderRefNum", "11001"); fields.put(“ orderRefNum”,“ 11001”);

    fields.put("expiryDate", "20150101 151515"); fields.put(“ expiryDate”,“ 20150101 151515”);

    fields.put("postBackURL", " http://localhost:9081/local/status.php "); fields.put(“ postBackURL”,“ http:// localhost:9081 / local / status.php ”);

  2. Get the list of field name from the map created in the first step 从第一步中创建的地图中获取字段名称列表

    List fieldNames = new ArrayList(fields.keySet()); 列出fieldNames = new ArrayList(fields.keySet());

  3. Sort the map fields based on map key in alphabetical order 根据地图关键字按字母顺序对地图字段进行排序

    Collections.sort(fieldNames); Collections.sort(fieldNames);

  4. Create a string in following format: amount=10&expiryDate=20150101151515&orderRefNum=11001&postBackURL= http://localhost:9081/local/status.php&storeId=28 创建以下格式的字符串:amount = 10&expiryDate = 20150101151515&orderRefNum = 11001&postBackURL = http:// localhost:9081 / local / status.php&storeId = 28

  5. Use AES/ECB/PKCS5Padding algorithm to encrypt with the key and string produced in the previous step 使用AES / ECB / PKCS5Padding算法使用上一步中生成的密钥和字符串进行加密

    Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); 密码cipher = Cipher.getInstance(“ AES / ECB / PKCS5Padding”);

    SecretKeySpec secretKey = new SecretKeySpec(key.getBytes(), "AES"); SecretKeySpec secretKey =新的SecretKeySpec(key.getBytes(),“ AES”);

    cipher.init(Cipher.ENCRYPT_MODE, secretKey); cipher.init(Cipher.ENCRYPT_MODE,secretKey);

    encryptedValue = new String(Base64.encodeBase64(cipher.doFinal(value.getBytes()))); 加密的值=新的String(Base64.encodeBase64(cipher.doFinal(value.getBytes())));

Now another problem is that I do not have any experience in Java. 现在另一个问题是我没有Java经验。

I called the third party payment portal helpline and they were only helpful enough to tell me the key. 我打了第三方支付门户服务热线,他们仅对告诉我钥匙有帮助。

If anyone can be helpful enough to tell me what would be the Ruby equivalent of step 5 I will be grateful. 如果有人可以帮助我告诉我第5步的Ruby等效标准,我将不胜感激。 Thanks 谢谢

Just tried the provided code on online java compiler:- 刚刚尝试了在线Java编译器上提供的代码:-

import java.security.Key;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;

public class encryptData {
  public static void main(String[] args) {

    String data="amount=10&expiryDate=20150101 151515&orderRefNum=11001&postBackURL=http://localhost:9081/local/status.php&storeId=28";
    String key="89OUITUPRL3I8H3G";

    Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
    SecretKeySpec secretKey = new SecretKeySpec(key.getBytes(), "AES");
    cipher.init(Cipher.ENCRYPT_MODE, secretKey);
    encryptedValue = new String(Base64.encodeBase64(cipher.doFinal(data.getBytes())));
  }
}

This is the error:- 这是错误:

/tmp/java_Ramvov/encryptData.java:16: error: cannot find symbol /tmp/java_Ramvov/encryptData.java:16:错误:找不到符号

encryptedValue = new String(Base64.encodeBase64(cipher.doFinal(data.getBytes())));
^

symbol: variable encryptedValue 符号:变量encryptedValue

location: class encryptData 位置:类cryptoData

/tmp/java_Ramvov/encryptData.java:16: error: cannot find symbol /tmp/java_Ramvov/encryptData.java:16:错误:找不到符号

encryptedValue = new String(Base64.encodeBase64(cipher.doFinal(data.getBytes())));
                                  ^

symbol: method encodeBase64(byte[]) 符号:方法encodeBase64(byte [])

location: class Base64 位置:Base64类

2 errors 2个错误

Any help will be appreciated 任何帮助将不胜感激

I have also tried to reproduce this java code in ruby:- 我也尝试在ruby中重现此Java代码:-

data = "amount=10&expiryDate=20150101151515&orderRefNum=11001&postBackURL=http://localhost:9081/local/status.php&storeId=28"                                                                                                                                            
cipher = OpenSSL::Cipher.new("AES-128-ECB")
cipher.encrypt()
cipher.key = "89OUITUPRL4I9H3G"
crypt = cipher.update(data) + cipher.final()
crypt_string = (Base64.encode64(crypt))

But the generated encryption is rejected by the payment portal 但是生成的加密被付款门户拒绝

Using ECB mode for tamper-proofing input is very stupid. 使用ECB模式进行防篡改输入非常愚蠢。

Having said that, and knowing it's not your fault, because it was not your idea in the first place, and that you just want the code to work, let's ask an independent party to give us a reference point: 话虽如此,并知道这不是您的错,因为这首先不是您的想法,并且您只是想让代码正常工作,所以让我们请一个独立的机构为我们提供参考点:

echo -n "amount=10&expiryDate=20150101151515&orderRefNum=11001&postBackURL=http://localhost:9081/local/status.php&storeId=28" | openssl enc -K 38394f5549545550524c334938483347 -aes-128-ecb -base64

Note that openssl takes the key as hexadecimal string, so 89OUITUPRL4I9H3G should be written as its ASCII sequence 38394f5549545550524c334938483347 请注意,openssl将密钥作为十六进制字符串,因此89OUITUPRL4I9H3G写入为其ASCII序列38394f5549545550524c334938483347

The output is: 输出为:

r7N11xE4HdbJyTByiTDifI1vifvZyNcNfKF+Jo7jEq4rN7c3EiOJxdWOUlCtVXeH
FBTdPSROSmTkUTWfAuOQnHWqe/q/Msd1ykUDIz9eP5L6X6RI0R5UtUXmaakr4klz
1kxEJOjR/WJ5xgd2clBh4iLcYi3caDrCkbD0kRDLQE4=

Let's try to replicate that in Java. 让我们尝试用Java复制它。 To do this, we have to change a few things in your code: 为此,我们必须在您的代码中进行一些更改:

  1. Your expiryDate is 20150101 151515 in the Java code, but 20150101151515 everywhere else. 您的expiryDate在Java代码中为20150101 151515 ,在其他地方为20150101151515 So let's standardize on 20150101151515 因此,让我们在20150101151515上进行20150101151515
  2. Base64.encodeBase64() does not exist. Base64.encodeBase64()不存在。 Java 8 has Base64 encoding built-in, and the code should be Base64.getEncoder().encodeToString(data) Java 8内置了Base64编码,代码应为Base64.getEncoder().encodeToString(data)
  3. The return type of that is already string so encryptedValue = new String(Base64...) is unnecessary. 该类型的返回类型已经是字符串,因此不需要encryptedValue = new String(Base64...)
  4. Furthermore, you need to declare the type of encryptedValue before you can use it. 此外,你需要声明的类型encryptedValue ,然后才能使用它。

With all that, this compiles in Java 8: 有了这些,就可以在Java 8中进行编译:

import java.security.Key;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;

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

    String data="amount=10&expiryDate=20150101151515&orderRefNum=11001&postBackURL=http://localhost:9081/local/status.php&storeId=28";
    String key="89OUITUPRL3I8H3G";

    Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
    SecretKeySpec secretKey = new SecretKeySpec(key.getBytes(), "AES");
    cipher.init(Cipher.ENCRYPT_MODE, secretKey);

    byte[] plaintext = data.getBytes();
    byte[] ciphertext = cipher.doFinal(plaintext);
    String encryptedValue = Base64.getEncoder().encodeToString(ciphertext);

    System.out.println(encryptedValue);
  }
}

and prints (linebreaks added by me): 和打印(我添加的换行符):

r7N11xE4HdbJyTByiTDifI1vifvZyNcNfKF+Jo7jEq4rN7c3EiOJxdWOUlCtVXeH
FBTdPSROSmTkUTWfAuOQnHWqe/q/Msd1ykUDIz9eP5L6X6RI0R5UtUXmaakr4klz
1kxEJOjR/WJ5xgd2clBh4iLcYi3caDrCkbD0kRDLQE4=

Ok so far. 好的,到目前为止。 What about ruby? 红宝石呢?

#!/usr/bin/ruby

require 'openssl'
require 'base64'

data = "amount=10&expiryDate=20150101151515&orderRefNum=11001&postBackURL=http://localhost:9081/local/status.php&storeId=28"

key = "89OUITUPRL4I9H3G"
cipher = OpenSSL::Cipher.new("AES-128-ECB")
cipher.encrypt()
cipher.key = key
crypt = cipher.update(data) + cipher.final

crypt_string = (Base64.encode64(crypt))
puts crypt_string

This prints: 打印:

mp8WVhyUHFDqvJKaRXbYKbZT1920TNboRpFLUdPaYsWTkiQ2fhN/tCL6wvtI
B9/Mu08McaKTVIWYeQAfVR5XcUKdeQ+CBcJJRs5krLBjtjiMNlBUq9JpCUaC
0eclfDMaGTE+Z4XSafjPictWzTG/Ye+vkJWC23yxW1zSjBnYBfg=

Why is the ruby code not working? 为什么红宝石代码不起作用? Well i suspect that ruby wants the key in the same way as openssl, because ruby crypto usually uses openssl under the hood. 好吧,我怀疑ruby与openssl一样需要密钥,因为ruby加密通常在幕后使用openssl。 So change the key definition to 因此将键定义更改为

key = "38394f5549545550524c334938483347"
key = [key].pack('H*')

This now prints: 现在打印:

r7N11xE4HdbJyTByiTDifI1vifvZyNcNfKF+Jo7jEq4rN7c3EiOJxdWOUlCt
VXeHFBTdPSROSmTkUTWfAuOQnHWqe/q/Msd1ykUDIz9eP5L6X6RI0R5UtUXm
aakr4klz1kxEJOjR/WJ5xgd2clBh4iLcYi3caDrCkbD0kRDLQE4=

which apart from linebreak positions is identical to the output of the other two. 除了换行符位置,它与其他两个输出相同。 Hope you'll be able to get communication with the other side right, and remember: 希望您能够与另一边进行交流,并记住:

Using ECB mode for tamper-proofing input is very stupid. 使用ECB模式进行防篡改输入非常愚蠢。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM