I have an Python application that creates a few files encrypted with AES/PKCS7. I have to read those files with a Java service. But my code is throwing an exception:
"javax.crypto.IllegalBlockSizeException: last block incomplete in decryption"
Here's my decryption code:
public String Decrypt(String strText)
{
try
{
// Text to decrypt
byte[] test = strText.getBytes();
//bytKey is the same key as Python app
SecretKeySpec objKey = new SecretKeySpec(bytKey, "AES");
Cipher objCipher = Cipher.getInstance("AES/ECB/PKCS7Padding", "BC");
objCipher.init(Cipher.DECRYPT_MODE, objKey);
// Here I got an exception >>
byte[] bytValue = objCipher.doFinal(test);
return new String(bytValue);
}
catch (Exception exc)
{
exc.printStackTrace();
}
return "";
}
If I decode the encrypted text before step into doFinal I get another exception:
"javax.crypto.BadPaddingException: pad block corrupted"
public String Decrypt(String strText)
{
try
{
BASE64Decoder decoder = new BASE64Decoder();
byte[] test = decoder.decodeBuffer(strText);
SecretKeySpec objKey = new SecretKeySpec(bytKey, "AES");
Cipher objCipher = Cipher.getInstance("AES/ECB/PKCS7Padding", "BC");
objCipher.init(Cipher.DECRYPT_MODE, objKey);
byte[] bytValue = objCipher.doFinal(test);
return new String(bytValue);
}
catch (Exception exc)
{
exc.printStackTrace();
}
return "";
}
I'm not an expert on encrypt/decrypt and I guess it's quite simple to solve.
Any ideas to solve this issue? Thanks in advance!
Related
i have the problam, in existing code encrypt code using php.
this is code for encrypt with php langunage :
<?PHP
$token = "The quick brown fox jumps over the lazy dog.";
$cipher_method = 'aes-256-cbc';
$enc_key = openssl_digest("keyssss", 'SHA256', TRUE);
$enc_iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length($cipher_method));
$crypted_token = openssl_encrypt($token, $cipher_method, $enc_key, 0, $enc_iv) . "::" . bin2hex($enc_iv);
unset($token, $cipher_method, $enc_key, $enc_iv);
?>
but, if i want to decode with java always failed :
this is java code :
#GetMapping("/verify")
public String tokenFB(#RequestParam("accessToken") String accessToken) {
try {
String[] accessTokenSplit = accessToken.split("::");
if (accessTokenSplit.length < 2) {
throw new BadRequestException("accessTokenInvalid");
}
String token = accessTokenSplit[0];
String iv = accessTokenSplit[1];
byte[] tokenByte = Base64.getDecoder().decode(token);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
SecretKey secretKey = makeKey();
IvParameterSpec ivParameterSpec = makeIv(iv);
cipher.init(Cipher.DECRYPT_MODE, secretKey, ivParameterSpec);
byte[] plainText = cipher.doFinal(tokenByte);
return new String(plainText);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private IvParameterSpec makeIv(String iv) {
byte[] ivHex = DatatypeConverter.parseHexBinary(iv);
return new IvParameterSpec(ivHex);
}
private SecretKey makeKey() {
try {
MessageDigest dg = MessageDigest.getInstance("SHA-256");
byte[] keyByte = dg.digest("secretKeys".getBytes());
return new SecretKeySpec(keyByte, "AES");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return null;
}
the error is Given final block not properly padded. Such issues can arise if a bad key is used during decryption.
how to be decrypt with java languange ?
Solved
I'm coding a program that generates a simmetrical AES key, encodes it using an assimetrical public key, writes that encoded AES key in a plain text file, and then it recovers the encoded AES key and decodes it.
This is the function that encodes the AES key and returns it as byte[]:
private static byte[] EncodeKey(String valueToEncode, Key myKey)
{
byte[] encodedKey = null;
try
{
//Cipher
Cipher c = Cipher.getInstance("RSA", "SunJCE");
c.init(Cipher.ENCRYPT_MODE, myKey);
//Encoding
encodedKey = c.doFinal(valueToEncode.getBytes());
}
catch (Exception ex)
{
System.out.println(ex.toString());
} //try
return encodedKey;
} //CifrarCadenaRSA
Then I write this byte array in a txt file with this another function:
private static void WriteTextFile(String path, byte[] valueToWrite)
{
try
{
//Check if file exists
File f = new File(path);
if (f.exists())
{
f.delete();
} //if
f.createNewFile();
//FileOutputStream
FileOutputStream fos = new FileOutputStream(f);
//Writing
fos.write(valueToWrite);
}
catch (Exception ex)
{
System.out.println(ex.toString());
} //try
} //EscribirArchivoTexto
And finally, I load the encoded data and try to decode it:
private static Key DecodeKeyFromFile(String path, Key keyToDecode)
{
//Clave descifrada
Key decodedKey = null;
try
{
//File
File f = new File(ruta);
//Reading text file
byte[] encodedKey = Files.readAllBytes(f.toPath());
//Cipher for decoding
Cipher c = Cipher.getInstance("RSA", "SunJCE");
c.init(Cipher.DECRYPT_MODE, clave);
//Decoding: THIS IS THROWING BadPaddingException
byte[] keyBytes = c.doFinal(encodedKey);
decodedKey = (Key) new SecretKeySpec(claveBytes, 0, claveBytes.length, "AES");
}
catch (Exception ex)
{
System.out.println(ex.toString());
} //try
return decodedKey;
} //CargarClaveCifrada
As you can see, I use the same algorithm and padding for both encoding and decoding. I also tried using "RSA/ECB/PKCS1Padding" but nothing changes. So why is decoding throwing a BadPaddingException?
The exception I get:
javax.crypto.BadPaddingException: Decryption error
And the trace:
javax.crypto.BadPaddingException: Decryption error
at sun.security.rsa.RSAPadding.unpadV15(Unknown Source)
at sun.security.rsa.RSAPadding.unpad(Unknown Source)
at com.sun.crypto.provider.RSACipher.doFinal(RSACipher.java:356)
at com.sun.crypto.provider.RSACipher.engineDoFinal(RSACipher.java:389)
at javax.crypto.Cipher.doFinal(Cipher.java:2165)
at cifradoHibrido.CifradoHibrido.DecodeKeyFromFile(CifradoHibrido.java:197)
at cifradoHibrido.CifradoHibrido.DecodeMessage(CifradoHibrido.java:174)
at cifradoHibrido.CifradoHibrido.main(CifradoHibrido.java:98)
I have the following code for encrypt
public static String encrypt(String value, char[] secret) {
try {
final byte[] bytes = value != null ? value.getBytes(StandardCharsets.UTF_8) : new byte[0];
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWithMD5AndDES");
SecretKey key = keyFactory.generateSecret(new PBEKeySpec(secret));
Cipher pbeCipher = Cipher.getInstance("PBEWithMD5AndDES");
pbeCipher.init(Cipher.ENCRYPT_MODE, key, new PBEParameterSpec(IsoGame.$().crossPlatformManager.getCrossPlatformUtilsInstance().getDeviceUniqueIdentifier().getBytes(StandardCharsets.UTF_8), 20));
return new String(Base64.encodeBase64(pbeCipher.doFinal(bytes)), StandardCharsets.UTF_8);
} catch (Exception e) {
e.printStackTrace();
}
return value;
}
and the following code for decrypt.
public static String decrypt(String value, char[] secret) {
try {
final byte[] bytes = value != null ? Base64.decodeBase64(value.getBytes(StandardCharsets.UTF_8)) : new byte[0];
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWithMD5AndDES");
SecretKey key = keyFactory.generateSecret(new PBEKeySpec(secret));
Cipher pbeCipher = Cipher.getInstance("PBEWithMD5AndDES");
pbeCipher.init(Cipher.DECRYPT_MODE, key, new PBEParameterSpec(IsoGame.$().crossPlatformManager.getCrossPlatformUtilsInstance().getDeviceUniqueIdentifier().getBytes(StandardCharsets.UTF_8), 20));
return new String(pbeCipher.doFinal(bytes), StandardCharsets.UTF_8);
} catch (Exception e) {
e.printStackTrace();
}
return value;
}
However, sometimes the exception is being thrown at
pbeCipher.doFinal(bytes)
in decrypt method.
The exception is javax.crypto.BadPaddingException: pad block corrupted
It's strange, as I'm getting this exception sometimes with the same values .
Any ideas?
Thanks.
The most likely reason would simply be the wrong password to be supplied. If the wrong password is supplied then the wrong key is derived. Then the ciphertext will be decrypted to garbage plaintext. This will only be noticed if the padding exception gets thrown: unpadding random bytes is likely to fail.
You could e.g. first validate that the derived key is correct by performing a HMAC over known data using the derived key. In addition, it would be a good idea to use some kind of authenticated encryption, so that if the key or data is wrong or corrupted that decryption does indeed fail. If you're unlucky then - at this time - the data will decrypt, unpadding will succeed and you end up with garbage plaintext.
Of course, you'd better upgrade to PBKDF2 for key derivation, and upgrade AES to e.g. AES-GCM instead of DES. Currently your encryption is entirely insecure, even if you use a strong password.
Your problem is
IsoGame.$().crossPlatformManager.getCrossPlatformUtilsInstance().getDeviceUniqueIdentifier().getBytes(StandardCharsets.UTF_8)
I have ran the following code multiple times and no exception occurred and the decrypted data was equal to "Hello there!":
public static void main(String[] args)
{
new CryptographyError();
}
private CryptographyError()
{
char[] secret = "MySecret".toCharArray();
String mesasge = "Hello there!";
EncryptedData encryptedData = encrypt(mesasge, secret);
System.out.println("ENCRYPTED " + encryptedData.encryptedString);
String decrypted = decrypt(encryptedData, secret);
System.out.println("DECRYPTED " + decrypted);
}
private static final SecureRandom RANDOM = new SecureRandom();
public static EncryptedData encrypt(String value, char[] secret) {
try {
final byte[] bytes = value != null ? value.getBytes(StandardCharsets.UTF_8) : new byte[0];
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWithMD5AndDES");
SecretKey key = keyFactory.generateSecret(new PBEKeySpec(secret));
Cipher pbeCipher = Cipher.getInstance("PBEWithMD5AndDES");
byte[] salt = new byte[8];
RANDOM.nextBytes(salt);
pbeCipher.init(Cipher.ENCRYPT_MODE, key, new PBEParameterSpec(salt, 20));
return new EncryptedData(salt, new String(Base64.getEncoder().encode(pbeCipher.doFinal(bytes)), StandardCharsets.UTF_8));
} catch (Exception e) {
e.printStackTrace();
System.out.println(value);
}
return null;
}
public static String decrypt(EncryptedData encryptedData, char[] secret) {
try {
String value = encryptedData.encryptedString;
byte[] salt = encryptedData.salt;
final byte[] bytes = value != null ? Base64.getDecoder().decode(value.getBytes(StandardCharsets.UTF_8)) : new byte[0];
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWithMD5AndDES");
SecretKey key = keyFactory.generateSecret(new PBEKeySpec(secret));
Cipher pbeCipher = Cipher.getInstance("PBEWithMD5AndDES");
pbeCipher.init(Cipher.DECRYPT_MODE, key, new PBEParameterSpec(salt, 20));
return new String(pbeCipher.doFinal(bytes), StandardCharsets.UTF_8);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
private static class EncryptedData
{
private final byte[] salt;
private final String encryptedString;
private EncryptedData(byte[] salt, String encryptedString)
{
this.salt = salt;
this.encryptedString = encryptedString;
}
}
The only main difference between my code and your code is
IsoGame.$().crossPlatformManager.getCrossPlatformUtilsInstance().getDeviceUniqueIdentifier().getBytes(StandardCharsets.UTF_8)
which means that must not return the same value on encryption and decryption.
Also if you want to test this you can just change the salt between them and notice the exception is thrown again.
Also Maarten Bodewes gave you some good notes about how to improve your code.
I am currently making an Android app that includes encrypting a String with AES. But for some reason my app does not decrypt properly. I tried to change the Base64 format but it does not fix it. The code is similar to the example on Android Encryption with the Android Cryptography API
Does anyone know where did I go wrong with my functions? Since it does not decode to the same string as my encoded string ("pls").
Your help is much appreciated.
byte[] a = encryptFIN128AES("pls");
String b = decryptFIN128AES(a);
Log.e("AES_Test", "b = " + b);
/**
* Encrypts a string with AES (128 bit key)
* #param fin
* #return the AES encrypted byte[]
*/
private byte[] encryptFIN128AES(String fin) {
SecretKeySpec sks = null;
try {
sks = new SecretKeySpec(generateKey("Test1".toCharArray(), "Test2".getBytes()).getEncoded(),"AES");
} catch (Exception e) {
Log.e("encryptFIN128AES", "AES key generation error");
}
// Encode the original data with AES
byte[] encodedBytes = null;
try {
Cipher c = Cipher.getInstance("AES");
c.init(Cipher.ENCRYPT_MODE, sks);
encodedBytes = c.doFinal(fin.getBytes());
} catch (Exception e) {
Log.e("encryptFIN128AES", "AES encryption error");
}
return encodedBytes;
}
/**
* Decrypts a string with AES (128 bit key)
* #param encodedBytes
* #return the decrypted String
*/
private String decryptFIN128AES(byte[] encodedBytes) {
SecretKeySpec sks = null;
try {
sks = new SecretKeySpec(generateKey("Test1".toCharArray(), "Test2".getBytes()).getEncoded(),"AES");
} catch (Exception e) {
Log.e("decryptFIN128AES", "AES key generation error");
}
// Decode the encoded data with AES
byte[] decodedBytes = null;
try {
Cipher c = Cipher.getInstance("AES");
c.init(Cipher.DECRYPT_MODE, sks);
decodedBytes = c.doFinal(encodedBytes);
} catch (Exception e) {
Log.e("decryptFIN128AES", "AES decryption error");
}
return Base64.encodeToString(decodedBytes, Base64.DEFAULT);
}
public static SecretKey generateKey(char[] passphraseOrPin, byte[] salt)
throws NoSuchAlgorithmException, InvalidKeySpecException {
final int iterations = 1000;
// Generate a 256-bit key
final int outputKeyLength = 128;
SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
KeySpec keySpec = new PBEKeySpec(passphraseOrPin, salt, iterations, outputKeyLength);
SecretKey secretKey = secretKeyFactory.generateSecret(keySpec);
return secretKey;
}
Output:
E/AES_Test: b = cGxz
**
[EDIT] Modified my code but now there is a NullPointerException
**
/**
* Encrypts a string with AES (128 bit key)
* #param fin
* #return the AES encrypted string
*/
private byte[] encryptFIN128AES(String fin) {
SecretKeySpec sks = null;
try {
sks = new SecretKeySpec(generateKey(PASSPHRASE, SALT.getBytes(StandardCharsets.UTF_8)).getEncoded(), "AES");
} catch (Exception e) {
Log.e("encryptFIN128AES", "AES key generation error");
}
// Encode the original data with AES
byte[] encodedBytes = null;
try {
Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");
c.init(Cipher.ENCRYPT_MODE, sks);
encodedBytes = c.doFinal(fin.getBytes(StandardCharsets.UTF_8));
} catch (Exception e) {
Log.e("encryptFIN128AES", "AES encryption error");
}
return encodedBytes;
}
/**
* Decrypts a string with AES (128 bit key)
* #param encodedBytes
* #return the decrypted String
*/
private String decryptFIN128AES(byte[] encodedBytes) {
SecretKeySpec sks = null;
try {
sks = new SecretKeySpec(generateKey(PASSPHRASE, SALT.getBytes(StandardCharsets.UTF_8)).getEncoded(), "AES");
} catch (Exception e) {
Log.e("decryptFIN128AES", "AES key generation error");
}
// Decode the encoded data with AES
byte[] decodedBytes = null;
try {
Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");
c.init(Cipher.DECRYPT_MODE, sks);
decodedBytes = c.doFinal(encodedBytes);
} catch (Exception e) {
Log.e("decryptFIN128AES", "AES decryption error");
}
//return Base64.encodeToString(decodedBytes, Base64.DEFAULT);
return new String(decodedBytes, StandardCharsets.UTF_8);
}
// generateKey(char[] passphraseOrPin, byte[] salt) remains the same
Error:
E/decryptFIN128AES: AES decryption error
E/AndroidRuntime: FATAL EXCEPTION: Thread-176
Process: testapp.ttyi.nfcapp, PID: 2920
java.lang.NullPointerException: Attempt to get length of null array
at java.lang.String.<init>(String.java:371)
at testapp.ttyi.nfcapp.DisplayQRActivity.decryptFIN128AES(DisplayQRActivity.java:254)
at testapp.ttyi.nfcapp.DisplayQRActivity.access$100(DisplayQRActivity.java:29)
at testapp.ttyi.nfcapp.DisplayQRActivity$1.run(DisplayQRActivity.java:77)
at java.lang.Thread.run(Thread.java:818)
**
[EDIT2] Resolved (But no Padding/Encryption Mode allowed)
**
I managed to resolve the issue. (Decodes to "pls") using Codo's solution ofreturn new String(decodedBytes, StandardCharsets.UTF_8);
Though it only works when the algorithm used is:
Cipher c = Cipher.getInstance("AES");
When I put Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");
The "NullPointerException" as seen above will happen. My observation shows that during decryption:
try {
Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");
c.init(Cipher.DECRYPT_MODE, sks);
decodedBytes = c.doFinal(encodedBytes);
} catch (Exception e) {
Log.e("decryptFIN128AES", "AES decryption error");
}
something will fail and it will always print out:
E/decryptFIN128AES: AES decryption error
And thus the NullPointerException will occur as decodedBytes is always initiated to NULL.
Your process is not balanced. For encryption you do:
Encode string using default charset (fin.getBytes()) to get binary data
Encrypt binary data to get encrypted data (doFinal)
For the decryption, you do:
Decrypt encrypted data to get unencrypted binary data (doFinal)
Encode binary data as a Base64 string
Instead of Base64 encoding, the last step should be the reverse of step 1 in the encryption, i.e. you should decode the binary data into a string:
return String(decodedBytes);
It strongly recommend, you do not use the default charset for encoding and decoding as it depends on the system's setting. So it could be different between the system where you encrypt and decyrpt.
So use:
fin.getBytes(StandardCharsets.UTF_8);
and:
return String(decodedBytes, StandardCharsets.UTF_8);
The same applies for the salt.
Also note that you should specify the padding and chaining mode. If you don't, provider-specific default values apply. See #Ryan's answer for more details.
You should research more on how to use AES correctly as you are missing some basic fundamentals of AES security: no IV (assuming using CBC), no mode specified (such as CBC), and no padding specified (such as PKCS5).
Looks like char encoding issue. With minor modifications it works.
in encryptFIN128AES:
encodedBytes = c.doFinal(Base64.getEncoder().encode(fin.getBytes()));
in decryptFIN128AES:
return new String(Base64.getDecoder().decode(decodedBytes));
I am working on android application in which i want to encrypt my text which is stored using ormlite, so if any one from outside get the DB file, he will not be able to read it with out decrypting it. My major concern is to encrypt the data using base64 and when i want to read the data it should decrypt it. I have read some sample code from a link for base64 please suggest me the way for encrypting my text before saving it to the DB and on retrieving time it should decrypt it.
// Sending side
byte[] data = text.getBytes("UTF-8");
String base64 = Base64.encodeToString(data, Base64.DEFAULT);
// Receiving side
byte[] data = Base64.decode(base64, Base64.DEFAULT);
String text = new String(data, "UTF-8");
You can use this code for encryption and decryption.
I am using 3DES encryption scheme here. I hope it will help you.
public PasswordEncryption_TrippleDES() throws Exception {
myEncryptionKey = //your encryption key
myEncryptionScheme = DESEDE_ENCRYPTION_SCHEME;
arrayBytes = myEncryptionKey.getBytes(UNICODE_FORMAT);
ks = new DESedeKeySpec(arrayBytes);
skf = SecretKeyFactory.getInstance(myEncryptionScheme);
cipher = Cipher.getInstance(myEncryptionScheme);
key = skf.generateSecret(ks);
}
public String encrypt(String unencryptedString) {
String encryptedString = null;
try {
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] plainText = unencryptedString.getBytes(UNICODE_FORMAT);
byte[] encryptedText = cipher.doFinal(plainText);
encryptedString = new String(Base64.encodeBase64(encryptedText));
} catch (Exception e) {
e.printStackTrace();
}
return encryptedString;
}
public String decrypt(String encryptedString) {
String decryptedText=null;
try {
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] encryptedText = Base64.decodeBase64(encryptedString);
byte[] plainText = cipher.doFinal(encryptedText);
decryptedText= new String(plainText);
} catch (Exception e) {
e.printStackTrace();
}
return decryptedText;
}