Java - AES CBC Algorithum different way to generating SecretKeySpec - java

I am trying to implement AES CBC 256 algorithm. And after studying online and checking few code samples I realise that there are two different ways to retrive SecretKeySpec and both results in different encrypted message.
private static SecretKeySpec getSecretKeySpec(String secretKey) throws NoSuchAlgorithmException, InvalidKeySpecException {
String salt = "a";
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
KeySpec spec = new PBEKeySpec(secretKey.toCharArray(), salt.getBytes(), 65536, 256);
SecretKey tmp = factory.generateSecret(spec);
return new SecretKeySpec(tmp.getEncoded(), "AES");
}
// private static SecretKeySpec getSecretKeySpec(String secretKey) throws NoSuchAlgorithmException {
//
// MessageDigest digest = MessageDigest.getInstance("SHA-256");
// digest.update(secretKey.getBytes(StandardCharsets.UTF_8));
// byte[] keyBytes = new byte[32];
// System.arraycopy(digest.digest(), 0, keyBytes, 0, keyBytes.length);
// return new SecretKeySpec(keyBytes, "AES");
// }
public static String encrypt(String strToEncrypt, String secret)
{
try
{
byte[] iv = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
IvParameterSpec ivspec = new IvParameterSpec(iv);
SecretKeySpec secretKeySpec = getSecretKeySpec(secret);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, 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;
}
Would some please let me know which one is the correct implemention of AES CBC 256 bit encryption?

AES & CBC don't specify anything about how you derive the key. Any 128, 192 and 256 bit key are valid. Using an actual key derivation function like PBKDF2WithHmacSHA256 is preferable than a single pass to SHA-256 to slow down brute force attack, but otherwise, they both generate valid key.

Related

BadPaddingExeception whilst decrypting AES256

Currently having issues with the decryption of an AES hash.
When i encrypt using AES256 i get the following result (After base64 encoding)
07sKQfb9dN86XAMxFmVKHQAAAAAAAAAAAAAAAAAAAAA=
I believe my issue is with the AAAAAAAA etc. for some reason I think anyway the padding isn't being removed during decryption.
My code
public String encrypt(String key, String initVector, String value) throws Exception {
IvParameterSpec iv = new IvParameterSpec(initVector.getBytes("UTF-8"), 0, initVector.getBytes().length);
SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), 0, key.getBytes().length, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
byte[] encrypted = new byte[32];
cipher.doFinal(value.getBytes(), 0, value.getBytes().length, encrypted, 0);
System.out.println("Base64: " + Base64.encode(encrypted));
System.out.println("Hex: " + bytesToHex(encrypted));
return Base64.encode(encrypted);
}
public String decrypt(String key, String initVector, String encrypted) {
try {
IvParameterSpec iv = new IvParameterSpec(initVector.getBytes("UTF-8"), 0, initVector.getBytes().length);
SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), 0, key.getBytes().length, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
byte[] decrypted = new byte[64];
cipher.doFinal(Base64.decode(encrypted), 0, Base64.decode(encrypted).length, decrypted, 0);
return new String(decrypted);
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}
What i am expecting when i encode base64 07sKQfb9dN86XAMxFmVKHQ=
String i am encrypting: Test
Secret key: password12345678password12345678
byte[] decrypted = new byte[64];
you created a 64 byte array but the decrypted value may be shorter (Java doesn't use zero string terminator unlike C does). So you should not make assumption over the parameter length
try using
encrypted = cipher.doFinal(value.getBytes());
and
decrypted = cipher.doFinal(Base64.decode(encrypted));

aes-gcm throws no error when message, tag, or key is changed

I am using AES/GCM/NoPadding encryption in java and (I am fairly new to it). If I understand it right GCM should recognize if encrypted message was manipulated, if so it should not decrypt it. As stated in this anwser:
The authentication TAG is an input to the decryption, if someone tampered with your associated data or with your encrypted data, GCM decryption will notice this and will not output any data (or return an error and you should discard the received data without processing it)
However, in my code, if I modify encrypted message (the message part or the tag part), I do not get any errors and the message will be decrypted (It will be different of course). I do not get any errors even when I provide different key, though message will be decrypted incorrectly...
What am I doing wrong? See the code below:
The util class:
public class CryptoUtils {
public static byte[] encrypt(byte[] key, byte[] iv, byte[] input) throws GeneralSecurityException {
try {
SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");
IvParameterSpec ivspec = new IvParameterSpec(iv);
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, ivspec);
int outputLength = cipher.getOutputSize(input.length);
byte[] output = new byte[outputLength];
int outputOffset = cipher.update(input, 0, input.length, output, 0);
cipher.doFinal(output, outputOffset);
return output;
} catch (NoSuchAlgorithmException e) {
Timber.wtf(e);
}
return null;
}
public static byte[] decrypt(byte[] key, byte[] iv, byte[] encrypted) throws GeneralSecurityException {
try {
SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");
IvParameterSpec ivspec = new IvParameterSpec(iv);
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, ivspec);
return cipher.doFinal(encrypted);
} catch (NoSuchAlgorithmException e) {
Timber.wtf(e);
}
return null;
}
}
The code that throws no errors:
byte[] key = getKey();
byte[] iv = generateIv();
byte[] message = "hello".getBytes();
byte[] encrypted = CryptoUtils.encrypt(key, iv, message);
//Split decrypted message into parts.
byte[] encryptedMessage = new byte[message.length];
System.arraycopy(encrypted, 0, encryptedMessage, 0, message.length);
byte[] tag = new byte[encrypted.length - message.length];
System.arraycopy(encrypted, message.length, tag, 0, tag.length);
//I am modifying the message here.
encryptedMessage[0] = 0;
// I am also modifying the key.
key[2] = 0;
//Put message and tag together.
byte[] toDecrypt = new byte[encrypted.length];
System.arraycopy(encryptedMessage, 0, toDecrypt, 0, encryptedMessage.length);
System.arraycopy(tag, 0, toDecrypt, encryptedMessage.length, tag.length);
//Still not getting any errors here.
byte[] decrypted = CryptoUtils.decrypt(key, iv, encryptedMessage);
byte[] decryptedMessage = new byte[message.length];
System.arraycopy(decrypted, 0, decryptedMessage, 0, message.length);
//Decrypted message is different than original.
Timber.d("Decrypted message: %s", new String(decryptedMessage));

AES 128 encryption in Android and .Net with custom key and IV

I have a password string in my android application. I need to the send the password through the .net web service (i.e. end with .aspx) using the SOAP web service. Before sending the password i need to encrypt the password with AES 128 encryption with the custom key and IV.
They have a encrypt/decrypt tool in .net with the custom key and Iv. The tool ask a custom key with 16 digit and IV 8 digit. If give the string it generate the encrypting string. example
Example:
Key : 1234567812345678
IV : 12345678
String : android
Encrypted string : oZu5E7GgZ83Z3yoK4y8Utg==
I didn't have any idea how to do this in android. Need help.
A complete example may help you:
The encrypt/decrypt functions, using IV
public static byte[] encrypt(byte[] data, byte[] key, byte[] ivs) {
try {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");
byte[] finalIvs = new byte[16];
int len = ivs.length > 16 ? 16 : ivs.length;
System.arraycopy(ivs, 0, finalIvs, 0, len);
IvParameterSpec ivps = new IvParameterSpec(finalIvs);
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivps);
return cipher.doFinal(data);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static byte[] decrypt(byte[] data, byte[] key, byte[] ivs) {
try {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");
byte[] finalIvs = new byte[16];
int len = ivs.length > 16 ? 16 : ivs.length;
System.arraycopy(ivs, 0, finalIvs, 0, len);
IvParameterSpec ivps = new IvParameterSpec(finalIvs);
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivps);
return cipher.doFinal(data);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
You can use it as below :
String dataToEncryptDecrypt = "android";
String encryptionDecryptionKey = "1234567812345678";
String ivs = "12345678";
byte[] encryptedData = encrypt(dataToEncryptDecrypt.getBytes(), encryptionDecryptionKey.getBytes(),
ivs.getBytes());
// here you will get the encrypted bytes. Now you can use Base64 encoding on these bytes, before sending to your web-service
byte[] decryptedData = decrypt(encryptedData, encryptionDecryptionKey.getBytes(), ivs.getBytes());
System.out.println(new String(decryptedData));
I don't know the details of AES algorithm in use(ie mode & padding method), bit it should be roughly like this:
public static byte[] encrypt(byte[] data, byte[] key) {
try {
Cipher cipher = Cipher.getInstance("AES/CBC/ZeroBytePadding");
SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");
byte[] empty = new byte[16]; // For better security you should use a random 16 byte key!!!
IvParameterSpec ivps = new IvParameterSpec(empty);
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivps);
return cipher.doFinal(data);
} catch (Exception e) {
// ...
}
return null;
}
Function above could be used like this:
String data = "android";
String key = "1234567812345678";
byte encrypted = encrypt(data.getbytes("UTF-8"), key.getbytes("UTF-8"));

Java: How implement AES with 128 bits with CFB and No Padding

could someone give me any lead to this problem ?
I need to know how to encrypt and decrypt with AES with at least 128 bits with CFB and No Padding.
Some code or links will be very appreciated. (i already look on google, but no lucky tough).
UPDATE:
Works fine!
public byte[] crypt() {
byte[] crypt = null;
try {
final Cipher cipher = Cipher.getInstance("AES/CFB/NoPadding", "SunJCE");
final SecretKey skeySpec = KeyGenerator.getInstance("AES").generateKey();
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
crypt = cipher.doFinal(new byte[]{0, 1, 2, 3});
} catch (Exception ex) {
throw new RuntimeException(ex);
}
return crypt;
}
Returns null .. why ?
public String decrypt(byte[] text) {
byte[] crypt = null;
String plainText = null;
try {
final Cipher cipher = Cipher.getInstance("AES/CFB/NoPadding", "SunJCE");
final SecretKey skeySpec = KeyGenerator.getInstance("AES").generateKey();
cipher.init(Cipher.DECRYPT_MODE, skeySpec);
crypt = cipher.doFinal(text);
plainText = new String(crypt);
} catch (Exception ex) {
throw new RuntimeException(ex);
}
return plainText;
}
Best regards,
Valter Henrique.
Give this a go:
final Cipher cipher = Cipher.getInstance("AES/CFB/NoPadding", "SunJCE");
final SecretKey skeySpec = KeyGenerator.getInstance("AES")
.generateKey();
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
System.out.println(Arrays.toString(cipher.doFinal(new byte[] { 0, 1, 2,
3 })));

256bit AES/CBC/PKCS5Padding with Bouncy Castle

I am having trouble mapping the following JDK JCE encryption code to Bouncy Castles Light-weight API:
public String dec(String password, String salt, String encString) throws Throwable {
// AES algorithm with CBC cipher and PKCS5 padding
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding", "BC");
// Construct AES key from salt and 50 iterations
PBEKeySpec pbeEKeySpec = new PBEKeySpec(password.toCharArray(), toByte(salt), 50, 256);
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWithSHA256And256BitAES-CBC-BC");
SecretKeySpec secretKey = new SecretKeySpec(keyFactory.generateSecret(pbeEKeySpec).getEncoded(), "AES");
// IV seed for first block taken from first 32 bytes
byte[] ivData = toByte(encString.substring(0, 32));
// AES encrypted data
byte[] encData = toByte(encString.substring(32));
cipher.init( Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec( ivData ) );
return new String( cipher.doFinal( encData ) );
}
The above works great, but is not very portable due to Oracle's restriction on encryption strengths. I've made several attempts at porting to Bouncy Castles Light-weight API but without success.
public String decrypt1(String password, String salt, String encString) throws Exception {
byte[] ivData = toByte(encString.substring(0, 32));
byte[] encData = toByte(encString.substring(32));
PKCS12ParametersGenerator gen = new PKCS12ParametersGenerator(new SHA256Digest());
gen.init(password.getBytes(), toByte(salt), 50);
CBCBlockCipher cbcBlockcipher = new CBCBlockCipher(new RijndaelEngine(256));
CipherParameters params = gen.generateDerivedParameters(256, 256);
cbcBlockcipher.init(false, params);
PaddedBufferedBlockCipher aesCipher = new PaddedBufferedBlockCipher(cbcBlockcipher, new PKCS7Padding());
byte[] plainTemp = new byte[aesCipher.getOutputSize(encData.length)];
int offset = aesCipher.processBytes(encData, 0, encData.length, plainTemp, 0);
int last = aesCipher.doFinal(plainTemp, offset);
byte[] plain = new byte[offset + last];
System.arraycopy(plainTemp, 0, plain, 0, plain.length);
return new String(plain);
}
The above attempt results in a org.bouncycastle.crypto.DataLengthException: last block incomplete in decryption.
I have searched for examples online, but there isn't many examples of providing your own IV data for 256bit AES with CBC using PKCS5/PKCS7 as padding.
NB: The toByte function converts a String into a byte array using base64 or similar.
This should work for you:
public String dec(String password, String salt, String encString)
throws Exception {
byte[] ivData = toByte(encString.substring(0, 32));
byte[] encData = toByte(encString.substring(32));
// get raw key from password and salt
PBEKeySpec pbeKeySpec = new PBEKeySpec(password.toCharArray(),
toByte(salt), 50, 256);
SecretKeyFactory keyFactory = SecretKeyFactory
.getInstance("PBEWithSHA256And256BitAES-CBC-BC");
SecretKeySpec secretKey = new SecretKeySpec(keyFactory.generateSecret(
pbeKeySpec).getEncoded(), "AES");
byte[] key = secretKey.getEncoded();
// setup cipher parameters with key and IV
KeyParameter keyParam = new KeyParameter(key);
CipherParameters params = new ParametersWithIV(keyParam, ivData);
// setup AES cipher in CBC mode with PKCS7 padding
BlockCipherPadding padding = new PKCS7Padding();
BufferedBlockCipher cipher = new PaddedBufferedBlockCipher(
new CBCBlockCipher(new AESEngine()), padding);
cipher.reset();
cipher.init(false, params);
// create a temporary buffer to decode into (it'll include padding)
byte[] buf = new byte[cipher.getOutputSize(encData.length)];
int len = cipher.processBytes(encData, 0, encData.length, buf, 0);
len += cipher.doFinal(buf, len);
// remove padding
byte[] out = new byte[len];
System.arraycopy(buf, 0, out, 0, len);
// return string representation of decoded bytes
return new String(out, "UTF-8");
}
I assume that you're actually doing hex encoding for toByte() since your code uses 32 characters for the IV (which provides the necessary 16 bytes). While I don't have the code you used to do the encryption, I did verify that this code will give the same decrypted output as your code.

Categories

Resources