I am migrating a java code that encrypts and decrypts messages just as openssl below 1.1.1 with this command :
echo -n "password" | openssl enc -aes-256-cbc -a -k secretKey -md sha256
This works fine and without problems, the problem comes when we want to use openssl 1.1.1 or higher, we get the famous message :
*** WARNING : deprecated key derivation used.
Using -iter or -pbkdf2 would be better.
Reading , both options are based on -pbkdf2 , in one case we choose the number of iterations (-iter) and in the other case we leave the default iterations (-pbkdf2)so I have kept it this way.
echo -n "password" | openssl enc -aes-256-cbc -a -k secretKey -pbkdf2
I have seen that the only method that uses the javax.crypto library and that makes reference to -pbkdf2 is SecretKeyFactory.getInstance("PBKDF2withHmacSHA256")
as it says here, I have tried to adapt it to my original code ( below with this line ) changing my keyandIV for this one but I can't make it work, I get this error :
Exception in thread "main" modified.EncryptionDecryptionException: Given final block not properly padded. Such issues can arise if a bad key is used during decryption.
Caused by: javax.crypto.BadPaddingException: Given final block not properly padded. Such issues can arise if a bad key is used during decryption.
I have added the new method to generate the keyAndIV and left the old one, but I can't get it to work. Do you have any idea what could be wrong ? Thank you very much !
import java.nio.charset.StandardCharsets;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
import java.util.Arrays;
import java.util.Base64;
import java.util.Base64.Decoder;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
public class modified
{
private static final String SECRET_KEY = "secretKey";
private static final String SALT_KEY = "Salted__";
private static final int keylen = 32;
private static final int ivlen = 16;
public static void main(String[] args) throws Exception {
String example= "U2FsdGVkX19hh2C4Gxe3ghDb/qY0x8rC4SZaZEMv5yg=";
String decryptedText = modified.decryptPassword( example.toCharArray());
System.out.println("value -> "+decryptedText);
}
private modified()
{
throw new IllegalStateException("Utility class");
}
public static String decryptPassword(char[] source) throws EncryptionDecryptionException
{
return decrypt(source, SECRET_KEY);
}
public static String encryptPassword(char[] source) throws EncryptionDecryptionException
{
return encrypt(source, SECRET_KEY);
}
private static String decrypt(char[] source, String sk) throws EncryptionDecryptionException
{
try
{
final byte[] magic = SALT_KEY.getBytes(StandardCharsets.UTF_8);
final Decoder decoder = Base64.getDecoder();
final byte[] inBytes = decoder.decode(String.valueOf(source));
checkMagic(magic, inBytes);
byte[] salt = Arrays.copyOfRange(inBytes, magic.length, magic.length + 8);
final Cipher cipher = getCipher(Cipher.DECRYPT_MODE, source, sk, salt);
final byte[] clear = cipher.doFinal(inBytes, 16, inBytes.length - 16);
return new String(clear, StandardCharsets.UTF_8);
}
catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException
| InvalidAlgorithmParameterException | IllegalBlockSizeException | BadPaddingException e)
{
throw new EncryptionDecryptionException(e.getMessage(), e);
}
}
private static String encrypt(char[] source, String sk) throws EncryptionDecryptionException
{
try
{
final byte[] magic = SALT_KEY.getBytes(StandardCharsets.UTF_8);
final byte[] inBytes = String.valueOf(source).getBytes(StandardCharsets.UTF_8);
byte[] salt = (new SecureRandom()).generateSeed(8);
final Cipher cipher = getCipher(Cipher.ENCRYPT_MODE, source, sk, salt);
byte[] data = cipher.doFinal(inBytes);
data = concat(concat(magic, salt), data);
return Base64.getEncoder().encodeToString(data);
}
catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException
| InvalidAlgorithmParameterException | IllegalBlockSizeException | BadPaddingException e)
{
throw new EncryptionDecryptionException(e.getMessage(), e);
}
}
private static Cipher getCipher(int cipherMode, char[] source, String sk, byte[] salt)
throws NoSuchAlgorithmException,
NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException
{
final byte[] secret = sk.getBytes(StandardCharsets.UTF_8);
final byte[] passAndSalt = concat(secret, salt);
//byte[] keyAndIv = getKeyAndIv(passAndSalt);
byte[] keyAndIv = getNewKeyAndIvThatSupportPBKDF(source, salt);
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(cipherMode, key, new IvParameterSpec(iv));
Arrays.fill(passAndSalt, (byte) 0);
return cipher;
}
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;
}
private static void checkMagic(final byte[] magic, final byte[] inBytes)
{
final byte[] shouldBeMagic = Arrays.copyOfRange(inBytes, 0,
magic.length);
if (!Arrays.equals(shouldBeMagic, magic))
{
throw new EncryptionDecryptionException("DecriptionException: Bad magic number");
}
}
private static byte[] getNewKeyAndIvThatSupportPBKDF(char[] source, byte[] salt) throws EncryptionDecryptionException
{
try
{
return SecretKeyFactory.getInstance("PBKDF2withHmacSHA256")
.generateSecret( new PBEKeySpec(source, salt, 10000, (keylen+ivlen)*8)
).getEncoded();
}
catch (InvalidKeySpecException | NoSuchAlgorithmException e)
{
throw new EncryptionDecryptionException(e.getMessage(), e);
}
}
private static byte[] getKeyAndIv(final byte[] passAndSalt) throws NoSuchAlgorithmException
{
byte[] hash = new byte[0];
byte[] keyAndIv = new byte[0];
for (int i = 0; i < 3; i++)
{
final byte[] data = concat(hash, passAndSalt);
MessageDigest md;
md = MessageDigest.getInstance("SHA-256");
hash = md.digest(data);
keyAndIv = concat(keyAndIv, hash);
}
return keyAndIv;
}
}
You use source
(which is the base64-encoded ciphertext-plus) where you should use the secret key. Here's a trimmed and minimally-corrected version of your code:
//nopackage
import java.nio.charset.StandardCharsets;
//--import java.security.MessageDigest;
//--import java.security.SecureRandom;
import java.util.Arrays;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
public class SO76624363encpbkdf2
{
private static final String SECRET_KEY = "secretKey";
private static final String SALT_KEY = "Salted__";
private static final int keylen = 32;
private static final int ivlen = 16;
public static void main(String[] args) throws Exception {
String example= "U2FsdGVkX19hh2C4Gxe3ghDb/qY0x8rC4SZaZEMv5yg=";
String decryptedText = decryptPassword( example.toCharArray());
System.out.println("value -> "+decryptedText);
}
public static String decryptPassword(char[] source) throws Exception
{
return decrypt(source, SECRET_KEY);
}
/*
public static String encryptPassword(char[] source) throws Exception
{
return encrypt(source, SECRET_KEY);
}
*/
private static String decrypt(char[] source, String sk) throws Exception
{
final byte[] magic = SALT_KEY.getBytes(StandardCharsets.UTF_8);
final byte[] inBytes = Base64.getDecoder() .decode(String.valueOf(source));
checkMagic(magic, inBytes);
byte[] salt = Arrays.copyOfRange(inBytes, magic.length, magic.length + 8);
final Cipher cipher = getCipher(Cipher.DECRYPT_MODE, source, sk, salt);
final byte[] clear = cipher.doFinal(inBytes, 16, inBytes.length - 16);
return new String(clear, StandardCharsets.UTF_8);
}
/*
private static String encrypt(char[] source, String sk) throws Exception
{
final byte[] magic = SALT_KEY.getBytes(StandardCharsets.UTF_8);
final byte[] inBytes = String.valueOf(source).getBytes(StandardCharsets.UTF_8);
byte[] salt = (new SecureRandom()).generateSeed(8);
final Cipher cipher = getCipher(Cipher.ENCRYPT_MODE, source, sk, salt);
byte[] data = cipher.doFinal(inBytes);
data = concat(concat(magic, salt), data);
return Base64.getEncoder().encodeToString(data);
}
*/
private static Cipher getCipher(int cipherMode, char[] source, String sk, byte[] salt)
throws Exception
{
//--final byte[] secret = sk.getBytes(StandardCharsets.UTF_8);
//--final byte[] passAndSalt = concat(secret, salt);
//byte[] keyAndIv = getKeyAndIv(passAndSalt);
byte[] keyAndIv = getNewKeyAndIvThatSupportPBKDF(sk, salt);
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(cipherMode, key, new IvParameterSpec(iv));
//--Arrays.fill(passAndSalt, (byte) 0);
return cipher;
}
/*
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;
}
*/
private static void checkMagic(final byte[] magic, final byte[] inBytes) throws Exception
{
final byte[] shouldBeMagic = Arrays.copyOfRange(inBytes, 0,
magic.length);
if (!Arrays.equals(shouldBeMagic, magic))
{
throw new Exception("Bad magic number");
}
}
private static byte[] getNewKeyAndIvThatSupportPBKDF(String secret, byte[] salt) throws Exception
{
return SecretKeyFactory.getInstance("PBKDF2withHmacSHA256")
.generateSecret( new PBEKeySpec(secret.toCharArray(), salt, 10000, (keylen+ivlen)*8)
).getEncoded();
}
/*
private static byte[] getKeyAndIv(final byte[] passAndSalt) throws NoSuchAlgorithmException
{
byte[] hash = new byte[0];
byte[] keyAndIv = new byte[0];
for (int i = 0; i < 3; i++)
{
final byte[] data = concat(hash, passAndSalt);
MessageDigest md;
md = MessageDigest.getInstance("SHA-256");
hash = md.digest(data);
keyAndIv = concat(keyAndIv, hash);
}
return keyAndIv;
}
*/
}