I have a problem with with
javax.crypto.Cipher
When I write this lines of code
Cipher cipher;
byte[] bytes = null;
try
{
cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, generateAESKey128b(key));
bytes = cipher.doFinal(input.getBytes("UTF-8"));
}
catch (NoSuchAlgorithmException e)
{
e.printStackTrace();
}
catch (NoSuchPaddingException e)
{
e.printStackTrace();
}
catch (InvalidKeyException e)
{
e.printStackTrace();
}
catch (UnsupportedEncodingException e)
{
e.printStackTrace();
}
catch (IllegalBlockSizeException e)
{
e.printStackTrace();
}
catch (BadPaddingException e)
{
e.printStackTrace();
}
The console give me this error
javax.crypto.IllegalBlockSizeException
Input length must be multiple of 16 when
decrypting with padded cipher
at com.sun.crypto.provider.SunJCE_f.b(DashoA13*..)
at com.sun.crypto.provider.SunJCE_f.b(DashoA13*..)
at com.sun.crypto.provider.AESCipher.engineDoFinal(DashoA13*..)
at javax.crypto.Cipher.doFinal(DashoA13*..)
at it.unitn.se.gym.backend.utils.Security.AES128Decode(Security.java:109)
at it.unitn.se.gym.backend.utils.Security.decode_AES128_Base64(Security.java:96)
at it.unitn.se.gym.backend.WebService.main(WebService.java:42)
Exception in thread "main" java.lang.NullPointerException
at it.unitn.se.gym.backend.utils.Security.decode_AES128_Base64(Security.java:97)
at it.unitn.se.gym.backend.WebService.main(WebService.java:42)
The first 2 lines of code are correct but when I pass the attribute "text", of type byte[], to the doFinal function, it give me the error.
Can someone tell me why?
SOLVED:
Okay, problem solved
byte[] encrypted = UniversalBase64Encoder.decode(input);
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, generateAESKey128b(key));
byte[] originalBytes = cipher.doFinal(encrypted);
This is the right code that I wrote
The problem is that you're trying to decrypt a string that was not encrypted, and in doing so are violating an assumption of the decryption algorithm (that its input size is always a multiple of 16).
Here's a block of code that encrypts and then decrypts a string. Notice that when the encrypted string is printed, it's 16 bytes long, even though the input string is not. The encryption algorithm pads the input string out to make it a multiple of 16 bytes before encrypting it. That 16-byte-long encrypted string is now a valid input for decryption.
This assumption (that the result of encrypting will be an even size) is quite standard. It not only makes the decryption/encryption algorithm easier to write, but it also prevents an attacker from knowing the length of the thing you encrypted.
byte[] keyBytes = new byte[16];
keyBytes[0] = 1;
SecretKeySpec key = new SecretKeySpec(keyBytes, "AES");
String input = "hello";
Cipher cipher;
byte[] bytes = null;
cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, key);
bytes = cipher.doFinal(input.getBytes("UTF-8"));
System.out.println("Encoded: "+Arrays.toString(bytes));
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] decoded = cipher.doFinal(bytes);
System.out.println("Decoded: "+new String(decoded, "UTF-8"));
Related
How would I go about this? I have code encrypting a file using AES/CBC/PKCS5Padding in Java and as I understand form this picture:
Partial decryption should be possible having part of ciphertext used by preceding block and the key. I have not found any examples of this, though. Any help?
The decrypting code is as fallows:
//skip the IV (ivSize is 16 here) - IV was pretended to the stream during encryption
data.skip(ivSize);
//skip n blocks
int n = 2;
System.out.println("skipped: " + data.skip(n*16));
byte[] iv = new byte[ivSize];
//use next 16 bytes as IV
data.read(iv);
// Hashing key.
MessageDigest digest = MessageDigest.getInstance("SHA-256");
digest.update(encryptionKey.getBytes(StandardCharsets.UTF_8));
byte[] keyBytes = new byte[16];
System.arraycopy(digest.digest(), 0, keyBytes, 0, keyBytes.length);
Cipher cipher;
try {
cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
IvParameterSpec ivSpec = new IvParameterSpec(iv);
cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
} catch (GeneralSecurityException e) {
throw new IOException(e);
}
CipherInputStream cis = new CipherInputStream(data, cipher);
try {
ByteStreams.copy(ByteStreams.limit(cis, limit), output);
} catch (IOException exception) {
// starting with java 8 the JVM wraps an IOException around a GeneralSecurityException
// it should be safe to swallow a GeneralSecurityException
if (!(exception.getCause() instanceof GeneralSecurityException)) {
throw exception;
}
log.warning(exception.getMessage());
} finally {
cis.close();
}
Yes it is possible.
You need to choose the "part" on a block boundary, a length that is a block size multiple and use the previous block as the IV. For AES the block size is 16-bytes.
If the "part" includes the last block specify the correct padding, otherwise specify no padding NoPadding: AES/CBC/NoPadding in this instance. This will eliminate a padding error. Only there last block is has/is padding.
Cipher must be instantiated with the correct padding option depending if it is the last block of the entire encrypted data or not.
See: PKCS#7 padding (sometimes called PKCS#5 in error) which is the most common padding.
This is a continuation from my question yesterday:
Android Java AES Encryption
I am currently testing AES encryption on Android. In my previous question I am able to encrypt and decrypt a string using Cipher c = Cipher.getInstance("AES");
I was informed in the replies that I should specify the IV, encryption mode and padding to prevent any potential issues in the future since no specification means the program will use the system's default value. So I changed my code to c = Cipher.getInstance("AES/CBC/PKCS5Padding");
But now my code no longer works, and it will return a NullPointerException.
My code:
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 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);
}
/**
* Build private key from a passpharase/PIN (incl. key derivation (Uses PBKDF2))
* #param passphraseOrPin
* #param salt
* #return The generated SecretKey (Used for AES-encryption, key size specified in outputKeyLength)
*/
public static SecretKey generateKey(char[] passphraseOrPin, byte[] salt)
throws NoSuchAlgorithmException, InvalidKeySpecException {
// Number of PBKDF2 hardening rounds to use. Larger values increase
// computation time. You should select a value that causes computation
// to take >100ms.
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/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)
testapp.ttyi.nfcapp.DisplayQRActivity.decryptFIN128AES(DisplayQRActivity.java:254) points to the last line of decryptFIN128AES, which is:
return new String(decodedBytes, StandardCharsets.UTF_8);
I understand that the NullPointerException occurs because something went wrong with the decryption process. Since it must have went into the catch case and thus decodedBytes remains as NULL and thus causes the error when I want to return decodedBytes. So now my question is: Why does this happen and how can I fix this?
Your help is much appreciated.
Many thanks to #Thomas W for your help. I changed the catch to throw and now I can see the actual error has something to do with BadPaddingException: error:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad decrypt
After some googling I found the solution is the lack of IV argument in my c.init. Previously I was using "AES" in which Java defaults to "AES/ECB/PKCS5Padding" and that works fine without IV.
(Source: Android: Encrypt a string with AES 256bit Encryption with iv and secret key)
But once I changed to "AES/CBC/PKCS5Padding" Java will have issues without a declared IV. Therefore by changing c.init(Cipher.ENCRYPT_MODE, sks); and c.init(Cipher.DECRYPT_MODE, sks); to
c.init(Cipher.ENCRYPT_MODE, sks, new IvParameterSpec(new byte[16])); and
c.init(Cipher.DECRYPT_MODE, sks, new IvParameterSpec(new byte[16])); fixed the issue.
Now my program can encrypt and decrypt properly.
I'm currently running into problems decrypting my data. The base64 of the encoded string is being stored in the database. So, I'm printing out the encoded string and then trying to run it back through with "DECRYPT" instead of "ENCRYPT". However, I never get a value that the Decrypter method likes, it always gives me an error about parameters or the value not being 16 bytes.
public class crypto {
public static void main(String [] args) {
String s = args[0];
String s1 = args[1];
String ivkey = "thisisasecretkey";
byte[] ivraw = ivkey.getBytes();
SecretKeySpec skeySpec = new SecretKeySpec(ivraw, "AES");
if (s.equalsIgnoreCase("ENCRYPT")) {
try {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
byte[] encrypted = cipher.doFinal(s1.getBytes());
System.out.println(new String(Base64.encodeBase64(encrypted)));
} catch (Exception e) {
throw new RuntimeException(e);
}
} else {
try {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, skeySpec);
byte[] encrypted = cipher.doFinal(s1.getBytes());
System.out.println(new String(Base64.decodeBase64(encrypted)));
} catch (Exception e) {
e.printStackTrace();
}
}
return;
};
}
command:crypto "ENCRYPT" "password"
output: 5eQvSzPG1TE2AybgCmeV6A==
command:crytpo "DECRYPT" "5eQvSzPG1TE2AybgCmeV6A=="
output: java.security.InvalidKeyException: Parameters missing
I'm aware of the security flaws, that's not what I'm asking about and I would prefer answers/comments not get cluttered with best practices.
You should do base 64 decoding, and you should do that before decrypting.
You are not including the initialization vector (IV).
AES in CBC mode has both a 16 byte IV and the 16 byte symmetric key.
String IV = "AAAAAAAAAAAAAAAA"; // generate this randomly
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, new IvParameterSpec(IV.getBytes()));
byte[] encrypted = cipher.doFinal(s.getBytes());
Edit: as it turns out, encryption does not require a IV to be provided (as owlstead pointed out), but decryption does. The best bet would be to be explicit and use IV in both encryption and decryption. Change your decryption function to include the IV, and you will run into the other error in your code that owlstead pointed out.
I am doing encryption and decryption of data as follows but getting error
protected Cipher aes_Gen_with_Key(byte[] key)
{
Cipher cipher=null;
try
{
byte[] key_hash = (key).toString().getBytes("UTF-8");
key_hash = Arrays.copyOf(key_hash, 32); // use only first 256 bit
SecretKeySpec secretKeySpec = new SecretKeySpec(key_hash, "AES");
cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
} catch (Exception e) {
System.out.println("Error Occured");
}
return cipher;
}
protected Cipher aes_Dec_with_Key(byte[] key,byte[] iv)
{
Cipher cipher=null;
try
{
byte[] key_hash = (key).toString().getBytes("UTF-8");
key_hash = Arrays.copyOf(key_hash, 32); // use only first 256 bit
SecretKeySpec secretKeySpec = new SecretKeySpec(key_hash, "AES");
cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE,secretKeySpec,new IvParameterSpec(iv));
}
catch (Exception e) {
System.out.println(e);
}
return cipher;
}
With above 2 functions I am getting ciphers with which I am doing encryption and decryption, but getting javax.crypto.BadPaddingException: Given final block not properly padded as error. Length of decryption byte array is 752 and IV at decryption is 16 byte long. Can any one suggest?
Here are few more relevant code blocks.
Apologies for invalid use java naming conventions
// Key Class
import java.io.Serializable;
#SuppressWarnings("serial")
public class Key implements Serializable{
byte[] gsmodp_hash=null;
byte[] iv_pass=null;
byte[] Nonce_Enc=null;
byte[] iv_non=null;
public Key() {
}
public Key(byte[] gsmodp_hash,byte[] iv_pass,byte[] Nonce_Enc,byte[] iv_non) {
this.gsmodp_hash=gsmodp_hash;
this.iv_pass=iv_pass;
this.Nonce_Enc=Nonce_Enc;
this.iv_non=iv_non;
}
}
// Client side code
JSONObject auth_step_obj=new JSONObject();
try {
BigInteger gsmodp=get_modu_frm_server(receivePacket);
BigInteger R2=get_R2_frm_server(receivePacket);
BigInteger N2=get_N2_frm_server(receivePacket);
N2=crypto.dec_NonceG(N2);
BigInteger a=crypto.get_RandLong();
BigInteger gamodp= crypto.dh_GenerationG(a, crypto.g, crypto.p);
BigInteger key=crypto.dh_GenerationG(a, gsmodp, crypto.p);
// Got hash of g^abmodp
byte[] dhkey=crypto.sha256G(key.toString());
key=null;
//Mixing passwords
SecretKey secretkey=(SecretKey) userCredentials.get("password");
byte[] mixed_hash=crypto.passwordMixerG(R2, secretkey.getEncoded());
//Working Fine Till now
// Getting Cipher for encrypting gsmodp using password and nonce
Cipher cipher_password=crypto.aes_Gen_with_Key(mixed_hash);
Cipher cipher_key=crypto.aes_Gen_with_Key(dhkey);
// Generating quantities for JSON Object
byte[] gsmodp_hash=cipher_password.doFinal(gamodp.toString().getBytes());
byte[] gsmodp_hash_iv=cipher_password.getIV();
byte[] nonce_enc=cipher_key.doFinal(N2.toString().getBytes());
byte[] nonce_enc_iv=cipher_key.getIV();
Key authetication_parameters=new Key(gsmodp_hash,gsmodp_hash_iv,nonce_enc,nonce_enc_iv);
auth_step_obj.put("obj",authetication_parameters);
} catch (Exception e) {
// TODO Auto-generated catch block
System.out.println("Hi:sec_DH_Step");
}
// This sends JSONObject to calling method which generates UDP packet and sends to server
return auth_step_obj;
}
// Server side code
// Once packet received on server following happens
ds.receive(receivePacket);
SecretKey userKey=cryptoObj.get_from_KeyStoreG(user_name);
// Adding R2 and userKey byte by byte
byte[] mixed_hash=cryptoObj.passwordMixerG(R2, userKey.getEncoded());
JSONObject authentication_nonce=new JSONObject();
authentication_nonce=cryptoObj.readRecievedPacket(receivePacket);
Key obj=(Key)authentication_nonce.get("obj");
Cipher cipher=cryptoObj.aes_Dec_with_Key(mixed_hash,obj.iv_pass);
// I am getting error on do final
System.out.println(new String(cipher.doFinal(obj.gsmodp_hash)));
The code you presented works just fine. The error must lie in the code you haven't shared with us (i.e. the code that actually uses the Cipher objects).
I wrote the code below to test your question code:
public static void main(String[] args) throws Exception {
Random random = new Random();
byte[] key = new byte[32];
random.nextBytes(key);
byte[] plaintext = new byte[100];
random.nextBytes(plaintext);
Cipher enc = aes_Gen_with_Key(key);
byte[] ciphertext = enc.doFinal(plaintext);
byte[] iv = enc.getIV();
Cipher dec = aes_Dec_with_Key(key, iv);
byte[] recoveredPlaintext = dec.doFinal(ciphertext);
System.out.println(Arrays.equals(plaintext, recoveredPlaintext));
}
Note, I made your two methods static. You should do the same, as they don't use any instance variables.
Iam encrypting data using the following code
final SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(),
"AES");
final String myIV = "89ABCDEF01234567";
Cipher c = null;
try {
try {
c = Cipher.getInstance("AES/CBC/PKCS5Padding");
} catch (NoSuchAlgorithmException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchPaddingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
c.init(Cipher.ENCRYPT_MODE, keySpec, new IvParameterSpec(
myIV.getBytes()));
byte[] encrypted = c.doFinal(msgfromEB.getBytes(),0,msgfromEB.getBytes().length);
and iam decyrpting like this
c.init(Cipher.DECRYPT_MODE, keySpec, new IvParameterSpec(
myIV.getBytes()));
byte[] decryptedBytes = c.doFinal(encrypted ,0,encrypted .length);
System.out.println("decrypted string is"+new String(decryptedBytes));
it is working iam able to encrypt and decrypt properly
But if iam converting into a string like this
String myString = new String (encrypted);
and again get the byte array like this
byte[] newbytearray = myString.getBytes();
Now iam trying to decrypt
c.init(Cipher.DECRYPT_MODE, keySpec, new IvParameterSpec(
myIV.getBytes()));
byte[] decryptedBytes = c.doFinal(newbytearray ,0,newbytearray .length);
System.out.println("decrypted string is"+new String(decryptedBytes));
now iam getting an error
javax.crypto.IllegalBlockSizeException: last block incomplete in decryption
If iam using base64 convertion it is working fine, but I don't want to do it as it increases the length. Any other alternative to over come this?
The alternative is to transfer bytes instead of Strings.
When you're doing new String(bytes), you're asking to use your platform's default encoding to interpret bytes and sequences of bytes as characters. The problem is that all the bytes and sequences of bytes do not represent valid characters. The algorithm is thus lossy. That's a bit like transforming a color picture to black and white, and then trying to go from black and white to color. It doesn't work. Hence the need for base64.
An additional problem is that you don't specify any explicit encoding, which means that the sender and the receiver could use different encodings if they're not on the exact same platform.