-
-
Save imuneer/3ac09e63451cb406db2c37a5b28f9d6f to your computer and use it in GitHub Desktop.
AES/CBC/PKCS5Padding encrypt/decrypt PHP and JAVA example classes
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment