Received byte array has different values than sent, AES encryption + IV - java

I'm trying to encrypt communication using AES, in java.
The key is hardcoded, and the IV is generated randomly through a SecureRandom and sent as the first 16 bytes of the encrypted message. However, when I try to read the first 16 bytes of the received message, I don't get the same byte array that I generated.
Here's the problematic code:
static byte[] bytes = new byte[16];
public static byte[] encrypt(String key, String message) {
try {
SecureRandom random = new SecureRandom();
random.nextBytes(bytes);
System.out.println("Outputting generated IV:");
for(int i=0; i < bytes.length; i++){
System.out.println(bytes[i]);
}
IvParameterSpec iv = new IvParameterSpec(bytes);
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 = Base64.encodeBase64(cipher.doFinal(message.getBytes()));
System.out.println("encrypted string: "
+ Base64.encodeBase64String(encrypted));
byte[] sendMe = new byte[bytes.length + encrypted.length];
System.arraycopy(bytes, 0, sendMe, 0, bytes.length);
System.arraycopy(encrypted, 0, sendMe, 0, encrypted.length);
return sendMe;
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}
public static String decrypt(String key, byte[] received) {
try {
byte[] initVector = Arrays.copyOfRange(received, 0, 16);
byte[] encrypted = Arrays.copyOfRange(received, 16, received.length+1);
System.out.println("Outputting received IV:");
for(int i = 0; i < initVector.length; i++){
System.out.println(initVector[i]);
}
IvParameterSpec iv = new IvParameterSpec(initVector);
SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
byte[] original = cipher.doFinal(Base64.decodeBase64(encrypted));
return new String(original);
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}
Running this once, for example's sake, with the text "Hello world!" yielded the following output:
Outputting generated IV:
-79
-3
102
-103
-13
67
-63
-18
23
-114
74
26
18
-97
77
115
Outputting received IV:
36
-118
-87
-72
-119
43
101
55
50
-62
125
-98
65
35
48
-87
Which are obviously not the same.
Any help would be appreciated.

You are overwriting your IV with the encrypted data:
System.arraycopy(bytes, 0, sendMe, 0, bytes.length);
System.arraycopy(encrypted, 0, sendMe, 0, encrypted.length); // Overwrites the iv
You probably want:
System.arraycopy(bytes, 0, sendMe, 0, bytes.length);
System.arraycopy(encrypted, 0, sendMe, 16, encrypted.length);

Related

How to use custom secret key instead of generating a new secret key in AES 256 encryption

I am now trying to develop a AES 256 CFB encryption program in Java.
I wish to use my own secret key (Hex). However, it returned that there is something wrong with the key length.
Here is the code
class encryption_different {
private static String SECRET_KEY = "afd7a42b34f4875e05d210ea1252e02d5305becdb752f3553d657bab1236f733";
public static String encrypt(String strToEncrypt) {
try {
//IV handling
byte[] iv = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
IvParameterSpec ivspec = new IvParameterSpec(iv);
//convert custom key string to Secret key format
byte[] decodedKey = Base64.getDecoder().decode(SECRET_KEY);
SecretKey originalKey = new SecretKeySpec(decodedKey, 0, decodedKey.length, "AES");
Cipher cipher = Cipher.getInstance("AES/CFB/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, originalKey, ivspec);
return Base64.getEncoder()
.encodeToString(cipher.doFinal(strToEncrypt.getBytes(StandardCharsets.UTF_8)));
} catch (Exception e) {
System.out.println("Error while encrypting: " + e.toString());
}
return null;
}
public static void main(String[] args){
encrypt("password1");
}
}
The error message I got:
Error while encrypting: java.security.InvalidKeyException: Invalid AES key length: 48 bytes
And therefore, I would like to know why I am getting this error message and how to fix it.

Java String not equal String?

I want to decrypt a String. Here my Decryption and Encryption methods.
public String encrypt(String message) throws Exception {
byte[] messageInBytes = message.getBytes();
encryptionCipher = Cipher.getInstance("AES/GCM/NoPadding");
encryptionCipher.init(Cipher.ENCRYPT_MODE, key);
byte[] encryptedBytes = encryptionCipher.doFinal(messageInBytes);
return encode(encryptedBytes);
}
public String decrypt(String encryptedMessage) throws Exception {
byte[] messageInBytes = decode(encryptedMessage);
Cipher decryptionCipher = Cipher.getInstance("AES/GCM/NoPadding");
GCMParameterSpec spec = new GCMParameterSpec(T_LEN , encryptionCipher.getIV());
decryptionCipher.init(Cipher.DECRYPT_MODE, key, spec);
byte[] decryptedBytes = decryptionCipher.doFinal(messageInBytes);
return new String(decryptedBytes);
}
Here the main:
public static void main(String[] args) {
try {
AES aes = new AES();
aes.convertStringKeyToSecretKey();
String encryptedMessage = aes.encrypt("Peter");
String decryptedMessage = aes.decrypt(encryptedMessage);
System.err.println("Encrypted Message : " + encryptedMessage);
System.err.println("Decrypted Message : " + decryptedMessage);
} catch (Exception ignored) {
}
}
When I change encryptedMessage to a own String like:
String decryptedMessage = aes.decrypt("xDFzl9HsenqKspdEbL/m9I5X6dqn");
It does nothing
I hope you can help me.
Best Regards
Christian
public static String encryptAES(String toEncrypt, final String key1, final String key2) throws Exception {
try {
byte[] iv = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
KeySpec keySpec = new PBEKeySpec(key1.toCharArray(), key2.getBytes(), 65536, 256);
SecretKey secretKey = secretKeyFactory.generateSecret(keySpec);
SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getEncoded(), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
return Base64.getEncoder().encodeToString(cipher.doFinal(toEncrypt.getBytes(StandardCharsets.UTF_8)));
} catch (Exception ex) {
throw new Exception(ex);
}
}
public static String decryptAES(String toDecrypt, final String key1, final String key2) throws Exception {
try {
byte[] iv = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
KeySpec keySpec = new PBEKeySpec(key1.toCharArray(), key2.getBytes(), 65536, 256);
SecretKey secretKey = secretKeyFactory.generateSecret(keySpec);
SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getEncoded(), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);
return new String(cipher.doFinal(Base64.getDecoder().decode(toDecrypt)));
} catch (Exception ex) {
throw new Exception(ex);
}
}
Here you can still expand its secureness by creating your own IV Spec key, which should contain only 16 character.
Actually, this is how AES encryption worked for me, You can also check this repo in GitHub for additional encryption methods.

Size file is not equal after encrypt and decrypt

My file is 1 MB . And after i encrypt and decrypt it with AES-128, It is only 960 kB. Is it problem with my code?
KeyGenerator keyGenerator = new KeyGenerator();
byte[] key = keyGenerator.generateKey(password, 16);
System.out.println("Key :::::::" + Arrays.toString(key));
SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
`
and next :
byte[] iv = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
IvParameterSpec ivSpec = new IvParameterSpec(iv);
cipher.init(cipherModel, keySpec, ivSpec);
System.out.println("cipher init done");
FileInputStream inputStream = new FileInputStream(inputFile);
FileOutputStream outputStream = new FileOutputStream(outputFile);
byte[] inputBytes = new byte[64];
int count;
while ((count = inputStream.read(inputBytes)) > 0) {
byte[] output = cipher.update(inputBytes, 0, count);
if (output != null)
outputStream.write(output);
}
byte[] outputBytes = cipher.doFinal();
if (outputBytes != null)
outputStream.write(outputBytes);
inputStream.close();
outputStream.close();`

AES-GCM: AEADBadTagException: mac check in GCM failed

While trying to implement AES-GCM for the first time, we are facing issue in generating AuthenticationTag, Encrypted cipher & GCM mac check fails in the end. For out current implementation tag[] is being populated but byte[] encrypted remains empty. And because of this cipher.doFinal(data1, offset) gives 'mac check in GCM failed'. It appears to be some issue around the size of byte arrays, can someone please share on what basis should the output buffer size be determined? Should this be done in chunks?
Any pointers/links to AES-GCM implementation will be highly appreciated.
Following is our implementation:
public class GCMTest {
public static void main(String[] args) throws Exception {
//***********************************************************
//Key
byte[] key = MessageDigest.getInstance("MD5").digest("1234567890123456".getBytes("UTF-8"));//this is the random key
//Iv
SecureRandom srand = SecureRandom.getInstance("SHA1PRNG");
byte[] iv = new byte[256];
srand.nextBytes(iv);
//Input
byte[] data="inputPlainText".getBytes();
final GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(16 * Byte.SIZE, iv);
//***********************************************************
//Encryption
final Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding", new BouncyCastleProvider());
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "AES"), gcmParameterSpec);
cipher.updateAAD("MyAAD".getBytes("UTF-8"));
//Encrypted output
final byte[] encrypted = new byte[cipher.getOutputSize(data.length)];
cipher.update(data, 0, data.length, encrypted, 0); //Not being updated for current data.
//Tag output
byte[] tag = new byte[cipher.getOutputSize(data.length)];
cipher.doFinal(tag, 0);
//***********************************************************
//Decryption
final SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
cipher.init(Cipher.DECRYPT_MODE, keySpec, gcmParameterSpec);
cipher.updateAAD("MyAAD".getBytes("UTF-8"));
//What size should be assigned to outputBuffer?
final byte[] data1 = new byte[256];
int offset = cipher.update(encrypted, 0, encrypted.length, data1, 0);
cipher.update(tag, 0, tag.length, data1, offset);
cipher.doFinal(data1, offset);
boolean isValid = checkEquals(data, data1);
System.out.println("isValid :"+isValid);
}
private static boolean checkEquals(byte[] a, byte[] b)
{
int diff = a.length ^ b.length;
for(int i = 0; i < a.length && i < b.length; i++)
diff |= a[i] ^ b[i];
return diff == 0;
}
}
It gives following exception:
Exception in thread "main" javax.crypto.AEADBadTagException: mac check in GCM failed
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:408)
at org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher$AEADGenericBlockCipher.doFinal(Unknown Source)
at org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher.engineDoFinal(Unknown Source)
at javax.crypto.Cipher.doFinal(Cipher.java:2068)
at GCMTest.main(GCMTest.java:56)
Thanks in advance!!
I was having this same issue. For me, it had to do with encoding the string. I ended up doing:
Get ASCII bytes from string you want to encrypt (UTF-8 in your case)
Encrypt bytes
Encode bytes in Base64 string
Then to decrypt string I did:
Decode encrypted string to Base64 bytes
Decrypt Base64 bytes
Create new string using ASCII.
Here is the code :
private String encrypt(String src) {
byte[] srcBytes = src.getBytes(StandardCharsets.US_ASCII);
cipher.init(Cipher.ENCRYPT_MODE, secretKey, secureRandom);
byte[] cipherText = cipher.doFinal(srcBytes);
byte[] encryptedBytes = new byte[12 + cipherText.length];
System.arraycopy(ivBytes, 0, encryptedBytes, 0, 12);
System.arraycopy(cipherText, 0, encryptedBytes, 12, cipherText.length);
return Base64.encodeToString(encryptedBytes, Base64.DEFAULT);
}
private String decrypt(String encryptedString) {
byte[] encryptedBytes = Base64.decode(encryptedString, Base64.DEFAULT);
cipher.init(Cipher.DECRYPT_MODE, secretKey, new GCMParameterSpec(128, encryptedBytes, 0, 12));
byte[] decryptedBytes = cipher.doFinal(encryptedBytes, 12, encryptedBytes.length-12);
return Base64.encodeToString(decryptedBytes, Base64.DEFAULT);
}
Any variables I didn't include how to initialize them can be inferred from the java docs. I was trying to do this in Android so I'm not sure how different it is. I found this post to be incredibly helpful: Java AES/GCM/NoPadding - What is cipher.getIV() giving me?
you should update section code
error section code:
//What size should be assigned to outputBuffer?
final byte[] data1 = new byte[256];
int offset = cipher.update(encrypted, 0, encrypted.length, data1, 0);
cipher.update(tag, 0, tag.length, data1, offset);
cipher.doFinal(data1, offset);
update the new code:
final byte[] data1 = new byte[encrypted.length];
int offset = cipher.update(encrypted, 0, encrypted.length, data1, 0);
offset += cipher.update(tag, 0, tag.length, data1, offset);
cipher.doFinal(data1, offset);

Decryption fails on chaging the IV in AES algorithm

I have some encoded value received by my webservice. Now I have to decode this encoded string and get the bytes of it.
Now I am using this byte array as my IV value for decrypting a values using AES algorithm. But it is not giving me the proper output rather throws some junk values.
Here is my code,
byte[] decoded = Base64.decodeBase64(((String) "MTIzNDU2Nzg5MTIzNDU2Nw==").getBytes());
System.out.println(new String(decoded, "UTF-8") + "\n");
MTIzNDU2Nzg5MTIzNDU2Nw== is the encoded string received from the request xml.
Now decoded will be IV for the next number to be decrypted,
String c = decrypt1("JHIlf4iXM53tgsKHQEv1dlsUTeLr5GP3LfSNGlWENkg=", decoded);
System.out.println(c);
JHIlf4iXM53tgsKHQEv1dlsUTeLr5GP3LfSNGlWENkg= is the digit to be decrypted.
public static String decrypt1(Object data, byte[] ivBytes) throws InvalidKeyException,
InvalidAlgorithmParameterException, IllegalBlockSizeException,
BadPaddingException, UnsupportedEncodingException {
byte[] keyBytes = "keyPhrase".getBytes();
Cipher cipher = null;
if (ivBytes.length<16) {
System.out.println("error" + ivBytes.length);
//ivBytes = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 97, 98, 99, 100, 101, 102, 103};
}
byte[] byteArr = null;
try {
SecretKey secretKey = new SecretKeySpec(keyBytes, "AES");
cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(
ivBytes));
if (data instanceof String) {
byteArr = Base64.decodeBase64(((String) data).getBytes("UTF-8"));
}
byteArr = (cipher.doFinal(byteArr));
} catch (Exception e) {
e.printStackTrace();
}
//return byteArr;
return new String(byteArr);
}
Note:
Instead if I use this IV,
byte[] ivBytes = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0}; it works as expected.
You need to use the same initialization vector for encryption and decryption; you can transmit the IV in plaintext (i.e. it isn't secret)
You should also use a different IV for each ciphertext, generated with e.g. a counter or with SecureRandom

Categories

Resources