AES encryption different behavior - java

I have the following code, it's pretty straight forward, however, the code results into different results when running in Spring Tool Suite vs. in Command Prompt. The JDK versions are the same in both environment. I also tried with both Java 8 and 11, but the same symptom. Any insight into what else I should be looking at? It's kinda strange situation for me. Any help with be highly appreciated.
public class CryptoUtil
{
public final static Charset ENCODING = StandardCharsets.UTF_8;
public final static String ALGO_AND_PARAMS = "AES/CBC/PKCS5PADDING";
public final static String ENCR_ALGO = "AES";
public final static String HASH_ALGO = "PBKDF2WithHmacSHA256";
public final static int KEYGEN_ITERATIONS = 65536 ;
public final static int KEYGEN_KEY_SIZE = 256;
public final static String KEYGEN_SALT = "w4sh3Vzp2ZX6GmPC";
public final static String KEYGEN_IV = "2QZv3t6OOCLIf6vG";
public static String decrypt(String cyphertext, String password)
throws Exception
{
String plaintext;
try {
IvParameterSpec ivspec = new IvParameterSpec(KEYGEN_IV.getBytes(ENCODING));
SecretKeyFactory factory = SecretKeyFactory.getInstance(HASH_ALGO);
KeySpec spec = new PBEKeySpec(password.toCharArray(), KEYGEN_SALT.getBytes(ENCODING), KEYGEN_ITERATIONS, KEYGEN_KEY_SIZE);
SecretKey tmp = factory.generateSecret(spec);
SecretKeySpec secretKey = new SecretKeySpec(tmp.getEncoded(), ENCR_ALGO);
Cipher cipher = Cipher.getInstance(ALGO_AND_PARAMS);
cipher.init(Cipher.DECRYPT_MODE, secretKey, ivspec);
plaintext = new String(cipher.doFinal(Base64.getDecoder().decode(cyphertext)));
} catch (Exception e) {
throw new Exception("Error while decrypting " + cyphertext, e);
}
return plaintext;
}
public static String encrypt(String plaintext, String password)
throws Exception
{
String cyphertext;
try {
IvParameterSpec ivspec = new IvParameterSpec(KEYGEN_IV.getBytes(ENCODING));
SecretKeyFactory factory = SecretKeyFactory.getInstance(HASH_ALGO);
KeySpec spec = new PBEKeySpec(password.toCharArray(), KEYGEN_SALT.getBytes(ENCODING), KEYGEN_ITERATIONS, KEYGEN_KEY_SIZE);
SecretKey tmp = factory.generateSecret(spec);
SecretKeySpec secretKey = new SecretKeySpec(tmp.getEncoded(), ENCR_ALGO);
Cipher cipher = Cipher.getInstance(ALGO_AND_PARAMS);
cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivspec);
cyphertext = new String(Base64.getEncoder().encode(cipher.doFinal(plaintext.getBytes(ENCODING))));
} catch (Exception e) {
throw new Exception("Error while encrypting " + plaintext, e);
}
return cyphertext;
}
}

Related

Cipher Algorithm AES CBC Insecure Android

I am currently using Cipher cipher = Cipher.getInstance ("AES / CBC / PKCS5Padding") on my Android app.
Those responsible for the security of the app tell me this: "It should also be noted that AES encryption is secure when it is not AES CBC.", so they want me to modify the algorithm.
I tried AES/GCM/NoPadding and AES/CTR/PKCS5Padding, if I uninstall the old version and install it again, the app works, but if I install it on top of a version with CBC I get the following error: javax.crypto.BadPaddingException: pad block corrupted
How can I solve this? I am afraid that if I update the app that is in the store, this error will happen to end users.
Notes:
public static String encrypt (String plaintext, String pwd) {
try {
byte [] salt = generateSalt ();
SecretKey key = deriveKey (salt, pwd);
Cipher cipher = Cipher.getInstance (CIPHER_ALGORITHM);
byte [] iv = generateIv (cipher.getBlockSize ());
IvParameterSpec ivParams = new IvParameterSpec (iv);
cipher.init (Cipher.ENCRYPT_MODE, key, ivParams);
byte [] cipherText = cipher.doFinal (plaintext.getBytes ("UTF-8"));
if (salt! = null) {
return String.format ("% s% s% s% s% s", toBase64 (salt), DELIMITER,
toBase64 (iv), DELIMITER, toBase64 (cipherText));
}
return String.format ("% s% s% s", toBase64 (iv), DELIMITER,
toBase64 (cipherText));
} catch (GeneralSecurityException e) {
throw new RuntimeException (e);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException (e);
}
}
public static String decrypt (byte [] cipherBytes, SecretKey key, byte [] iv) {
try {
Cipher cipher = Cipher.getInstance (CIPHER_ALGORITHM);
IvParameterSpec ivParams = new IvParameterSpec (iv);
cipher.init (Cipher.DECRYPT_MODE, key, ivParams);
byte [] plaintext = cipher.doFinal (cipherBytes);
String plainrStr = new String (plaintext, "UTF-8");
return plainrStr;
} catch (GeneralSecurityException e) {
throw new RuntimeException (e);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException (e);
}
}
etc...
Update!
I decided to make the code cleaner and here it is for you to understand. My goal was to replace "AES / CBC / PKCS7Padding" with "AES / GCM / NoPadding", as they consider AES with CBC unsafe :(
Questions that arose for this GCM implementation: do I have to use GCMParameterSpec on the cipher? Do I need to import the BouncyCastle provider?
public static final String PBKDF2_DERIVATION_ALGORITHM = "PBKDF2WithHmacSHA1";
private static final String CIPHER_ALGORITHM = "AES/CBC/PKCS7Padding";
//private static final String CIPHER_ALGORITHM = "AES/GCM/NoPadding";
private static String DELIMITER = "]";
private static final int PKCS5_SALT_LENGTH = 8;
private static SecureRandom random = new SecureRandom();
private static final int ITERATION_COUNT = 1000;
private static final int KEY_LENGTH = 256;
public static String encrypt(String plaintext, String pwd) {
byte[] salt = generateSalt();
SecretKey key = deriveKey(pwd, salt);
try {
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
byte[] iv = generateIv(cipher.getBlockSize());
IvParameterSpec ivParams = new IvParameterSpec(iv);
cipher.init(Cipher.ENCRYPT_MODE, key, ivParams);
byte[] cipherText = cipher.doFinal(plaintext.getBytes("UTF-8"));
return String.format("%s%s%s%s%s", toBase64(salt), DELIMITER, toBase64(iv), DELIMITER, toBase64(cipherText));
} catch (GeneralSecurityException | UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
public static String decrypt(String ciphertext, String pwd) {
String[] fields = ciphertext.split(DELIMITER);
if (fields.length != 3) {
throw new IllegalArgumentException("Invalid encypted text format");
}
byte[] salt = fromBase64(fields[0]);
byte[] iv = fromBase64(fields[1]);
byte[] cipherBytes = fromBase64(fields[2]);
SecretKey key = deriveKey(pwd, salt);
try {
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
IvParameterSpec ivParams = new IvParameterSpec(iv);
cipher.init(Cipher.DECRYPT_MODE, key, ivParams);
byte[] plaintext = cipher.doFinal(cipherBytes);
return new String(plaintext, "UTF-8");
} catch (GeneralSecurityException e) {
throw new RuntimeException(e);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
private static byte[] generateSalt() {
byte[] b = new byte[PKCS5_SALT_LENGTH];
random.nextBytes(b);
return b;
}
private static byte[] generateIv(int length) {
byte[] b = new byte[length];
random.nextBytes(b);
return b;
}
private static SecretKey deriveKey(String pwd, byte[] salt) {
try {
KeySpec keySpec = new PBEKeySpec(pwd.toCharArray(), salt, ITERATION_COUNT, KEY_LENGTH);
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(PBKDF2_DERIVATION_ALGORITHM);
byte[] keyBytes = keyFactory.generateSecret(keySpec).getEncoded();
return new SecretKeySpec(keyBytes, "AES");
} catch (GeneralSecurityException e) {
throw new RuntimeException(e);
}
}
private static String toBase64(byte[] bytes) {
return Base64.encodeToString(bytes, Base64.NO_WRAP);
}
public static byte[] fromBase64(String base64) {
return Base64.decode(base64, Base64.NO_WRAP);
}

Use Java Cipher equivalent in Flutter

a i need help to decrypt values generated in Android Java App on Flutter.
I need a class who this but in Dart/Flutter:
public class TrippleDes {
// public static String ALGO = "DESede/CBC/PKCS7Padding";
public static String ALGO = "DESede/ECB/PKCS7Padding";
public static String _encrypt(String message, String secretKey) throws Exception {
Cipher cipher = Cipher.getInstance(ALGO);
cipher.init(Cipher.ENCRYPT_MODE, getSecreteKey(secretKey));
byte[] plainTextBytes = message.getBytes("UTF-8");
byte[] buf = cipher.doFinal(plainTextBytes);
byte[] base64Bytes = Base64.encode(buf, Base64.DEFAULT);
String base64EncryptedString = new String(base64Bytes);
return base64EncryptedString;
}
public static String _decrypt(String encryptedText, String secretKey) throws Exception {
byte[] message = Base64.decode(encryptedText.getBytes(), Base64.DEFAULT);
Cipher decipher = Cipher.getInstance(ALGO);
decipher.init(Cipher.DECRYPT_MODE, getSecreteKey(secretKey));
byte[] plainText = decipher.doFinal(message);
return new String(plainText, "UTF-8");
}
public static SecretKey getSecreteKey(String secretKey) throws Exception {
MessageDigest md = MessageDigest.getInstance("SHA-1");
byte[] digestOfPassword = md.digest(secretKey.getBytes("utf-8"));
byte[] keyBytes = Arrays.copyOf(digestOfPassword, 24);
SecretKey key = new SecretKeySpec(keyBytes, "DESede");
return key;
}
}

Decrypting returns, javax.crypto.BadPaddingException: Given final block not properly padded

Im trying to decrypt the encrypted xml file. Im getting it as a inputstream as follows.I have the correct encrypt key. but each time my program returns empty string. Every time i enter the correct key. but each time it returns Badpadding Exception.
try{
InputStream is = new ByteArrayInputStream(decryption.getFileData().getBytes());
String xmlEncryptedStr = getStringFromInputStream(is);
String xmlStr = CipherUtils.decrypt(xmlEncryptedStr, new Long(key));
.......
here is my CipherUtils.java class
.........
public static String decrypt(String strToDecrypt,Long key)
{
String keyString=String.format("%016d", key);
//System.out.println("decrypt keyString :"+keyString);
return decrypt(strToDecrypt, keyString.getBytes());
}
public static String decrypt(String strToDecrypt,byte[] key)
{
if(strToDecrypt==null)
return strToDecrypt;
try
{
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
final SecretKeySpec secretKey = new SecretKeySpec(key, "AES");
cipher.init(Cipher.DECRYPT_MODE, secretKey);
final String decryptedString = new String(cipher.doFinal(Base64.decodeBase64(strToDecrypt)));
System.out.println("CipherUtils.decryptedString :"+decryptedString);
return decryptedString;
}
catch (Exception e)
{
log.error("Ops!", e);
}
return null;
}
.......
For more information here is my encrypting code
public static String encrypt(String strToEncrypt,Long key)
{
String keyString=String.format("%016d", key);
//System.out.println("encrypt keyString :"+keyString);
return encrypt(strToEncrypt,keyString.getBytes());
}
public static String encrypt(String strToEncrypt,byte[] key)
{
if(strToEncrypt==null)
return strToEncrypt;
try
{
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
final SecretKeySpec secretKey = new SecretKeySpec(key, "AES");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
final String encryptedString = Base64.encodeBase64String(cipher.doFinal(strToEncrypt.getBytes()));
// System.out.println("CipherUtils.encrypt :"+encryptedString);
return encryptedString;
}
catch (Exception e)
{
e.printStackTrace();
}
return null;
}
I am sorry I couldn't comment so I am writing in answers section.
I faced this issue when I was using different keys though I was passing the same but i used CBC methodology.
Just to note that have you checked that encryption is also done by the AES/ECB/PKCS5Padding and not other format like AES/CBC/PKCS5Padding
Also check if key format for encryption is also having the same format like %016d of your keyValue. Also the key is 16 char long.
I created a simple AES and DESede encryption utility and it worked fine.
private static final byte[] keyValue = new String(
"CjxI&S#V&#DSA_S0dA-SDSA$").getBytes();
public static void main(String[] args) throws Exception {
Client cli = new Client();
System.out.println(cli.encrypt("your password for encryption"));
Client cli1 = new Client();
System.out.println(cli1.decrypt("fTsgVQtXvv49GynHazT4OGZ4Va1H57d+6AM+44Ex040="));
}
public String encrypt(String Data) throws Exception {
Key key = new SecretKeySpec(keyValue, "AES");
Cipher c = Cipher.getInstance("AES/ECB/PKCS5Padding");
c.init(Cipher.ENCRYPT_MODE, key);
byte[] encVal = c.doFinal(Data.getBytes());
String encryptedValue = DatatypeConverter.printBase64Binary(encVal);
// String encryptedValue = new BASE64Encoder().encode(encVal);
return encryptedValue;
}
public String decrypt(String encryptedData) throws Exception {
Key key = new SecretKeySpec(keyValue, "AES");
Cipher c = Cipher.getInstance("AES/ECB/PKCS5Padding");
c.init(Cipher.DECRYPT_MODE, key);
byte[] decordedValue = DatatypeConverter
.parseBase64Binary(encryptedData);
byte[] decValue = c.doFinal(decordedValue);
String decryptedValue = new String(decValue);
return decryptedValue;
}

Android/Java Decryption Block Size Calculation - Cipher functions:EVP_DecryptFinal_ex:WRONG_FINAL_BLOCK_LENGTH

I am getting an error "Cipher functions:EVP_DecryptFinal_ex:WRONG_FINAL_BLOCK_LENGTH" and I am not sure why. Can someone please tell me how to fix this?
Here is how I call it
String encrypted = "E5ADDEB05D9D7B3925B7DE16B560D87C";
String sKey = "3985661DD71D591665BD39476636486B";
This is the procedure
public static String decrypt2(final String sEncryptedMessageBase64,
final String sSymKeyHex,
final String sIvHex)
{
final byte[] byteEncryptedMessage = Base64.decode(sEncryptedMessageBase64, Base64.DEFAULT);
try
{
final Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
final SecretKeySpec symKey = new SecretKeySpec(byteSymKeyData, "AES");
final IvParameterSpec iv = new IvParameterSpec(byteIvData);
cipher.init(Cipher.DECRYPT_MODE, symKey, iv);
final byte[] encodedMessage = cipher.doFinal(byteEncryptedMessage);
final String message = new String(encodedMessage, Charset.forName("UTF-8"));
return message;
}
catch (GeneralSecurityException e) {
Log.e("%%%%%", e.getMessage());
throw new IllegalStateException(
"Unexpected exception during decryption", e);
}
}
This is the error I am getting
error:1e06b07b:Cipher functions:EVP_DecryptFinal_ex:WRONG_FINAL_BLOCK_LENGTH
Thanks for any help you can provide

Java: How to convert String to base64string?

I am using this method to decrypt my incoming messages:
private static String decrypt(String key, String initVector, String dataToDecrypt) {
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.DECRYPT_MODE, skeySpec, iv);
String safeString = dataToDecrypt.replace('-', '+').replace('_', '/');
byte[] decodedString = Base64.decodeBase64(safeString);
byte[] original = cipher.doFinal(decodedString);
return new String(original);
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}
However, my Android app crashes, showing the following exception:
java.lang.NoSuchMethodError: No static method
decodeBase64(Ljava/lang/String;)[B in class
Lorg/apache/commons/codec/binary/Base64; or its super classes
(declaration of 'org.apache.commons.codec.binary.Base64' appears in
/system/framework/ext.jar)
accordingly, the method decodeBase64 takes base64string, but I pass string. Here comes my question:
How to convert String to base64string ?!
Please note that I am trying to DECODE not ENCODE. Almost all the solutions provided are for the encoding part which is not my worry.
P.S.: I am developing an Android-app on Android-Studio
try this:
Base64.encodeToString(mStringToEncode.getBytes(), Base64.NO_WRAP)
it exist many encoding mode use autocompletion to see more Base64.NO_WRAP, Base64.CRLF, etc...
you need to import package:
import android.util.Base64;
public static String toBase64(String value){
if (value == null)
value = "";
return Base64.encodeToString(value.trim().getBytes(), android.util.Base64.DEFAULT);
}
Have you check this answer
// decode data from base 64
private static byte[] decodeBase64(String dataToDecode)
{
byte[] dataDecoded = Base64.decode(dataToDecode, Base64.DEFAULT);
return dataDecoded;
}
//enconde data in base 64
private static byte[] encodeBase64(byte[] dataToEncode)
{
byte[] dataEncoded = Base64.encode(dataToEncode, Base64.DEFAULT);
return dataEncoded;
}
Try this code
private static String encryptNew(String key, String initVector, String dataToEncrypt) throws Exception{
byte[] plainTextbytes = dataToEncrypt.getBytes("UTF-8");
byte[] keyBytes = key.getBytes("UTF-8");
byte[] IvkeyBytes = initVector.getBytes("UTF-8");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, "AES");
IvParameterSpec ivParameterSpec = new IvParameterSpec(IvkeyBytes);
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
plainTextbytes = cipher.doFinal(plainTextbytes);
return Base64.encodeToString(plainTextbytes, Base64.DEFAULT);
}
private static String decrypt(String key, String initVector, String dataToDecrypt) {
try {
byte[] cipheredBytes = Base64.decode(dataToDecrypt, Base64.DEFAULT);
byte[] keyBytes = key.getBytes("UTF-8");
byte[] IvkeyBytes = initVector.getBytes("UTF-8");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
SecretKeySpec secretKeySpecy = new SecretKeySpec(keyBytes, "AES");
IvParameterSpec ivParameterSpec = new IvParameterSpec(IvkeyBytes);
cipher.init(Cipher.DECRYPT_MODE, secretKeySpecy, ivParameterSpec);
cipheredBytes = cipher.doFinal(cipheredBytes);
return new String(cipheredBytes,"UTF-8");
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}

Categories

Resources