Skip to content

Instantly share code, notes, and snippets.

@demisang
Last active December 16, 2022 02:16
Show Gist options
  • Save demisang/716250080d77a7f65e66f4e813e5a636 to your computer and use it in GitHub Desktop.
Save demisang/716250080d77a7f65e66f4e813e5a636 to your computer and use it in GitHub Desktop.
AES/CBC/PKCS5Padding encrypt/decrypt PHP and JAVA example classes
import android.support.annotation.Nullable;
import android.util.Base64;
import java.nio.ByteBuffer;
import java.security.SecureRandom;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
/**
* AesCipher
* <p>Encode/Decode text by password using AES-128-CBC algorithm</p>
*/
public class AesCipher {
public static final int INIT_VECTOR_LENGTH = 16;
/**
* @see <a href="https://stackoverflow.com/questions/9655181/how-to-convert-a-byte-array-to-a-hex-string-in-java">how-to-convert-a-byte-array-to-a-hex-string</a>
*/
private final static char[] hexArray = "0123456789ABCDEF".toCharArray();
/**
* Encoded/Decoded data
*/
protected String data;
/**
* Initialization vector value
*/
protected String initVector;
/**
* Error message if operation failed
*/
protected String errorMessage;
private AesCipher() {
super();
}
/**
* AesCipher constructor.
*
* @param initVector Initialization vector value
* @param data Encoded/Decoded data
* @param errorMessage Error message if operation failed
*/
private AesCipher(@Nullable String initVector, @Nullable String data, @Nullable String errorMessage) {
super();
this.initVector = initVector;
this.data = data;
this.errorMessage = errorMessage;
}
/**
* Encrypt input text by AES-128-CBC algorithm
*
* @param secretKey 16/24/32 -characters secret password
* @param plainText Text for encryption
* @return Encoded string or NULL if error
*/
public static AesCipher encrypt(String secretKey, String plainText) {
String initVector = null;
try {
// Check secret length
if (!isKeyLengthValid(secretKey)) {
throw new Exception("Secret key's length must be 128, 192 or 256 bits");
}
// Get random initialization vector
SecureRandom secureRandom = new SecureRandom();
byte[] initVectorBytes = new byte[INIT_VECTOR_LENGTH / 2];
secureRandom.nextBytes(initVectorBytes);
initVector = bytesToHex(initVectorBytes);
initVectorBytes = initVector.getBytes("UTF-8");
IvParameterSpec ivParameterSpec = new IvParameterSpec(initVectorBytes);
SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getBytes("UTF-8"), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
// Encrypt input text
byte[] encrypted = cipher.doFinal(plainText.getBytes("UTF-8"));
ByteBuffer byteBuffer = ByteBuffer.allocate(initVectorBytes.length + encrypted.length);
byteBuffer.put(initVectorBytes);
byteBuffer.put(encrypted);
// Result is base64-encoded string: initVector + encrypted result
String result = Base64.encodeToString(byteBuffer.array(), Base64.DEFAULT);
// Return successful encoded object
return new AesCipher(initVector, result, null);
} catch (Throwable t) {
t.printStackTrace();
// Operation failed
return new AesCipher(initVector, null, t.getMessage());
}
}
/**
* Decrypt encoded text by AES-128-CBC algorithm
*
* @param secretKey 16/24/32 -characters secret password
* @param cipherText Encrypted text
* @return Self object instance with data or error message
*/
public static AesCipher decrypt(String secretKey, String cipherText) {
try {
// Check secret length
if (!isKeyLengthValid(secretKey)) {
throw new Exception("Secret key's length must be 128, 192 or 256 bits");
}
// Get raw encoded data
byte[] encrypted = Base64.decode(cipherText, Base64.DEFAULT);
// Slice initialization vector
IvParameterSpec ivParameterSpec = new IvParameterSpec(encrypted, 0, INIT_VECTOR_LENGTH);
// Set secret password
SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getBytes("UTF-8"), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);
// Trying to get decrypted text
String result = new String(cipher.doFinal(encrypted, INIT_VECTOR_LENGTH, encrypted.length - INIT_VECTOR_LENGTH));
// Return successful decoded object
return new AesCipher(bytesToHex(ivParameterSpec.getIV()), result, null);
} catch (Throwable t) {
t.printStackTrace();
// Operation failed
return new AesCipher(null, null, t.getMessage());
}
}
/**
* Check that secret password length is valid
*
* @param key 16/24/32 -characters secret password
* @return TRUE if valid, FALSE otherwise
*/
public static boolean isKeyLengthValid(String key) {
return key.length() == 16 || key.length() == 24 || key.length() == 32;
}
/**
* Convert Bytes to HEX
*
* @param bytes Bytes array
* @return String with bytes values
*/
public static String bytesToHex(byte[] bytes) {
char[] hexChars = new char[bytes.length * 2];
for (int j = 0; j < bytes.length; j++) {
int v = bytes[j] & 0xFF;
hexChars[j * 2] = hexArray[v >>> 4];
hexChars[j * 2 + 1] = hexArray[v & 0x0F];
}
return new String(hexChars);
}
/**
* Get encoded/decoded data
*/
public String getData() {
return data;
}
/**
* Get initialization vector value
*/
public String getInitVector() {
return initVector;
}
/**
* Get error message
*/
public String getErrorMessage() {
return errorMessage;
}
/**
* Check that operation failed
*
* @return TRUE if failed, FALSE otherwise
*/
public boolean hasError() {
return this.errorMessage != null;
}
/**
* To string return resulting data
*
* @return Encoded/decoded data
*/
public String toString() {
return getData();
}
}
// USAGE
String secretKey = "26kozQaKwRuNJ24t";
String text = "Some text";
AesCipher encrypted = AesCipher.encrypt(secretKey, text);
AesCipher decrypted = AesCipher.decrypt(secretKey, encrypted);
encrypted.hasError(); // TRUE if operation failed, FALSE otherwise
encrypted.getData(); // Encoded/Decoded result
encrypted.getInitVector(); // Get used (random if encode) init vector
// decrypted.* has identical methods
<?php
/**
* AesCipher
*
* Encode/Decode text by password using AES-128-CBC algorithm
*/
class AesCipher
{
const CIPHER = 'AES-128-CBC';
const INIT_VECTOR_LENGTH = 16;
/**
* Encoded/Decoded data
*
* @var null|string
*/
protected $data;
/**
* Initialization vector value
*
* @var string
*/
protected $initVector;
/**
* Error message if operation failed
*
* @var null|string
*/
protected $errorMessage;
/**
* AesCipher constructor.
*
* @param string $initVector Initialization vector value
* @param string|null $data Encoded/Decoded data
* @param string|null $errorMessage Error message if operation failed
*/
public function __construct($initVector, $data = null, $errorMessage = null)
{
$this->initVector = $initVector;
$this->data = $data;
$this->errorMessage = $errorMessage;
}
/**
* Encrypt input text by AES-128-CBC algorithm
*
* @param string $secretKey 16/24/32 -characters secret password
* @param string $plainText Text for encryption
*
* @return self Self object instance with data or error message
*/
public static function encrypt($secretKey, $plainText)
{
try {
// Check secret length
if (!static::isKeyLengthValid($secretKey)) {
throw new \InvalidArgumentException("Secret key's length must be 128, 192 or 256 bits");
}
// Get random initialization vector
$initVector = bin2hex(openssl_random_pseudo_bytes(static::INIT_VECTOR_LENGTH / 2));
// Encrypt input text
$raw = openssl_encrypt(
$plainText,
static::CIPHER,
$secretKey,
OPENSSL_RAW_DATA,
$initVector
);
// Return base64-encoded string: initVector + encrypted result
$result = base64_encode($initVector . $raw);
if ($result === false) {
// Operation failed
return new static($initVector, null, openssl_error_string());
}
// Return successful encoded object
return new static($initVector, $result);
} catch (\Exception $e) {
// Operation failed
return new static(isset($initVector), null, $e->getMessage());
}
}
/**
* Decrypt encoded text by AES-128-CBC algorithm
*
* @param string $secretKey 16/24/32 -characters secret password
* @param string $cipherText Encrypted text
*
* @return self Self object instance with data or error message
*/
public static function decrypt($secretKey, $cipherText)
{
try {
// Check secret length
if (!static::isKeyLengthValid($secretKey)) {
throw new \InvalidArgumentException("Secret key's length must be 128, 192 or 256 bits");
}
// Get raw encoded data
$encoded = base64_decode($cipherText);
// Slice initialization vector
$initVector = substr($encoded, 0, static::INIT_VECTOR_LENGTH);
// Slice encoded data
$data = substr($encoded, static::INIT_VECTOR_LENGTH);
// Trying to get decrypted text
$decoded = openssl_decrypt(
$data,
static::CIPHER,
$secretKey,
OPENSSL_RAW_DATA,
$initVector
);
if ($decoded === false) {
// Operation failed
return new static(isset($initVector), null, openssl_error_string());
}
// Return successful decoded object
return new static($initVector, $decoded);
} catch (\Exception $e) {
// Operation failed
return new static(isset($initVector), null, $e->getMessage());
}
}
/**
* Check that secret password length is valid
*
* @param string $secretKey 16/24/32 -characters secret password
*
* @return bool
*/
public static function isKeyLengthValid($secretKey)
{
$length = strlen($secretKey);
return $length == 16 || $length == 24 || $length == 32;
}
/**
* Get encoded/decoded data
*
* @return string|null
*/
public function getData()
{
return $this->data;
}
/**
* Get initialization vector value
*
* @return string|null
*/
public function getInitVector()
{
return $this->initVector;
}
/**
* Get error message
*
* @return string|null
*/
public function getErrorMessage()
{
return $this->errorMessage;
}
/**
* Check that operation failed
*
* @return bool
*/
public function hasError()
{
return $this->errorMessage !== null;
}
/**
* To string return resulting data
*
* @return null|string
*/
public function __toString()
{
return $this->getData();
}
}
// USAGE
$secretKey = '26kozQaKwRuNJ24t';
$text = 'Some text';
$encrypted = AesCipher::encrypt($secretKey, $text);
$decrypted = AesCipher::decrypt($secretKey, $encrypted);
$encrypted->hasError(); // TRUE if operation failed, FALSE otherwise
$encrypted->getData(); // Encoded/Decoded result
$encrypted->getInitVector(); // Get used (random if encode) init vector
// $decrypted->* has identical methods
@SjonC
Copy link

SjonC commented Nov 11, 2020

my php decrypt code :

    $key = "Cw3eLYMOXslhAg5iK4N36khd/NdjIX70WOzzJmb4DCU="
    list($encrypted, $iv) = explode('::', base64_decode($cipherStr), 2); 
    $result = openssl_decrypt($encrypted, "AES-128-CBC", base64_decode($key), 0, $iv);
   it work!

but my java decrypt code :

    byte[] cipherBytes = Base64Utils.decodeFromString(test);
    byte[] iv =   Arrays.copyOfRange(cipherBytes, 130, 146);
    byte[] content = Arrays.copyOfRange(cipherBytes, 0, 128);
    byte[] key = Base64Utils.decodeFromString("Cw3eLYMOXslhAg5iK4N36khd/NdjIX70WOzzJmb4DCU=");

    Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
    SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES"); 
    IvParameterSpec ivParameterSpec = new IvParameterSpec(iv, 0, cipher.getBlockSize());
    cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);
    String result = new String(cipher.doFinal(content));

It still doesnt work 。 i cant change the encrypt code
Please help me to solve this

Hello, I also encountered such a problem. Have you solved it?

@rohanhande
Copy link

rohanhande commented Sep 15, 2021

How to encrypt and decrypt above example in javascript?

@yushine
Copy link

yushine commented Sep 17, 2021

Thank you so much.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment