PHP Equivalent for Java Triple DES encryption/decryption - java

Am trying to decrypt a key encrypted by Java Triple DES function using PHP mcrypt function but with no luck. Find below the java code
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class Encrypt3DES {
private byte[] key;
private byte[] initializationVector;
public Encrypt3DES(){
}
public String encryptText(String plainText, String key) throws Exception{
//---- Use specified 3DES key and IV from other source --------------
byte[] plaintext = plainText.getBytes();
byte[] myIV = key.getBytes();
byte[] tdesKeyData = {(byte)0xA2, (byte)0x15, (byte)0x37, (byte)0x08, (byte)0xCA, (byte)0x62,
(byte)0xC1, (byte)0xD2, (byte)0xF7, (byte)0xF1, (byte)0x93, (byte)0xDF,
(byte)0xD2, (byte)0x15, (byte)0x4F, (byte)0x79, (byte)0x06, (byte)0x67,
(byte)0x7A, (byte)0x82, (byte)0x94, (byte)0x16, (byte)0x32, (byte)0x95};
Cipher c3des = Cipher.getInstance("DESede/CBC/PKCS5Padding");
SecretKeySpec myKey = new SecretKeySpec(tdesKeyData, "DESede");
IvParameterSpec ivspec = new IvParameterSpec(myIV);
c3des.init(Cipher.ENCRYPT_MODE, myKey, ivspec);
byte[] cipherText = c3des.doFinal(plaintext);
sun.misc.BASE64Encoder obj64=new sun.misc.BASE64Encoder();
return obj64.encode(cipherText);
}
public String decryptText(String encryptText, String key) throws Exception{
byte[] initializationVector = key.getBytes();
byte[] tdesKeyData = {(byte)0xA2, (byte)0x15, (byte)0x37, (byte)0x08, (byte)0xCA, (byte)0x62,
(byte)0xC1, (byte)0xD2, (byte)0xF7, (byte)0xF1, (byte)0x93, (byte)0xDF,
(byte)0xD2, (byte)0x15, (byte)0x4F, (byte)0x79, (byte)0x06, (byte)0x67,
(byte)0x7A, (byte)0x82, (byte)0x94, (byte)0x16, (byte)0x32, (byte)0x95};
byte[] encData = new sun.misc.BASE64Decoder().decodeBuffer(encryptText);
Cipher decipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");
SecretKeySpec myKey = new SecretKeySpec(tdesKeyData, "DESede");
IvParameterSpec ivspec = new IvParameterSpec(initializationVector);
decipher.init(Cipher.DECRYPT_MODE, myKey, ivspec);
byte[] plainText = decipher.doFinal(encData);
return new String(plainText);
}
}
I want to write a PHP function equivalent to the decryptText Java function above. am finding difficulty in generating the exact IV value generated by the Java code for encryption, which is required for the decryption.

This is the PHP equivalent of your Java code (I have copied the PKCS#5-padding from the comment 20-Sep-2006 07:56 of The mcrypt reference)
function encryptText($plainText, $key) {
$keyData = "\xA2\x15\x37\x08\xCA\x62\xC1\xD2"
. "\xF7\xF1\x93\xDF\xD2\x15\x4F\x79\x06"
. "\x67\x7A\x82\x94\x16\x32\x95";
$padded = pkcs5_pad($plainText,
mcrypt_get_block_size("tripledes", "cbc"));
$encText = mcrypt_encrypt("tripledes", $keyData, $padded, "cbc", $key);
return base64_encode($encText);
}
function decryptText($encryptText, $key) {
$keyData = "\xA2\x15\x37\x08\xCA\x62\xC1\xD2"
. "\xF7\xF1\x93\xDF\xD2\x15\x4F\x79\x06"
. "\x67\x7A\x82\x94\x16\x32\x95";
$cipherText = base64_decode($encryptText);
$res = mcrypt_decrypt("tripledes", $keyData, $cipherText, "cbc", $key);
$resUnpadded = pkcs5_unpad($res);
return $resUnpadded;
}
function pkcs5_pad ($text, $blocksize)
{
$pad = $blocksize - (strlen($text) % $blocksize);
return $text . str_repeat(chr($pad), $pad);
}
function pkcs5_unpad($text)
{
$pad = ord($text{strlen($text)-1});
if ($pad > strlen($text)) return false;
if (strspn($text, chr($pad), strlen($text) - $pad) != $pad) return false;
return substr($text, 0, -1 * $pad);
}
But there are some problems you should be aware of:
In your Java code you call String.getBytes() without indicating an encoding. This makes your code non portable if your clear text contains non ASCII-characters such as umlauts, because Java uses the system-default character set. If you can change that I certainly would do so. I recommend you to use utf-8 on both sides (Java and PHP).
You have hard coded the cipher-key and use the IV as "key". I'm by no means a crypto-expert but to me it just feels wrong and may open a huge security leak.
Create a random IV and just concatenate it at the start or at the end of your message. Since the size of the IV is AFAIK equal to the block-size of your cipher you just remove that much bytes from the start or end and have easily separated the IV from the message.
As for the key, it's best to use some kind of key derivation method to generate a key with the right size from a "human generated" password.
Of course, if you have to fulfil some given requirements you can't change your method.

The answer is almost good! Just reverse $keyData and $key in
$encText = mcrypt_encrypt("tripledes", $keyData, $padded, "cbc", $key);
and
$res = mcrypt_decrypt("tripledes", $keyData, $cipherText, "cbc", $key);
otherwise you will always use the same 3DES key. And it's better to rename $keyData to $iv.
Anyway, thanks a lot for the Java sample and the Php-Java translation.

Related

PHP AES-256-CBC encrypted data is different from JAVA AES/CBC/PKCS5PADDING

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.

javax.crypto.BadPaddingException: Given final block not properly padded - Strange error

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?.

How to decrypt a cipher text using Java which is encrypted with Rijndael-256 with custom IV?

I have found a source code at stackoverflow on Rinjndael-256 encryption and decryption written in c#. It is using custom IV appending some extra string. Now I need a decryption method on Java platform. I have found some source code; tried to change and test that. Here is the encryption method on c#:
public static string Encrypt(byte[] text, string key)
{
RijndaelManaged aes = new RijndaelManaged();
aes.KeySize = 256;
aes.BlockSize = 256;
aes.Padding = PaddingMode.None;
aes.Mode = CipherMode.CBC;
aes.Key = Encoding.Default.GetBytes(key);
aes.GenerateIV();
string IV = ("-[--IV-[-" + Encoding.Default.GetString(aes.IV));
ICryptoTransform AESEncrypt = aes.CreateEncryptor(aes.Key, aes.IV);
byte[] buffer = text;
return Convert.ToBase64String(Encoding.Default.GetBytes(Encoding.Default.GetString(AESEncrypt.TransformFinalBlock(buffer, 0, buffer.Length)) + IV));
}
The decryption method on java which is not working for me is:
public static String decrypt(byte[] cipherText, String encryptionKey) throws Exception{
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding", "SunJCE");
SecretKeySpec key = new SecretKeySpec(encryptionKey.getBytes("UTF-8"), "AES");
cipher.init(Cipher.DECRYPT_MODE, key,new IvParameterSpec(IV.getBytes("UTF-8")));
return new String(cipher.doFinal(cipherText),"UTF-8");
}
Edit 1:
I have implemented the php code for decryption.
function decrypt($text, $pkey)
{
$key = $pkey;
$text = base64_decode($text);
$IV = substr($text, strrpos($text, "-[--IV-[-") + 9);
$text = str_replace("-[--IV-[-" . $IV, "", $text);
return rtrim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $key, $text, MCRYPT_MODE_CBC, $IV), "\0");
}
Is there any manual way of implementing Rijndael-256 in Java? As someone said that
There is no support in any of the Sun JCE providers for anything other than Rijndael with the 128-bit blocksize
I don't have an option to use library

Encryption in java, decryption in PHP and vice versa

PHP code:
<?php
class Crypter {
private $Key;
private $Algo;
private $Mode;
private $Iv;
public function __construct() {
$this->Algo = MCRYPT_BLOWFISH;
$this->Mode = MCRYPT_MODE_CBC;
$this->Key = substr('7890', 0, mcrypt_get_key_size($this->Algo, $this->Mode));
}
public function Encrypt($data) {
$iv_size = mcrypt_get_iv_size($this->Algo, $this->Mode);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
$this->Iv = $iv;
$blocksize = mcrypt_get_block_size('blowfish', 'cbc'); // get block size
$pkcs = $blocksize - (strlen($data) % $blocksize); // get pkcs5 pad length
$data.= str_repeat(chr($pkcs), $pkcs); // append pkcs5 padding to the data
$crypt = mcrypt_encrypt($this->Algo, $this->Key, $data, $this->Mode, $this->Iv);
return base64_encode($crypt);
}
public function Decrypt($data) {
$crypt = base64_decode($data);
$decrypt = mcrypt_decrypt($this->Algo, $this->Key, $crypt, $this->Mode, $this->Iv);
$block = mcrypt_get_block_size('blowfish', 'cbc');
$pad = ord($decrypt[($len = strlen($decrypt)) - 1]);
return substr($decrypt, 0, strlen($decrypt) - $pad);
}
}
?>
JAVA code:
package blowfishcbc;
import java.security.MessageDigest;
import java.security.SecureRandom;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
public class BlowfishCBC {
public static void main(String[] args) throws Exception {
String keyString = "7890";
String input = "some data";
Cipher cipher = Cipher.getInstance("Blowfish/CBC/PKCS5Padding");
// for IV
byte[] iv = new byte[cipher.getBlockSize()];
new SecureRandom().nextBytes(iv);
IvParameterSpec ivSpec = new IvParameterSpec(iv);
// for key
byte[] keyData = (keyString).getBytes();
SecretKeySpec keySpec = new SecretKeySpec(keyData, "Blowfish");
// encrypt
cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);
byte[] encrypted = cipher.doFinal(input.getBytes("UTF-8"));
String enc = new BASE64Encoder().encode(encrypted);
System.out.println("encrypted: " + new String(enc));
// decrypt
cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
byte[] dec = new BASE64Decoder().decodeBuffer(enc);
byte[] decrypted = cipher.doFinal(dec);
System.out.println("decrypted: " + new String(decrypted, "UTF-8"));
}
}
I have a scenario where encoding and decoding will be in between two systems like JAVA and PHP. Now the above classes are both examples of PHP and JAVA respectively.
First of all if I use Blowfish algorithm in CBC mode I have to provide a parameter IV (initialization vector). Which is generated randomly and length is equal to the block size. And the block size depends on the encryption mode and algorithm which are being used. Now if I want to decode some data in PHP which was encoded in JAVA then first of all the encryption key must be same as well as the IV (initialization vector).
Now in the java code the IV is generated randomly and if I pass that same IV in the PHP code along with the encoded string (encoded in java) then I get an error in the PHP system that the IV size must be same as the block size.
Now,
for PHP
$iv_size = mcrypt_get_iv_size(MCRYPT_BLOWFISH, MCRYPT_MODE_CBC);
and for JAVA
cipher.getBlockSize();
Both gives the size is 8. But when I pass the IV generated in JAVA to PHP it gives the error of Message: mcrypt_encrypt(): The IV parameter must be as long as the blocksize Now how can I solve this problem.
So far I understand the IV generation process in both system must be same. To be specific the IV length must be same. A little help will be much appreciated. Thanks.

Require similler java code for php encryption script

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.

Categories