简体   繁体   中英

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. This encrypted request is sent along with the main request, which then reconciled at OPS end to detect if parameter is changed or not. 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();

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

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

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

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

    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());

  3. Sort the map fields based on map key in alphabetical order

    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

  5. Use AES/ECB/PKCS5Padding algorithm to encrypt with the key and string produced in the previous step

    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(value.getBytes())));

Now another problem is that I do not have any experience in 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. Thanks

Just tried the provided code on online java compiler:-

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

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

symbol: variable encryptedValue

location: class encryptData

/tmp/java_Ramvov/encryptData.java:16: error: cannot find symbol

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

symbol: method encodeBase64(byte[])

location: class Base64

2 errors

Any help will be appreciated

I have also tried to reproduce this java code in ruby:-

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.

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

The output is:

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

Let's try to replicate that in 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. So let's standardize on 20150101151515
  2. Base64.encodeBase64() does not exist. Java 8 has Base64 encoding built-in, and the code should be Base64.getEncoder().encodeToString(data)
  3. The return type of that is already string so encryptedValue = new String(Base64...) is unnecessary.
  4. Furthermore, you need to declare the type of encryptedValue before you can use it.

With all that, this compiles in 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. 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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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