Java equivalent of an OpenSSL AES CBC encryption
Following is a Java program to decrypt the above OPENSSL encryption (it requires Java 8):
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.MessageDigest;
import java.util.Arrays;
import java.util.Base64;
import java.util.Base64.Decoder;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class TestAesDecrypt {
public static void main(final String[] args) throws Exception {
final byte[] pass = "testpass".getBytes(StandardCharsets.US_ASCII);
final byte[] magic = "Salted__".getBytes(StandardCharsets.US_ASCII);
final String inFile = "e:/t/e.txt";
String source = new String(Files.readAllBytes(Paths.get(inFile)),
StandardCharsets.US_ASCII);
source = source.replaceAll("\\s", "");
final Decoder decoder = Base64.getDecoder();
final byte[] inBytes = decoder.decode(source);
final byte[] shouldBeMagic = Arrays.copyOfRange(inBytes, 0,
magic.length);
if (!Arrays.equals(shouldBeMagic, magic)) {
System.out.println("Bad magic number");
return;
}
final byte[] salt = Arrays.copyOfRange(inBytes, magic.length,
magic.length + 8);
final byte[] passAndSalt = concat(pass, salt);
byte[] hash = new byte[0];
byte[] keyAndIv = new byte[0];
for (int i = 0; i < 3; i++) {
final byte[] data = concat(hash, passAndSalt);
final MessageDigest md = MessageDigest.getInstance("MD5");
hash = md.digest(data);
keyAndIv = concat(keyAndIv, hash);
}
final byte[] keyValue = Arrays.copyOfRange(keyAndIv, 0, 32);
final byte[] iv = Arrays.copyOfRange(keyAndIv, 32, 48);
final Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
final SecretKeySpec key = new SecretKeySpec(keyValue, "AES");
cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv));
final byte[] clear = cipher.doFinal(inBytes, 16, inBytes.length - 16);
final String clearText = new String(clear, StandardCharsets.ISO_8859_1);
System.out.println(clearText);
}
private static byte[] concat(final byte[] a, final byte[] b) {
final byte[] c = new byte[a.length + b.length];
System.arraycopy(a, 0, c, 0, a.length);
System.arraycopy(b, 0, c, a.length, b.length);
return c;
}
}
Looking for Java implementation for decrypting a message encrypted using openssl -aes-256-cbc -a -salt command?
Also got an improved solution from the following site. Got the code for both encryption and decryption for now...
http://qaru.site/questions/19874/java-equivalent-of-an-openssl-aes-cbc-encryption
import java.net.URLEncoder;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Arrays;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;
import static java.nio.charset.StandardCharsets.*;
/**
* Mimics the OpenSSL AES Cipher options for encrypting and decrypting messages using a
* shared key (aka password) with symetric ciphers.
*/
public class OpenSslAesQu {
/** OpenSSL magic initial bytes. */
private static final String SALTED_STR = "Salted__";
private static final byte[] SALTED_MAGIC = SALTED_STR.getBytes(US_ASCII);
public static String encryptAndURLEncode(String password, String clearText) {
String encrypted = null;
try {
encrypted = URLEncoder.encode(encrypt(password, clearText),UTF_8.name());
} catch (Exception e) {e.printStackTrace();}
return encrypted;
}
/**
*
* @param password The password / key to encrypt with.
* @param data The data to encrypt
* @return A base64 encoded string containing the encrypted data.
*/
public static String encrypt(String password, String clearText) {
String encryptedMsg = null;
final byte[] pass = password.getBytes(US_ASCII);
final byte[] salt = (new SecureRandom()).generateSeed(8);
final byte[] inBytes = clearText.getBytes(UTF_8);
final byte[] passAndSalt = array_concat(pass, salt);
byte[] hash = new byte[0];
byte[] keyAndIv = new byte[0];
try {
for (int i = 0; i < 3 && keyAndIv.length < 48; i++) {
final byte[] hashData = array_concat(hash, passAndSalt);
final MessageDigest md = MessageDigest.getInstance("MD5");
hash = md.digest(hashData);
keyAndIv = array_concat(keyAndIv, hash);
}
final byte[] keyValue = Arrays.copyOfRange(keyAndIv, 0, 32);
final byte[] iv = Arrays.copyOfRange(keyAndIv, 32, 48);
final SecretKeySpec key = new SecretKeySpec(keyValue, "AES");
final Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(iv));
byte[] data = cipher.doFinal(inBytes);
data = array_concat(array_concat(SALTED_MAGIC, salt), data);
//return Base64.getEncoder().encodeToString( data );
encryptedMsg = org.apache.commons.codec.binary.Base64.encodeBase64String(data);
} catch(Exception e) {e.printStackTrace();}
return encryptedMsg;
}
/**
* @see http://stackoverflow.com/questions/32508961/java-equivalent-of-an-openssl-aes-cbc-encryption for what looks like a useful answer. The not-yet-commons-ssl also has an implementation
* @param password
* @param source The encrypted data
*/
public static String decrypt(String password, String source) {
String decryptedMsg = null;
final byte[] pass = password.getBytes(US_ASCII);
//final byte[] inBytes = Base64.getDecoder().decode(source);
final byte[] inBytes = Base64.decodeBase64(source);
final byte[] shouldBeMagic = Arrays.copyOfRange(inBytes, 0, SALTED_MAGIC.length);
if (!Arrays.equals(shouldBeMagic, SALTED_MAGIC)) {
throw new IllegalArgumentException("Initial bytes from input do not match OpenSSL SALTED_MAGIC salt value.");
}
final byte[] salt = Arrays.copyOfRange(inBytes, SALTED_MAGIC.length, SALTED_MAGIC.length + 8);
final byte[] passAndSalt = array_concat(pass, salt);
byte[] hash = new byte[0];
byte[] keyAndIv = new byte[0];
try {
for (int i = 0; i < 3 && keyAndIv.length < 48; i++) {
final byte[] hashData = array_concat(hash, passAndSalt);
final MessageDigest md = MessageDigest.getInstance("MD5");
hash = md.digest(hashData);
keyAndIv = array_concat(keyAndIv, hash);
}
final byte[] keyValue = Arrays.copyOfRange(keyAndIv, 0, 32);
final SecretKeySpec key = new SecretKeySpec(keyValue, "AES");
final byte[] iv = Arrays.copyOfRange(keyAndIv, 32, 48);
final Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv));
final byte[] clear = cipher.doFinal(inBytes, 16, inBytes.length - 16);
decryptedMsg = new String(clear, UTF_8);
} catch (Exception e) {e.printStackTrace();}
return decryptedMsg;
}
private static byte[] array_concat(final byte[] a, final byte[] b) {
final byte[] c = new byte[a.length + b.length];
System.arraycopy(a, 0, c, 0, a.length);
System.arraycopy(b, 0, c, a.length, b.length);
return c;
}
public static void main(String[] args) throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
String msg = "the decrypted message is this";
String password = "pass";
System.out.println(">> "+encrypt(password,msg));
//System.out.println("<< "+decrypt(encrypt(msg, password), password));
String encryptedMsg = "U2FsdGVkX190A5FsNTanwTKBdex29SpnH4zWkZN+Ld+MmbJgK4BH1whGIRRSpOJT";
String encryptedMsg2 = "U2FsdGVkX1/B6oOznz5+nd7W/qXwXI7G7rhj5o9pjx8MS0TXp9SNxO3AhM9HBJ/z";
System.out.println(decrypt(password,encryptedMsg));
System.out.println(decrypt(password,encryptedMsg2));
System.out.println(decrypt(password,encrypt(password,msg)));
}
}
Different AES256 result between Java and OpenSSL on iOS
For ECB:
The java code will be
public static byte[] encryptWithAES(byte[] message, Key AESkey) throws Exception {
byte[] decryptedMessage = null;
try {
Cipher encrypt = Cipher.getInstance("AES/ECB/PKCS5Padding"); // here is the change
encrypt.init(Cipher.ENCRYPT_MODE, AESkey);
decryptedMessage = encrypt.doFinal(message);
} catch (Exception ex) {
System.err.println(ex.getMessage());
throw ex;
}
return decryptedMessage;
}
For CBC:
The java code will be
public static byte[] encryptWithAES(byte[] message, Key AESkey, byte[] iv) throws Exception {
byte[] decryptedMessage = null;
try {
Cipher encrypt = Cipher.getInstance("AES/CBC/PKCS5Padding"); // here is the change
encrypt.init(Cipher.ENCRYPT_MODE, AESkey,new IvParameterSpec(iv)); // and I add the iv for cbc
decryptedMessage = encrypt.doFinal(message);
} catch (Exception ex) {
System.err.println(ex.getMessage());
throw ex;
}
return decryptedMessage;
}
Edit (2):
Depends on @zaph said the previous objective c will take twice process,
I updated the objective c code :
- (NSData *) AESECB : (NSData *) data withKey :(NSData *) key : (CCOperation) operation{
CCCryptorStatus ccStatus = kCCSuccess;
NSUInteger dataLength = data.length;
size_t bufferSize = dataLength + kCCBlockSizeAES128;
void *buffer = malloc( bufferSize );
size_t numBytes = 0;
ccStatus = CCCrypt(operation, // will pass kCCDecrypt or kCCEncrypt
kCCAlgorithmAES,
kCCOptionECBMode | kCCOptionPKCS7Padding,
[key bytes],
[key length],
nil,
[data bytes],
[data length],
buffer,
bufferSize,
&numBytes);
if( ccStatus == kCCSuccess )
{
return [NSData dataWithBytes:buffer length:numBytes];
}
free(buffer);
return nil;
}
- (NSData *) AESCBC : (NSData *) data withKey: (NSData *) key : (CCOperation) operation{
CCCryptorStatus ccStatus = kCCSuccess;
NSUInteger dataLength = data.length;
size_t bufferSize = dataLength + kCCBlockSizeAES128;
void *buffer = malloc( bufferSize );
size_t numBytes = 0;
ccStatus = CCCrypt(operation, // will pass kCCDecrypt or kCCEncrypt
kCCAlgorithmAES,
kCCOptionPKCS7Padding,
[key bytes],
[key length],
self.IV.bytes,
[data bytes],
[data length],
buffer,
bufferSize,
&numBytes);
if( ccStatus == kCCSuccess )
{
return [NSData dataWithBytes:buffer length:numBytes];
}
free(buffer);
return nil;
}
Many thanks @zaph for your advice again.
Java AES 128 encrypting differently to openssl
It is indeed a problem of providing a string or a file. If you put a "\n" at the end of your Java code the result will be the same as in openSSL.
Related Topics
How to Determine If a Date Is Between Two Dates in Java
Make Arraylist.Toarray() Return More Specific Types
Java Linkedhashmap Get First or Last Entry
Stand-Alone Java Code Formatter/Beautifier/Pretty Printer
Gradle: Could Not Determine Java Version from '11.0.2'
Lambda Expression to Convert Array/List of String to Array/List of Integers
Should I Call Ugi.Checktgtandreloginfromkeytab() Before Every Action on Hadoop
How to Import Spring-Config.Xml of One Project into Spring-Config.Xml of Another Project
Dynamically Change Spring Data Source
How to Get Absolute Path to File in /Resources Folder of Your Project
Java:Parse Java Source Code, Extract Methods
Getting Java.Lang.Classnotfoundexception: Org.Apache.Commons.Logging.Logfactory Exception
How to Get a List of Ip Connected in Same Network (Subnet) Using Java