I got an exception in the following code for AES algorithm in java.
Code decryptes an encrypted string and returns the original string.
Plz help me to fix this.
Code:
public class AES
{
public byte[] encrypted;
public byte[] original;
public String originalString;
public static String asHex (byte buf[])
{
StringBuffer strbuf = new StringBuffer(buf.length * 2);
int i; for (i = 0; i < buf.length; i++)
{
if (((int) buf[i] & 0xff) < 0x10) strbuf.append("0");
strbuf.append(Long.toString((int) buf[i] & 0xff, 16));
}
return strbuf.toString();
}
public String AESencryptalgo(byte[] text)
{
String newtext="";
// Get the KeyGenerator
try
{
KeyGenerator kgen = KeyGenerator.getInstance("AES");
kgen.init(128); // 192 and 256 bits may not be available
// Generate the secret key specs.
SecretKey skey = kgen.generateKey();
byte[] raw = skey.getEncoded();
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
// Instantiate the cipher Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec); encrypted = cipher.doFinal(text);
System.out.println("encrypted string: " + asHex(encrypted));
cipher.init(Cipher.DECRYPT_MODE, skeySpec); original = cipher.doFinal(encrypted);
originalString = new String(original); System.out.println("Original string: " + originalString + " " + asHex(original));
}
catch(Exception e)
{ }
finally
{
newtext=new String(encrypted);
System.out.println("ENCRYPTED "+newtext);
//AESdecryptalgo(newtext.getBytes());
return newtext;
}
}
public String AESdecryptalgo(byte[] text)
{
// Get the KeyGenerator
try
{
KeyGenerator kgen = KeyGenerator.getInstance("AES");
kgen.init(128); // 192 and 256 bits may not be available
// Generate the secret key specs.
SecretKey skey = kgen.generateKey();
byte[] raw = skey.getEncoded();
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
// Instantiate the cipher
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, skeySpec);
original = cipher.doFinal(text); //Exception occurs here
originalString = new String(original);
System.out.println("Original string: " + originalString + " " + asHex(original));
}
catch(Exception e)
{
System.out.println("exception");
}
finally
{
System.out.println("DECRYPTED "+originalString);
return originalString;
}
}
public static void main(String[] args)
{
AES a=new AES();
a.AESencryptalgo("hello".getBytes());
System.out.println();
}}
`
exception:
javax.crypto.BadPaddingException: Given final block not properly padded 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*..)
According to Java™ Cryptography Architecture (JCA) Reference Guide (emphasis mine):
Cipher objects are obtained by using
one of the Cipher getInstance() static
factory methods. Here, the algorithm
name is slightly different than with
other engine classes, in that it
specifies not just an algorithm name,
but a "transformation". A
transformation is a string that
describes the operation (or set of
operations) to be performed on the
given input to produce some output. A
transformation always includes the
name of a cryptographic algorithm
(e.g., DES), and may be followed by a
mode and padding scheme.
A transformation is of the form:
"algorithm/mode/padding" or
"algorithm"
For example, the following are valid transformations:
"DES/CBC/PKCS5Padding"
"DES"
If just a transformation name is specified, the system will
determine if there is an
implementation of the requested
transformation available in the
environment, and if there is more than
one, returns there is a preferred one.
If both a transformation name and a
package provider are specified, the
system will determine if there is an
implementation of the requested
transformation in the package
requested, and throw an exception if
there is not.
If no mode or padding is specified,
provider-specific default values for
the mode and padding scheme are used.
For example, the SunJCE provider uses
ECB as the default mode, and
PKCS5Padding as the default padding
scheme for DES, DES-EDE and Blowfish
ciphers. This means that in the case
of the SunJCE provider:
Cipher c1 = Cipher.getInstance("DES/ECB/PKCS5Padding");
and
Cipher c1 = Cipher.getInstance("DES");
are equivalent statements.
Using modes
such as CFB and OFB, block ciphers can
encrypt data in units smaller than the
cipher's actual block size. When
requesting such a mode, you may
optionally specify the number of bits
to be processed at a time by appending
this number to the mode name as shown
in the "DES/CFB8/NoPadding" and
"DES/OFB32/PKCS5Padding"
transformations. If no such number is
specified, a provider-specific default
is used. (For example, the SunJCE
provider uses a default of 64 bits for
DES.) Thus, block ciphers can be
turned into byte-oriented stream
ciphers by using an 8 bit mode such as
CFB8 or OFB8.
Appendix A of this document contains a
list of standard names that can be
used to specify the algorithm name,
mode, and padding scheme components of
a transformation.
The objects returned by factory
methods are uninitialized, and must be
initialized before they become usable.
Because your code does not specify mode or padding, provider-specific default values are being used. It appears that your provider is SunJCE and that it's default padding is probably "NoPadding". With this padding, you are responsible for ensuring that the size of the byte array being encrypted is a multiple of the number of bytes in the secret key. You can make you're life easier by specifying the mode and padding in your transformation:
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
WARNING: You should not use ECB mode in real code. Try CBC instead.
Update: I didn't think it was fair to recommend CBC mode without offering a little sample of how it works:
public static void main(String... args) throws Exception {
byte[] data = "hello".getBytes();
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
keyGenerator.init(128); // 192 and 256 bits may not be available
SecretKey secretKey = keyGenerator.generateKey();
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
// By initializing the cipher in CBC mode, an "initialization vector" has been randomly
// generated. This initialization vector will be necessary to decrypt the encrypted data.
// It is safe to store the initialization vector in plain text for later use. You can obtain
// it's bytes by calling iv.getIV().
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
IvParameterSpec iv = cipher.getParameters().getParameterSpec(IvParameterSpec.class);
byte[] encryptedData = cipher.doFinal(data);
// When decrypting the encrypted data, you must provide the initialization vector used
// during the encryption phase.
cipher.init(Cipher.DECRYPT_MODE, secretKey, iv);
byte[] decryptedData = cipher.doFinal(encryptedData);
if (!Arrays.equals(data, decryptedData)) {
throw new Exception("Data was not decrypted successfully");
}
}
Well if this is the error Input length must be multiple of 16 when decrypting with padded cipher. Than the answear is obvious, the length of your buffer must be a multiple of 16.
Have you checked the length of buf[]?
Your code manages to get almost everything wrong. Just for a start, your mistakes include:
generating a new random symmetric key before encryption and decryption. You have to use the same key to decrypt that was used to encrypt.
Using String as a container for binary data. cipher output is cannot be reliably made into a String unless you use an encoding, such as base64.
Your exception handling is incorrect.
Furthermore, your code runs without exceptions for me.
Related
I have implemented AES encryption in java, but the algorithm is not accepted by team as it is implemented in ECB mode which is not security compliant.I am very new to cryptography and Security requirements.
Can someone please help me changing algorithm to CBC mode. I have attached my code implemented in ECB mode.
public String encrypt(String plainPwd)
{
byte[] outputBytes = new byte[] {};
String returnString = "";
try
{
byte[] raw = "XXXXX#XXXXXX.XXX".getBytes("UTF-8");
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
outputBytes = cipher.doFinal(plainPwd.getBytes("UTF-8"));
if (null != outputBytes)
{
returnString = Base64Encrypter.getInstance().encode(outputBytes);
}
return returnString.trim();
}
catch (Exception e)
{
System.out.println(e);
}
return new String(outputBytes).trim();
}
public String decrypt(String encryptedPwd)
{
byte[] outputBytes = new byte[] {};
try
{
byte[] raw = "XXXXX#XXXXXX.XXX".getBytes("UTF-8");
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, skeySpec);
byte[] inputBytes = Base64Encrypter.getInstance().decode(encryptedPwd);
if (null != inputBytes)
{
outputBytes = cipher.doFinal(inputBytes);
}
}
catch (Exception e)
{
System.out.println(e);
}
return new String(outputBytes).trim();
}
Early reply will be highly appreciated.
Thanks in advance
Change the request string from AES to AES/CBC/PKCS5PADDING and add an iv. While padding is not CBC specific it is good to explicitly define all parameters and with few exceptions padding is needed.
For the iv generate a cryptographically secure random number of block size (16-bytes for AES). In order to have the iv available for decryption common practice is to prepend it to the encrypted data, it does not need to be secret.
AES has around 6 different encryption modes. It's important that you use the correct mode for the application that you are using it for. as #eckes says, ECB is fine for small amounts of data where random encrypt/decrypt access is useful. The disadvantage of ECB is that the same input will have the same output, so an attacker could see patterns and may reverse engineer it if there are limited numbers of practical values.
Check out how to choose AES encryption mode for more guidance on picking the correct operation mode.
If your data is short and random ECB might be acceptable (at least not worse than CBC). But it is most likely a good idea to not even try to get this right.
Keep in mind, CBC does also not offer integrity protection. Using a extra HMAC or a dedicated mode for wrapping secrets (AESKeywrap) or using an authenticated mode (AES/GCM) is better. (and this is not only a question of avoiding modifications it also closes a class of attacks against the privacy protection of the protocols).
If the data is not random/predictable you need to use a mode which also uses an IV. In case of CBC Java will pick a random IV if not specified.
However for decrypt (especially if you have a padding which does verification) you need to specify the exactly same IV, so dont forget to retrieve and transmit it. So the (unsecure because not authenticated) encryption becomes:
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
byte[] iv = cipher.getIV(); // randomly filled.
...
// on decrypt specify this IV again
cipher.init(Cipher.DECRYPT_MODE, skeySpec, new IvParameterSpec(iv));
This also has the advantage that it actually specifies the padding to use, so you do not depend on the default selection of Java (always specify the full mode string).
When I try to encrypt the ciphertext again with the same key, it produces the original plaintext.
Algoritm used is AES with COUNTER MODE. Key and IV remains the same.
Is this the way the algorithm is supposed to behave? And if, what is the use of Cipher.ENCRYTMODE which is to be given as the first parameter of Cipher.init()?
Here is the sample program with which I tested,
import javax.crypto.*;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class EncryptionTest {
public static void main(String[] args) throws Exception {
SecretKeySpec key = null;
IvParameterSpec ivSpec = null;
byte[] keyBytes = "usethiskeyusethiusethiskeyusethi".getBytes();
byte[] ivBytes = "usethisIusethisI".getBytes();
key = new SecretKeySpec(keyBytes, "AES"); //No I18N
ivSpec = new IvParameterSpec(ivBytes);
Cipher AesCipher = Cipher.getInstance("AES/CTR/NoPadding");
byte[] byteText = "Your Plain Text Here".getBytes();
AesCipher.init(Cipher.ENCRYPT_MODE, key, ivSpec);
byte[] byteCipherText = AesCipher.doFinal(byteText);
System.out.println("Encrypted : " + new String(byteCipherText));
AesCipher.init(Cipher.ENCRYPT_MODE, key, ivSpec);
byte[] bytePlainText = AesCipher.doFinal(byteCipherText);
System.out.println("Double Encrypted : " + new String(bytePlainText));
}
}
Yes, that is expected behavior. The CTR mode of operation for block ciphers makes a stream cipher out of a block cipher. Since stream ciphers work in a way that they generate a keystream and XOR the keystream with the plaintext to produce the ciphertext:
plaintext XOR AES-CTR(nonce, key) = ciphertext
The XOR operation works in a way where XORing x with a key k twice results in x again:
x ^ k ^ k = x
This is the reason why encryption and decryption are exactly the same operation for block ciphers in CTR mode (sans nonce generation and putting it into the ciphertext).
If you don't want that the encryption and decryption algorithm are the same, then you should use a different mode such as CBC, but there is nothing wrong with this kind of thing.
Beware that for CTR mode to be secure, you have to use a different nonce/IV under the same key for every encryption.
I use the code like 3des-encryption-decryption-in-java,but when I use decrypt it,it got a error like this
javax.crypto.IllegalBlockSizeException: last block incomplete in decryption
07-17 11:27:27.580: WARN/System.err(22432): at com.android.org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher.engineDoFinal(BaseBlockCipher.java:705)
07-17 11:27:27.580: WARN/System.err(22432): at javax.crypto.Cipher.doFinal(Cipher.java:1111)
But if I change final Cipher decipher = Cipher.getInstance("DESede/CBC/PKCS5Padding"); to final Cipher decipher = Cipher.getInstance("DESede/CFB/NoPadding");,the method can run but got a wrong result (the mode is different from server).
So I want to know the reason about it.
The decrypt method:
public static String decrypt(byte[] message) throws Exception {
final MessageDigest md = MessageDigest.getInstance("SHA-1");
final byte[] digestOfPassword = md.digest(token.getBytes("utf-8"));
final byte[] keyBytes = Arrays.copyOf(digestOfPassword, 24);
for (int j = 0, k = 16; j < 8;) {
keyBytes[k++] = keyBytes[j++];
}
final SecretKey key = new SecretKeySpec(keyBytes, "DESede");
final IvParameterSpec iv = new IvParameterSpec(new byte[8]);
final Cipher decipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");
// final Cipher decipher = Cipher.getInstance("DESede/CFB/NoPadding");
decipher.init(Cipher.DECRYPT_MODE, key, iv);
final byte[] plainText = decipher.doFinal(message);
return new String(plainText, "UTF-8");
}
The encrypt method:
public static byte[] encrypt(String message) throws Exception {
final MessageDigest md = MessageDigest.getInstance("SHA-1");
final byte[] digestOfPassword = md.digest(token
.getBytes("utf-8"));
final byte[] keyBytes = Arrays.copyOf(digestOfPassword, 24);
for (int j = 0, k = 16; j < 8; ) {
keyBytes[k++] = keyBytes[j++];
}
final SecretKey key = new SecretKeySpec(keyBytes, "DESede");
final IvParameterSpec iv = new IvParameterSpec(new byte[8]);
final Cipher cipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, key, iv, new SecureRandom(new byte[5]));
cipher.init(Cipher.ENCRYPT_MODE, key, iv);
final byte[] plainTextBytes = message.getBytes("utf-8");
final byte[] cipherText = cipher.doFinal(plainTextBytes);
return cipherText;
}
There are many possibilitiesThe most common is if you atr encoding the key as a String, especially without specifying the character encoding. If you want to do this, use Base-64, which is designed to encode any binary data, rather than a character encodingAlso make sure that source platform and target platform encoding should be same.As you are using UTF-8 here and then on the other en , UTF-8 must be usedNow have a look under the facts which you are telling that code is running with final Cipher decipher = Cipher.getInstance("DESede/CFB/NoPadding"); but not with final Cipher decipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");
While Decryption, you must know the padding size and mode in which you have selected at encryption time.As you are saying that when you use CBC mode than it throws exception but when you changed it to CFB then it got able to run.In this case you need to make sure which mode you are using at encrytpion time.
As a side note: CBC, OFB and CFB are identical, however OFB/CFB is better because you only need encryption and not decryption, which can save code space.
CBC(Cipher Block Chaining) is used where the data goes through the AES function, and feedback is applied to modify the pre-encrypted data, so that repeated plain data does not yield the same encrypted data. Data can only be processed in blocks which match the block-size of the underlying encryption function (so 128-bit blocks on the case of AES), and synchronisation at this block level must be provided between the encrypting and decrypting engines, otherwise data will be indecipherable
CFB(Cipher FeedBack mode) is also a common mode, and offers the possibility of making an underlying block cipher work like a stream cipher; ie. so that the data being processed can be a stream of shorter values (for example bytes or even individual bits) rather than being processed only as much larger blocks.In CFB mode, the data itself does not go through the AES engine, but gets XORed with a value which the AES engine generates from previous message history. This means that the latency through the CFB function can be minimised, as the only processing applied to the data is an XOR function. Data widths can be set to any size up to the underlying cipher block size, but note that throughput decreases as the widths get smaller in the ratio of the data width to the block size.(Side note ended :D)
If you encrypt using Cipher-Feedback (CFB) or Output-Feedback (OFB) or counter (CTR) modes then the ciphertext will be the same size as the plaintext and so padding is not required. Be careful, though, when using these modes, because the initialisation vectors (IV) must be unique.
Similarly, encrypting using a stream cipher like RC4 or PC1 does not require padding.
Now if we investigate more critically than you should take care about the block size and padding size(already mentioned above).Now the first thing you need to make sure is that the padding size defined by your encryption algorithm.As I mentioned that in CFB case padding is not required so first try it without giving padding.if issue still resides then check either it is pkcs5 or pkcs7.Try your code by setting decrytpion padding size to pkcs7.If it is pkcs7 then I guess it should work with CBC too.I recommend you to read Using Padding in Encryption As a additional information
PKCS#5 padding is defined in RFC 2898 (PKCS #5: Password-Based Cryptography Specification Version 2.0).
PKCS5 padding is a padding scheme for extending arbitrary data to match the block-size of a block cipher in a way that allows the receiving end to reliably remove the padding.
PKCS#7 (CMS, RFC 3369) defines a padding scheme, but it is an extension of PKCS#5 padding for block ciphers that have more than 8 bytes for block.
I have function that crypts string:
BASE64Decoder decoder = new BASE64Decoder();
BASE64Encoder encoder = new BASE64Encoder();
public String encryptValueWithBlowfish(String data, String secretKey) {
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
try {
SecretKeySpec key = new SecretKeySpec(decoder.decodeBuffer(secretKey), "Blowfish");
Cipher cipher = Cipher.getInstance("Blowfish/CBC/NoPadding", "BC");
String iv = "\0\0\0\0\0\0\0\0";
IvParameterSpec ivs = new IvParameterSpec(iv.getBytes());
cipher.init(Cipher.ENCRYPT_MODE, key, ivs);
MessageDigest sha = MessageDigest.getInstance("SHA-1");
return encoder.encode(sha.digest(cipher.doFinal(decoder.decodeBuffer(data))));
} catch (Exception e) {
lg.info("Failed to encryptValueWithBlowfish: " + e.getMessage());
return "";
}
}
Line cipher.init(Cipher.ENCRYPT_MODE, key, ivs); rises exception "Unsupported keysize or algorithm parameters". This code forks fine in another Linux machine. Parameters passed in both cases are the same. I'm not strong in crypto things. What might be wrong?
What might be wrong?
Well, you're using the default character encoding here: iv.getBytes() - that's never a good start. Perhaps the two different machines have different default encodings.
If you want to create a byte array of all-zeroes and a particular size, why not just use:
IvParameterSpec ivs = new IvParameterSpec(new byte[8]);
Or use 16 if you wanted a 16 byte IV.
It's not clear what decoder is here - but you use it twice and again, if that's using the default character encoding it could vary by machine.
My objective is to encrypt data in Iphone and decrypt it on java server.
I am using Symmetric encryption .
I have generated the key using KeyGenerator at the java side.
code for generating key is as follows:
//Java Code for key generation
File keyFile = new File("F:/key","mykey.key");
KeyGenerator kgen = KeyGenerator.getInstance("AES");
kgen.init(128);
SecretKey skey = kgen.generateKey();
byte[] enc= skey.getEncoded();
FileUtils.writeStringToFile(keyFile ,Base64.encodeBase64String(enc),"UTF-8");
Following is the java code for decryption:
//get key from file
File file = new File("F:/key", "mykey.key");
SecretKeySpec keySpec= null;
try {
byte[] keyBytes = Base64.decodeBase64(FileUtils.readFileToString(file,"UTF-8"));
keySpec= new SecretKeySpec(keyBytes, 0, 16, "AES");
byte[] raw = keySpec.getEncoded();
} catch (Exception e) {
e.printStackTrace();
}
//Decrypt String encryptedString(from Iphone)
byte[] tempByte = Base64.decodeBase64(encryptedString);
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, keySpec);
byte[] cipherData = cipher.doFinal(tempByte);
String ttt = new String(cipherData ,"UTF-8");
System.out.println(ttt);
And the iphone code is similar to th code given in following link:
Encrypting data with Objective-C and decrypt it with Java Problem
I am getting the following exception while decrypting in java.
javax.crypto.BadPaddingException: Given final block not properly padded
Please help...
Well the padding and mode has to match. If you copied the Objective-C code, then the ciphertext on the Objective-C side has the ECB mode and the PKCS7 padding.
By default the java AES cipher has the CBC mode and PKCS5 padding (though I'm not sure, and AFAIK PKCS5 and PKCS7 are somewhat compatible). I guess you have to specify these explicitly. Those settings have to match otherwise something goes wrong. So you have to create the cipher like that:
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
Btw. if you can choose the encryption-mode you should use CBC (but then on both sides).