PHP PEAR Crypt_CBC implementation in java - java

I am trying to encrypt a value using the BLOWFISH cipher. The implementation must be in java and having the same result as the php version:
require_once('/usr/share/php/Crypt/CBC.php');
....
$cipher = new Crypt_CBC($key, 'BLOWFISH');
$encryptedPassword = $cipher->encrypt($valueToEncrypt);
echo "ENCODED STRING: " . $encryptedPassword;
$encodedPassword = base64_encode($encryptedPassword);
echo "ENCRYPTED STRING: " . $encodedPassword;
I tried something like this, but I cannot manage to get something even remotely similar:
SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(), "Blowfish");
String IV = "RandomIV";
Cipher cipher = Cipher.getInstance("Blowfish/CBC/PKCS5Padding");
init(Cipher.ENCRYPT_MODE, keySpec
, new javax.crypto.spec.IvParameterSpec(IV.getBytes())
);
byte[] encryptedBytes = cipher.doFinal(toEncrypt.getBytes());
String encoded = Base64.getEncoder().encodeToString(encryptedBytes);
Any helping idea is appreciated

Related

Encrypt payload using a key and iv by AES/GCM/NoPadding algorithm in node js and decrypt in java

I have encrypt the file using node.js and decrypt in JAVA. Decryption is done in JAVA using "AES/GCM/Nopadding" algorithm and it is third party app hence I cannot see the JAVA code.
I am encrypting the payload in node.js using "aes-128-gcm" algorithm.
for this, I am try mimicking a working java encryption code
I have tried with crypto and node-forge.
iam getting the output but am getting an error "Bad encryption - payload is not encrypted properly" when submitting payload.
pleas help me to find what I did wrong in this code.
working code in java
public void encrypt(#NonNull final byte[] payload, #NonNull final byte[] key) throws GeneralSecurityException
{
SecretKeySpec codingKey = new SecretKeySpec(key, AES);
Cipher cipher = AEC_GCM_THREAD_CIPHER.get();
byte[] iv = new byte[cipher.getBlockSize()];
RANDOM.nextBytes(iv);
cipher.init(Cipher.ENCRYPT_MODE, codingKey, new IvParameterSpec(iv));
final byte[] encryptedPayload = cipher.doFinal(payload);
byte[] encryptMerchantKey = encryptMerchantKey(key);
String payloadFinal = encodeToUrlString(encryptedPayload); // final payload
String ivFinal = encodeToUrlString(iv); // final iv
String keyFinal = encodeToUrlString(encryptMerchantKey); // final key
System.out.println("Payload");
System.out.println(payloadFinal);
System.out.println("iv");
System.out.println(ivFinal);
System.out.println("key");
System.out.println(keyFinal);
}
code iam tried in node js
function encrypt(payload) {
let key = forge.random.getBytesSync(16);
let iv = forge.random.getBytesSync(16);
let cipher = forge.cipher.createCipher("AES-GCM", key);
cipher.start({ iv: iv});
cipher.update(forge.util.createBuffer(payload));
cipher.finish();
let encrypted = forge.util.encode64(cipher.output.getBytes());
let tag = forge.util.encode64(cipher.mode.tag.getBytes());
let iv64 = forge.util.encode64(iv);
let encryptedPayload = encrypted+tag;
//RSA Encryption
encryptedkey = RSAencrypt(forge.util.encode64(key));
return {
"payload" : base64url.fromBase64(encryptedPayload) ,
"iv" : base64url.fromBase64(iv64).length,
"key" : base64url.fromBase64(encryptedkey)
};
}
Rsa description is working fine abling to decrypt the key.
some problem with aes encryption. as see the code, I added auth tag and encrypted data together but no use.
I have complete example of encryption and decryption in angular and java you can take this example as it is and make changes according to you.
Install node-forge with command "npm install node-forge".
encrypt(msg, pass) {
const key = CryptoJS.lib.WordArray.random(8).toString();
const iv = CryptoJS.lib.WordArray.random(8).toString();
// encrypt some bytes using GCM mode
const cipher = forge.cipher.createCipher('AES-GCM', key);
cipher.start({
iv: iv,
additionalData: 'nvn', // optional
tagLength: 128 // optional, defaults to 128 bits
});
cipher.update(forge.util.createBuffer(msg));
cipher.finish();
const encrypted = cipher.output;
const encodedB64 = forge.util.encode64(encrypted.data);
const tag = cipher.mode.tag;
const tagB64 = forge.util.encode64(tag.data);
// outputs encrypted hex
const trasmitmsg = key+iv+tagB64+encodedB64;
return trasmitmsg
}
I have used CryptoJS to generate random string because random of node-forge giving nontransferable strings.
java code to decrypt this trasmitmsg is
public String getDecrypt(String transmsg) throws Exception {
String keyString = transmsg.substring(0, 16);
String ivString = transmsg.substring(16, 32);
String additionalString = transmsg.substring(32, 56);
String cipherString = transmsg.substring(56);
byte[] keyBytes = keyString.getBytes();
SecretKey key = new SecretKeySpec(keyBytes, "AES");
byte[] ivBytes = ivString.getBytes();
byte[] one = Base64.getDecoder().decode(cipherString);
byte[] two = Base64.getDecoder().decode(additionalString);
byte[] cipherText = ArrayUtils.addAll(one, two);
return decrypt(cipherText, key, ivBytes);
}
public static String decrypt(byte[] cipherText, SecretKey key, byte[] IV) throws Exception {
// Get Cipher Instance
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
// Create SecretKeySpec
SecretKeySpec keySpec = new SecretKeySpec(key.getEncoded(), "AES");
// Create GCMParameterSpec
GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(GCM_TAG_LENGTH , IV);
// Initialize Cipher for DECRYPT_MODE
cipher.init(Cipher.DECRYPT_MODE, keySpec, gcmParameterSpec);
cipher.updateAAD("nvn".getBytes());
byte[] decryptedText = cipher.doFinal(cipherText);
return new String(decryptedText);
}
Cheers!!!
The problem was with forge buffer it need to convert to node buffer
this code is working now. thanks, #Maarten Bodewes for the advice.
function encrypt(payload) {
//initialize forge random buffer
var key = forge.random.getBytesSync(16);
var iv = forge.random.getBytesSync(16);
let cipher = forge.cipher.createCipher("AES-GCM", key);
cipher.start({iv : iv});
cipher.update(forge.util.createBuffer(payload));
cipher.finish();
let encrypted = cipher.output.data;
let tag = cipher.mode.tag.data;
let encryptedLoad = encrypted+tag;
// node buffer and forge buffer differ, so the forge buffer must be converted to node Buffer
iv = Buffer.from(iv, "binary");
encryptedLoad = Buffer.from(encryptedLoad, "binary");
//Calling RSA Encryption
encryptedKey = RSAencrypt(key);
return {
"payload" : base64url(encryptedLoad) ,
"iv" : base64url(iv),
"key" : base64url.fromBase64(encryptedKey)
};
}

Blowfish encryption - encrypted in php and java, I got different encrypted values

The encryption test data:
key: 'ABC';
data:'1234567';
algorithm: MCRYPT_BLOWFISH;
mode: MCRYPT_MODE_ECB;
PHP code
$key = 'ABC';
$data = '1234567';
$alg = MCRYPT_BLOWFISH;
$mode = MCRYPT_MODE_ECB;
$encrypted_data = mcrypt_encrypt($alg, $key, $data, $mode);
$phpgeneratedtoken = base64_encode($encrypted_data);
print "PHP generated token: " . $phpgeneratedtoken." ";
// return
// In6uDpDqt1g=
// Decode the token just generated by php
$decoded = mcrypt_decrypt($alg,$key,base64_decode("In6uDpDqt1g="),$mode);
print "Decoded from php generated token:" . $decoded." ";
//return
//1234567
// This is the encrypted token generated by java with same key and value
$javageneratedtoken = "Cg8qY4gRMaI=";
// Decode the token generated by Java
$decoded = mcrypt_decrypt($alg,$key,base64_decode("Cg8qY4gRMaI="),$mode);
print "Decoded from Java Generated token: " . $decoded." ";
// return
// 1234567
// Both tokens generated by java and php, are decrypted back to the same value.
Java Code:
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
public class BlowfishTest {
public static void main(String[] args) throws Exception {
encrypt("1234567");
decrypt("In6uDpDqt1g=");
}
private static void encrypt(String password) throws Exception {
byte[] keyData = ("ABC").getBytes();
SecretKeySpec secretKeySpec = new SecretKeySpec(keyData, "Blowfish");
Cipher cipher = Cipher.getInstance("Blowfish");
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
byte[] hasil = cipher.doFinal(password.getBytes());
System.out.println(new BASE64Encoder().encode(hasil));
}
private static void decrypt(String string) throws Exception {
byte[] keyData = ("ABC").getBytes();
SecretKeySpec secretKeySpec = new SecretKeySpec(keyData, "Blowfish");
Cipher cipher = Cipher.getInstance("blowfish/ecb/nopadding");
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);
byte[] hasil = cipher.doFinal(new BASE64Decoder().decodeBuffer(string));
System.out.println(new String(hasil));
}
}
The PHP generated encrypted value is: In6uDpDqt1g=.
The Java generated encrypted value is: Cg8qY4gRMaI=.
The issue is at Java Code's
Cipher cipher = Cipher.getInstance("blowfish");
I need to find a way to make my PHP generated encrypted value the same as the Java generated encrypted value.
Both encrypted values I can decrypt them back in PHP. Both encrypted values I could decrypt them back in java too, ONLY IF I set,
Cipher cipher = Cipher.getInstance("blowfish/ecb/nopadding");
But when I try to decrypt PHP encrypted value, In6uDpDqt1g=, in Java.
If I set,
Cipher cipher = Cipher.getInstance("blowfish");
I got the error:
"Given final block not properly padded".
The problem is I am supposed to use PHP to encrypt value, and my client would use java to decrypt value. With the setup Cipher cipher = Cipher.getInstance("blowfish") to decrypt my value.
So I want to find a way that I should use PHP to get the same encrypted value as Java, with Cipher cipher = Cipher.getInstance("Blowfish"), would get.
If there is no such solution, then I would have to ask my client to change his java codes to use
Cipher cipher = Cipher.getInstance("blowfish/ecb/nopadding");
OK, I find my answer. I need to change my php code to
$key = 'ABC';
$data = '1234567';
$alg = MCRYPT_BLOWFISH;
$mode = MCRYPT_MODE_ECB;
$blocksize = mcrypt_get_block_size('blowfish', 'ecb'); // get block size
$pkcs = $blocksize - (strlen($data) % $blocksize); // get pkcs5 pad length
$data.= str_repeat(chr($pkcs), $pkcs);
$encrypted_data = mcrypt_encrypt($alg, $key, $data, $mode);
$phpgeneratedtoken = base64_encode($encrypted_data);
print "PHP generated token: " . $phpgeneratedtoken." ";
// return
// Cg8qY4gRMaI=

Convert Java code to NodeJS - Encryption method

I want to duplicate the JAVA encryption code in NodeJS.
private String DEFAULT_KEY = "abcdwAYserXbzcSeqL/zPg==";
private String text = "abc";
Base64 base64decoder = new Base64();
byte[] raw = base64decoder.decode(key);
SecretKeySpec fSecretKeySpec = new SecretKeySpec(raw, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, fSecretKeySpec);
byte[] encrypted = cipher.doFinal(text.getBytes());
Base64 base64encoder = new Base64();
result = base64encoder.encodeToString(encrypted);
System.out.println("result: "+ result);
The above code generate the encrypted code as: ZkojvMTW+9EEK0owxMuA7A==
I have tried few ways in NodeJS. It is not generating same code for me.
I have tried the following code.
var bKey = new Buffer('abcdwAYserXbzcSeqL/zPg==', 'base64');
var cipher = crypto.createCipher('aes-128-ecb',bKey);
//cipher.setAutoPadding(auto_padding=false);
var crypted = cipher.update('abc',null,'base64');
crypted+=cipher.final('base64');
console.log(crypted);
Can someone help me out?
You probably are running into the issue that createCipher with two arguments takes a password, not a key. This password is first run through a key derivation function before it becomes a key.
Try to use the createCipheriv method instead, using any value for the IV. ECB mode doesn't take an IV, but at least you would be using a key instead of a password.

Java PBEwithMD5andDES decryption issue

I have an encryption method that works well and passes me an encrypted String.
KeySpec keySpec = new PBEKeySpec(encryptionPassword.toCharArray(), salt, iterations);
SecretKey key = SecretKeyFactory.getInstance("PBEWithMD5AndDES").generateSecret(keySpec);
AlgorithmParameterSpec paramSpec = new PBEParameterSpec(salt, iterations);
//encryption
Cipher encoder = Cipher.getInstance(key.getAlgorithm());
encoder.init(Cipher.ENCRYPT_MODE, key, paramSpec);
String str_to_encrypt = "Hello";
byte[] enc = encoder.doFinal(str_to_encrypt.getBytes("UTF8"));
System.out.println("encrypted = " + DatatypeConverter.printBase64Binary(enc));
output: encrypted = vjXsSX0cBNc=
However I also wish to Decrypt this String I've recieved, I am having trouble with this however, especially the reverse of getBytes and printBase64Binary.
This is the first time I have attempted to decrypt, so I used a lot of Googling, I found that 'parseBase64Binary' could get bytes from a string, with 'new String(dec, "US-ASCII") then turning the bytes into a string...somewhere something went askew.
//decryption
Cipher encoder = Cipher.getInstance(key.getAlgorithm());
encoder.init(Cipher.DECRYPT_MODE, key, paramSpec);
String str_to_decrypt = "vjXsSX0cBNc=";
byte[] dec = DatatypeConverter.parseBase64Binary(str_to_decrypt);
System.out.println("decrypted = " + new String(dec, "UTF8"));
output: decrypted = ?5?I}?
The only thing I can think of is that I haven't actually decrypted the string properly, as I have not used encoder.doFinal anywhere... somewhat stumped as to where to use it.
EDIT: Answered own question for completeness, all sorted!
Played around a bit more, I was right in saying I hadn't properly decrypted the String as I hadn't used encoder.doFinal... trial and error led me to this:
String str_to_decrypt = "vjXsSX0cBNc=";
byte[] dec = encoder.doFinal(DatatypeConverter.parseBase64Binary(str_to_decrypt));
System.out.println("decrypted = " + new String(dec, "UTF8"));
output: decrypted = Hello

Decrypting Blowfish/CBC in Java

I have Perl code that decrypts a String and I want to do the same in Java. This is the Perl code:
my $c = Crypt::CBC->new( -key => $keyString, -cipher => 'Blowfish', -header => 'randomiv');
return $c->decrypt_hex($config->{encrypted_password})
This is my attempt at the Java code:
Cipher cipher = Cipher.getInstance("Blowfish/CBC/PKCS5Padding");
// setup an IV (initialization vector) that should be
// randomly generated for each input that's encrypted
byte[] iv = new byte[cipher.getBlockSize()];
new SecureRandom().nextBytes(iv);
IvParameterSpec ivSpec = new IvParameterSpec(iv);
// decrypt
SecretKey secretKey = new SecretKeySpec(Base64.decodeBase64(keyString), "Blowfish");
cipher.init(Cipher.DECRYPT_MODE, secretKey, ivSpec);
byte[] decrypted = cipher.doFinal(Base64.decodeBase64(input));
return Hex.encodeHexString(decrypted);
I'm getting:javax.crypto.BadPaddingException: Given final block not properly padded. But according to this, the Crypt CBC library uses PKCS5 as the default padding.
Also, am I doing the hex encoding at the end right?
One of the problems you have is that you generate a random IV instead of importing the one used for encryption. Do you have access to the IV used at encryption? Could it be at the start of the ciphertext?
I don't do Perl, so I'm not quite sure if my response is valid. Base64 is probably not the right decoding you're looking for.
For creating your SecretKeySpec, try doing something like:
SecretKey secretKey = new SecretKeySpec(keyString.getBytes("ASCII"), "Blowfish");
For decoding the text, check out Hex.decodeHex(char[]) which can be found at http://commons.apache.org/codec/apidocs/org/apache/commons/codec/binary/Hex.html ... so your code might look something like this:
byte[] decrypted = cipher.doFinal(Hex.decodeHex(input.toCharArray()));
String unencryptedStuff = new String(decrypted);

Categories

Resources