Java Equivalent of an Openssl Aes Cbc Encryption

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



Leave a reply



Submit