Convert Encrypt Code in Java to Ruby

Convert Encrypt code in java to Ruby

Encrypt Code:

def aes(key,string)
cipher = OpenSSL::Cipher::Cipher.new("aes-128-cbc")
cipher.encrypt
cipher.padding = 1
cipher.key = hex_to_bin(Digest::SHA1.hexdigest('secret_key')[0..32])
cipher_text = cipher.update(string)
cipher_text << cipher.final
return bin_to_hex(cipher_text).upcase
end

Decrypt Code:

def aes_decrypt(key, encrypted)
encrypted = hex_to_bin(encrypted.downcase)
cipher = OpenSSL::Cipher::Cipher.new("aes-128-cbc")
cipher.decrypt
cipher.padding = 1
cipher.key = hex_to_bin(Digest::SHA1.hexdigest('secret_key')[0..32])
d = cipher.update(encrypted)
d << cipher.final
end

hex_to_bin and bin_to_hex

def hex_to_bin(str)
[str].pack "H*"
end

def bin_to_hex(s)
s.unpack('C*').map{ |b| "%02X" % b }.join('')
end

In My case, The java code was using default initialization vector, So I did not set any iv, Also, there was hex_to_bin was a missing piece there. So after that, all started working properly.

I hope it helps someone if they come across this issue.

Java encryption / decryption to Ruby

You need to use the IV and Key from your Java Example, not a new/random IV/Key:

require "openssl"
require "base64"
require 'byebug'

include Base64

plain_text = "abceeffslaj"

key = 'Bar12345Bar12345'
iv = 'RandomInitVector'

cipher = OpenSSL::Cipher::AES128.new(:CBC)
cipher.encrypt
cipher.key = key
cipher.iv = iv
cipher_text = cipher.update(plain_text) + cipher.final

cipher = OpenSSL::Cipher::AES128.new(:CBC)
cipher.decrypt
cipher.key = key
cipher.iv = iv
decrypted_plain_text = cipher.update(cipher_text) + cipher.final

puts "AES128 in CBC mode"
puts "Key: " + urlsafe_encode64(key)
puts "Iv: " + urlsafe_encode64(iv)
puts "Plain text: " + plain_text
puts "Cipher text: " + urlsafe_encode64(cipher_text)
puts "Decrypted plain text: " + decrypted_plain_text

Encrypt Ruby decrypt Java

The exact code that is produced by Ruby is not specified (which I would consider a bug), you can find the format by reading the source code, especially this part:

    blob = "#{::Base64.strict_encode64 encrypted_data}--#{::Base64.strict_encode64 iv}"
blob = "#{blob}--#{::Base64.strict_encode64 cipher.auth_tag}" if aead_mode?

Where the IV is a random IV, generated using Cipher::new of the openssl module.

Migrate String encryption from Ruby to Java

The first thing to do is to derive the IV and key from the given password.

From the link above, you will get an encoded IV and KEY that corresponds with "VDiJjncs4ak=" and "s9e42J3PpmQv8n5T8L3zzuFaGdrzK/wU" respectively.
This means that the key and IV vector used in the Java code are wrong as it was said in the comments.

Below is the resulting Java code:

public class SymmetricDESedeCipher {
private static final String DATA = "Whackabad";
private static final String ALGORITHM = "DESede";
private static final String XFORM = "DESede/CBC/PKCS5Padding";
private static final String KEY = "s9e42J3PpmQv8n5T8L3zzuFaGdrzK/wU";
private static final String IV = "VDiJjncs4ak=";

private static byte[] encrypt(String data,
SecretKey key, String XFORM, byte[] iv) throws Exception {
Cipher cipher = Cipher.getInstance(XFORM);
cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(iv));
return cipher.doFinal(data.getBytes());
}

public static void main(String[] unused) throws Exception {
DESedeKeySpec spec = new DESedeKeySpec(new BASE64Decoder().decodeBuffer(KEY));
SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance(ALGORITHM);
SecretKey secretKey = secretKeyFactory.generateSecret(spec);

byte[] encBytes = encrypt(DATA, secretKey, XFORM, new BASE64Decoder().decodeBuffer(IV));

System.out.println("Data: " + DATA);
System.out.println("Encrypted Data: " + new BASE64Encoder().encode(encBytes));
}
}

Output:

Data: Whackabad
Encrypted Data: AEsDXVcgh2jsTjlDgh+REg==

How to Encrypt data in ruby same as SecretKeySpec?

The following version outputs the same result as your Java code:

# 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

Notable differences from your ruby script version:

  • You must specify the cipher mode. The problem is that Java defaults to the ECB mode whereas ruby defaults to the CBC mode. By the way, the ECB mode is considered less-secure today and you actually should not use it.

  • In ruby, you tried to re-encrypt the Base64-decoded version of your encryption key, which is something you don't do in the Java version.

  • In ruby, there is no need to do the string to byte conversions, you can encrypt strings right away.

So, although the two scripts output the same encrypted data now, I would strongly consider changing the AES mode of operation, to actually stay secure.

java DES encrypt code convert to ruby

des = OpenSSL::Cipher::Cipher.new("DES-CBC")
des.encrypt
des.key = "xxxx"
des.iv = "xxxx"
cipher = des.update(str) + des.final
Base64.strict_encode64(cipher)

Encrypt in Ruby and Decrypt in Java - Why is it not working?

This is the problem - or at least a problem:

byte[] result = cipher.doFinal(encryptedData);
return result.toString();

You're calling toString() on a byte array. Arrays don't override toString(). That won't give you what you want at all - as you can see. Instead, you need to write something like:

return new String(result, "UTF-8");

... but you need to know what encoding was used to turn the original string into bytes before encryption. It's not clear to me from the Ruby code what encoding is used, but if you can be explicit about it (ideally using UTF-8) it'll make your life a lot easier.

In short, I suspect this problem has nothing to do with encryption at all - it has everything to do with converting text to bytes in Ruby and then converting the same sequence of bytes back into a string in Java.

Of course the encryption may be failing as well but that's a different matter.

Java to ruby AES/ECB/PKCS5Padding encryption

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.



Related Topics



Leave a reply



Submit