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:
- Your expiryDate is
20150101 151515
in the Java code, but20150101151515
everywhere else. So let's standardize on20150101151515
Base64.encodeBase64()
does not exist. Java 8 has Base64 encoding built-in, and the code should beBase64.getEncoder().encodeToString(data)
- The return type of that is already string so
encryptedValue = new String(Base64...)
is unnecessary. - 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
What Is the Equivalent to a JavaScript Setinterval/Settimeout in Android/Java
Libavcodec.So: Has Text Relocations
How to Convert a String Date to Long Millseconds
Preload Multiple Images with Glide
How Does Breakiterator Work in Android
How to Move/Rename File from Internal App Storage to External Storage on Android
Setnavigationitemselectedlistener Not Working
Setting a Spinner Onclicklistener() in Android
How to Create Thumbnail of Video Url Form Server
== and .Equals() Not Working in Java
Android Calculate Days Between Two Dates
What Is the Easiest Way to Parse an Ini File in Java