As you are trying to learn I'm providing a Java and PHP full solution that works identical and the results are exchangeable, so can encrypt with Java and decrypt with PHP and vice versa. Both programs derive the encryption key from a passphrase using PBKDF2, after encryption the salt, iv and ciphertext are concatenated. For decryption the complete string is split up into these parts.
This is the output:
AES CBC 256 String encryption with PBKDF2 derived key
plaintext: The quick brown fox jumps over the lazy dog
encryptionKey (Base64): sI5RagrofCpzDe1ucLI7cHb3GILAMsySr0A8CD04o0I=
* * * Encryption * * *
ciphertext (Base64): Vq5mhwLqgl2LoC/QNRKWA/5YpjrlZ1f3504NzpwEhUA=:QiEdP5gfXzNQwWcxNk4p1w==:Rwy3LC7SbeyDUREiitjjdtioQWkctU9H9OEvzv1ctWymzVh3A0SFQN4Ek/Ku4nVp
output is (Base64) salt : (Base64) iv : (Base64) ciphertext
* * * Decryption * * *
AES CBC 256 String decryption with PBKDF2 derived key
ciphertext (Base64): Vq5mhwLqgl2LoC/QNRKWA/5YpjrlZ1f3504NzpwEhUA=:QiEdP5gfXzNQwWcxNk4p1w==:Rwy3LC7SbeyDUREiitjjdtioQWkctU9H9OEvzv1ctWymzVh3A0SFQN4Ek/Ku4nVp
input is (Base64) salt : (Base64) iv : (Base64) ciphertext
plaintext: The quick brown fox jumps over the lazy dog
Security warning: the following codes do not have any exception handling and are for educational purpose only.
Java:
import javax.crypto.*;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.util.Base64;
public class AesCbc256Pbkdf2StringEncryption_Full {
public static void main(String[] args) throws NoSuchAlgorithmException, IllegalBlockSizeException, InvalidKeyException, BadPaddingException, InvalidAlgorithmParameterException, NoSuchPaddingException, InvalidKeySpecException {
System.out.println("AES CBC 256 String encryption with PBKDF2 derived key");
String plaintext = "The quick brown fox jumps over the lazy dog";
System.out.println("plaintext: " + plaintext);
char[] password = "secret password".toCharArray();
// encryption
System.out.println("\n* * * Encryption * * *");
String ciphertextBase64 = aesCbcPbkdf2EncryptToBase64(password, plaintext);
System.out.println("ciphertext (Base64): " + ciphertextBase64);
System.out.println("output is (Base64) salt : (Base64) iv : (Base64) ciphertext");
// decryption
System.out.println("\n* * * Decryption * * *");
System.out.println("AES CBC 256 String decryption with PBKDF2 derived key");
String ciphertextDecryptionBase64 = ciphertextBase64;
System.out.println("ciphertext (Base64): " + ciphertextDecryptionBase64);
System.out.println("input is (Base64) salt : (Base64) iv : (Base64) ciphertext");
String decryptedtext = aesCbcPbkdf2DecryptFromBase64(password, ciphertextDecryptionBase64);
System.out.println("plaintext: " + decryptedtext);
}
private static byte[] generateSalt32Byte() {
SecureRandom secureRandom = new SecureRandom();
byte[] salt = new byte[32];
secureRandom.nextBytes(salt);
return salt;
}
private static byte[] generateRandomInitvector() {
SecureRandom secureRandom = new SecureRandom();
byte[] iv = new byte[16];
secureRandom.nextBytes(iv);
return iv;
}
private static String aesCbcPbkdf2EncryptToBase64(char[] password, String data) throws NoSuchPaddingException, NoSuchAlgorithmException,
InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, InvalidKeySpecException {
int PBKDF2_ITERATIONS = 15000;
byte[] salt = generateSalt32Byte();
SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
KeySpec keySpec = new PBEKeySpec(password, salt, PBKDF2_ITERATIONS, 32 * 8);
SecretKeySpec secretKeySpec = new SecretKeySpec(secretKeyFactory.generateSecret(keySpec).getEncoded(), "AES");
byte[] iv = generateRandomInitvector();
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
String ciphertextBase64 = Base64.getEncoder().encodeToString(cipher.doFinal(data.getBytes(StandardCharsets.UTF_8)));
String saltBase64 = Base64.getEncoder().encodeToString(salt);
String ivBase64 = Base64.getEncoder().encodeToString(iv);
return saltBase64 + ":" + ivBase64 + ":" + ciphertextBase64;
}
private static String aesCbcPbkdf2DecryptFromBase64(char[] password, String data) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, InvalidKeySpecException {
String[] parts = data.split(":", 0);
byte[] salt = base64Decoding(parts[0]);
byte[] iv = base64Decoding(parts[1]);
byte[] encryptedData = base64Decoding(parts[2]);
int PBKDF2_ITERATIONS = 15000;
SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
KeySpec keySpec = new PBEKeySpec(password, salt, PBKDF2_ITERATIONS, 32 * 8);
SecretKeySpec secretKeySpec = new SecretKeySpec(secretKeyFactory.generateSecret(keySpec).getEncoded(), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);
return new String(cipher.doFinal(encryptedData));
}
private static String base64Encoding(byte[] input) {
return Base64.getEncoder().encodeToString(input);
}
private static byte[] base64Decoding(String input) {
return Base64.getDecoder().decode(input);
}
}
PHP:
<?php
function aesCbcPbkdf2EncryptToBase64($password, $data)
{
$PBKDF2_ITERATIONS = 15000;
$salt = generateSalt32Byte();
$key = hash_pbkdf2("sha256", $password, $salt, $PBKDF2_ITERATIONS, 32, $raw_output = true);
$iv = generateRandomInitvector();
$ciphertext = openssl_encrypt($data, 'aes-256-cbc', $key, OPENSSL_RAW_DATA, $iv);
return base64_encode($salt) . ':' . base64_encode($iv) . ':' . base64_encode($ciphertext);
}
function generateSalt32Byte()
{
return openssl_random_pseudo_bytes(32, $crypto_strong);
}
function aesCbcPbkdf2DecryptFromBase64($password, $data)
{
$PBKDF2_ITERATIONS = 15000;
list($salt, $iv, $encryptedData) = explode(':', $data, 3);
$key = hash_pbkdf2("sha256", $password, base64_decode($salt), $PBKDF2_ITERATIONS, 32, $raw_output = true);
return openssl_decrypt(base64_decode($encryptedData), 'aes-256-cbc', $key, OPENSSL_RAW_DATA, base64_decode($iv));
}
function generateRandomInitvector()
{
return openssl_random_pseudo_bytes(16, $crypto_strong);
}
echo 'AES CBC 256 String encryption with PBKDF2 derived key' . PHP_EOL;
$plaintext = 'The quick brown fox jumps over the lazy dog';
echo 'plaintext: ' . $plaintext . PHP_EOL;
$password = "secret password";
// encryption
echo PHP_EOL . '* * * Encryption * * *' . PHP_EOL;
$ciphertextBase64 = aesCbcPbkdf2EncryptToBase64($password, $plaintext);
echo 'ciphertext: ' . $ciphertextBase64 . PHP_EOL;
echo 'output is (Base64) salt : (Base64) iv : (Base64) ciphertext' .PHP_EOL;
// decryption
echo PHP_EOL;
echo PHP_EOL . '* * * Decryption * * *' . PHP_EOL;
$ciphertextDecryptionBase64 = $ciphertextBase64;
echo 'ciphertextDecryption (Base64): ' . $ciphertextDecryptionBase64 . PHP_EOL;
echo 'input is (Base64) salt : (Base64) iv : (Base64) ciphertext' .PHP_EOL;
$decryptedtext = aesCbcPbkdf2DecryptFromBase64($password, $ciphertextDecryptionBase64);
echo 'plaintext: ' . $decryptedtext . PHP_EOL;
?>
ecrypt,eproviderandebitare missing. In the PHP code, the ciphertext is Base64 decoded and then IV and actual ciphertext are separated (i.e. both are expected to be concatenated). Please post sample data (plaintext, salt, password,enc.getValue(),provider.getValue(),bit.getValue(), ciphertext) for the Java code.