AES128 Decryption :javax.crypto.badpaddingexception pad block corrupted - java

I try to decrypt an encrypted data that I receive from a web service.
The encryption is done using AES 128.
I use the following code to decrypt the data:
public static String decrypt(String strToDecrypt)
{
try
{
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding"); //AES/CBC/PKCS7Padding
SecretKeySpec secretKey = new SecretKeySpec(AppConstants.AESEncryptionKey.getBytes("UTF8"), "AES");
int blockSize = cipher.getBlockSize();
cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(new byte[blockSize])); //new IvParameterSpec(new byte[16])
byte decBytes[] = cipher.doFinal(Base64.decode(strToDecrypt, 0));
// byte decBytes[] = cipher.doFinal(Base64.decodeBase64(strToDecrypt));
String decStr = new String(decBytes);
System.out.println("After decryption :" + decStr);
return decStr;
}
catch (Exception e)
{
System.out.println("Exception in decryption : " + e.getMessage());
}
return null;
}
At
cipher.doFinal()
I got the following Exception:
javax.crypto.badpaddingexception pad block corrupted
I went through my post but ended up with no solution. I am badly stuck over here.

SecureRandom sr = SecureRandom.getInstance("SHA1PRNG","Crypto");
works perfectly
Note: This code works only on devices up to Android 6. Starting with Android 7.0 the "Crypto" provider has been removed, therefore this code will fail.

AES keys should consist of random data. If you store them as a String then you are likely to loose information, especially if you use encodings such as UTF-8. Your line:
AppConstants.AESEncryptionKey.getBytes("UTF8")
Makes it likely that you've lost data during conversion to/from a string. Use hexadecimals instead if you require a string, or simply store the key as a byte array.
Note that this answer doesn't indicate any security related hints. In general you only want to derive keys or store them in containers. You don't want to use CBC over an insecure channel either.

In my case issue is came because encrypted key and decrypted key both are different, when I check both key with same value then issue is not came

Related

How to encrypt text into plain text strings only

I have to implement basic encryption in my program. I can use Base64 it was rejected by the client. So I am using the following methods. The problem which I am facing is the there are special characters in the encrypted which are resulting in exceptions. Can I change this code to somehow encrypt into plain text without special characters.
protected static byte[] encrypt(String text)
{
try
{
String key = "6589745268754125";
// Create key and cipher
Key aesKey = new SecretKeySpec(key.getBytes(), "AES");
Cipher cipher = Cipher.getInstance("AES");
// encrypt the text
cipher.init(Cipher.ENCRYPT_MODE, aesKey);
byte[] encrypted = cipher.doFinal(text.getBytes());
return encrypted;
}
catch(Exception ex)
{
WriteLog("Encryption Failed");
WriteLog(ex.getMessage());
return null;
}
}
protected static String decrypt(byte[] pass)
{
try
{
String key = "6589745268754125";
// Create key and cipher
Key aesKey = new SecretKeySpec(key.getBytes(), "AES");
Cipher cipher = Cipher.getInstance("AES");
// decrypt the text
cipher.init(Cipher.DECRYPT_MODE, aesKey);
String decrypted = new String(cipher.doFinal(pass));
return decrypted;
}
catch(Exception ex)
{
WriteLog("Encryption Failed");
WriteLog(ex.getMessage());
return null;
}
}
The exception message says "Given final block not properly padded"
javax.crypto.BadPaddingException: Given final block not properly padded
so, basically you don't know about encryption and have the problem that your client wants encryption
ok, a quick headsup:
encoding: transforming an input to an output that holds identical information but in another representation ... ex: 1,2,3 -> a,b,c
as you can see the output looks differently but holds the same information
please note that no secret information is necessary to encode/decode
encryption: might look similar at first glance but here you need some secrets ... an encryption takes 2 inputs ... a secret and the input data
the resulting output can be decrypted, but ONLY if you have the corresponding secret
if your client wants you to encrypt something, make sure that thing can be represented as bytes ... encrypting a string... not good... encrypting a string that has been transformed into < insert arbitrary byte encoding here, for example unicode > ... ok
encryptions usually handle bytes (let's not care about historic ciphers here)
when you decide for an encryption/cipher you have to know that there are essentially 2 distinct groups: symetric and asymetric
symetric: the same key (read secret) you use to encrypt will be needed for decryption
asymetric: there are keypairs consisting of a public and a private part (public/private key) the public part is used for encryption, the private part is used for decryption ... makes no sense unless you have different parties that need to exchange keys
asymetric ciphers are usually used to encrypt decrypt the keys for symetric ciphers because they are SLOW while symetric ciphers usually are FAST
asymetric ciphers are not intended to encrypt large amounts of data
symetric ciphers are intended for bulk data
if your goal is just to keep an information encrypted while it is laying around on a harddisk, a symetric cipher is what you want
you will need a key for the cipher to operate ... and... you will have the problem where to store it ... so if you can, have the user enter a sufficiently complex password ... use the password and a function called PBKDF2 with a sufficiently high iteration count (sufficiently high= increase this number until the process takes either a few seconds if you only need this on startup, or until your users start complaining about the delay) to make binary key from the password.
use this key for AES in GCM mode (symetric cipher)
the cipher will want something called IV or initialization vector ...
the iv is no secret, you may prepend this thing to your ciphertext as clear text information
the iv needs to be the size of one block of your cipher, so in the case of AES 128 bit = 16 byte
so your IV when encrypting is a 16 byte (unique) random number (means that you may not use an IV two times or more: persist the used IVs and when getting a new one, check if it was already stored, if yes startover IV generation, if no, store it and then use it)
when decrypting, read the prepended cleartext IV from your file (first 16 byte)
if you just want to store the ciphertext on disk, write it into a binary file
if the file has to contain only printable text apply an encoding like base16/32/64 before writing your bytes to the file and decode into a byte array before decrypting (unless your data is too big for that, then you will have to find/write a stream wrapper that will add/strip encoding for you)
If the client doesn't like Base64, then try Base32 or Base16 (= hex). They are less common but well defined alternatives to Base64.
You might also find out exactly why the client doesn't want you to use Base64.
You should Base64 the encrypted content. It's usual technique by the way.
I guess the client's problem wasn't Base64 format itself but the fact, that Base64 isn't (a strong) encryption.
The problem was padding. I had use AES/CBC/NoPadding and make sure that my strings are multiple of 16 bytes. So in addition to changing the ecryption and decryption I had to add two methods. One to add \0 i.e. implicit null terminators to the end end of the text to make it a multiple of 16 and another to remove them after decryption. So the final version is like this.
public class crypto {
static String IV = "AAAAAAAAAAAAAAAA";
static String plaintext = "my non padded text";
static String encryptionKey = "0123456789abcdef";
public static void main(String[] args)
{
byte[] cipher = encrypt(plaintext);
String decrypted = decrypt(cipher);
}
protected static String covertto16Byte(String plainText)
{
while(plainText.length()%16 != 0)
plainText += "\0";
return plainText;
}
protected static String removePadding(String plainText)
{
return plainText.replace("\0","");
}
protected static byte[] encrypt(String plainText)
{
try
{
String _plaintText_16 = covertto16Byte(plainText);
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding", "SunJCE");
SecretKeySpec key = new SecretKeySpec(encryptionKey.getBytes("UTF-8"), "AES");
cipher.init(Cipher.ENCRYPT_MODE, key,new IvParameterSpec(IV.getBytes("UTF-8")));
return cipher.doFinal(_plaintText_16.getBytes("UTF-8"));
} catch (Exception ex)
{
//catch mechanism
return null;
}
}
protected static String decrypt(byte[] cipherText)
{
try
{
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding", "SunJCE");
SecretKeySpec key = new SecretKeySpec(encryptionKey.getBytes("UTF-8"), "AES");
cipher.init(Cipher.DECRYPT_MODE, key,new IvParameterSpec(IV.getBytes("UTF-8")));
return removePadding(new String(cipher.doFinal(cipherText), "UTF-8"));
} catch (Exception ex)
{
//catch mechanism
return null;
}
}
}

CryptoJS AES encryption and Java AES decryption

I'm only asking this because I have read many posts for 2 days now about crypto AES encryption, and just when I thought I was getting it, I realized I wasn't getting it at all.
This post is the closest one to my issue, I have exactly the same problem but it is unanswered:
CryptoJS AES encryption and JAVA AES decryption value mismatch
I have tried doing it in many ways but I haven't gotten it right.
First Off
I'm getting the already encrypted string (I only got the code to see how they were doing it), so modifying the encryption way is not an option. That's why all the similar questions aren't that useful to me.
Second
I do have access to the secret key and I can modify it (so adjusting length is an option if neccessary).
The encryption is done on CryptoJS and they send the encrypted string as a GET parameter.
GetParamsForAppUrl.prototype.generateUrlParams = function() {
const self = this;
return new Promise((resolve, reject) => {
const currentDateInMilliseconds = new Date().getTime();
const secret = tokenSecret.secret;
var encrypted = CryptoJS.AES.encrypt(self.authorization, secret);
encrypted = encrypted.toString();
self.urlParams = {
token: encrypted,
time: currentDateInMilliseconds
};
resolve();
});
};
I can easily decrypt this on javascript using CryptoJS with:
var decrypted = CryptoJS.AES.decrypt(encrypted_string, secret);
console.log(decrypted.toString(CryptoJS.enc.Utf8));
But I don't want to do this on Javascript, for security reasons, so I'm trying to decrypt this on Java:
String secret = "secret";
byte[] cipherText = encrypted_string.getBytes("UTF8");
SecretKey secKey = new SecretKeySpec(secret.getBytes(), "AES");
Cipher aesCipher = Cipher.getInstance("AES");
aesCipher.init(Cipher.DECRYPT_MODE, secKey);
byte[] bytePlainText = aesCipher.doFinal(byteCipherText);
String myDecryptedText = = new String(bytePlainText);
Before I had any idea of what I was doing, I tried base64 decoding, adding some IV and a lot of stuff I read, of course none of it worked.
But after I started to understand, kinda, what I was doing, I wrote that simple script above, and got me the same error on the post: Invalid AES key length
I don't know where to go from here. After reading a lot about this, the solution seems to be hashing or padding, but I have no control on the encryption method, so I can't really hash the secret or pad it.
But as I said, I can change the secret key so it can match some specific length, and I have tried changing it, but as I'm shooting in the dark here, I don't really know if this is the solution.
So, my question basically is, If I got the encrypted string (in javascript like the first script) and the secret key, is there a way to decrypt it (in Java)? If so, how to do it?
Disclaimer: Do not use encryption unless you understand encryption concepts including chaining mode, key derivation functions, IV and block size. And don't roll your own security scheme but stick to an established one. Just throwing in encryption algorithms doesn't mean an application has become any more secure.
CryptoJS implements the same key derivation function as OpenSSL and the same format to put the IV into the encrypted data. So all Java code that deals with OpenSSL encoded data applies.
Given the following Javascript code:
var text = "The quick brown fox jumps over the lazy dog. 👻 👻";
var secret = "René Über";
var encrypted = CryptoJS.AES.encrypt(text, secret);
encrypted = encrypted.toString();
console.log("Cipher text: " + encrypted);
We get the cipher text:
U2FsdGVkX1+tsmZvCEFa/iGeSA0K7gvgs9KXeZKwbCDNCs2zPo+BXjvKYLrJutMK+hxTwl/hyaQLOaD7LLIRo2I5fyeRMPnroo6k8N9uwKk=
On the Java side, we have
String secret = "René Über";
String cipherText = "U2FsdGVkX1+tsmZvCEFa/iGeSA0K7gvgs9KXeZKwbCDNCs2zPo+BXjvKYLrJutMK+hxTwl/hyaQLOaD7LLIRo2I5fyeRMPnroo6k8N9uwKk=";
byte[] cipherData = Base64.getDecoder().decode(cipherText);
byte[] saltData = Arrays.copyOfRange(cipherData, 8, 16);
MessageDigest md5 = MessageDigest.getInstance("MD5");
final byte[][] keyAndIV = GenerateKeyAndIV(32, 16, 1, saltData, secret.getBytes(StandardCharsets.UTF_8), md5);
SecretKeySpec key = new SecretKeySpec(keyAndIV[0], "AES");
IvParameterSpec iv = new IvParameterSpec(keyAndIV[1]);
byte[] encrypted = Arrays.copyOfRange(cipherData, 16, cipherData.length);
Cipher aesCBC = Cipher.getInstance("AES/CBC/PKCS5Padding");
aesCBC.init(Cipher.DECRYPT_MODE, key, iv);
byte[] decryptedData = aesCBC.doFinal(encrypted);
String decryptedText = new String(decryptedData, StandardCharsets.UTF_8);
System.out.println(decryptedText);
The result is:
The quick brown fox jumps over the lazy dog. 👻 👻
That's the text we started with. And emojis, accents and umlauts work as well.
GenerateKeyAndIV is a helper function that reimplements OpenSSL's key derivation function EVP_BytesToKey (see https://github.com/openssl/openssl/blob/master/crypto/evp/evp_key.c).
/**
* Generates a key and an initialization vector (IV) with the given salt and password.
* <p>
* This method is equivalent to OpenSSL's EVP_BytesToKey function
* (see https://github.com/openssl/openssl/blob/master/crypto/evp/evp_key.c).
* By default, OpenSSL uses a single iteration, MD5 as the algorithm and UTF-8 encoded password data.
* </p>
* #param keyLength the length of the generated key (in bytes)
* #param ivLength the length of the generated IV (in bytes)
* #param iterations the number of digestion rounds
* #param salt the salt data (8 bytes of data or <code>null</code>)
* #param password the password data (optional)
* #param md the message digest algorithm to use
* #return an two-element array with the generated key and IV
*/
public static byte[][] GenerateKeyAndIV(int keyLength, int ivLength, int iterations, byte[] salt, byte[] password, MessageDigest md) {
int digestLength = md.getDigestLength();
int requiredLength = (keyLength + ivLength + digestLength - 1) / digestLength * digestLength;
byte[] generatedData = new byte[requiredLength];
int generatedLength = 0;
try {
md.reset();
// Repeat process until sufficient data has been generated
while (generatedLength < keyLength + ivLength) {
// Digest data (last digest if available, password data, salt if available)
if (generatedLength > 0)
md.update(generatedData, generatedLength - digestLength, digestLength);
md.update(password);
if (salt != null)
md.update(salt, 0, 8);
md.digest(generatedData, generatedLength, digestLength);
// additional rounds
for (int i = 1; i < iterations; i++) {
md.update(generatedData, generatedLength, digestLength);
md.digest(generatedData, generatedLength, digestLength);
}
generatedLength += digestLength;
}
// Copy key and IV into separate byte arrays
byte[][] result = new byte[2][];
result[0] = Arrays.copyOfRange(generatedData, 0, keyLength);
if (ivLength > 0)
result[1] = Arrays.copyOfRange(generatedData, keyLength, keyLength + ivLength);
return result;
} catch (DigestException e) {
throw new RuntimeException(e);
} finally {
// Clean out temporary data
Arrays.fill(generatedData, (byte)0);
}
}
Note that you have to install the Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy. Otherwise, AES with key size of 256 won't work and throw an exception:
java.security.InvalidKeyException: Illegal key size
Update
I have replaced Ola Bini's Java code of EVP_BytesToKey, which I used in the first version of my answer, with a more idiomatic and easier to understand Java code (see above).
Also see How to decrypt file in Java encrypted with openssl command using AES?.
When encrypting on one system and decrypting on another you are at the mercy of system defaults. If any system defaults do not match (and they often don't) then your decryption will fail.
Everything has to be byte for byte the same on both sides. Effectively that means specifying everything on both sides rather than relying on defaults. You can only use defaults if you are using the same system at both ends. Even then, it is better to specify exactly.
Key, IV, encryption mode, padding and string to bytes conversion all need to be the same at both ends. It is especially worth checking that the key bytes are the same. If you are using a Key Derivation Function (KDF) to generate your key, then all the parameters for that need to be the same, and hence specified exactly.
Your "Invalid AES key length" may well indicate a problem with generating your key. You use getBytes(). That is probably an error. You need to specify what sort of bytes you are getting: ANSI, UTF-8, EBCDIC, whatever. The default assumption for the string to byte conversion is the likely cause of this problem. Specify the conversion to be used explicitly at both ends. That way you can be sure that they match.
Crypto is designed to fail if the parameters do not match exactly for encryption and decryption. For example, even a one bit difference in the key will cause it to fail.

Encrypt in python and decrypt in Java with AES-CFB

I am aware of a question very similar to this (How do I encrypt in Python and decrypt in Java?) but I have a different problem.
My problem is, I am not able to decrypt in Java correctly. Despite using the correct key and IV, I still get garbage characters after decryption. I don't have any compile/run-time errors or exceptions in Java so I believe I am using the right parameters for decryption.
Python Encryption Code -
from Crypto.Cipher import AES
import base64
key = '0123456789012345'
iv = 'RandomInitVector'
raw = 'samplePlainText'
cipher = AES.new(key,AES.MODE_CFB,iv)
encrypted = base64.b64encode(iv + cipher.encrypt(raw))
Java Decryption Code -
private static String KEY = "0123456789012345";
public static String decrypt(String encrypted_encoded_string) throws NoSuchAlgorithmException, NoSuchPaddingException,
InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
String plain_text = "";
try{
byte[] encrypted_decoded_bytes = Base64.getDecoder().decode(encrypted_encoded_string);
String encrypted_decoded_string = new String(encrypted_decoded_bytes);
String iv_string = encrypted_decoded_string.substring(0,16); //IV is retrieved correctly.
IvParameterSpec iv = new IvParameterSpec(iv_string.getBytes());
SecretKeySpec skeySpec = new SecretKeySpec(KEY.getBytes("UTF-8"), "AES");
Cipher cipher = Cipher.getInstance("AES/CFB/NoPadding");
cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
plain_text = new String(cipher.doFinal(encrypted_decoded_bytes));//Returns garbage characters
return plain_text;
} catch (Exception e) {
System.err.println("Caught Exception: " + e.getMessage());
}
return plain_text;
}
Is there anything obvious that I am missing?
The Cipher Feedback (CFB) mode of operation is a family of modes. It is parametrized by the segment size (or register size). PyCrypto has a default segment size of 8 bit and Java (actually OpenJDK) has a default segment size the same as the block size (128 bit for AES).
If you want CFB-128 in pycrypto, you can use AES.new(key, AES.MODE_CFB, iv, segment_size=128). If you want CFB-8 in Java, you can use Cipher.getInstance("AES/CFB8/NoPadding");.
Now that we have that out the way, you have other problems:
Always specify the character set you're using, because it can change between different JVMs: new String(someBytes, "UTF-8") and someString.getBytes("UTF-8"). When you do, be consistent.
Never use a String to store binary data (new String(encrypted_decoded_bytes);). You can copy the bytes directly: IvParameterSpec iv = new IvParameterSpec(Arrays.copyOf(encrypted_decoded_bytes, 16)); and cipher.doFinal(Arrays.copyOfRange(encrypted_decoded_bytes, 16, encrypted_decoded_bytes.length)).
In Java, you're assuming that the IV is written in front of the ciphertext and then encoded together, but in Python, you're never doing anything with the IV. I guess you posted incomplete code.
It is crucial for CFB mode to use a different IV every time if the key stays the same. If you don't change the IV for every encryption, you will create a multi-time pad which enables an attacker to deduce the plaintext even without knowing the key.

Looking for an encrypt/decrypt AES example for Android

As I am new to encryption and particularly in Java/Android, I am struggling to find tutorials and code that work fine so that I can learn from them but the results.
As in this site: https://www.owasp.org/index.php/Using_the_Java_Cryptographic_Extensions
I couldn't find the problem that hit me with BASE64Encoder class, which seems to be inside package a sun.utils but I can find Base64 class but I could not tweak code so that it could work for me.
Similarly in this
android encryption/decryption with AES
The encryption is done in Bitmap Image I could not realize the same technique in normal text string.
Would someone supply a simple AES encryption/decryption example in Android just showing how to use key,message, encryption and decryption?
I have used this for my project.
'com.scottyab:aescrypt:0.0.1'(ref: [enter link description here][1]
encryption:
String encryptedMsg = AESCrypt.encrypt(password, message);
decryption:
String messageAfterDecrypt = AESCrypt.decrypt(password, encryptedMsg);
if you are more concerned about security go with SHA1 or SHA256.
Hope this helps.
Edit:
In my case, i have to encrypt login password and send it to server over the network.
String userPassword = password.getText().toString();
try {
encryptedMsg = AESCrypt.encrypt(userPassword, Config.secretlogin);
} catch (GeneralSecurityException e) {
e.printStackTrace();
}
So in backend, i have decrypted the secure password with the key and in both sides i am using the same AES(Android and backend).
I couldn't find the problem that hit me with BASE64Encoder class,
which seems to be inside package a sun.utils but i can find Base64
class but i could not tweak code so that it could work for me.
You might not be using the right library for Base64. You mention sun.utils, where the link you sent is using:
import org.apache.commons.codec.binary.Base64;
Since Java 8, you can use java.util.Base64, as detailed on Oracle documentation here. It supports Basic encoding, URL encoding and MIME encoding. You can find some examples in this tutorial.
The second example should work for text strings. Replace
byte[] b = baos.toByteArray();
with
byte[] b = yourString.getBytes();
Note that you have to store the key in some way because at some point have to be deciphered
Stored on the device is not a good idea because you're leaving the key to the door. You can store on your server or use a passphrase (fixed or asked the user). I give you a real sample of the last option
private static String getPassphraseSize16(String key) {
if (TextUtils.isEmpty(key)) {
return null;
}
char controlChar = '\u0014';
String key16 = key + controlChar;
if (key16.length() < 16) {
while (key16.length() < 16) {
key16 += key + controlChar;
}
}
if (key16.length() > 16) {
key16 = key16.substring(key16.length() - 16, key16.length());
}
return key16;
}
public static byte[] encodeAES(byte[] message, String passphrase) {
String passphrase16 = getPassphraseSize16(passphrase);
SecretKeySpec secretKey = new SecretKeySpec(passphrase16.getBytes(), "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
byte[] encodedText = cipher.doFinal(message);
return encodedText;
}
public static byte[] decodeAES(byte[] encodedMessage, String key) {
String passphrase16 = getPassphraseSize16(key);
SecretKeySpec secretKey = new SecretKeySpec(passphrase16.getBytes(), "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, secretKey);
byte[] decodedText = cipher.doFinal(encodedMessage);
return decodedText;
}
This example is similar to provided in https://github.com/scottyab/AESCrypt-Android
The encryption and decryption process worked fine after:
I replaced the byte array obtained from :
Bitmap
by byte array of message string as instructed in
android encryption/decryption with AES
So even though my problem is not fully solved, this question should be marked as answered.

IllegalBlocksizeException - How to use Base64 to solve

I am having some troubles with the following code - I seem to be getting an IllegalBlocksizeException and am unsure what it is that I maybe doing incorrectly here? Would it be possible to get some advice / pointers?
Thanks
public class Encryption
{
private SecretKeyFactory factory;
private SecretKey tmp;
private SecretKey secret;
private Cipher cipher;
private byte[] iv;
private byte[] cipherText;
private final KeySpec spec = new PBEKeySpec("somepassword".toCharArray(), SALT, 65536, 256);
private static final byte[] SALT = {(byte)0xc3, (byte)0x23, (byte)0x71, (byte)0x1c, (byte)0x2e, (byte)0xc2, (byte)0xee, (byte)0x77};
public Encryption()
{
try
{
factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
tmp = factory.generateSecret(spec);
secret = new SecretKeySpec(tmp.getEncoded(), "AES");
cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secret);
iv = cipher.getParameters().getParameterSpec(IvParameterSpec.class).getIV();
}
catch (Exception e)
{
e.printStackTrace();
}
}
public String encrypt(String valueToEncrypt) throws Exception
{
cipher.init(Cipher.ENCRYPT_MODE, secret, new IvParameterSpec(iv));
cipherText = cipher.doFinal(Base64.decodeBase64(valueToEncrypt.getBytes()));
return Base64.encodeBase64String(cipherText);
}
public String decrypt(String encryptedValueToDecrypt) throws Exception
{
cipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(iv));
return new String(cipher.doFinal(new Base64().encode(encryptedValueToDecrypt.getBytes())));
}
public static void main(String[] args ) throws Exception
{
Encryption manager = new Encryption();
String encrypted = manager.encrypt("this is a string which i would like to encrypt");
System.out.println(encrypted);
String decrypted = manager.decrypt(encrypted);
System.out.println(decrypted);
System.out.println(encrypted.equals(decrypted));
}
}
The exception is as follows
Exception in thread "main" javax.crypto.IllegalBlockSizeException: Input length must be multiple of 16 when decrypting with padded cipher
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:750)
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:676)
at com.sun.crypto.provider.AESCipher.engineDoFinal(AESCipher.java:313)
at javax.crypto.Cipher.doFinal(Cipher.java:2087)
at encrypt.Encryption.decrypt(Encryption.java:52)
at encrypt.Encryption.main(Encryption.java:60)
The only approach to begin with the implementation of cryptographic algorithms, from the functional point of view (please keep in mind that a working code is not necessarily a secure one, and a lot of thought should come in that direction), is incremental: first try raw AES with a fixed key, then add the key generated by PBKDF2 and only later Base64. The latter is just an encoding tool and should be the easiest part of the process.
But let's take a look to the code:
1. The initialization seems fine, if your goal is to generate the key out of a password.
2. During the decryption, this line stands off:
cipherText = cipher.doFinal(Base64.decodeBase64(valueToEncrypt.getBytes()));
valueToEncrypt is a readable string, but you're trying to decrypt it. Since it has only lowercase letters and spaces, it might not trigger an error, but you're trying to base64-decode something that hasn't been base64-encoded. It would make more sense to try:
cipherText = cipher.doFinal(valueToEncrypt.getBytes());
Then the cipherText can be base64-encoded.
For the decryption part, undo the operations in encryption in the reverse order. If you encrypted and then base64-encoded, then base64-decode first and then decrypt.
As a final recommendation: think modular. Encode in one line and encrypt in another, so if you want to remove or add a layer you just have to toggle the comments on one line.
You have reversed the base-64 encoding and decoding operations. Base-64 takes raw bytes and makes them into printable text. You can encode the output of an encryption operation to make it printable. But then you will need to base‑64–decode that text before trying to decrypt it.
This part of your decrypt() method is causing the problem:
cipher.doFinal(new Base64().encode(encryptedValueToDecrypt.getBytes()))
That should be:
cipher.doFinal(Base64.decodeBase64(encryptedValueToDecrypt.getBytes()))
Asking for "pointers" is pretty open-ended. The best pointer I can give you: don't write this code yourself. Choose a package that provides a higher-level API, selecting high-security algorithms and applying them according to best practices. You don't know what you are doing, and you won't be able to write secure code. But using a high quality, open source library might help you begin to learn more about encryption.
You probably should base64 decode encryptedValueToDecrypt before you decrypt it.

Categories

Resources