BadPaddingException: cipher doesn't work when used in two separate methods - java

When I encrypt and decrypt in the same method, the cipher works perfectly, however as soon as I send an encrypted message to a server, which sends the same message straight back, the decryption throws a "BadPaddingException: Data must start with zero", any ideas as to why?
Here are my decrpytion and encryption methods, including the method they are called from:
public byte[] getServerResponse(byte[] message) throws IOException
{
Log.debug("Getting server response for: " + message.toString());
byte[] encMsg = encrypt(message);
out.write(encMsg);
out.flush();
in = new DataInputStream(s.getInputStream());
byte[] response = new byte[128];
in.readFully(response);
byte[] strResponse = decrypt(response);
Log.debug("Server response: " + response);
return strResponse;
}
private byte[] encrypt(byte[] message) {
byte[] encryptedBytes = new byte[128];
try {
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
encryptedBytes = cipher.doFinal(message);
} catch (GeneralSecurityException ex) {
Logger.getLogger(Client.class.getName()).log(Level.SEVERE, null, ex);
}
return encryptedBytes;
}
private byte[] decrypt(byte[] message) {
byte[] cleartext = new byte[128];
try {
cipher.init(Cipher.DECRYPT_MODE, privateKey);
cleartext = cipher.doFinal(message);
} catch (InvalidKeyException ex) {
Logger.getLogger(Client.class.getName()).log(Level.SEVERE, null, ex);
} catch (IllegalBlockSizeException ex) {
Logger.getLogger(Client.class.getName()).log(Level.SEVERE, null, ex);
} catch (BadPaddingException ex) {
Logger.getLogger(Client.class.getName()).log(Level.SEVERE, null, ex);
}
return cleartext;
}
The cipher is available to the whole class, and is initialised as follows:
cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
Do I need to use the IV? If so, how does one do that?

Related

Android Decryption Throwing java.security.InvalidAlgorithmParameterException: IV must be specified in CBC mode

Despite checking that both my IV and derived keys are equal for encryption and decryption, it still throws the same error. The byte array will encrypt no problem, but decryption always fails. My decryption and encryption code looks like:
private final static int BLOCK_SIZE = 256;
private final static int IV_SIZE = 16;
private final static int ITERATION_COUNT = 10;
private final static String ENCRYPTION_ALGORITHM = "PBKDF2WithHmacSHA256";
private final static String ALGORITHM = "AES/CBC/PKCS5Padding";
public byte[] encryptByteArray(byte[] plaintext, String fileName) throws InvalidKeyException
{
extractIVfromName(fileName);
genKey();
try {
Cipher cipher = Cipher.getInstance(ALGORITHM);
GCMParameterSpec parameterSpec = new GCMParameterSpec(BLOCK_SIZE, iv);
Log.d("encryptByteArray", "Size: " + plaintext.length + " IV SPEC: " +
Base64.encodeToString(parameterSpec.getIV(), Base64.DEFAULT) + "DerivedKey: " +
Base64.encodeToString(derivedKey.getEncoded(), Base64.DEFAULT));
cipher.init(Cipher.ENCRYPT_MODE, derivedKey, parameterSpec);
return cipher.doFinal(plaintext);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (InvalidAlgorithmParameterException e) {
e.printStackTrace();
}
return null;
}
And here we have decryption, where the error is occuring:
public byte[] decryptByteArray(byte[] ciphertext, String fileName) throws InvalidKeyException
{
extractIVfromName(fileName);
genKey();
try {
//IV fails for some reason
Cipher cipher = Cipher.getInstance(ALGORITHM);
GCMParameterSpec parameterSpec = new GCMParameterSpec(BLOCK_SIZE, iv);
Log.d("decryptByteArray", "Size: " + ciphertext.length + " IV SPEC: " +
Base64.encodeToString(parameterSpec.getIV(), Base64.DEFAULT) + "DerivedKey: " +
Base64.encodeToString(derivedKey.getEncoded(), Base64.DEFAULT));
cipher.init(Cipher.DECRYPT_MODE, derivedKey, parameterSpec);
return cipher.doFinal(ciphertext);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (InvalidAlgorithmParameterException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
}
return null;
}
An example of the debug log gives this:
2020-07-26 22:40:03.667 9089-9089/com.iso.gallery256 D/decryptByteArray: Size: 45312 IV SPEC: NWZmYmUzZGItZGE4MS00Yw==
DerivedKey: pa2OYoRLjTphldeSi1L6EQCmlXTzQJLeXgPIuu6kRus=
2020-07-26 22:40:00.184 9089-9089/com.iso.gallery256 D/encryptByteArray: Size: 45307 IV SPEC: NWZmYmUzZGItZGE4MS00Yw==
DerivedKey: pa2OYoRLjTphldeSi1L6EQCmlXTzQJLeXgPIuu6kRus=
I don't think the problem is with how the IV or key are generated, since they come out exactly the same for decryption and encryption, but rather with my cipher options.

Why does encrypting in one Java class and decrypting in another causes BadPaddingError?

I have a test function that returns a byte[] in one Java class, FirstClass:
public static byte[] testA(){
CipherClass crypto = new CipherClass();
byte[] a = crypto.encrypt("helloWorld");
return a;
}
Where CipherClass contains my java.crypto.Cipher methods to encrypt and decrypt inputs.
In another Java class, SecondClass, I instantiate my previous FirstClass and call its testA() method which I save to a byte[] variable and then try to decrypt it:
FirstClass fc = new FirstClass();
byte[] b = fc.testA();
System.out.println(crypto.decrypt(b)); //Outputs BadPaddingError
Why does encrypting a String in one Java file and then passing the resulting byte[] into another Java file to decrypt it causes a BadPaddingError?
What can be done to fix a way around this?
Why does the Cipher only work if both encryption and decryption are in the same class as follows?:
CipherClass crypto = new CipherClass();
byte[] a = crypto.encrypt("helloWorld"); //Outputs [B#5474c6c
byte[] b = a;
System.out.println(crypto.decrypt(b)); //Outputs "helloWorld"
EDIT: My CipherClass code as requested below.
SecretKey and IVParameterSpec generator:
public List<KeyGenerator> getKeyGenerator(){
List<KeyGenerator> list = new ArrayList<KeyGenerator>();
KeyGenerator keyGenerator;
int keyBitSize = 128;
try
{
SecureRandom secureRandom = new SecureRandom();
keyGenerator = KeyGenerator.getInstance("AES");
keyGenerator.init(keyBitSize, secureRandom);
list.add(keyGenerator);
return list;
}
catch (NoSuchAlgorithmException e)
{
e.printStackTrace();
}
return list;
}
public List<SecretKey> getSecretKey(List<KeyGenerator> keygen) {
List<SecretKey> list = new ArrayList<SecretKey>();
KeyGenerator keyGenerator;
SecretKey secretKey;
keyGenerator = keygen.get(0);
secretKey = keyGenerator.generateKey();
list.add(secretKey);
return list;
}
public List<IvParameterSpec> getIvSpec(List<SecretKey> secretKey) {
List<IvParameterSpec> list = new ArrayList<IvParameterSpec>();
Cipher cipher;
byte[] iv;
try
{
cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
iv = new byte[cipher.getBlockSize()];
IvParameterSpec ivSpec = new IvParameterSpec(iv);
list.add(ivSpec);
}
catch (NoSuchAlgorithmException e)
{
e.printStackTrace();
}
catch (NoSuchPaddingException e)
{
e.printStackTrace();
}
return list;
}
List<KeyGenerator> kgList = getKeyGenerator();
List<SecretKey> skList = getSecretKey(kgList); //skList.get(0) is the SecretKey
List<IvParameterSpec> ivList = getIvSpec(skList); //ivList.get(0) is the IVParameterSpec
My encrypt() method:
public byte[] encrypt(String inputStr) {
Cipher cipher;
SecretKey secretKey = skList.get(0);
IvParameterSpec ivSpec = ivList.get(0);
byte[] plainText;
byte[] cipherText = new byte[]{};
try
{
cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
try
{
secretKey = skList.get(0);
ivSpec = ivList.get(0);
cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivSpec);
try
{
plainText = inputStr.getBytes("UTF-8");
cipherText = cipher.doFinal(plainText);
return cipherText;
}
catch (IllegalBlockSizeException e)
{
e.printStackTrace();
}
catch (BadPaddingException e)
{
e.printStackTrace();
}
catch (UnsupportedEncodingException e)
{
e.printStackTrace();
}
}
catch (InvalidKeyException e)
{
e.printStackTrace();
}
catch (InvalidAlgorithmParameterException e1)
{
e1.printStackTrace();
}
}
catch (NoSuchAlgorithmException e)
{
e.printStackTrace();
}
catch (NoSuchPaddingException e)
{
e.printStackTrace();
}
return cipherText;
}
My decrypt() method:
public String decrypt(byte[] cipherText){
Cipher cipherDe;
SecretKey secretKey = skList.get(0);
IvParameterSpec ivSpec = ivList.get(0);
byte[] deciByte;
String decryptText = "";
try
{
cipherDe = Cipher.getInstance("AES/CBC/PKCS5Padding");
try
{
secretKey = skList.get(0);
ivSpec = ivList.get(0);
cipherDe.init(Cipher.DECRYPT_MODE, secretKey, ivSpec);
try
{
//De-cryption
deciByte = cipherDe.doFinal(cipherText);
decryptText = new String(deciByte);
return decryptText;
}
catch (IllegalBlockSizeException e)
{
e.printStackTrace();
}
catch (BadPaddingException e)
{
e.printStackTrace();
}
}
catch (InvalidKeyException e)
{
e.printStackTrace();
}
//De-cryption
catch (InvalidAlgorithmParameterException e1)
{
e1.printStackTrace();
}
}
catch (NoSuchAlgorithmException e)
{
e.printStackTrace();
}
catch (NoSuchPaddingException e)
{
e.printStackTrace();
}
return decryptText;
}

javax.crypto.BadPaddingException: Decryption error - Can't decrypt the encrypted public key

Here are my encryption settings
public static final String ALGORITHM = "RSA";
public static final String CIPHER = "RSA/ECB/PKCS1Padding";
public static final String HASH_ALGORITHM = "SHA-256";
public static final String SIGNATURE_ALGORITHM = "SHA256withRSA";
public static final int KEY_SIZE = 1048;
Here is my code to encrypt
public byte[] encrypt(byte[] toEncrypt, String keyPath){
int keyLength = KEY_SIZE/8 - 11;
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
String toEncryptString = new String(toEncrypt,StandardCharsets.UTF_8);
String[] lines = toEncryptString.split("(?<=\\G.{" + keyLength + "})");
byte[] encryptedData = new byte[0];
try{
inputStream = new ObjectInputStream(new FileInputStream(keyPath));
final PublicKey publicKey = (PublicKey) inputStream.readObject();
final Cipher cipher = Cipher.getInstance(CIPHER);
cipher.init(Cipher.ENCRYPT_MODE,publicKey);
if(toEncrypt.length >= keyLength){
for(String line : lines){
byteArrayOutputStream.write(cipher.doFinal(line.getBytes("UTF-8")));
}
}else{
byteArrayOutputStream.write(cipher.doFinal(toEncrypt));
}
encryptedData = byteArrayOutputStream.toByteArray();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
}
byte[] cipheredData = base64Encoder(encryptedData);
System.out.println(Arrays.toString(cipheredData));
return cipheredData;
}
Here is my code to decrypt
public byte[] decrypt(byte[] toDecrypt, String keyPath) {
byte[] decypherText = base64Decoder(toDecrypt);
System.out.println(toDecrypt.length);
System.out.println(decypherText.length);
int keyLength = KEY_SIZE/8 - 11;
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
String toEncryptString = Arrays.toString(decypherText);
String[] lines = toEncryptString.split("(?<=\\G.{" + keyLength + "})");
byte[] decipheredData = new byte[0];
try{
inputStream = new ObjectInputStream(new FileInputStream(keyPath));
final PrivateKey privateKey = (PrivateKey) inputStream.readObject();
final Cipher cipher = Cipher.getInstance(CIPHER);
cipher.init(Cipher.DECRYPT_MODE,privateKey);
if(decypherText.length >= keyLength){
for(String line : lines){
byteArrayOutputStream.write(cipher.doFinal(line.getBytes("UTF-8")));
}
}else{
byteArrayOutputStream.write(cipher.doFinal(decypherText));
}
decipheredData = byteArrayOutputStream.toByteArray();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
}
System.out.println(Arrays.toString(decipheredData));
return decipheredData;
}
I'm trying to encrypt a Public Key "A" with the Public Key "B". Encryption is successful, but when I try to decrypt it with the Private Key "B", it gives me that error.
The code looks fine to me, already reviewed it several times, these past 16 hours, already searched through several posts in here, and did not found a suitable answer for my problem.
Also, it already gave me BadPaddingException when decrypting. "Data must not be longer than 131 bytes". However, I'm using a Cipher with padding, so it can only decrypt data with 120 bytes. Why this error, if the Public key ciphered is splitted into blocks of 120 bytes?
Edit: before anyone else says that encrypting a Public Key is a mistery, have in your mind that it's the purpose of the project.. to have a Public Key as an ID and, as such, the need to encrypt so that no one discovers the ID of the user.
Your code doesn't make sense. You're trying to split the ciphertext on a plaintext string. It isn't there. It got removed when you split the string. In any case the data has been encrypted, so searching for a plaintext in it is futile.
You should be base64-decoding, decrypting, reading objects, and then recombining them using "(?<=\\G.{" + keyLength + "})" as a delimiter.
In fact why you're splitting in the first place and then encrypting multiple lines is a mystery.
And why you're serializing is another. Just encrypt the entire thing, without splitting, base64-encode it, and save that. When decrypting, just base64-decode it and decrypt it.
And, finally, why you're encrypting a public key at all is a complete mystery. It's PUBLIC. Not a secret.

Decryption returns an empty string when encrypting text in Android

I'm trying to save few text fields securely. For that I'm trying to encrypt and decrypt the content. This is the code:
public class SecureStorage {
public String getPassword() {
if(!isRooted()) {
String password = pref.getPassword("");
System.out.println("pass getPass: " + password);
return password.isEmpty() ? password : new String(decrypt(Base64.decode(password, Base64.DEFAULT)));
} else
return "";
}
public void setPassword(String passwordStr) {
if(!isRooted()) {
byte[] password = encrypt(passwordStr.getBytes());
pref.setPassword(password == null ? "" : Base64.encodeToString(password, Base64.DEFAULT));
}
}
private SecretKey generateKey() {
// Generate a 256-bit key
final int outputKeyLength = 256;
try {
SecureRandom secureRandom = new SecureRandom();
// Do *not* seed secureRandom! Automatically seeded from system entropy.
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
keyGenerator.init(outputKeyLength, secureRandom);
return keyGenerator.generateKey();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return null;
}
private byte[] getRawKey(byte[] key) throws Exception {
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG", "Crypto");
secureRandom.setSeed(key);
keyGenerator.init(128, secureRandom); // 192 and 256 bits may not be available
SecretKey secretKey = keyGenerator.generateKey();
byte[] rawKey = secretKey.getEncoded();
return rawKey;
}
/** The method that encrypts the string.
#param toEncrypt The string to be encrypted.
#return The encrypted string in bytes. */
//****************************************
private byte[] encrypt(byte[] toEncrypt) {
byte[] encryptedByte = new String().getBytes();
try {
SecretKeySpec secretKeySpec = new SecretKeySpec(getRawKey(Utils.generateUID().getBytes()), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
encryptedByte = cipher.doFinal(toEncrypt);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return encryptedByte;
}
//**************************************
/** The method that decrypts the string.
#param encryptedByte The string to be encrypted.
#return The decrypted string in bytes. */
//****************************************
private byte[] decrypt(byte[] encryptedByte) {
byte[] decryptedByte = new String().getBytes();
try {
SecretKeySpec secretKeySpec = new SecretKeySpec(getRawKey(Utils.generateUID().getBytes()), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);
decryptedByte = cipher.doFinal(encryptedByte);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return decryptedByte;
}
}
I'm able to encrypt the Text.
I'm using SharedPreferences to store the encrypted text and getting the sharedprefs to decrypt the text and give it to a TextView. But in the getPassword() I'm getting the SharedPreference value and trying to decrypt if there is any value in the SharedPrefs. I'm getting the SharedPrefs into a string (password) and trying to decrypt it, but I'm unable to! I'm getting an empty String!
CBC mode needs an initialization vector (IV) in order to operate. The IV is there to randomize the ciphertext and prevent an attacker from determining whether previous plaintexts had the same prefix as the current one.
Since you're not generating any IV, it will be generated for you. A wrong IV only affects the first block (first 16 bytes for AES). If your plaintext is shorter than a block, then this will lead to completely different decryption and then the padding cannot be removed with a probability of roughly 255/256.
The IV is not supposed to be secret. It is common to prepend it to the ciphertext and slice it off before decryption.
public byte[] encrypt(byte[] toEncrypt) throws Exception {
try {
SecretKeySpec secretKeySpec = new SecretKeySpec(getRawKey(Utils.generateUID().getBytes()), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
byte[] iv = cipher.getIV();
byte[] ct = cipher.doFinal(toEncrypt);
byte[] result = new byte[ct.length + iv.length];
System.arraycopy(iv, 0, result, 0, iv.length);
System.arraycopy(ct, 0, result, iv.length, ct.length);
return result;
} catch(...) {...}
return new byte[0];
}
public byte[] decrypt(byte[] encryptedByte) throws Exception {
try {
SecretKeySpec secretKeySpec = new SecretKeySpec(getRawKey(Utils.generateUID().getBytes()), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
byte[] iv = new byte[cipher.getBlockSize()];
byte[] ct = new byte[encryptedByte.length - cipher.getBlockSize()];
System.arraycopy(encryptedByte, 0, iv, 0, cipher.getBlockSize());
System.arraycopy(encryptedByte, cipher.getBlockSize(), ct, 0, ct.length);
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, new IvParameterSpec(iv));
return cipher.doFinal(ct);
} catch (...) {...}
return new byte[0];
}
The problem with this might be that the ciphertext is bigger than anticipated (16 bytes additionally for the IV). If you can make sure that an attacker doesn't get any useful information from determining that previous plaintexts had the same prefix, then you could use a static IV. But be aware that this is usually not that great of an idea and should only be done if you really need that space.
private static final byte[] IV = new byte[16];
...
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, new IvParameterSpec(IV));
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, new IvParameterSpec(IV));

AES key string throws exceptions

I am trying to write a class that will encrypt/decrypt text using AES.
I want to generate a key, store the key in a database column and use that key to encrypt/decrypt corresponding text in the database row containing the key.
The following is the class that I wrote to generate the key and do encryption and decryption tasks.
import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
public class StringDecryptor {
public static String encrypt(String text, String key) {
Key aesKey = null;
Cipher cipher = null;
byte[] encrypted = null;
try {
aesKey = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, aesKey);
encrypted = cipher.doFinal(text.getBytes());
} catch (NoSuchAlgorithmException ex) {
Logger.getLogger(StringDecryptor.class.getName()).log(Level.SEVERE, null, ex);
} catch (NoSuchPaddingException ex) {
Logger.getLogger(StringDecryptor.class.getName()).log(Level.SEVERE, null, ex);
} catch (InvalidKeyException ex) {
Logger.getLogger(StringDecryptor.class.getName()).log(Level.SEVERE, null, ex);
} catch (IllegalBlockSizeException ex) {
Logger.getLogger(StringDecryptor.class.getName()).log(Level.SEVERE, null, ex);
} catch (BadPaddingException ex) {
Logger.getLogger(StringDecryptor.class.getName()).log(Level.SEVERE, null, ex);
} catch (UnsupportedEncodingException ex) {
Logger.getLogger(StringDecryptor.class.getName()).log(Level.SEVERE, null, ex);
}
return new String(encrypted);
}
public static String decrypt(String text, String key) {
Key aesKey = null;
Cipher cipher;
String decrypted = null;
try {
aesKey = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, aesKey);
decrypted = new String(cipher.doFinal(text.getBytes()));
} catch (NoSuchAlgorithmException ex) {
Logger.getLogger(StringDecryptor.class.getName()).log(Level.SEVERE, null, ex);
} catch (NoSuchPaddingException ex) {
Logger.getLogger(StringDecryptor.class.getName()).log(Level.SEVERE, null, ex);
} catch (InvalidKeyException ex) {
Logger.getLogger(StringDecryptor.class.getName()).log(Level.SEVERE, null, ex);
} catch (IllegalBlockSizeException ex) {
Logger.getLogger(StringDecryptor.class.getName()).log(Level.SEVERE, null, ex);
} catch (BadPaddingException ex) {
Logger.getLogger(StringDecryptor.class.getName()).log(Level.SEVERE, null, ex);
} catch (UnsupportedEncodingException ex) {
Logger.getLogger(StringDecryptor.class.getName()).log(Level.SEVERE, null, ex);
}
return decrypted;
}
public static String generateKey() {
SecretKey secretKey = null;
try {
secretKey = KeyGenerator.getInstance("AES").generateKey();
} catch (NoSuchAlgorithmException ex) {
Logger.getLogger(StringDecryptor.class.getName()).log(Level.SEVERE, null, ex);
}
String keyString = bytesToString(secretKey.getEncoded());
return keyString;
}
public static String bytesToString(byte[] b) {
String decoded = null;
try {
decoded = new String(b, "UTF-8");
} catch (UnsupportedEncodingException ex) {
Logger.getLogger(StringDecryptor.class.getName()).log(Level.SEVERE, null, ex);
}
return decoded;
}
public static void main(String args[]) {
String key = generateKey();
System.out.println("key: " + key);
String str = "This is the original string...";
String enc = encrypt(str, key);
System.out.println("enc: " + enc);
String dec = decrypt(enc, key);
System.out.println("dec: " + dec);
}
}
This code is throwing the following exception.
SEVERE: null
java.security.InvalidKeyException: Invalid AES key length: 26 bytes
at com.sun.crypto.provider.AESCipher.engineGetKeySize(AESCipher.java:372)
at javax.crypto.Cipher.passCryptoPermCheck(Cipher.java:1052)
at javax.crypto.Cipher.checkCryptoPerm(Cipher.java:1010)
at javax.crypto.Cipher.implInit(Cipher.java:786)
at javax.crypto.Cipher.chooseProvider(Cipher.java:849)
at javax.crypto.Cipher.init(Cipher.java:1213)
at javax.crypto.Cipher.init(Cipher.java:1153)
at com.innolabmm.software.luckydraw.utils.StringDecryptor.encrypt(StringDecryptor.java:27)
at com.innolabmm.software.luckydraw.utils.StringDecryptor.main(StringDecryptor.java:95)
Exception in thread "main" java.lang.NullPointerException
at java.lang.String.<init>(String.java:556)
at com.innolabmm.software.luckydraw.utils.StringDecryptor.encrypt(StringDecryptor.java:42)
at com.innolabmm.software.luckydraw.utils.StringDecryptor.main(StringDecryptor.java:95)
Java Result: 1
Is there any way to generate a key string that does not cause the AES key conversion to throw exceptions?
To be able to encrypt with the AES-algorithm you will need to base your key on an atleast 128bit String. (Other values are also legal, but I don't have them in my head)
To convert that into number of characters needed in your provided key-string you can take 128 divided by 8
128 / 8 = 16 alpha-numeric characters
This should solve your problem.
EDIT:
Answer on comment about BASE64:
BASE64 is a different way of encoding a string. The result of a BASE64-encoding may be a 128bit string, but not by default. There is not actually a default length of the outcome of a BASE64-encoding. The result may be 8 characters or 512, or some other output that conforms with the encoding rules of BASE64, it all depends on the string you are encoding.
AES needs a key that is of length either 128, 192, or 256 bits. Try using a longer key.
Here's a working example to generate AES key:
private static SecretKeySpec key;
private static final int ENCRYPTION_KEY_SIZE = 128;
private static SecretKey generateKey() throws NoSuchAlgorithmException {
KeyGenerator keyGenerator = null;
keyGenerator = KeyGenerator.getInstance("AES");
keyGenerator.init(ENCRYPTION_KEY_SIZE);
SecretKey key = keyGenerator.generateKey();
return key;
}
public void createNewEncrytionKey() throws NoSuchAlgorithmException, Exception {
SecretKey newKey = generateKey();
saveKey(newKey);
LogUtil.logMessage("createNewEncrytionKey", "New random encryption key was created.", logger);
key = (SecretKeySpec) newKey;
}
you can then use that key to encrypt\decrypt your code (in the example I convert the encrypted value to hexdecimal format:
/**
* Encrypt a given text
*
* #param value String to encrypt
* #return encrypted String value
*/
public static String encrypt(String value) {
if (null == key) {
throw new RuntimeException("Secret encryption key not found!");
}
Cipher c;
String result = null;
try {
c = Cipher.getInstance(ENCRYPTION_ALGORITHM);
c.init(Cipher.ENCRYPT_MODE, key);
// 1. convert value to byte array
byte[] bvalue = value.getBytes();
// 2. convert to encrypted byte array
byte[] encrypted = c.doFinal(bvalue);
// 3. convert to hexadecimal representation
result = toHexString(encrypted);
} catch (Exception e) {
LogUtil.logError("encrypt", e, logger);
}
return result;
}
/**
* Decrypt a given text
*
* #param value encrypted String value
* #return Decrypted value
*/
public static String decrypt(String value) {
if (null == key) {
throw new RuntimeException("Secret encryption key not found!");
}
Cipher c;
String result = null;
try {
// 1. convert hex to encrypted byte array
byte[] encrypted = hexToByteArray(value);
// 2. convert to decrypted byte array
c = Cipher.getInstance(ENCRYPTION_ALGORITHM);
c.init(Cipher.DECRYPT_MODE, key);
byte[] decrypted = c.doFinal(encrypted);
// 3. convert to plain string
result = bytesToString(decrypted);
} catch (Exception e) {
LogUtil.logError("decrypt", e, logger);
}
return result;
}
Your problem has nothing to do with AES or cryptography. You are however trying to interpret a random sequence of bytes as UTF-8 encoded characters (b being the AES key):
String decoded = new String(b, "UTF-8");
That is not going to work, since your byte array will most likely contain several byte sequences, which are invalid for UTF-8. If you need a string representation of the byte array content, you have to encode the data using e.g. base64 or hex encoding.

Categories

Resources