I have a PHP encryption function. I need a java counter part for the same. Due to my limited knowledge in PHP I am unable to understand. Some one knows both the language, kindly help.
PHP code:
function encrypt($decrypted, $keyvalue) {
// Build a 256-bit $key which is a SHA256 hash of $keyvalue.
$key = hash('SHA256', $keyvalue, true);
// Build $iv and $iv_base64. We use a block size of 128 bits (AES compliant) and CBC mode. (Note: ECB mode is inadequate as IV is not used.)
srand(); $iv = mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC), MCRYPT_RAND);
if (strlen($iv_base64 = rtrim(base64_encode($iv), '=')) != 22) return false;
// Encrypt $decrypted and an MD5 of $decrypted using $key. MD5 is fine to use here because it's just to verify successful decryption.
$encrypted = base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $decrypted . md5($decrypted), MCRYPT_MODE_CBC, $iv));
// We're done!
return $iv_base64 . $encrypted;
}
Thanks in advance
Aniruddha
This should do it.
public static byte[] encrypt(byte[] decrypted, byte[] keyvalue) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException{
MessageDigest sha256 = MessageDigest.getInstance("SHA-256");
byte[] key = sha256.digest(keyvalue);
MessageDigest md5 = MessageDigest.getInstance("MD5");
byte[] checksum = md5.digest(decrypted);
//The length of the value to encrypt must be a multiple of 16.
byte[] decryptedAndChecksum = new byte[(decrypted.length + md5.getDigestLength() + 15) / 16 * 16];
System.arraycopy(decrypted, 0, decryptedAndChecksum, 0, decrypted.length);
System.arraycopy(checksum, 0, decryptedAndChecksum, decrypted.length, checksum.length);
//The remaining bytes of decryptedAndChecksum stay as 0 (default byte value) because PHP pads with 0's.
SecureRandom rnd = new SecureRandom();
byte[] iv = new byte[16];
rnd.nextBytes(iv);
IvParameterSpec ivSpec = new IvParameterSpec(iv);
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "AES"), ivSpec);
byte[] encrypted = Base64.encodeBase64(cipher.doFinal(decryptedAndChecksum));
byte[] ivBase64 = Base64.encodeBase64String(iv).substring(0, 22).getBytes();
byte[] output = new byte[encrypted.length + ivBase64.length];
System.arraycopy(ivBase64, 0, output, 0, ivBase64.length);
System.arraycopy(encrypted, 0, output, ivBase64.length, encrypted.length);
return output;
}
The equivalent of MCRYPT_RIJNDAEL_128 and MCRYPT_MODE_CBC in java is AES/CBC/NoPadding. You also need a utility for Base64 encoding, the above code uses Base64 from the Apache Codec library.
Also, because the encryption key is 256 bits, you'll need the Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files. These can be downloaded from Oracle's website.
Finally, do heed ntoskrnl's warning. This encryption really could be better, don't copy-paste from the PHP manual.
Related
I have a java code working perfectly
public static String encrypt(String message, String sercretKey)
{
String base64EncryptedString = "";
MessageDigest md = MessageDigest.getInstance("SHA-256");
byte[] digestOfPassword = md.digest(sercretKey.getBytes("utf-8"));
byte[] keyBytes = Arrays.copyOf(digestOfPassword, 24);
byte[] iv = Arrays.copyOf(digestOfPassword, 16);
SecretKey key = new SecretKeySpec(keyBytes, "AES");
javax.crypto.Cipher cipher = javax.crypto.Cipher.getInstance("AES/CBC/PKCS5Padding");
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
cipher.init(javax.crypto.Cipher.ENCRYPT_MODE, key, ivParameterSpec);
byte[] plainTextBytes = message.getBytes("utf-8");
byte[] buf = cipher.doFinal(plainTextBytes);
byte[] base64Bytes = Base64.getEncoder().encode(buf);
base64EncryptedString = new String(base64Bytes);
return base64EncryptedString;
}
I have tried using below code to recreate this above code in PHP
function encryptTest($sSecretKey,$sValue)
{
$key = hash('sha256', $sSecretKey,false);
$key = utf8_encode($key);
$key = substr($key, 0, 24);
$iv = substr($key, 0, 16);
$data = $sValue;
$outEnc = openssl_encrypt($data, "AES-256-CBC", $key, OPENSSL_RAW_DATA, $iv);
return base64_encode($outEnc);
}
But showing different results. What I have missed.
(Same types of questions are available in StackOverflow, but pointing my issues)
There are the following issues:
In the PHP code, the key is currently returned hex encoded. Instead, it must be returned as bytes string. To do this, the third parameter in hash() must be switched from false to true.
In the Java code a 192 bits key is used, i.e. AES-192. Accordingly, in the PHP code "AES-192-CBC" must be applied (and not "AES-256-CBC").
The utf8_encode() call in the PHP code is to be removed, as this corrupts the key.
With these changes, both codes provide the same ciphertext.
Security:
Using SHA256 as key derivation is insecure. Instead apply a dedicated algorithm like Argon2 or PBKDF2. Also, using the key (or a part of it) as IV is insecure as it results in the reuse of key/IV pairs. Instead, a randomly generated IV should be applied for each encryption.
I have some problem decrypting text with CryptoJS that has been encrypted with Java. The decryption should be done with AES/CBC/PKCS5Padding. The encrypted string is base64 encoded and I decode it before trying to decrypt the string.
This is how the Java code looks like:
private static byte[] doAES(int mode, byte[] dataToEncrypt, String secretKey, String salt) throws Exception {
byte[] iv = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
IvParameterSpec ivspec = new IvParameterSpec(iv);
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
KeySpec spec = new PBEKeySpec(secretKey.toCharArray(), salt.getBytes(), 65536, 256);
SecretKey tmp = factory.generateSecret(spec);
SecretKeySpec secretKeySpec = new SecretKeySpec(tmp.getEncoded(), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(mode, secretKeySpec, ivspec);
return cipher.doFinal(dataToEncrypt);
}
And this is how I attempt to decrypt it in CryptoJS. SharedSecretKey is both the secretKey and the salt value in Java.
let decoded = Buffer.from(encodedString, 'base64')
console.log("Result: " + CryptoJS.AES.decrypt({
ciphertext: CryptoJS.enc.Hex.parse(decoded.toString().substring(32))
}, CryptoJS.enc.Hex.parse(CryptoJS.SHA1(sharedSecretKey).toString().substring(0,32)),
{
iv: CryptoJS.lib.WordArray.create(Buffer.alloc(16)),
}).toString(CryptoJS.enc.Utf8))
Where decoded is the decoded Base64-string that I want to decrypt. However, this does not work and I get the error 'Error: Malformed UTF-8 data'. I am not sure if there is anything I have missed, any help is very much appreciated.
In the CryptoJS code the key derivation with PBKDF2 is missing. CryptoJS uses SHA1 for this by default.
The ciphertext can be passed Base64 encoded and will be implicitly converted to a CipherParams object:
var encodedString = "0O15lUg8sE1G0+BjO5N2j8AjVKXV4J+18z5DinbM6tYjoILhL0WDTFWbcTiN+pG/";
var key = CryptoJS.PBKDF2(
"my passphrase",
"my salt",
{
keySize: 256 / 32,
iterations: 65536
}
);
var decryptedData = CryptoJS.AES.decrypt(encodedString, key,
{
iv: CryptoJS.enc.Utf8.parse("\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"),
}
);
console.log("Result: " + decryptedData.toString(CryptoJS.enc.Utf8));
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js"></script>
The ciphertext in the above example was previously generated with the posted Java code.
Note that a static IV is insecure. The IV is usually randomly generated for each encryption and passed along with the ciphertext (typically concatenated). Analogously for the salt, it is randomly generated for each key derivation and passed together with ciphertext and IV, e.g. salt|iv|ciphertext. Both, IV and salt, are not secret and need not be encrypted.
In addition, the encoding should be specified in the Java code when encoding with getBytes() and decoding with new String(), otherwise cross-platform problems may occur.
javax.crypto.BadPaddingException: Given final block not properly padded - This is the error which I've got. I do not know why. My code seems to be alright. The key is the same during encrypting and decrypting. What I can say is that none of the already answered questions contain solution for my problem. Here is my code:
public String decrypt(String text) throws Exception {
String key = "SiadajerSiadajer"; // 128 bit key
// Create key and cipher
Key aesKey = new SecretKeySpec(key.getBytes(), "AES");
Cipher cipher = Cipher.getInstance("AES");
// encrypt the text
byte[]encrypted = text.getBytes();
cipher.init(Cipher.DECRYPT_MODE, aesKey);
String decrypted = new String(cipher.doFinal(encrypted)); //Here is the error
return decrypted;
}
And the encryption mechanism in php
function encrypt($data, $size)
{
$length = $size - strlen($data) % $size;
return $data . str_repeat(chr($length), $length);
}
function decrypt($data)
{
return substr($data, 0, -ord($data[strlen($data) - 1]));
}
$key = "SiadajerSiadajer";
$iv_size = 16; // 128 bits
$iv = openssl_random_pseudo_bytes($iv_size, $strong);
$name = openssl_encrypt(encrypt($name, 16), 'AES-256-CBC', $key, 0, $iv);
EDIT
Now in the php part my code is:
$key = "SiadajerSiadajer";
$iv_size = 16; // 128 bits
$iv = openssl_random_pseudo_bytes($iv_size, $strong);
$EIV = base64_encode($iv);
$name = openssl_encrypt(encrypt($name, 16), 'AES-256-CBC', $key, 0, $EIV);
And it gives me warning:Warning: openssl_encrypt(): IV passed is 24 bytes long which is longer than the 16 expected by selected cipher, truncating in C:\xampp2\htdocs\standardfinalinserting.php on line 68
And in Java part my decryption method is exactly like in the answer for my question but after running it gives me an error: java.security.InvalidKeyException: Illegal key size on the line:
cipher.init(Cipher.DECRYPT_MODE, aesKey, ivParameterSpec);
EDIT 2:
So this is my whole Main class. It contains your whole Java code example
public class Main {
private byte[] padKey(byte[] key) {
byte[] paddedKey = new byte[32];
System.arraycopy(key, 0, paddedKey, 0, key.length);
return paddedKey;
}
private byte[] unpad(byte[] data) {
byte[] unpaddedData = new byte[data.length - data[data.length - 1]];
System.arraycopy(data, 0, unpaddedData, 0, unpaddedData.length);
return unpaddedData;
}
public String decrypt(String encodedJoinedData) throws Exception {
// Base64-decode the joined data
byte[] joinedData = Base64.decode(encodedJoinedData);
// Get IV and encrypted data
byte[] iv = new byte[16];
System.arraycopy(joinedData, 0, iv, 0, iv.length);
byte[] encryptedData = new byte[joinedData.length - iv.length];
System.arraycopy(joinedData, iv.length, encryptedData, 0, encryptedData.length);
// Pad key
byte[] key = padKey("SiadajerSiadajer".getBytes());
Key aesKey = new SecretKeySpec(key, "AES");
// Specify CBC-mode
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
cipher.init(Cipher.DECRYPT_MODE, aesKey, ivParameterSpec); //HERE IS THE ERROR
// Decrypt data
byte[] decryptedData = cipher.doFinal(encryptedData);
// Remove custom padding
byte[] unpaddedData = unpad(decryptedData);
return new String(unpaddedData);
}
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
String encodedJoinedData = "RB6Au33KGOF1/z3BQKqievUmh81Y8q4uw7s4vEs9xurQmNZAKwmhRQtS9wGYKQj8cJaPpK2xaDzx3RppQ8PsM/rQ9/p0Lme+x75dozBEbmFn6Q9eCXOCiCivVsKzZ8Vz";
String decryptedData = new Main().decrypt(encodedJoinedData);
System.out.println(decryptedData + " - " + decryptedData.length());
}
}
Running the code results in error:
Exception in thread "main" java.security.InvalidKeyException: Illegal key size
at javax.crypto.Cipher.checkCryptoPerm(Cipher.java:1039)
at javax.crypto.Cipher.implInit(Cipher.java:805)
at javax.crypto.Cipher.chooseProvider(Cipher.java:864)
at javax.crypto.Cipher.init(Cipher.java:1396)
at javax.crypto.Cipher.init(Cipher.java:1327)
at com.dd.escuel.Main.decrypt(Main.java:43)
at com.dd.escuel.Main.main(Main.java:57)
There are a few issues with the Java-code:
In the PHP-code AES-256 is used, thus, the key must have a length of 32 Byte. Shorter keys are automatically right padded with zeros. This happens in your PHP-code since your key SiadajerSiadajer has a length of only 16 Byte. The key-padding must also be done in the Java-code. For this, e.g. the following Java-method can be used:
private byte[] padKey(byte[] key) {
byte[] paddedKey = new byte[32];
System.arraycopy(key, 0, paddedKey, 0, key.length);
return paddedKey;
}
With Cipher.getInstance("AES") the ECB-mode and PKCS5Padding are chosen by default. The CBC-mode must be explicitly specified in the Java-code with
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
In the PHP-method openssl_encrypt the fourth parameter $options is set to 0 which means that the returned data are Base64-encoded. Thus, in the Java-code the data have to be Base64-decoded before decryption:
byte[]encryptedData = Base64.decode(text);
Since the CBC-mode is used the IV of the encryption must be considered. A possible approach is to Base64-encode the IV in the PHP-code (subsequent to the encryption) with
$encodedIV = base64_encode($iv);
and pass that value to the Java-method decrypt as second parameter. Here, the IV must be decoded and used for the decryption:
byte[] iv = Base64.decode(ivEncoded);
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
...
cipher.init(Cipher.DECRYPT_MODE, aesKey, ivParameterSpec);
In the PHP-method openssl_encrypt the fourth parameter $options is set to 0 which means that the default padding (PKCS7) is used. Moreover, in the PHP-method encrypt a kind of custom padding is implemented (btw: the name of the method is unsuitable since it doesn't encrypt) and thus, it's padded twice. Therefore, after the decryption the custom padding (which may consist of white spaces) must be removed in the Java-code:
byte[] unpaddedData = unpad(decryptedData);
with
private byte[] unpad(byte[] data) {
byte[] unpaddedData = new byte[data.length - data[data.length - 1]];
System.arraycopy(data, 0, unpaddedData, 0, unpaddedData.length);
return unpaddedData;
}
Altogether:
public String decrypt(String text, String ivEncoded) throws Exception {
// Pad key
byte[] key = padKey("SiadajerSiadajer".getBytes());
Key aesKey = new SecretKeySpec(key, "AES");
// Base64 decode data
byte[]encryptedData = Base64.decode(text);
// Base64 decode iv
byte[] iv = Base64.decode(ivEncoded);
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
// Specify CBC-mode
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, aesKey, ivParameterSpec);
// Decrypt
byte[] decryptedData = cipher.doFinal(encryptedData);
// Remove custom padding
byte[] unpaddedData = unpad(decryptedData);
return new String(unpaddedData);
}
Test:
PHP-code input: Plain text ($data), key ($key):
$data = 'This is a plain text which needs to be encrypted...';
$key = "SiadajerSiadajer";
PHP-code output: Base64 encoded IV ($encodedIV), encrypted data ($name):
$encodedIV = 'Dg+Zs3mqIJeDOOEPMT5F4Q==';
$name = '8dXjeQhx2WSswQOQXLcyMKNpa5s413yI2Ku8WiIB/xtA2pEjrKcl5kWtrOh9k4A12Jl0N/z6tH67Wybhp/OwTi1NtiJOZxl3w6YQufE29oU=';
If that output is used as input for the Java-method decrypt the decrypted data equals the plain text.
Concerning the PHP-code I would suggest to remove either the custom or the default (PKCS7) padding (if you have the choice). The latter can be achieved by using the flag OPENSSL_ZERO_PADDING as fourth parameter in the openssl_encrypt method (note: that flag doesn't mean "pad with zero-values", but "no padding"). If the custom padding is kept, you should at least rename the PHP-methods encrypt and decrypt to pad and unpad (or something similar), respectively.
As stated already in the comments, the GCM-mode may be a better choice than the-CBC mode. However, it would be useful to get informed about the basics before coding, e.g. difference between CBC- and GCM-mode,
explanation of the GCM-mode here and here and the pitfalls coming along with the GCM-mode (GCM is secure, but only if you follow certain guidelines, e.g. a uniqe IV/nonce for each message encrypted with the same key).
You can use the PHP-method openssl_get_cipher_methods to find out the permissible AES-modes supported in PHP. This is explained in more detail here. For AES-256 and the AES-mode GCM you have to specify aes-256-gcm (with lowercase(!) letters). Presumably, that's why you get the "Unknown cipher algorithm"-error.
EDIT:
You can use for the encryption the following PHP-code (which is a slightly modified version of your PHP-code from the question):
<?php
function pad($data, $size) {
$length = $size - strlen($data) % $size;
return $data . str_repeat(chr($length), $length);
}
function unpad($data) {
return substr($data, 0, -ord($data[strlen($data) - 1]));
}
$data = 'This is a plain text which needs to be encrypted...';
$key = "SiadajerSiadajer";
$iv_size = 16;
$iv = openssl_random_pseudo_bytes($iv_size, $strong);
$encryptedData = openssl_encrypt(pad($data, 16), 'AES-256-CBC', $key, 0, $iv);
print base64_encode($iv)."\n".$encryptedData."\n";
EDIT 2:
The IV and the encrypted data can be joined before or after the Base64-encoding. The first is more efficient and thus, I've implemented this variant. However, some changes are needed in the PHP- and Java-code and therefore, I post all methods which have changed.
The PHP-code becomes:
<?php
function pad($data, $size) {
$length = $size - strlen($data) % $size;
return $data . str_repeat(chr($length), $length);
}
function unpad($data) {
return substr($data, 0, -ord($data[strlen($data) - 1]));
}
$data = 'This is a plain text which needs to be encrypted...';
$key = "SiadajerSiadajer";
$iv_size = 16;
$iv = openssl_random_pseudo_bytes($iv_size, $strong);
$encryptedData = openssl_encrypt(pad($data, 16), 'AES-256-CBC', $key, OPENSSL_RAW_DATA, $iv);
$joinedData = $iv.$encryptedData;
$encodedJoinedData = base64_encode($joinedData);
print $encodedJoinedData."\n";
And the Jave-method decrypt becomes:
public String decrypt(String encodedJoinedData) throws Exception {
// Base64-decode the joined data
byte[] joinedData = Base64.decode(encodedJoinedData);
// Get IV and encrypted data
byte[] iv = new byte[16];
System.arraycopy(joinedData, 0, iv, 0, iv.length);
byte[] encryptedData = new byte[joinedData.length - iv.length];
System.arraycopy(joinedData, iv.length, encryptedData, 0, encryptedData.length);
// Pad key
byte[] key = padKey("SiadajerSiadajer".getBytes());
Key aesKey = new SecretKeySpec(key, "AES");
// Specify CBC-mode
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
cipher.init(Cipher.DECRYPT_MODE, aesKey, ivParameterSpec);
// Decrypt data
byte[] decryptedData = cipher.doFinal(encryptedData);
// Remove custom padding
byte[] unpaddedData = unpad(decryptedData);
return new String(unpaddedData);
}
and an example for the Java-method main is:
public static void main(String[] args) throws Exception {
String encodedJoinedData = "RB6Au33KGOF1/z3BQKqievUmh81Y8q4uw7s4vEs9xurQmNZAKwmhRQtS9wGYKQj8cJaPpK2xaDzx3RppQ8PsM/rQ9/p0Lme+x75dozBEbmFn6Q9eCXOCiCivVsKzZ8Vz";
String decryptedData = new Main().decrypt(encodedJoinedData);
System.out.println(decryptedData + " - " + decryptedData.length());
}
The usage is as follows:
Encrypt your plain text with the PHP-code. In the example above (PHP-code) the plain text is
$data = 'This is a plain text which needs to be encrypted...';
Then, pass the string contained in $encodedJoinedData to the Java-method decrypt. In the example above (main-method) the string is
String encodedJoinedData = "RB6Au33KGOF1/z3BQKqievUmh81Y8q4uw7s4vEs9xurQmNZAKwmhRQtS9wGYKQj8cJaPpK2xaDzx3RppQ8PsM/rQ9/p0Lme+x75dozBEbmFn6Q9eCXOCiCivVsKzZ8Vz";
The Java-method decrypt will provide the initial plain text.
A final note: If you decide to remove the (redundant) custom padding replace in the PHP-code the line
$encryptedData = openssl_encrypt(pad($data, 16), 'AES-256-CBC', $key, OPENSSL_RAW_DATA, $iv);
with
$encryptedData = openssl_encrypt($data, 'AES-256-CBC', $key, OPENSSL_RAW_DATA, $iv);
and remove the pad- and the unpad-method.
In the Java-code replace the lines
// Remove custom padding
byte[] unpaddedData = unpad(decryptedData);
return new String(unpaddedData);
with
return new String(decryptedData);
and remove the unpad-method.
Edit 3:
The InvalidKeyException (Illegal key size) mentioned in the Edit2-section of the question is already discussed e.g. here InvalidKeyException Illegal key size and here Java Security: Illegal key size or default parameters?.
Hello guys,
I'm working on a project that need to encrypt file content in Android and upload it to a server and then decrypt it with NodeJS service.
I have looked over a few projects that based on AES in Java and NodeJS and found something that worked on Java and not on NodeJS.
Here is the code in Java:
public static String encrypt(String encodeKey, String inputFile) throws Exception {
byte[] input = getStringFromFile(inputFile).toString().getBytes("utf-8");
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] thedigest = md.digest(encodeKey.getBytes("UTF-8"));
SecretKeySpec skc = new SecretKeySpec(thedigest, "AES/ECB/PKCS5Padding");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, skc);
byte[] cipherText = new byte[cipher.getOutputSize(input.length)];
int ctLength = cipher.update(input, 0, input.length, cipherText, 0);
String data = Base64.encodeToString(cipherText, Base64.DEFAULT);
Log.d("Crypto | Length", String.valueOf(ctLength));
Log.d("Crypto | Keypass", encodeKey);
return data;
}
And the code in NodeJS:
var
decipher = crypto.createDecipher('aes-128-ecb', encryption_key),
chunks = [];
chunks.push( decipher.update( new Buffer(fullBuffer, "base64").toString("binary")) );
chunks.push( decipher.final('binary') );
var decrypted = chunks.join("");
As you can see, for each of the files i'm generating a new key-hash for salt (it's the same for decrypt and encrypt);
My problem is that when i'm trying to decrypt it i'm getting this error from NodeJS:
digital envelope routines:EVP_DecryptFinal_ex:wrong final block length
You aren't using the Java Cipher class correctly. You need to call the doFinal method. Since you aren't doing piecemeal encryption anyway you can dispense with the pre-sizing of the ciphertext, eliminate the call to update, and simply call
byte [] cipherText = cipher.doFinal(input);
I'm trying to read a base64 encoded and AES 128-bit encrypted string from PHP, but I'm getting IllegalBlockSizeException.
PHP encrypt:
encrypt("My f awesome test !");
function encrypt($string){
$td = mcrypt_module_open('rijndael-128', '', 'cbc', "1cc251f602cf49f2");
mcrypt_generic_init($td, "f931c96c4a4e7e47", "1cc251f602cf49f2");
$enc = mcrypt_generic($td, $string);
mcrypt_generic_deinit($td);
mcrypt_module_close($td);
return base64_encode($enc);
}
And the returned value is:
McBeY73GQ5fawxIunVKpqUupipeRlt9ntyMRzjbPfTI=
Now I want to read it in Java:
static public String decrypt(String data) throws Exception {
data = new String( Base64.decode(data, Base64.NO_WRAP) );
byte[] keyByte = "f931c96c4a4e7e47".getBytes("UTF-8");
byte[] ivByte = "1cc251f602cf49f2".getBytes("UTF-8");
Key key = new SecretKeySpec(keyByte, "AES");
IvParameterSpec iv = new IvParameterSpec(ivByte);
Cipher c = Cipher.getInstance("AES/CBC/NoPadding");
c.init(Cipher.DECRYPT_MODE, key, iv);
byte[] bval = c.doFinal( data.getBytes("UTF-8") );
return new String( bval );
}
And I'm getting an Exception:
javax.crypto.IllegalBlockSizeException: data not block size aligned
This might be caused by padding?
EDIT
Your error was caused by the conversion of the plaintext to and from a string. It's not necessary anyway - just use byte arrays:
byte[] data = Base64
.decodeBase64("McBeY73GQ5fawxIunVKpqUupipeRlt9ntyMRzjbPfTI=");
byte[] keyByte = "f931c96c4a4e7e47".getBytes("UTF-8");
byte[] ivByte = "1cc251f602cf49f2".getBytes("UTF-8");
Key key = new SecretKeySpec(keyByte, "AES");
IvParameterSpec iv = new IvParameterSpec(ivByte);
Cipher c = Cipher.getInstance("AES/CBC/NoPadding");
c.init(Cipher.DECRYPT_MODE, key, iv);
byte[] bval = c.doFinal(data);
System.out.println(new String(bval)); // Prints My f awesome test !
I recommend you use padding in your encryption, otherwise you cannot cope with arbitrarily-sized input.
the IllegalBlockSizeException thrown on call to doFinal() if: "cipher is a block cipher, no padding has been requested (only in encryption mode), and the total input length of the data processed by this cipher is not a multiple of block size; or if this encryption algorithm is unable to process the input data provided." -http://docs.oracle.com/javase/6/docs/api/javax/crypto/Cipher.html#doFinal%28%29. So its either bad input data or block size.
This is a working version of encryption/decryption between
https://github.com/chaudhuri-ab/CrossPlatformCiphers
Hope this helps someone as it took me a while to work through the little details between the platforms.