Hi I am trying to encrypt a string using AES encryption with initalvector and key. The problem is I am not able to generate a IV which is 16 bytes long. what ever I do its showing IV is not 16 bytes so an exception is raised.
public static String encryptString(String str){
char[] CHARSET_AZ_09 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".toCharArray();
String initialVectorString = Helper.randomString(CHARSET_AZ_09, 16);
byte[] vectorbytes = (new org.apache.commons.codec.binary.Base64()).encode(initialVectorString.getBytes());
String encryptedString = Helper.encrypt(str, initialVectorString, "mykey");
return encryptedString;
}
public static String randomString(char[] characterSet, int length) {
Random random = new SecureRandom();
char[] result = new char[length];
for (int i = 0; i < result.length; i++) {
// picks a random index out of character set > random character
int randomCharIndex = random.nextInt(characterSet.length);
result[i] = characterSet[randomCharIndex];
}
String str = new String(result);
try{
str = new String(str.getBytes(),"UTF8");
}
catch(Exception e){
}
Log.d("check",str.getBytes().length+"");
return str;
}
public static String encrypt(String data,String initialVectorString,String secretKey){
String encryptedString = null;
try{
SecretKeySpec skeySpec = new SecretKeySpec(md5(secretKey).getBytes(), "AES");
IvParameterSpec initialVector = new IvParameterSpec((new org.apache.commons.codec.binary.Base64()).decode(initialVectorString.getBytes()));
Cipher cipher = Cipher.getInstance("AES/CFB8/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, initialVector);
byte[] plainTextByteArray = (new org.apache.commons.codec.binary.Base64()).decode(data.getBytes());
byte[] encryptedByteArray = cipher.doFinal(plainTextByteArray);
encryptedString = new String(encryptedByteArray, "UTF8");
}
catch (Exception e) {
Log.d("Problem decrypting the data", e.getLocalizedMessage());
}
return encryptedString;
}
Does anybody knows how to generate a 16 byte long IV?
Are you asking how to create an array of 16 bytes?
byte[] iv = new byte[ 16 ];
And while we are here, use this to fill it with random data:
SecureRandom random = SecureRandom.getInstance( "SHA1PRNG" );
random.nextBytes( iv );
Your method of obtaining an IV value is quite broken:
String initialVectorString = Helper.randomString(CHARSET_AZ_09, 16);
// ....
IvParameterSpec initialVector = new IvParameterSpec(
(new org.apache.commons.codec.binary.Base64()).decode(
initialVectorString.getBytes()));
It looks like you are creating a 16 character alphanumeric string, then treating this as base64-encoded data (which it isn't) and using that result as an IV. Very odd!
Instead, just use the Random class to generate random bytes. I would suggest Random is secure enough for generating a public IV value, but if you feel super paranoid you could use SecureRandom.
byte[] iv = new byte[16];
random.nextBytes(iv); // where 'random' is a private static final field of
// your class, with type Random
Related
I first generated IV using this method:
public static String generateRandomIV() {
SecureRandom ranGen = new SecureRandom();
byte[] aesKey = new byte[16];
ranGen.nextBytes(aesKey);
StringBuffer result = new StringBuffer();
for (byte b : aesKey) {
result.append(String.format("%02x", b));
}
if (16 > result.toString().length()) {
return result.toString();
} else {
return result.toString().substring(0, 16);
}
}
And then tried to decrypt a String which was encrypted using IV generated by above method:
private String decrypt(String _inputText, String _encryptionKey,
String _initVector) throws Exception {
String _out = "";
int len = _encryptionKey.getBytes("UTF-8").length;
if (_encryptionKey.getBytes("UTF-8").length >= _key.length) {
len = _key.length;
int ivlen = _initVector.getBytes("UTF-8").length;
if (_initVector.getBytes("UTF-8").length > _iv.length)
ivlen = _iv.length;
System.arraycopy(_encryptionKey.getBytes("UTF-8"), 0, _key, 0, len);
System.arraycopy(_initVector.getBytes("UTF-8"), 0, _iv, 0, ivlen);
SecretKeySpec keySpec = new SecretKeySpec(_key, "AES");
IvParameterSpec ivSpec = new IvParameterSpec(_iv);
//Decryption starts
_cx.init(Cipher.DECRYPT_MODE, keySpec);
byte[] decodeValue = Base64.decode(_inputText.getBytes(), Base64.DEFAULT);
Following Exception is coming at last line of above code snippet:
java.lang.RuntimeException: java.security.InvalidAlgorithmParameterException: IV must be specified in CBC mode
I don't see any use of your IV. Try the following cipher initialization:
_cx.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
Hi I have java code which decrypt the ciphertext encrypted using CryptoJS library(AES).
Now i wanted to write the javacode which will encrypt the plaintext again.
Please find the below code.
try {
String secret = "René Über";
String cipherText="U2FsdGVkX1+tsmZvCEFa/iGeSA0K7gvgs9KXeZKwbCDNCs2zPo+BXjvKYLrJutMK+hxTwl/hyaQLOaD7LLIRo2I5fyeRMPnroo6k8N9uwKk=";
byte[] cipherData = Base64.decode(cipherText, Base64.DEFAULT);
byte[] saltData = Arrays.copyOfRange(cipherData, 8, 16);
MessageDigest md5 = MessageDigest.getInstance("MD5");
final byte[][] keyAndIV = GenerateKeyAndIV(32, 16, 1, saltData, secret.getBytes("utf-8"), md5);
SecretKeySpec key = new SecretKeySpec(keyAndIV[0], "AES");
IvParameterSpec iv = new IvParameterSpec(keyAndIV[1]);
byte[] encrypted = Arrays.copyOfRange(cipherData, 16, cipherData.length);
Cipher aesCBC = Cipher.getInstance("AES/CBC/PKCS5Padding");
aesCBC.init(Cipher.DECRYPT_MODE, key, iv);
byte[] decryptedData = aesCBC.doFinal(encrypted);
String decryptedText = new String(decryptedData,"utf-8");
System.out.println("Decrypted "+decryptedText);
//Here I get right plain text as
//System.out: Decrypted The quick brown fox jumps over the lazy dog.
Cipher abc=Cipher.getInstance("AES/CBC/PKCS5Padding");
abc.init(Cipher.ENCRYPT_MODE,key,iv);
byte[] encryptedData=abc.doFinal(decryptedData);
String str=Base64.encodeToString(encryptedData,Base64.DEFAULT);
System.out.println("encrypted "+str);
//Here i want the encrypted text as
// encrypted U2FsdGVkX1+tsmZvCEFa/iGeSA0K7gvgs9KXeZKwbCDNCs2zPo+BXjvKYLrJutMK+hxTwl/hy//aQLOaD7LLIRo2I5fyeRMPnroo6k8N9uwKk=
//but i receive
//System.out: encrypted IZ5IDQruC+Cz0pd5krBsIM0KzbM+j4FeO8pgusm60wr6HFPCX+HJpAs5oPssshGjYjl/J5Ew+//eui
}catch (Exception e)
{}
When I decrypt the code I get correct Plain Text but when I again encrypt the plain text I didnt get the encrypted text as previous.
Please Help.
GenerateKeyAndIV function code:-
public static byte[][] GenerateKeyAndIV(int keyLength, int ivLength, int iterations, byte[] salt, byte[] password, MessageDigest md) {
int digestLength = md.getDigestLength();
int requiredLength = (keyLength + ivLength + digestLength - 1) / digestLength * digestLength;
byte[] generatedData = new byte[requiredLength];
int generatedLength = 0;
try {
md.reset();
// Repeat process until sufficient data has been generated
while (generatedLength < keyLength + ivLength) {
// Digest data (last digest if available, password data, salt if available)
if (generatedLength > 0)
md.update(generatedData, generatedLength - digestLength, digestLength);
md.update(password);
if (salt != null)
md.update(salt, 0, 8);
md.digest(generatedData, generatedLength, digestLength);
// additional rounds
for (int i = 1; i < iterations; i++) {
md.update(generatedData, generatedLength, digestLength);
md.digest(generatedData, generatedLength, digestLength);
}
generatedLength += digestLength;
}
// Copy key and IV into separate byte arrays
byte[][] result = new byte[2][];
result[0] = Arrays.copyOfRange(generatedData, 0, keyLength);
if (ivLength > 0)
result[1] = Arrays.copyOfRange(generatedData, keyLength, keyLength + ivLength);
return result;
} catch (DigestException e) {
throw new RuntimeException(e);
} finally {
// Clean out temporary data
Arrays.fill(generatedData, (byte)0);
}
}
Your ciphertext has "Salted__<8 byte salt>" at the beginning, which you skip when decrypting. You need to prefix the same in your encryption mode if you want to create OpenSSL compatible ciphertext.
Your encryption code ciphertext seems correct when you view it in a base64 to hex decoder, e.g. the one provided here. However, because each character only contains 64 bits and since the bytes have shifted 16 places (which is not divisible by 3), it just seams that your entire ciphertext is incorrect, while it is just missing 16 bytes at the front.
Here posting my working code for android I have used crypto for decryption on the server. Below code is using AES Algorithm
private static final String key = "aesExamplekey";
private static final String initVector = "exampleintvec";
public static String encrypt(String value) {
try {
IvParameterSpec iv = new IvParameterSpec(initVector.getBytes("UTF-8"));
SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
byte[] encrypted = cipher.doFinal(value.getBytes());
// byte[] finalCiphertext = new byte[encrypted.length+2*16];
return Base64.encodeToString(encrypted, Base64.NO_WRAP);
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}`
Server side code asp.net
public string DecryptStringAES(string cipherText)
{
// var keybytes = Encoding.UTF8.GetBytes("7061737323313233");
// var iv = Encoding.UTF8.GetBytes("7061737323313233");
var keybytes = Encoding.UTF8.GetBytes("aesExamplekey");
var iv = Encoding.UTF8.GetBytes("exampleintvec");
var encrypted = Convert.FromBase64String(cipherText);
var decriptedFromJavascript = DecryptStringFromBytes(encrypted, keybytes, iv);
return string.Format(decriptedFromJavascript);
}
I am doing some simple encryption/decryption coding, and I am having a problem, which I cannot figure out by myself.
I have a ciphertext which is hex encoded. The ciphertext is AES with a block length of 128bits and a key length of 256bits. The cipher block mode is CBC. IV is the first block of the cipher text.
The Exception Message is Illegal Key Size.
Here is my decrypt() function:
public static byte[] decrypt() throws Exception
{
try{
byte[] ciphertextBytes = convertToBytes("cb12f5ca1bae224ad44fdff6e66f9a53e25f1000183ba5568958430c11c6eafc62c04de8bf27e0ac7104b598fb492142");
byte[] keyBytes = convertToBytes("CFDC65CB003DD50FF5D6D826D62CF9CA6C64489D60CB02D18C1B58C636F8220D");
byte[] ivBytes = convertToBytes("cb12f5ca1bae224a");
SecretKey aesKey = new SecretKeySpec(keyBytes, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(Cipher.DECRYPT_MODE, aesKey, new IvParameterSpec(ivBytes));
byte[] result = cipher.doFinal(ciphertextBytes);
return result;
}
catch(Exception e)
{
System.out.println(e.getMessage());
}
return null;
}
And I have those functions to do the conversion String/ByteArray
//convert ByteArray to Hex String
public static String convertToHex(byte[] byteArray)
{
StringBuilder sb = new StringBuilder();
for (byte b : byteArray)
{
sb.append(String.format("%02X", b));
}
return sb.toString();
}
//convert String to ByteArray
private static byte[] convertToBytes(String input) {
int length = input.length();
byte[] output = new byte[length / 2];
for (int i = 0; i < length; i += 2) {
output[i / 2] = (byte) ((digit(input.charAt(i), 16) << 4) | digit(input.charAt(i+1), 16));
}
return output;
}
Maybe you can help me.
Thank you very much!
You might have hit the key-size limit in Oracle JRE. From the linked document:
If stronger algorithms are needed (for example, AES with 256-bit keys), the JCE Unlimited Strength Jurisdiction Policy Files must be obtained and installed in the JDK/JRE.
It is the user's responsibility to verify that this action is permissible under local regulations.
I am able to encrypt an SMS and send it from one simulator (Android 2.2) to another.
On the receiving end I am able to do the decryption successfully. But the problem is if do the encryption in one OS version (i.e Android 2.2) and trying to decrypt in another OS version ( Android 2.3 ) i am getting 'Bad padding exception'. I checked that i used the same key on both ends.
The code is shown below
public class ED {
private String Key;
public ED() {
Key = "abc12"; // Assigning default key.
}
public ED(String key) {
// TODO Auto-generated constructor stub
Key = key;
}
public String encrypt(String toEncrypt) throws Exception {
byte[] rawKey = getRawKey(Key.getBytes("UTF-8"));
byte[] result = encrypt(rawKey, toEncrypt.getBytes("UTF-8"));
return toHex(result);
}
public byte[] encrypt(byte[] key, byte[] toEncodeString) throws Exception {
SecretKeySpec sKeySpec = new SecretKeySpec(key, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, sKeySpec);
byte[] encrypted = cipher.doFinal(toEncodeString);
return encrypted;
}
private byte[] getRawKey(byte[] key) throws Exception {
KeyGenerator kGen = KeyGenerator.getInstance("AES");
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
sr.setSeed(key);
kGen.init(128, sr);
SecretKey sKey = kGen.generateKey();
byte[] raw = sKey.getEncoded();
return raw;
}
/************************************* Decription *********************************************/
public String decrypt(String encryptedString) throws Exception {
byte[] rawKey = getRawKey(Key.getBytes("UTF-8"));
System.out.println("Decrypted Key in bytes : "+rawKey);
System.out.println("Key in decryption :"+rawKey);
SecretKeySpec sKeySpec = new SecretKeySpec(rawKey, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, sKeySpec);
byte[] decrypted = cipher.doFinal(toByte(encryptedString));
System.out.println("Decrypted mess in bytes---------->" +decrypted);
return new String(decrypted);
}
public String toHex(byte[] buf) {
if (buf == null)
return "";
StringBuffer result = new StringBuffer(2*buf.length);
for (int i = 0; i < buf.length; i++) {
appendHex(result, buf[i]);
}
return result.toString();
}
private final String HEX = "0123456789ABCDEF";
private void appendHex(StringBuffer sb, byte b) {
sb.append(HEX.charAt((b>>4)&0x0f)).append(HEX.charAt(b&0x0f));
}
public byte[] toByte(String hexString) {
int len = hexString.length()/2;
byte[] result = new byte[len];
for (int i = 0; i < len; i++)
result[i] = Integer.valueOf(hexString.substring(2*i, 2*i+2), 16).byteValue();
return result;
}
}
And I am using sendTextMessage() function to send an sms. I read that encryption/decryption doesn't depend on OS but in this case that is not true. Am I missing any important things while configuring the Cipher (in AES) ? Please let me know.
It's setSeed(). It does not do what you think it does: it just adds the entropy of the given seed to the underlying algorithm. You'll probably find out that it returns somehthing different on both platforms. SHA1PRNG is a pseudo random function, but if it is already seeded, it's likely to return different results.
If the problem is in the key length, you could derivate a key from your password, instead of using it directly. You could use a Hash (like SHA-1, MD5, etc) and crop it to the correct size (128, 192 or 256 bits), or use PBEKeySpec instead of SecretKeySpec.
That to remove problems with the key length. If the padding problems were in the plaintext, I suggest you to use CipherInputStream and CipherOutputStream, which are more programmer-friendly to use than Cipher.doFinal.
Don't rely on KeyGenerator to generate the same key just because you seeded the RNG the same way. If you are pre-sharing a key, share the key, not the seed.
You should also specify the encryption transform completely: "AES/ECB/PKCS5Padding"
Finally, ECB mode is not secure for general use.
See another answer of mine for an example to perform encryption correctly with the JCE.
The problem is with SecureRandom generation. It is giving different results on different platforms. It's because of a bug fix on line 320 (in Gingerbread source) of SHA1PRNG_SecureRandomImpl.java in the engineNextBytes() method where
bits = seedLength << 3 + 64;
was changed to
bits = (seedLength << 3) + 64;
Use SecretKeyFactory() to generate a Secure key instead of secure random.
public class Crypto {
Cipher ecipher;
Cipher dcipher;
byte[] salt = { 1, 2, 4, 5, 7, 8, 3, 6 };
int iterationCount = 1979;
Crypto(String passPhase) {
try {
// Create the key
KeySpec keySpec = new PBEKeySpec(passPhase.toCharArray(), salt, iterationCount);
SecretKey key = SecretKeyFactory.getInstance("PBEWITHSHA256AND128BITAES-CBC-BC").generateSecret(keySpec);
ecipher = Cipher.getInstance(key.getAlgorithm());
dcipher = Cipher.getInstance(key.getAlgorithm());
AlgorithmParameterSpec paramSpec = new PBEParameterSpec(salt, iterationCount);
ecipher.init(Cipher.ENCRYPT_MODE, key, paramSpec);
dcipher.init(Cipher.DECRYPT_MODE, key, paramSpec);
} catch (Exception e) {
// TODO: handle exception
//Toast.makeText(this, "I cought ", Toast.LENGTH_LONG).show();
}
}
public String encrypt(String str) {
String rVal;
try {
byte[] utf8 = str.getBytes("UTF8");
byte[] enc = ecipher.doFinal(utf8);
rVal = toHex(enc);
} catch (Exception e) {
// TODO: handle exception
rVal = "Exception Caught "+e.getMessage();
}
return rVal;
}
public String decrypt(String str) {
String rVal;
try {
byte[] dec = toByte(str);
byte[] utf8 = dcipher.doFinal(dec);
rVal = new String(utf8, "UTF8");
} catch(Exception e) {
rVal = "Error in decrypting :"+e.getMessage();
}
return rVal;
}
private static byte[] toByte(String hexString ) {
int len = hexString.length()/2;
byte[] result = new byte[len];
for ( int i=0; i<len; i++ ) {
result[i] = Integer.valueOf(hexString.substring(2*i, 2*i+2), 16 ).byteValue();
}
return result;
}
private static String toHex(byte[] buf) {
if (buf == null)
return "";
StringBuffer result = new StringBuffer( 2*buf.length);
for ( int i=0; i<buf.length; i++) {
appendHex(result, buf[i]);
}
return result.toString();
}
private final static String HEX = "0123456789ABCDEF";
private static void appendHex(StringBuffer sb, byte b) {
sb.append(HEX.charAt((b >> 4) & 0x0f)).append(HEX.charAt(b & 0x0f));
}
}
For certain reasons I need to implement Rijndael de/compression with a blocksize of 256 bits instead of AES which uses a block size of 128 bits (reason: data is encrypted in PHP using Rijndael...).
How can I change the block-size for a cipher?
If i just get a cipher with "RIJNDAEL/CFB/PKCS5Padding" and try to initialize a IV with 256 bits I get an exception, because the block-size is only 128 bits.
There is no support in any of the Sun JCE providers for anything other than Rijndael with the 128-bit blocksize: this is the AES algorithm. To get rijndael with the 256-bit blocksize you will have to go somewhere else. I suggest the Bouncycastle java library. The RijndaelEngine class has a constructor that accepts a block size in bits. Most people find the PaddedBufferedBlockCipher class to be more convenient when used with suitable padding, e.g.
PaddedBufferedBlockCipher c = new PaddedBufferedBlockCipher(new RijndaelEngine(256), new PKCS7Padding());
Note that PHP mcrypt uses Zero Byte padding so new ZeroBytePadding() should be used instead of new PKCS7Padding().
Bellow a full implementation using CBC and RIJNDAEL 256.
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.InvalidCipherTextException;
import org.bouncycastle.crypto.engines.RijndaelEngine;
import org.bouncycastle.crypto.modes.CBCBlockCipher;
import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher;
import org.bouncycastle.crypto.paddings.ZeroBytePadding;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.crypto.params.ParametersWithIV;
import org.bouncycastle.util.encoders.Base64;
public static String encryptWithAesCBC(String plaintext, String key, String iv)
{
try {
PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(new CBCBlockCipher(new RijndaelEngine(256)), new ZeroBytePadding());
CipherParameters ivAndKey = new ParametersWithIV(new KeyParameter(key.getBytes()), iv.getBytes());
cipher.init(true, ivAndKey);
return new String(Base64.encode(cipherData(cipher, plaintext.getBytes())));
} catch (InvalidCipherTextException e) {
throw new RuntimeException(e);
}
}
public static String decryptWithAesCBC(String encrypted, String key, String iv)
{
try {
byte[] ciphertext = Base64.decode(encrypted);
PaddedBufferedBlockCipher aes = new PaddedBufferedBlockCipher(new CBCBlockCipher(new RijndaelEngine(256)), new ZeroBytePadding());
CipherParameters ivAndKey = new ParametersWithIV(new KeyParameter(key.getBytes()), iv.getBytes());
aes.init(false, ivAndKey);
return new String(cipherData(aes, ciphertext));
} catch (InvalidCipherTextException e) {
throw new RuntimeException(e);
}
}
private static byte[] cipherData(PaddedBufferedBlockCipher cipher, byte[] data) throws InvalidCipherTextException
{
int minSize = cipher.getOutputSize(data.length);
byte[] outBuf = new byte[minSize];
int length1 = cipher.processBytes(data, 0, data.length, outBuf, 0);
int length2 = cipher.doFinal(outBuf, length1);
int actualLength = length1 + length2;
byte[] cipherArray = new byte[actualLength];
for (int x = 0; x < actualLength; x++) {
cipherArray[x] = outBuf[x];
}
return cipherArray;
}
private String md5(String string)
{
try {
java.security.MessageDigest md = java.security.MessageDigest.getInstance("MD5");
byte[] array = md.digest(string.getBytes());
StringBuffer sb = new StringBuffer();
for (int i = 0; i < array.length; ++i) {
sb.append(Integer.toHexString((array[i] & 0xFF) | 0x100).substring(1, 3));
}
return sb.toString();
} catch (java.security.NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
}
When using CFB, PaddedBufferedBlockCipher should be replace by the following:
PaddedBufferedBlockCipher aes = new PaddedBufferedBlockCipher(new CFBBlockCipher(new RijndaelEngine(256),8), new ZeroBytePadding());
// PHP mcrypt uses a blocksize of 8 bit for CFB
Usage:
String salt = "fbhweui3497";
String key = md5(salt);
String iv = md5(md5(salt));
String encrypted = encryptWithAesCBC("text to encript", key, iv);
String decrypted = decryptWithAesCBC(encrypted, key, iv);