Java Equivalent to mcrypt_create_iv in PHP [duplicate] - java

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

Decrypting CommonCrypto encrypted Base 64 encoded string in Java (AES/CBC/PKCS7Padding)

I am trying to decrypt a String with a known key in Java using standard Cipher API.
The encrypted String comes from a Web Service using the standard CommonCrypto Library which responds with some statistics as encrypted strings at regular intervals.
The specs are AES/CBC/PKCS7Padding with KeySize = 32 Bytes and BlockSize = 16 Bytes, and Encoding UTF-8 (raw) & Base64. I intend to write a Java client that can request these statistics, decrypt them and store them for later analyses.
Question 1. Does the CommonCrypto automatically pad keys with extra chars if the key is short? For instance less than 16 Bytes or 32 Bytes.
Question 2. What encoding measures should I take to ensure an identical encryption/decryption on both ends?
Example Strings and Key
String message = "mQp9sp8ri1E0V1Xfso1d5g==Mrf3wtaqUjASlZmUO+BI8MrWsrZSC0MxxMocswfYnqSn/VKB9luv6E8887eCxpLNNAOMB0YXv6OS7rFDFdlvC53pCHo3cVZiLJFqgWN/eNiC9p4RMxyFCcOzWrwKzT5P8sy55DwE25DNJkvMthSaxK5zcP1OdLgBiZFOSxYRsX4rBk7VP7p5xr2uTGjRL+jmGgB9u3TmeCNCr8NxGLNt6g==";
String userKey = "123456789";
private static String decrypt (String message, String userKey) throws UnsupportedEncodingException,
NoSuchPaddingException,
NoSuchAlgorithmException,
InvalidKeyException,
ShortBufferException, BadPaddingException, IllegalBlockSizeException, InvalidAlgorithmParameterException, NoSuchProviderException {
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
if (message.length() >= 48) {
ivFromEncryptedString = message.substring(0, Math.min(message.length(), 24));
messageFromEncryptedString = message.substring(24, message.length());
System.out.println(ivFromEncryptedString);
System.out.println(messageFromEncryptedString);
byte[] data = decodeBase64(messageFromEncryptedString);
byte[] ivData = decodeBase64(ivFromEncryptedString);
paddedKey = padShortKeys(userKey);
byte[] keyBytes = paddedKey.getBytes(CHARSET);
MessageDigest sha = MessageDigest.getInstance("SHA-256"); //key
keyBytes = sha.digest(keyBytes);
SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
IvParameterSpec ivParameterSpec = new IvParameterSpec(ivData);
try {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding", "BC");
cipher.init(Cipher.DECRYPT_MODE, keySpec, ivParameterSpec);
byte [] encrypted = new byte[cipher.getOutputSize(data.length)];
int ctLength = cipher.update(data, 0, data.length, encrypted, 0);
ctLength += cipher.doFinal(encrypted, ctLength);
} catch (Exception e) {
System.out.println(e);
} finally {
return encrypted;
}
}
return null;
}
private static String encodeBase64(byte [] in){
return Base64.getEncoder().encodeToString(in);
}
private static byte[] decodeBase64(String str) throws UnsupportedEncodingException {
return DatatypeConverter.parseBase64Binary(str);
}
Also with the current code status I am getting placehoder characters instead of the desired result.
Thanks in advance folks. :)
CommonCrypto is unclear, which implementation are you using? Apple, Apache, Java Class Cipher or another, please supply a link to it.
Never assume an encryption will pad the key or IV, they should always be provided in the exact length, there is no standard for such padding. If they need padding (they shouldn't) do it yourself.
Typically if encrypted data needs to be expressed as a character string Base64 encoding is used.
As James states, for one-shot encryption just use doFinal(ByteBuffer input, ByteBuffer output) which
encrypts or decrypts data in a single-part operation.
Note: A 9 digit key only has about 33-bits of security which is not close to sufficient. Simple using a hash function is insufficient for deriving an encryption key from a password, instead PBKDF2 or Argon2 should be used.

AES CBC No padding gives extra characters in decrypt JAVa

I am trying to encrypt and decrypt using AES/CBC/NoPadding in JAVA. I did the encryption in both JAVA and PHP using (mcrypt) and got the same result, using the same key and iv. However, when I try to decrypt in JAVA, I get the word correctly but always with extra characters. I read other questions and found that I need to add padding. So I added Padding5 but got the same result. Anyways, I need it without padding because that is how it works in PHP. Any help is appreciated. My code is below and the result is here:]2
public class RijndaelCrypt {
//private String key = "2a4e2471c77344b3bf1de28ab9aa492a444abc1379c3824e3162664a2c2b811d";
private static String iv = "beadfacebadc0fee";
private static String hashedKey = "6a2dad9f75b87f5bdd365c9de0b9c842";
private static Cipher cipher;
public static String decrypt(String text) throws UnsupportedEncodingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException, NoSuchAlgorithmException, NoSuchPaddingException, NoSuchProviderException {
SecretKeySpec keyspec = new SecretKeySpec(hashedKey.getBytes("UTF-8"), "AES");
IvParameterSpec ivspec = new IvParameterSpec(iv.getBytes("UTF-8"));
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
cipher.init(Cipher.DECRYPT_MODE, keyspec, ivspec);
byte[] decodedValue = Base64.decode(text.getBytes("UTF-8"));
byte[] decryptedVal = cipher.doFinal(decodedValue);
return new String(decryptedVal);
}
public static String encryptNew(String data) throws Exception {
cipher = Cipher.getInstance("AES/CBC/NoPadding");
int blockSize = cipher.getBlockSize();
byte[] dataBytes = data.getBytes("UTF-8");
int plaintextLength = dataBytes.length;
if (plaintextLength % blockSize != 0) {
plaintextLength = plaintextLength + (blockSize - (plaintextLength % blockSize));
}
byte[] plaintext = new byte[plaintextLength];
System.arraycopy(dataBytes, 0, plaintext, 0, dataBytes.length);
SecretKeySpec keyspec = new SecretKeySpec(hashedKey.getBytes("UTF-8"), "AES");
IvParameterSpec ivspec = new IvParameterSpec(iv.getBytes("UTF-8"));
cipher.init(Cipher.ENCRYPT_MODE, keyspec, ivspec);
byte[] encrypted = cipher.doFinal(plaintext);
return DatatypeConverter.printBase64Binary(encrypted);
}
public static void main (String [] args) throws Exception
{
Security.addProvider(new BouncyCastleProvider());
String data = "Hello";
System.out.println("New Decrypted: " + RijndaelCrypt.decrypt(RijndaelCrypt.encryptNew(data)));
System.out.println("New Encryption: " + RijndaelCrypt.encryptNew(data));
}
}
The PHP mcrypt wrapper (or underlying mcrypt library) pads with zero bytes up to the block length (zero to 15 padding bytes, if 16 is the block size of the cipher). After that the blocks are encrypted by the cipher.
When decrypting in Java you need to manually remove any zero bytes from the right hand side of the plaintext after decryption using NoPadding. The zero valued padding bytes can of course be seen when hex-encoding the decrypted plaintext. However when outputting a string the zero bytes are either left out or converted to a replacement character (depending on the character set and terminal).
Note that the PHP zero padding has one big drawback: if the plaintext ends with one or more zero valued bytes it could be stripped from the decrypted plaintext by any unpadding routine. This is why PKCS#7 padding (which pads 1 to 16 bytes) should be preferred.
Also note that PHP actually needs rtrim("\0") to remove the zero bytes itself; mcrypt just leaves them there, but they generally won't be printed.
Note that Bouncy Castle crypto libraries also has ZeroPadding as option. However, this is zero padding of 1 to 16 bytes (i.e. it always pads/unpads) so it is incompatible with the padding defined used by PHP mcrypt and may fail if the size of the plaintext can be divided by the block size of the cipher.

AES-256: IV vector misunderstanding between Ruby and Java implementations

I have "inherited" a Ruby on Rails app, and I must translate this app from Ruby to Java, and the most important thing, I don't have contact with the creator.
My problem is with the IV vector in AES-256 authentication. Ruby app uses AESCrypt gem to encrypt and decrypt user's password. It works fine, and I have already some thousands of users in DB.
The problem is when I try to do the same in Java (I've already updated JCE to allow 256bit key lenght). The Key and the IV are writen as binary strings in ruby source code (see bellow), and when I try to use it in Java I get a exception which say that the IV lenght must be 16 bytes long (I know that it must be 16 bytes long, but the binary string in Ruby has 32 characters).
Ruby code (works fine):
require 'openssl'
require 'digest/md5'
require 'base64'
module AESCrypt
KEY = "AB1CD237690AF13B6721AD237A"
IV = "por874hyufijdue7w63ysxwet4320o90"
TYPE = "AES-256-CBC"
def AESCrypt.key(key)
key = Digest::MD5.hexdigest(key)
key.slice(0..32)
end
# Encrypts a block of data given an encryption key and an
# initialization vector (iv). Keys, iv's, and the data returned
# are all binary strings. Cipher_type should be "AES-256-CBC",
# "AES-256-ECB", or any of the cipher types supported by OpenSSL.
# Pass nil for the iv if the encryption type doesn't use iv's (like
# ECB).
#:return: => String
#:arg: data => String
#:arg: key => String
#:arg: iv => String
#:arg: cipher_type => String
def AESCrypt.encrypt(data)
return nil if data.nil?
return data if data.blank?
aes = OpenSSL::Cipher::Cipher.new(TYPE)
aes.encrypt
aes.key = AESCrypt.key(KEY)
aes.iv = IV if IV != nil
result = aes.update(data) + aes.final
Base64.encode64(result)
end
end
and this is my Java code (it should do the same, seems that works with a 16 chars/bytes IV):
public static void main(String[] args) throws UnsupportedEncodingException {
String KEY = "AB1CD237690AF13B6721AD237A";
String IV = "por874hyufijdue7w63ysxwet4320o90";
SecretKeySpec key = generateKey(KEY);
String message = "password";
final Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
IvParameterSpec ivSpec = new IvParameterSpec(IV.getBytes());
cipher.init(Cipher.ENCRYPT_MODE, key, ivSpec);
byte[] ciphedText = cipher.doFinal(message.getBytes());
String encoded = Base64.encodeBase64String(ciphedText);
System.out.println("ENCRYPTED text= " + encoded);
}
public static SecretKeySpec generateKey(final String password) throws NoSuchAlgorithmException, UnsupportedEncodingException {
final MessageDigest digest = MessageDigest.getInstance("MD5");
byte[] bytes = password.getBytes("UTF-8");
digest.update(bytes, 0, bytes.length);
byte[] key = digest.digest();
SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");
return secretKeySpec;
}
And I'm getting this exception (obviously):
java.security.InvalidAlgorithmParameterException: Wrong IV length: must be 16 bytes long
at com.sun.crypto.provider.CipherCore.init(CipherCore.java:516)
at com.sun.crypto.provider.AESCipher.engineInit(AESCipher.java:339)
at javax.crypto.Cipher.implInit(Cipher.java:801)
at javax.crypto.Cipher.chooseProvider(Cipher.java:859)
at javax.crypto.Cipher.init(Cipher.java:1370)
at javax.crypto.Cipher.init(Cipher.java:1301)
at com.javi.test.security.Test.main(Test.java:129)
I guess my problem is the way I convert the IV java string in byte[]. I think that openSSL code in ruby is unpacking (or doing something internally) the 32 bytes of the IV to 16 bytes. I have tried a lot of things, but I'm going crazy.
Anyone had the same problem or figure out where could be my problem?
I have posted the encryption code but I hace the same issue with decryption.
Thanks in advance, I'll be very grateful with every answer. :)
First, your IV is not actually iv, IV should be HEX encoded, but you have ASCII string "por874hyufijdue7w63ysxwet4320o90", may be it is some how encoded?
Second, IV.getBytes() will transofr IV's each character to hex encoding like p = 0x70, o = 0x6F, r = 0x72, etc...
It is not a useful answer, but may be hint.
Actually IV must be the same length as block cipher single block length. You have 32 bytes long IV itself, if you make IV.getBytes() IV length should match the cipher block length

C# encryption realization in java

I am developing an android application in which i need to implement some encryption.
At the same time i have to keep compatibility with other versions of application (e.g. for the WP platform), which are already in production.
This is C# code:
static public byte[] Encrypt(String passphrase, byte[] data)
{
//encrypted data
byte[] buffer = null;
//crypto handles
IntPtr hProvider = IntPtr.Zero;
IntPtr hKey = IntPtr.Zero;
try
{
if (!WinApi.CryptAcquireContext(ref hProv, null, WinApi.MS_DEF_PROV,
WinApi.PROV_RSA_FULL, WinApi.CRYPT_VERIFYCONTEXT))
Failed("CryptAcquireContext");
//128 bit hash object
if (!WinApi.CryptCreateHash(hProv,
WinApi.CALG_MD5, IntPtr.Zero, 0, ref hHash))
Failed("CryptCreateHash");
// add passphrase to hash
byte[] keyData = ASCIIEncoding.ASCII.GetBytes(passphrase);
if (!WinApi.CryptHashData(hHash, keyData, (uint)keyData.Length, 0))
Failed("CryptHashData");
// create 40 bit crypto key from passphrase hash
if (!WinApi.CryptDeriveKey(hProv, WinApi.CALG_RC2,
hHash, WinApi.CRYPT_EXPORTABLE, ref hKey))
Failed("CryptDeriveKey");
// determine how large of a buffer is required
// to hold the encrypted data
uint dataLength = (uint)data.Length;
uint bufLength = (uint)data.Length;
if (!WinApi.CryptEncrypt(hKey, IntPtr.Zero, true,
0, null, ref dataLength, bufLength))
Failed("CryptEncrypt");
// allocate and fill buffer with encrypted data
buffer = new byte[dataLength];
Buffer.BlockCopy(data, 0, buffer, 0, data.Length);
dataLength = (uint)data.Length;
bufLength = (uint)buffer.Length;
if (!WinApi.CryptEncrypt(hKey, IntPtr.Zero, true,
0, buffer, ref dataLength, bufLength))
Failed("CryptEncrypt");
}
.......
}
I have tried to implement it in Java. AFAIK, there is no default RC2 crypto provider in android, so i used Spongy Castle library (bouncycastle fork for android).
This is my Java code:
public static byte[] encryptLB(byte[] key, byte[] iv, byte[] unencrypted)
throws NoSuchAlgorithmException, ... {
MessageDigest digest = MessageDigest.getInstance("MD5");
digest.update(key);
byte[] hash = digest.digest(); //build the hash (128 bit)
Cipher cipher = Cipher.getInstance("RC2/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(hash, "RC2"));
byte[] unByte = unencrypted;
byte[] encrypted = cipher.doFinal(unencrypted);
return encrypted;
}
And the results of these functions are different.
What i am doing wrong?
How do i do it right?
Any examples and suggestions are welcome.
With best regards.
UPD The main goal is to get identical byte arrays from both functions. I can't modify c# code. First, i want to clarify am i right with c#-code:
it creates MD5 hash from the passphrase's bytes array
it generates crypto key using proprietary WinApi.CryptDeriveKey function
this key is used to encrypt data using RC2 algorithm
Second, i want to know whether there is an analogue of WinApi.CryptDeriveKey function - as i see this is the main problem.
Sorry, my question is too general, because i am not sure that the problem above (CryptDeriveKey) is the only.
Unfortunately I don't have access to a Windows machine to test this on right now but here is what I think should be interoperable.
public static byte[] encrypt(String passphrase, byte[] data) throws Exception {
// Hash the ASCII-encoded passphrase with md5
byte[] keyData = passphrase.getBytes(Charset.forName("US-ASCII"));
MessageDigest md = MessageDigest.getInstance("MD5");
byte [] md5HashOfKey = md.digest(keyData);
// Need to use bouncycastle (spongycastle on Android) to get RC2
Security.addProvider(new BouncyCastleProvider());
Cipher rc2 = Cipher.getInstance("RC2/CBC/PKCS5PADDING");
// Create an RC2 40-bit key from the 1st 5 bytes of the hash.
SecretKeySpec rc2KeySpec = new SecretKeySpec(md5HashOfKey, 0, 5, "RC2");
rc2.init(Cipher.ENCRYPT_MODE, rc2KeySpec);
byte [] cipher = rc2.doFinal(data);
return cipher;
}

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