Java String AES Encryption/Decryption not working - java

I'm trying to encrypt and decrypt a string in java using AES/CBC/PKCS5Padding. It encrypts/decrypts without any compilation errors but the string after decryption is just random characters. this is my code:
Encryption:
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
KeySpec spec = new PBEKeySpec(key.toCharArray(), "salt".getBytes(), 65536, 256);
SecretKey secret =new SecretKeySpec(factory.generateSecret(spec).getEncoded(),"AES");
byte[] iv = new byte[16];
new SecureRandom().nextBytes(iv);
IvParameterSpec ivp = new IvParameterSpec(iv);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secret, ivp);
byte[] encryptedText = cipher.doFinal(input.getBytes("UTF-8));
return Base64.getEncoder().encodeToString(encryptedText);
Decryption:
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
KeySpec spec = new PBEKeySpec(key.toCharArray(), "salt".getBytes(), 65536, 256);
SecretKey secret =new SecretKeySpec(factory.generateSecret(spec).getEncoded(),"AES");
byte[] iv = new byte[16];
new SecureRandom().nextBytes(iv);
IvParameterSpec ivp = new IvParameterSpec(iv);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secret, ivp);
byte[] decryptedtext = cipher.doFinal(Base64.getDecoder().decode(input));
return new String(decryptedtext, "UTF-8");
The given text is the same every time but the result changes and is filled with random characters.
Thanks in advance!

Related

Invalid AES key length: 20 bytes (Java 11)

I am trying to generate a key using Java. To be honest I am not that experienced with keys, password, ciphers and encryption.
And from whatever I have searched from this site, I see it as a very common problem. I did some reading and came up with this code that I wrote:
Security.setProperty("crypto.policy", "unlimited");
String valueToEncode = "some_random_text";
SecureRandom secureRandom = new SecureRandom();
byte[] salt = new byte[256];
secureRandom.nextBytes(salt);
KeySpec keySpec = new PBEKeySpec("some_random_password".toCharArray(), salt, 65536, 256); // AES-256
SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("PBEWITHHMACSHA512ANDAES_256");
byte[] key = secretKeyFactory.generateSecret(keySpec).getEncoded();
SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");
byte[] ivBytes = new byte[16];
secureRandom.nextBytes(ivBytes);
IvParameterSpec ivParameterSpec = new IvParameterSpec(ivBytes);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
byte[] encValue = cipher.doFinal(valueToEncode.getBytes(StandardCharsets.UTF_8));
byte[] finalCiphertext = new byte[encValue.length + 2 * 16];
System.arraycopy(ivBytes, 0, finalCiphertext, 0, 16);
System.arraycopy(salt, 0, finalCiphertext, 16, 16);
System.arraycopy(encValue, 0, finalCiphertext, 32, encValue.length);
System.out.println(finalCiphertext.toString());
This is modified from an answer that I saw on another post. But I still get the "invalid length" error.
The error that I get is:
Exception in thread "main" java.security.InvalidKeyException: Invalid AES key length: 20 bytes
at com.sun.crypto.provider.AESCrypt.init(AESCrypt.java:87)
at com.sun.crypto.provider.CipherBlockChaining.init(CipherBlockChaining.java:93)
at com.sun.crypto.provider.CipherCore.init(CipherCore.java:591)
at com.sun.crypto.provider.AESCipher.engineInit(AESCipher.java:346)
at javax.crypto.Cipher.implInit(Cipher.java:805)
at javax.crypto.Cipher.chooseProvider(Cipher.java:863)
at javax.crypto.Cipher.init(Cipher.java:1395)
at javax.crypto.Cipher.init(Cipher.java:1326)
at com.att.logicalprovisioning.simulators.Trial.main(Trial.java:47)
Trial.java:47 being the line: cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
Is there a one-size fits all solution to this? Or is it just my lack of understanding?
Any help would be appreciated.
Your key is 20 bytes long because secretKeyFactory.generateSecret(keySpec).getEncoded() returns the password some_random_password.
An easy way to fix the code is to use the key derivation PBKDF2WithHmacSHA512 instead of PBEWITHHMACSHA512ANDAES_256. This generates a key of the specified length based on the password and salt:
...
SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512");
...
However, PBEWITHHMACSHA512ANDAES_256 can also be applied. This algorithm specifies a key derivation with PBKDF2WithHmacSHA512 and subsequent AES encryption. The implementation is functionally identical to yours, but requires a few more changes to the code:
...
IvParameterSpec ivParameterSpec = new IvParameterSpec(ivBytes);
PBEParameterSpec parameterSpec = new PBEParameterSpec(salt, 65536, ivParameterSpec);
PBEKeySpec keySpec = new PBEKeySpec("some_random_password".toCharArray());
SecretKeyFactory kf = SecretKeyFactory.getInstance("PBEWITHHMACSHA512ANDAES_256");
SecretKey secretKey = kf.generateSecret(keySpec);
Cipher cipher = Cipher.getInstance("PBEWITHHMACSHA512ANDAES_256");
cipher.init(Cipher.ENCRYPT_MODE, secretKey, parameterSpec);
byte[] encValue = cipher.doFinal(valueToEncode.getBytes(StandardCharsets.UTF_8));
...
Two other issues are:
You are using a 256 bytes salt, but only storing 16 bytes when concatenating. To be consistent with the concatenation, apply a 16 bytes salt: byte[] salt = new byte[16].
The output finalCiphertext.toString() returns only class and hex hashcode of the object, s. here. For a meaningful output use a Base64 or hex encoding of the byte[] instead, e.g. Base64.getEncoder().encodeToString(finalCiphertext).

How can I send parameters to AES encryptor?

I have to encrypt some parameters for a web portal that recibe encrypted parameters via POST with AES/CBC/PKCS5Padding, the encryption method was provided by the web portal creator(another team), but when I use their method the result is not accepted by ther web portal. I assume that the parameters are not well provided to the method. Am I missing somethig?
I requested some fixed IV value but the another teams told me that they used random IV's.
public static byte[] Encrypt(byte[] plaintext, SecretKey key, byte[] IV) throws Exception {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
SecretKeySpec keySpec = new SecretKeySpec(key.getEncoded(), "AES");
IvParameterSpec ivSpec = new IvParameterSpec(IV);
cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);
byte[] cipherText = cipher.doFinal(plaintext);
return cipherText;
}
//this is the way I build the parameters
String input_text = txtPlainText.getText();
byte[] plaintext = input_text.getBytes("UTF-8");
String encodedKey = "6qp4Y?kLmaY8+Fsd";
byte[] decodedKey = encodedKey.getBytes();
SecretKey key = new SecretKeySpec(decodedKey, 0, decodedKey.length, "AES");
byte[] IV = new byte[16];
SecureRandom random = new SecureRandom();
random.nextBytes(IV);
byte[] cipherText = Encrypt(plaintext,key,IV);
String Encrypted_text = java.util.Base64.getEncoder().encodeToString(cipherText);
txtEncryptedText.setText(Encrypted_text );
Every timeI use this encription method i get a diferent value, for the plain text 'System Administrator' I get vlaues like 'U+za3baTIvMpiFgCPCfqoKe+ljDutraUrUTJhroqHPg=', 'lHT0Kq6GlyzYx2TEyQmqdPp4KfVi5sdlC0udtSlZ5uk='

how can I create function from java to objective-c

I'm a fresh IOS developer, and facing a problem in encryption and decryption.
decrypt code(java)
byte[] keyPass = pass.getBytes("ASCII");
byte[] aesIV = new byte[16];
byte[] Decryptdata = Base64.decode(encodedString, Base64.NO_WRAP);
System.arraycopy(Decryptdata, 0, aesIV, 0, 16);
byte[] data = new byte[Decryptdata.length - 16];
System.arraycopy(Decryptdata, 16, data, 0, dataToDecrypt.length - 16);
Key aesKey = new SecretKeySpec(keyPass, "AES");
IvParameterSpec ivSpec = new IvParameterSpec(aesIV);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
cipher.init(Cipher.DECRYPT_MODE, aesKey, ivSpec);
Text = new String(cipher.doFinal(data), "UTF-8");
encrypt code(java)
byte[] keyPass = pass.getBytes("ASCII");
final Key key = new SecretKeySpec(keyPass, "AES");
final Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
byte[] byteMessage = text.getBytes("UTF-8");
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] cipherText = cipher.doFinal(byteMessage);
byte[] ivByte = cipher.getIV();
byte[] bytesTotal = new byte[ivByte.length+cipherText.length];
System.arraycopy(ivByte, 0, bytesTotal, 0, ivByte.length);
System.arraycopy(cipherText, 0, bytesTotal, ivByte.length, cipherText.length);
encyoted = Base64.encodeToString(bytesTotal, Base64.NO_WRAP);
How can I create encrypt and decrypt in objective-c?
"how can i create function from java to objective-c"
For iOS use Common Crypto.
Study, write code and debug. Repeat as necessary.

Java Android - Encrypt/Decrypt file contents

So at the moment I'm trying to write some encrypted text to a file and then be able to read that back in, decrypt it and display it to the user. I'm currently using AES-256 with PBKDF2 password derivation as I'd like to be able to use a user's password to encrypt/decrypt the files. The files are simple text files. The code I am currently using to encrypt some text and save it to a file is below. As far as I can tell, from having a look using adb, this works correctly.
FileOutputStream out = new FileOutputStream(mypath);
String defaultMessage = "Empty File";
int iterationCount = 1000;
int keyLength = 256;
int saltLength = keyLength / 8;
SecureRandom randomGenerator = new SecureRandom();
byte[] salt = new byte[saltLength];
randomGenerator.nextBytes(salt);
KeySpec keySpec = new PBEKeySpec(submittedPassword.toCharArray(), salt, iterationCount, keyLength);
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
byte[] keyBytes = keyFactory.generateSecret(keySpec).getEncoded();
SecretKey key = new SecretKeySpec(keyBytes, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
byte[] iv = new byte[cipher.getBlockSize()];
randomGenerator.nextBytes(iv);
IvParameterSpec ivParams = new IvParameterSpec(iv);
cipher.init(Cipher.ENCRYPT_MODE, key, ivParams);
byte[] ciphertext = cipher.doFinal(defaultMessage.getBytes("UTF-8"));
String finalMessage = ciphertext.toString() + "]" + iv.toString() + "]" + salt.toString();
out.write(finalMessage.getBytes());
out.close();
P.S The above is within a Try/Except.
The code below is what I'm currently trying to use to read in the file and then decrypt it, however, when I try to display the decrypted contents via the test view at the end, it does not show up.
FileInputStream fileInputStream = new FileInputStream(mypath);
InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream);
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
StringBuffer stringBuffer = new StringBuffer();
while ((fileContents = bufferedReader.readLine()) != null) {
stringBuffer.append(fileContents + "\n");
}
String fileContentsString = stringBuffer.toString();
String[] fileContentsList = fileContentsString.split("]");
byte[] cipherText = fileContentsList[0].getBytes();
Toast.makeText(getApplicationContext(), fileContentsList[0], Toast.LENGTH_LONG).show();
byte[] iv = fileContentsList[1].getBytes();
byte[] salt = fileContentsList[2].getBytes();
KeySpec keySpec = new PBEKeySpec(submittedPassword.toCharArray(), salt, 1000, 256);
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
byte[] keyBytes = keyFactory.generateSecret(keySpec).getEncoded();
SecretKey key = new SecretKeySpec(keyBytes, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
IvParameterSpec ivParams = new IvParameterSpec(iv);
cipher.init(Cipher.DECRYPT_MODE, key, ivParams);
byte[] plaintext = cipher.doFinal(cipherText);
String plainrStr = new String(plaintext , "UTF-8");
textEdit.setText(plainrStr);
Hopefully someone can provide me with some assistance here. Again, the second code segment is within a Try/Except statement.
You have multiple problems with your code.
Encryption
This code
String finalMessage = ciphertext.toString() + "]" + iv.toString() + "]" + salt.toString();
does not produce a ciphertext. See here: Java: Syntax and meaning behind "[B#1ef9157"? Binary/Address?
The IV and salt have fixed sizes, so they can be placed in front of the ciphertext. After you've written the whole ciphertext, you need to use something like Base64 or Hex in order to get a String. Modern ciphers like AES produce ciphertexts that can contain bytes of any value which don't always constitute valid character encodings such as UTF-8. Strings are no containers for arbitrary byte[] contents.
ByteArrayOutputStream baos = new ByteArrayOutputStream();
baos.write(iv);
baos.write(salt);
baos.write(ciphertext);
String finalMessage = Base64.encodeToString(baos.toByteArray(), Base64.DEFAULT);
But you don't need that at all, because you can directly write your ciphertext into the file:
out.write(iv);
out.write(salt);
out.write(ciphertext);
Decryption
Don't use InputStreamReader, a BufferedReader and a StringBuffer for binary data. Otherwise, you'll corrupt your binary ciphertext.
You only need this:
byte[] iv = new byte[16];
byte[] salt = new byte[32];
byte[] ctChunk = new byte[8192]; // not for whole ciphertext, just a buffer
if (16 != fileInputStream.read(iv) || 32 != fileInputStream.read(salt)) {
throw new Exception("IV or salt too short");
}
KeySpec keySpec = new PBEKeySpec(submittedPassword.toCharArray(), salt, 1000, 256);
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
byte[] keyBytes = keyFactory.generateSecret(keySpec).getEncoded();
SecretKey key = new SecretKeySpec(keyBytes, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
IvParameterSpec ivParams = new IvParameterSpec(iv);
cipher.init(Cipher.DECRYPT_MODE, key, ivParams);
int read;
ByteArrayOutputStream ctBaos = new ByteArrayOutputStream();
while((read = fileInputStream.read(ctChunk)) > 0) {
ctBaos.write(cipher.update(cipherText, 0, read));
}
ctBaos.write(cipher.doFinal());
String plainrStr = new String(ctBaos.toByteArray(), "UTF-8");
textEdit.setText(plainrStr);
This handles randomization properly but doesn't provide integrity. If you want to detect (malicious) manipulations of your ciphertexts (and generally you'll want that to prevent some attacks), you'd need to use an authenticated mode like GCM or EAX, or employ an encrypt-then-MAC scheme with a strong MAC like HMAC-SHA256.
Use a library like tozny/java-aes-crypto in order to use good defaults.
How to write some encrypted text to a file and then be able to read that back in?
public static byte[] generateKey(String password) throws Exception
{
byte[] keyStart = password.getBytes("UTF-8");
KeyGenerator kgen = KeyGenerator.getInstance("AES");
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG", "Crypto");
sr.setSeed(keyStart);
kgen.init(128, sr);
SecretKey skey = kgen.generateKey();
return skey.getEncoded();
}
public static byte[] encodeFile(byte[] key, byte[] fileData) throws Exception
{
SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
byte[] encrypted = cipher.doFinal(fileData);
return encrypted;
}
public static byte[] decodeFile(byte[] key, byte[] fileData) throws Exception
{
SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, skeySpec);
byte[] decrypted = cipher.doFinal(fileData);
return decrypted;
}
To save a encrypted file to sd do:
File file = new File(Environment.getExternalStorageDirectory() + File.separator + "your_folder_on_sd", "file_name");
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file));
byte[] yourKey = generateKey("password");
byte[] filesBytes = encodeFile(yourKey, yourByteArrayContainigDataToEncrypt);
bos.write(fileBytes);
bos.flush();
bos.close();
To decode a file use:
byte[] yourKey = generateKey("password");
byte[] decodedData = decodeFile(yourKey, bytesOfYourFile);
For reading in a file to a byte Array there a different way out there. A Example: http://examples.javacodegeeks.com/core-java/io/fileinputstream/read-file-in-byte-array-with-fileinputstream/

How do I create a password based cipher with Shiro?

I have created a method that lets me encrypt and decrypt an object with a password. However, I am using Java's native encryption libraries but these are not very secure. (For small changes in the object I am trying to serialize, I get 208 identical bytes out of a total of 243 encrypted bytes.) I think Shiro has alternatives, but I can't seem to find them (at least in 1.1.0 for password-based-encryption). Here is my code for encrypting. I inject the password value to the class and I'm leaving out any exception handling to keep things simpler:
public String encryptToString(Serializable object) {
SecretKeyFactory keyFactory =
SecretKeyFactory.getInstance(ALGORITHM);
KeySpec keySpec = new PBEKeySpec(password.toCharArray());
SecretKey secretKey = keyFactory.generateSecret(keySpec);
PBEParameterSpec paramSpec = new PBEParameterSpec(SALT, ITERATIONS);
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, secretKey, paramSpec);
// Serialize map
final ByteArrayOutputStream byteArrayOutputStream =
new ByteArrayOutputStream();
CipherOutputStream cout =
new CipherOutputStream(byteArrayOutputStream, cipher);
ObjectOutputStream out = new ObjectOutputStream(cout);
out.writeObject(object);
out.close();
cout.close();
byteArrayOutputStream.close();
return new String(
Base64.encode(byteArrayOutputStream.toByteArray()));
}
And my code for decrypting:
public Object decryptToObject(String encodedString) {
SecretKeyFactory keyFactory =
SecretKeyFactory.getInstance(ALGORITHM);
KeySpec keySpec = new PBEKeySpec(password.toCharArray());
SecretKey secretKey = keyFactory.generateSecret(keySpec);
PBEParameterSpec paramSpec = new PBEParameterSpec(SALT, ITERATIONS);
Cipher decipher = Cipher.getInstance(ALGORITHM);
decipher.init(Cipher.DECRYPT_MODE, secretKey, paramSpec);
final ByteArrayInputStream byteArrayInputStream =
new ByteArrayInputStream(Base64.decode(encodedString
.getBytes()));
CipherInputStream cin =
new CipherInputStream(byteArrayInputStream, decipher);
ObjectInputStream in = new ObjectInputStream(cin);
Object result = in.readObject();
in.close();
cin.close();
byteArrayInputStream.close();
return result;
}

Categories