I have implemented a CBC Mode AES encryption and decryption mechanism in which I am generating Random IV and Random Secret key for each encryption attempt which is recommended.
Now, I have saved my key in a separate file and IV in another file, but after going through the different forums I have found that the IV should not be kept secure and shall be appended with the Ciphertext while encryption and at the time of decryption we can get the 16 bytes plucked out from that cipher byte array..
Now, I tried a snippet of code to achieve the same, but the result were not good as the first block was not encrypting properly; however the rest of the block does.
Can someone tell me whats wrong with my approach?
Any help will be highly appreciated thanks :).
public static byte[] encrypt (byte[] plaintext,SecretKey key,byte[] IV ) throws Exception {
//Get Cipher Instance
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
//Create SecretKeySpec
SecretKeySpec keySpec = new SecretKeySpec(key.getEncoded(), "AES");
//Create IvParameterSpec
IvParameterSpec ivSpec = new IvParameterSpec(IV);
System.out.println( "IV encrypt= " + ivSpec );
//Initialize Cipher for ENCRYPT_MODE
cipher.init(Cipher.ENCRYPT_MODE, keySpec);
//Perform Encryption
byte[] cipherText = cipher.doFinal(plaintext);
ByteArrayOutputStream b = new ByteArrayOutputStream();
b.write(IV);
b.write( cipherText );
return b.toByteArray();
}
--------------------------------------------------------------------------
public static String decrypt (byte[] cipherText, SecretKey key ) throws Exception
{
//Get Cipher Instance
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
//Create SecretKeySpec
SecretKeySpec keySpec = new SecretKeySpec(key.getEncoded(), "AES");
byte[] iv = Arrays.copyOfRange( cipherText , 0, 16);
//Create IvParameterSpec
IvParameterSpec ivSpec = new IvParameterSpec(iv);
//Initialize Cipher for DECRYPT_MODE
cipher.init(Cipher.DECRYPT_MODE, keySpec,ivSpec);
//Perform Decryption
byte[] decryptedText = cipher.doFinal(cipherText);
return new String(decryptedText);
}
----------------------------------------------------------------------------
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
keyGenerator.init(128);
// Generate Key
SecretKey key = keyGenerator.generateKey();
// Generating IV.
byte[] IV = new byte[16];
SecureRandom random = new SecureRandom();
random.nextBytes(IV);
System.out.println("Original Text : " + plainText);
byte[] cipherText = encrypt(plainText.getBytes("UTF-8") ,key, IV);
String decryptedText = decrypt(cipherText,key, IV);
System.out.println("DeCrypted Text : "+decryptedText);
RESULT
Original Text : This is a plain text which need to be encrypted by AES Algorithm with CBC Mode
DeCrypted Text : ûª¯Î¥pAï2EÞi+¼‹Ý$8ŶÄDDNâOæàtext which need to be encrypted by AES Algorithm with CBC Mode
Just because you copy out the IV here:
byte[] iv = Arrays.copyOfRange( cipherText , 0, 16);
Doesn't mean it isn't still present when you try to decrypt it during:
byte[] decryptedText = cipher.doFinal(cipherText);
You should decrypt everything in ciphertext except for the first 16 bytes. At the moment you're also performing AES decryption on the IV - which is why you're getting garbage.
Related
Encryption done in java using AES and wanted to decrypt in Python, but the result is empty in python (no errors).
See the code before marking as duplicate. I want to detect problem in my code.
java encryption code:
public static String encrypt(String plainText) throws Exception {
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
keyGenerator.init(128);
SecretKey secretKey = keyGenerator.generateKey();
String keyStr = Base64.encodeToString(secretKey.getEncoded(), Base64.NO_WRAP);
byte[] initVector = new byte[16];
(new Random()).nextBytes(initVector);
IvParameterSpec iv = new IvParameterSpec(initVector);
SecretKeySpec skeySpec = new SecretKeySpec(secretKey.getEncoded(), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
byte[] plainTextByte = plainText.getBytes();
byte[] encryptedByte = cipher.doFinal(plainTextByte);
byte[] messagebytes = new byte[initVector.length + encryptedByte.length];
System.arraycopy(initVector, 0, messagebytes, 0, 16);
System.arraycopy(encryptedByte, 0, messagebytes, 16, encryptedByte.length);
return Base64.encodeToString(messagebytes, Base64.NO_WRAP);
}
Python code
def decrypt(key, message):
"""
Input encrypted bytes, return decrypted bytes, using iv and key
"""
byte_array = message.encode("UTF-8")
iv = byte_array[0:16] # extract the 16-byte initialization vector
messagebytes = byte_array[16:] # encrypted message is the bit after the iv
cipher = AES.new(key.encode("UTF-8"), AES.MODE_CBC, iv)
decrypted_padded = cipher.decrypt(messagebytes)
decrypted = unpad(decrypted_padded)
return decrypted.decode("UTF-8");
I see that the Java is explicitly encoding the string as Base 64 at return Base64.encodeToString(messagebytes, Base64.NO_WRAP);
But i see that you are again encoding in python byte_array = message.encode("UTF-8") where in you will have to decode the encrypted message
# Standard Base64 Decoding
decodedBytes = base64.b64decode(encodedStr)
decodedStr = str(decodedBytes, "utf-8")
And only then Decrypt the decoded message.
I need to implement AES-CBC encryption algorithm, but the example that I have creates the initialize IV manually:
//initialize IV manually
byte[] ivBytes = new byte[]{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
The AES-CBC that I have:
package xxxx;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class AES_CBC
{
public static void main(String[] args)
{
try
{
//Lookup a key generator for the AES cipher
KeyGenerator kg = KeyGenerator.getInstance("AES");
SecretKey key = kg.generateKey();
SecretKeySpec keySpec = new
SecretKeySpec(key.getEncoded(), "AES");
//Lookup an instance of a AES cipher
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
//initialize IV manually
byte[] ivBytes = new byte[]{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
//create IvParameterSpecobject
IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);
//Initialize the cipher using the secter key
cipher.init(Cipher.ENCRYPT_MODE, keySpec,ivSpec);
//Message to encrypt
String plainText = "This is a secret!";
//Sequence of byte to encrypt
//Encrypt the message
byte[] cipherText = cipher.doFinal(plainText.getBytes());
System.out.println("Resulting Cipher Text:\n");
for(int i=0;i<cipherText.length;i++)
{
System.out.print(cipherText[i] + " ");
}
System.out.println("");
} catch (Exception e)
{
e.printStackTrace();
} } }
Instead of setting IV manually, I would like to use the output of SHA-1
as the initialize IV, as each time I have different integer value I use SHA-1 to get fixed length and reuse it as initialize IV in AES, for example:
input : 10
digest : b1d5781111d84f7b3fe45a0852e59758cd7a87e5
So, AES code needs 16Byte, and SHA-1 produces 20 Bytes, how can I transfer 20B to be fitted as 16 Byte in AES code?
How to reuse :b1d5781111d84f7b3fe45a0852e59758cd7a87e5 instead of 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
It's unclear what you mean by "use the output of SHA-1 as the initialize IV". What information do you hash? It sounds like security through obscurity.
A good IV should be absolutely random and unpredictable in order to be secure.
Just get random bytes from a SecureRandom to use as IV:
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
SecureRandom rnd = new SecureRandom();
byte[] iv = new byte[cipher.getBlockSize()];
rnd.nextBytes(iv);
IvParameterSpec ivParams = new IvParameterSpec(iv);
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "AES"), ivParams);
byte[] ciphertext = cipher.doFinal(input.getBytes());
Then store the generated IV in the encrypted output together with the ciphertext.
During decryption, first read in the IV, then the ciphertext, and then decrypt.
I receive an error while decrypting: (javax.crypto.BadPaddingException: error:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad decrypt)
My code encryption / decryption:
private static byte[] password = null; // this.password = editText.getBytes();
static final byte[] ivBytes = {'6','g','6','o','d','a','0','u','4','n','w','i','6','9','i','j'};
public static byte[] encrypt(String text) throws Exception {
byte[] clear = text.getBytes("UTF-8");
IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);
KeyGenerator kgen = KeyGenerator.getInstance("AES");
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
sr.setSeed(password);
kgen.init(256, sr); // 192 and 256 bits may not be available
SecretKey skey = kgen.generateKey();
byte[] key = skey.getEncoded();
SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, ivSpec);
byte[] encrypted = cipher.doFinal(clear);
return encrypted;
}
public static String decrypt(byte[] encrypted) throws Exception {
IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);
KeyGenerator kgen = KeyGenerator.getInstance("AES");
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
sr.setSeed(password);
kgen.init(256, sr); // 192 and 256 bits may not be available
SecretKey skey = kgen.generateKey();
byte[] key = skey.getEncoded();
SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, skeySpec, ivSpec);
String decrypted = new String(cipher.doFinal(encrypted));
return decrypted;
}
I suspect that the bug generateKey.
You're doing two things wrong:
Generating a key from a password by using the key to seed a PRNG is a bad idea. Use password-based-encryption instead. Java has an implementation of PKCS#5 that will generate a key from a password.
You need to use a new strong-random IV for each encryption:
When you encrypt, don't specify an IV in cipher.init(). A new one will be generated for you.
encrypt() needs to serialise both the IV (cipher.getIV()) and the ciphertext into a byte array.
decrypt(): separate the IV from the ciphertext, build an IvParameterSpec from it and feed into cipher.init() as you currently do.
Your issues is that when you decrypt, you generate a new secret key instead of deriving it from password. Check out this blog post to see how password-based encryption has to be implemented. There are examples of encryption and decryption functions.
Replace the below line:
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
with below line:
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding","BC");
I am doing a simple implementation for AES Algorithm.
// Get the Key Generator
KeyGenerator kgen = KeyGenerator.getInstance("AES");
kgen.init(128); // 192 and 256 bits may not be available
// Generate the secret key specs.
SecretKey secret = kgen.generateKey();
byte[] raw = secret.getEncoded();
String key = new String(Base64.encodeBase64(raw));
I am saving "key" in database.
Now again in another operation i am fetching a "key" from database n trying to decrypt data. i call decryption function as
String dencryptReq = Utils.decrypt2(new String(Base64.decodeBase64(secretKeyInformation.getSecretKey().getBytes())),Base64.decodeBase64(encryptReq.getBytes()) );
public static String decrypt2(String key, byte[] encrypted)
throws GeneralSecurityException {
byte[] raw = Base64.decodeBase64(key.getBytes());
if (raw.length != 16) {
throw new IllegalArgumentException("Invalid key size.");
}
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, skeySpec,
new IvParameterSpec(new byte[16]));
byte[] original = cipher.doFinal(encrypted);
return new String(original, Charset.forName("US-ASCII"));
}
But it is throwing me invalid key size exception.
If i do in one time this without saving in databse and fetching from database it is working fine.
I have tried your code with some modifications I have used apache commons codec library for Base64 conversion,
/* Derive the key, given password and salt. */
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
KeySpec spec = new PBEKeySpec("password".toCharArray(), "salt".getBytes(), 65536, 128);
SecretKey tmp = factory.generateSecret(spec);
SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");
/* Encrypt the message. */
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secret);
AlgorithmParameters params = cipher.getParameters();
byte[] iv = params.getParameterSpec(IvParameterSpec.class).getIV();
byte[] ciphertext = cipher.doFinal("Hello, World! My data is here.. !".getBytes("UTF-8"));
System.out.println("cipher :"+new String(ciphertext));
/*String-key convertion */
String stringKey=Base64.encodeBase64String(secret.getEncoded());//To String key
byte[] encodedKey = Base64.decodeBase64(stringKey.getBytes());
SecretKey originalKey = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");// Convert from string
/* Decrypt the message, given derived key and initialization vector. */
Cipher cipher1 = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher1.init(Cipher.DECRYPT_MODE, originalKey, new IvParameterSpec(iv));
String plaintext = new String(cipher1.doFinal(ciphertext), "UTF-8");
System.out.println(plaintext);
This code worked perfectly in my system.
I have generated on my android application a pair of RSA Keys.
I receive from a web service
- an AES Key, encrypted with my RSA public key
- a String encoded with the AES key.
So I must do the following:
- decrypt the AES Key
- decrypt the string with the obtained AES Key.
To generate the RSA Keys I did:
keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(size);
keypair = keyGen.genKeyPair();
privateKey = keypair.getPrivate();
publicKey = keypair.getPublic();
On RSA decrypt I use :
public static byte[] decryptRSA( PrivateKey key, byte[] text) throws Exception
{
byte[] dectyptedText = null;
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.DECRYPT_MODE, key);
dectyptedText = cipher.doFinal(text);
return dectyptedText;
}
On AES decrypt I use:
public static byte[] decryptAES(byte[] key, byte[] text) throws Exception {
SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS1Padding");
cipher.init(Cipher.DECRYPT_MODE, skeySpec);
byte[] decrypted = cipher.doFinal(text);
return decrypted;
}
So, in my code, to obtain the decrypted AES Key I do
byte[] decryptedAESKey = sm.decryptRSA(key, Base64.decode(ReceivedBase64EncryptedAESKey));
byte[] decryptedString = sm.decryptAES(decryptedAESKey, Base64.decode(ReceivedEncryptedAESString));
On the end I get a null for decryptedString.
What am I doing wrong ?
Well, the thing is that the key decrypted was 8 byte long and I had to make it 16 byte to be AES 128 bits compatible
So, I made a method to convert it back
private static byte[] GetKey(byte[] suggestedKey)
{
byte[] kRaw = suggestedKey;
ArrayList<Byte> kList = new ArrayList<Byte>();
for (int i = 0; i < 128; i += 8)
{
kList.add(kRaw[(i / 8) % kRaw.length]);
}
byte[] byteArray = new byte[kList.size()];
for(int i = 0; i<kList.size(); i++){
byteArray[i] = kList.get(i);
}
return byteArray;
}
And the rewritten decrypt method:
public static byte[] decryptAES(byte[] key, byte[] text) throws Exception {
SecretKeySpec skeySpec = new SecretKeySpec(GetKey(key), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding","BC");
byte [] iv = new byte[cipher.getBlockSize()];
for(int i=0;i<iv.length;i++)iv[i] = 0;
IvParameterSpec ivSpec = new IvParameterSpec(iv);
cipher.init(Cipher.DECRYPT_MODE, skeySpec, ivSpec);
byte[] decrypted = cipher.doFinal(text);
return decrypted;
}
I'm not sure what language or libraries you are using (looks like Java?), but some really general things to try:
Did you get the encrypted string, ok? Check the length of ReceivedEncryptedAESString and the output of the Base64.decode to check they look alright.
AES decryption can't fail so it must be a problem in the library initialisation. Check the value/state of cipher after the construction step and the init step.
Try a simpler testcase: ignore the RSA encryption and just try to decrypt something using your Cipher object.