fudriyak
fudriyak

Reputation: 1

How can I fix my encrypting with OpenSSL and decrypting in Java using AES?

I'm struggling on this problem: I want to encrypt a password with openssl, and decrypt it in java.

Here's what I have for the moment:

String aesKeyBase64 = "BDC17ABE1F194C8DD49840C57A155013";
SecretKey secretKey = new SecretKeySpec(Base64.getDecoder().decode(aesKeyBase64), "AES");

Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);

// encrypt
byte[] cipherText = cipher.doFinal("password".getBytes());
String encodedPassword =  Base64.getEncoder().encodeToString(cipherText); //returns ITmSsa+9aKeW776ABM5B+g==
System.out.println("encrypted password = " + encodedPassword);

// decrypt 
cipher.init(Cipher.DECRYPT_MODE, secretKey);
cipherText = cipher.doFinal(Base64.getDecoder().decode("b3JCq/QmtKSmkQABYdbiVA==".getBytes()));
System.out.println("decrypted password = " + new String(cipherText));

Here's my openssl command:
openssl enc -nosalt -aes-128-ecb -base64 -K BDC17ABE1F194C8DD49840C57A155013 -in in.txt -out out.txt which returns b3JCq/QmtKSmkQABYdbiVA== in the file out.txt

So I noticed that my two encryption are different, with aes-ecb there isn't IV, I haven't used salt, and pad is set to PKCS5 which is default pad with openssl enc

When I tried to decrypt openssl enc result here's my Java output:

Exception in thread "main" javax.crypto.BadPaddingException: Given final block not properly padded. Such issues can arise if a bad key is used during decryption.

I made sure to use a generate my key in Java directly with a string key to ensure I'm using same key in both methods.

Thanks for the help!

Upvotes: 0

Views: 202

Answers (1)

dave_thompson_085
dave_thompson_085

Reputation: 38990

Your key is not base64. It is hex (aka hexadecimal).

It's pretty obvious just looking at it, but you can also use logic: it is 32 characters, and a 16-byte key (for AES-128) in hex is 32 characters, but in base64 it would be 24 or 22 characters (depending whether you use one of the forms with or without padding). A 32-character base64 value with no padding would be 24-bytes which could be an AES-192 key but not a an AES-128 key. Plus openssl enc -K (and -iv if applicable, which it isn't here) uses hex -- read the man page.

In the old days you could just call java.xml.bind.DatatypeConverter to handle hex, but in modern Java (9 up) that is a nondefault module. Now you can (1) make your code modular, (2) use some other library, or (3) write your own -- for a simple SO post, I have done (3):

$ cat SO76396076.java
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;

class SO76396076 {
  public static void main (String[] args) throws Exception {
    //String aesKeyBase64 = "BDC17ABE1F194C8DD49840C57A155013";
    //SecretKey secretKey = new SecretKeySpec(Base64.getDecoder().decode(aesKeyBase64), "AES");
    String aesKeyHEX = "BDC17ABE1F194C8DD49840C57A155013";
    byte[] aesKeyBin = new byte[aesKeyHEX.length()/2];
    for( int i = 0; i < aesKeyBin.length; i++ )
      aesKeyBin[i] = (byte)Integer.parseInt(aesKeyHEX.substring(i*2,i*2+2),16);
    SecretKeySpec secretKey = new SecretKeySpec(aesKeyBin,"AES");

    Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
    cipher.init(Cipher.ENCRYPT_MODE, secretKey);
    
    // encrypt
    byte[] cipherText = cipher.doFinal("password".getBytes());
    String encodedPassword =  Base64.getEncoder().encodeToString(cipherText); //returns ITmSsa+9aKeW776ABM5B+g==
    System.out.println("encrypted password = " + encodedPassword);
    
    // decrypt 
    cipher.init(Cipher.DECRYPT_MODE, secretKey);
    cipherText = cipher.doFinal(Base64.getDecoder().decode("b3JCq/QmtKSmkQABYdbiVA==".getBytes()));
    System.out.println("decrypted password = " + new String(cipherText));
  }
}

$ java SO76396076.java
encrypted password = b3JCq/QmtKSmkQABYdbiVA==
decrypted password = password

Also: byte[] String.getBytes(/*noarg*/) and new String(byte[]) use the JVM default charset, which in all versions until recently varied by platform and user, so if you tried to use your encrypted data in anything other than a test environment this was likely to mangle data containing any non-ASCII character(s) -- which your example value doesn't, and unlike most sensitive data passwords usually shouldn't (but sometimes do). Java 18 up makes the default consistent at UTF-8 -- unless changed by the user(s). Thus for data in general you should still specify a suitable charset -- usually UTF-8 unless there's an application or environment specific reason for something else -- in both places; for passwords it's a closer call.

Upvotes: 0

Related Questions