I am encrypting a string and storing it in a cookie at client side.. but when I send that exact encrypted string from js to java code it gives me the aforementioned exception.
The code I am using for encryption and decryption is:
public static final String UNICODE_FORMAT = "UTF8";
public static String encrypt(String Data, SecretKeySpec skeySpec,IvParameterSpec ivspec) throws Exception {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, ivspec);
byte[] plainBytes = Data.getBytes(UNICODE_FORMAT);
byte[] encrypted = cipher.doFinal(plainBytes);
String encryption = bytesToString(encrypted);
return encryption;
}
public static String decrypt(String encryptedData,SecretKeySpec skeySpec,IvParameterSpec ivspec) throws Exception {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, skeySpec, ivspec);
byte[] decryptval = hexToBytes(encryptedData);
byte[] decrypted = cipher.doFinal(decryptval);
return new String(decrypted);
}
public static String bytesToString(byte[] bytes) {
HexBinaryAdapter adapter = new HexBinaryAdapter();
String s = adapter.marshal(bytes);
return s;
}
public static byte[] hexToBytes(String hexString) {
HexBinaryAdapter adapter = new HexBinaryAdapter();
byte[] bytes = adapter.unmarshal(hexString);
return bytes;
}
can you tell me what the issue could be?? I have tried the solutions mentioned at stackoverflow.com and also a few other solutions but none worked.. am I getting this error because I am sending the encrypted string to JS and it is changing the padding of the string?
As # JoopEggen mentioned - creating sting from byte[] can break it.
Can you just serialize, instead of creating string from it?
Related
I am developing an application, where I am encrypting and decrypting a text entered by the user.
But, I am getting the following error:
javax.crypto.IllegalBlockSizeException: last block incomplete in
decryption
below is my code for encryption and decryption. Encryption works perfectly, while I am getting this error while decrypting. Please refer the code below:
public static String fncEncrypt(String strClearText, String strKey) throws Exception{
String strData = "";
try {
SecretKeySpec sKeySpec = new SecretKeySpec(strKey.getBytes(), "Blowfish");
Cipher cipher = Cipher.getInstance("Blowfish");
cipher.init(Cipher.ENCRYPT_MODE, sKeySpec);
byte[] encrypted = cipher.doFinal(strClearText.getBytes());
strData = new String(encrypted);
}catch (Exception e){
e.printStackTrace();
}
return strData;
}
public static String fncDecrypt(String strEecrypted, String strKey) throws Exception {
String strData = "";
try {
SecretKeySpec skeySpec = new SecretKeySpec(strKey.getBytes(), "Blowfish");
Cipher cipher = Cipher.getInstance("Blowfish");
cipher.init(Cipher.DECRYPT_MODE, skeySpec);
byte[] decrypted = cipher.doFinal(strEecrypted.getBytes());
strData = new String(decrypted);
}catch (Exception e){
e.printStackTrace();
}
return strData;
}
Please respond if you have a solution for this.
You should decode the string instead of encoding the platform specific representation of the string, right at the start of your method.
byte[] base64TextToDecrypt = Base64.decodeBase64(textToDecrypt);
or more precisely:
byte[] bytesToDecrypt = Base64(base64TextToDecrypt);
if you name your variables correctly.
In general, each time you (feel like you have to) use the String.getBytes(): byte[] method or the String(byte[]) constructor you are likely doing something wrong. You should first think about what you are trying to do, and specify a character-encoding if you do need to use it.
In your case, the output in the converted variable is probably character-encoded. So you you could use the following fragment:
String plainText = new String(converted, Charset.forName("UTF8"));
System.out.println(plainText);
instead of what you have now.
Reference : https://stackoverflow.com/a/13274072/8416317
String class method getBytes() or new String(byte bytes[]) encode / decode with Charset.defaultCharset().name(), and some encrypted data would be ignored by encoding with special charset.
you could directly return byte[] by fncEncrypt and input byte[] to fncDecrypt. or encode result with BASE64.
public static byte[] fncEncrypt(String strClearText, String strKey) throws Exception{
byte[] encrypted = null;
try {
SecretKeySpec sKeySpec = new SecretKeySpec(strKey.getBytes(), "Blowfish");
Cipher cipher = Cipher.getInstance("Blowfish");
cipher.init(Cipher.ENCRYPT_MODE, sKeySpec);
encrypted = cipher.doFinal(strClearText.getBytes());
} catch (Exception e){
e.printStackTrace();
}
return encrypted;
}
public static String fncDecrypt(byte[] ecrypted, String strKey) throws Exception {
String strData = "";
try {
SecretKeySpec skeySpec = new SecretKeySpec(strKey.getBytes(), "Blowfish");
Cipher cipher = Cipher.getInstance("Blowfish");
cipher.init(Cipher.DECRYPT_MODE, skeySpec);
byte[] decrypted = cipher.doFinal(ecrypted);
strData = new String(decrypted);
}catch (Exception e){
e.printStackTrace();
}
return strData;
}
The reason is when you use new String(encrypted) it will not fully encode the bytes to string. Try the code below
public static byte[] fncEncrypt(String strClearText, String strKey) throws Exception{
SecretKeySpec sKeySpec = new SecretKeySpec(strKey.getBytes(), "Blowfish");
Cipher cipher = Cipher.getInstance("Blowfish");
cipher.init(Cipher.ENCRYPT_MODE, sKeySpec);
byte[] encrypted = cipher.doFinal(strClearText.getBytes());
return encrypted;
}
public static String fncDecrypt(byte[] encrypted, String strKey) throws Exception {
SecretKeySpec skeySpec = new SecretKeySpec(strKey.getBytes(), "Blowfish");
Cipher cipher = Cipher.getInstance("Blowfish");
cipher.init(Cipher.DECRYPT_MODE, skeySpec);
byte[] decrypted = cipher.doFinal(encrypted);
return new String(decrypted);
}
You can encrypt and decrypt using the code below
String message = "Hello!";
byte[] encrypted = fncEncrypt(message, "key");
String decrypted = fncDecrypt(encrypted, "key");
System.out.println(decrypted);
I am using the below code to create a 128 bit AES cipher,
String initializer = "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000";
byte[] initializer2 = new BigInteger(initializer, 2).toByteArray();
String encrypt = encrypt(binary_string_firsthalf, initializer2);
public static String encrypt(String plainText, byte[] key) {
try {
SecretKeySpec secretKey = new SecretKeySpec(key, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
byte[] cipherText = cipher.doFinal(plainText.getBytes("UTF8"));
String encryptedString = new String(Base64.encode(cipherText));
return encryptedString;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
But the code is returning a exception saying invalid AES key length.
How to create a cipher using 128 0's.
byte[] initializer2 = new byte[16];
is all you need to initialize a byte array with the default value of 0x00.
Don't use a static predictable key such as all 0x00 bytes for actual encryption.
I need to encode a string in Java and php where the result must be the same.
The following conditions are given:
algorithm: RIJNDAEL-128
key: 5P443m2Q1R9A7f5r3e1z08642
mode: ECB
initialization vector: N/A (Since we're using ECB, IV's are ignored)
String to encode: 201412181656005P443m2Q1R9A7f5r3e1z08642
PHP
<?php
class Cipher
{
private $securekey, $iv;
function __construct($textkey)
{
$this->securekey = $textkey;
$this->iv = mcrypt_create_iv(32);
}
function encryptR($input)
{
$enc = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $this->securekey, $input, MCRYPT_MODE_ECB, $this->iv);
return base64_encode($enc);
}
function decryptR($input)
{
return trim(mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $this->securekey, base64_decode($input), MCRYPT_MODE_ECB, $this->iv));
}
}
$raw_text = '201412181656005P443m2Q1R9A7f5r3e1z08642';
$secretKey = '5P443m2Q1R9A7f5r3e1z08642';
$cipher = new Cipher($secretKey);
$encrypted = $cipher->encryptR($raw_text);
?>
Output: MbDHhIanWgySlMTOX+ItgVKudVLXbtj7ig2GMQacVM9JhyAPvVQxLJnHpEj/vhqW
JAVA
encrypted = encrypt("201412181656005P443m2Q1R9A7f5r3e1z08642","5P443m2Q1R9A7f5r3e1z08642");
public class Crypt {
private final String characterEncoding = "UTF-8";
private final String cipherTransformation = "AES/ECB/PKCS5Padding";
private final String aesEncryptionAlgorithm = "AES";
public byte[] decrypt(byte[] cipherText, byte[] key) throws Exception
{
Cipher cipher = Cipher.getInstance(cipherTransformation);
SecretKeySpec secretKeySpecy = new SecretKeySpec(key, aesEncryptionAlgorithm);
cipher.init(Cipher.DECRYPT_MODE, secretKeySpecy);
cipherText = cipher.doFinal(cipherText);
return cipherText;
}
public byte[] encrypt(byte[] plainText, byte[] key) throws Exception
{
Cipher cipher = Cipher.getInstance(cipherTransformation);
SecretKeySpec secretKeySpec = new SecretKeySpec(key, aesEncryptionAlgorithm);
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
plainText = cipher.doFinal(plainText);
return plainText;
}
private byte[] getKeyBytes(String key) throws UnsupportedEncodingException{
byte[] keyBytes= new byte[16];
byte[] parameterKeyBytes= key.getBytes(characterEncoding);
System.arraycopy(parameterKeyBytes, 0, keyBytes, 0, Math.min(parameterKeyBytes.length, keyBytes.length));
return keyBytes;
}
#SuppressLint("NewApi")
public String encrypt(String plainText, String key) throws Exception {
byte[] plainTextbytes = plainText.getBytes(characterEncoding);
byte[] keyBytes = getKeyBytes(key);
// Log.i("iv", ""+keyBytesIV);
return Base64.encodeToString(encrypt(plainTextbytes,keyBytes), Base64.DEFAULT);
}
#SuppressLint("NewApi")
public String decrypt(String encryptedText, String key) throws Exception {
byte[] cipheredBytes = Base64.decode(encryptedText, Base64.DEFAULT);
byte[] keyBytes = getKeyBytes(key);
return new String(decrypt(cipheredBytes, keyBytes), characterEncoding);
}
}
Output: wd0FHYpLbgdpHhcSql7VVCiKWJWN5hvP0W9F4sgKWAWeDcSjvfKWTM5LHBCZJSRw
Updated:
I changed the padding from NoPadding to PKCS5Padding
Is this correct? I'm not sure, cause if you look at the PHP code. There wasn't any padding specified(my own assumption based on syntax).
Info on Mcrypt
Additional Insight:
Read this document regarding padding(No Padding). Must've been related to the issue.
Looks like your PHP version uses AES-128, which by definition, uses 128-bit (16-byte) keys. However looks like you passed in a 25-byte key (5P443m2Q1R9A7f5r3e1z08642), which I'm not sure what PHP does when that happens.
Your Java version's getKeyBytes() method only returns the first 16 bytes of the supplied key, so it encrypts with only that.
Try truncating the key in your PHP version to 5P443m2Q1R9A7f5r and you'd get the same result. Except the end part which may be different. At that point, the issue then would be the padding. You can apply the pkcs5_pad PHP function on your plaintext so it matches your Java version.
All that said, if this was just for learning purposes, it's ok. Otherwise, for actual use it's important that you do not use ECB cipher mode.
I changed byte[] keyBytes= new byte[16]; to byte[] keyBytes= new byte[32]; in getKeyBytes method then it worked fine.
I want a secure solution for caching a user's password on their PC during their session.
I have trawled numerous AES examples and know that this has been answered elsewhere but I must say it is a little confusing. My aesSecretKey or aesInitialisationVector are not working in the decryption correctly but not sure where the issue lies.
Decrypting results in a javax.crypto.BadPaddingException: Given final block not properly padded exception.
My class looks like this
public class LockManagerTest {
// Need to share the IV and key between encode and decode
private static byte[] aesInitialisationVector;
private static SecretKey aesSecretKey;
private static Cipher aesCipher;
public LockManagerTest(String sessionKey) {
try {
byte[] key = getSecretKey(sessionKey.toCharArray(), getSalt(32),
65536, 128);
aesSecretKey = new SecretKeySpec(key, "AES");
aesCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
aesCipher.init(Cipher.ENCRYPT_MODE, aesSecretKey);
AlgorithmParameters params = aesCipher.getParameters();
aesInitialisationVector =
params.getParameterSpec(IvParameterSpec.class).getIV();
} catch (Exception e) {
Util.handleException(e);
}
}
private static byte[] getSecretKey(char[] plaintext,
byte[] salt,
int iterations,
int keySize)
throws Exception {
PBEKeySpec spec = new PBEKeySpec(plaintext, salt, iterations, keySize);
SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
return skf.generateSecret(spec).getEncoded();
}
private static byte[] getSalt(int keyLength) throws Exception {
SecureRandom random = SecureRandom.getInstance("SHA1PRNG", "SUN");
byte[] salt = new byte[keyLength];
random.nextBytes(salt);
return salt;
}
public byte[] encryptedAes(char[] input) throws Exception {
// WRONG
// aesCipher.init(Cipher.ENCRYPT_MODE, aesSecretKey);
//
aesCipher.init(Cipher.ENCRYPT_MODE, aesSecretKey,
new IvParameterSpec(aesInitialisationVector);
CharBuffer cBuf = CharBuffer.wrap(input);
byte[] normalised = Charset.forName("UTF-8").encode(cBuf).array();
byte[] ciphertext = aesCipher.doFinal(normalised);
return ciphertext;
}
public byte[] decryptAes(byte[] ciphertext) throws Exception {
aesCipher.init(Cipher.DECRYPT_MODE,
aesSecretKey, new IvParameterSpec(aesInitialisationVector));
byte[] plaintext = aesCipher.doFinal(ciphertext);
return plaintext;
}
}
Comments regarding the level of security appreciated also.
You need to pass the IV when calling init() in encryptedAes().
AES is a CBC algorithm and divides input into blocks. These blocks must be of a specific size. In the case of AES, I believe it is 16 bytes. If the input is not a multiple of 16 bytes, it must be padded with nulls before encryption.
Instead of generating new IV while decrypting, you need to pass same IV which you use for encrypting. Remember AES is Symmetric Cipher.
Edit:
What you are doing is:
public byte[] encryptedAes(char[] input) throws Exception {
// WRONG
// aesCipher.init(Cipher.ENCRYPT_MODE, aesSecretKey);
//
aesCipher.init(Cipher.ENCRYPT_MODE, aesSecretKey,
new IvParameterSpec(aesInitialisationVector);
CharBuffer cBuf = CharBuffer.wrap(input);
byte[] normalised = Charset.forName("UTF-8").encode(cBuf).array();
byte[] ciphertext = aesCipher.doFinal(normalised);
return ciphertext;
}
Instead store the IvParameterSpec as a static, as per below (u can do proper variable declaration in your program)
public byte[] encryptedAes(char[] input) throws Exception {
//declare as static so initVector can be reused when decrypting
IvParamterSpec initVector = new IvParameterSpec(aesSecretKey);
aesCipher.init(Cipher.ENCRYPT_MODE, aesSecretKey, initVector);
CharBuffer cBuf = CharBuffer.wrap(input);
byte[] normalised = Charset.forName("UTF-8").encode(cBuf).array();
byte[] ciphertext = aesCipher.doFinal(normalised);
return ciphertext;
}
make the changes and then run your program. make sure you use the same initVector while decrypting. in your program you are creating new IvParameterSpec(...)
In my application I am encrypting and decrypting data using secretKey. For that I am using AES algorithm. But I am getting exception in decrypt, one value out of three already encrypted values using secret key.
Exception is:
Illegal Block Size Exception Input length must be multiple of 16 when decrypting with padded cipher.
Below is my code:
Function to encyrpt value
public static String symmetricEncrypt(String text, String secretKey) {
BASE64Decoder decoder = new BASE64Decoder();
byte[] raw;
String encryptedString;
SecretKeySpec skeySpec;
BASE64Encoder bASE64Encoder = new BASE64Encoder();
byte[] encryptText = text.getBytes();
Cipher cipher;
try {
raw = decoder.decodeBuffer(secretKey);
skeySpec = new SecretKeySpec(raw, "AES");
cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
encryptedString = bASE64Encoder.encode(cipher.doFinal(encryptText));
}
catch (Exception e) {
e.printStackTrace();
return "Error";
}
return encryptedString;
}
Function to decrypt value
public static String symmetricDecrypt(String text, String secretKey) {
BASE64Decoder decoder = new BASE64Decoder();
BASE64Decoder base64Decoder = new BASE64Decoder();
Cipher cipher;
String encryptedString;
byte[] encryptText = null;
byte[] raw;
SecretKeySpec skeySpec;
try {
raw = decoder.decodeBuffer(secretKey);
skeySpec = new SecretKeySpec(raw, "AES");
encryptText = base64Decoder.decodeBuffer(text);
cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, skeySpec);
encryptedString = new String(cipher.doFinal(encryptText));
} catch (Exception e) {
e.printStackTrace();
return "Error";
}
return encryptedString;
}
Following are the values that I am encrypting and decrypting
String secretKey = "XMzDdG4D03CKm2IxIWQw7g==";
String value1= "ABCD";
String enctypedValue1= "3uweh4pzoVyH1uODQmVNJA==";
String enctypedValue2= "37PTC20w4DMZYjG3f+GWepSvAbEJUccMXwS/lXilLav1qM/PrCTdontw5/82OdC1zzyhDEsFVRGo rV6gXAQcm+Zai15hliiUQ8l8KRMtUl4=";
String value4= "20000";
/** Ecnryption and decryption of value1 **/
String encryptedValue1= symmetricEncrypt(value1, secretKey);
String decryptedValue1 = symmetricDecrypt(encryptedValue1, secretKey);
/** Decryption of enctypedValue1 **/
String decryptedValue2 = symmetricDecrypt(enctypedValue1, secretKey);
System.out.println(decryptedValue2);
/** Decryption of enctypedValue2 (Not decrypted)**/
String decryptedValue3 = symmetricDecrypt(enctypedValue2, secretKey);
System.out.println(decryptedValue3);
/** Ecnryption and decryption of value4 **/
String encryptedValue4= symmetricEncrypt(value4, secretKey);
String decryptedValue4 = symmetricDecrypt(encryptedValue4, secretKey);
In the test function, I have written the following three test cases.
A new value (value1) being encrypted and decrypted using a secret key.
Two example encrypted values (enctypedValue1, enctypedValue2) which are being decrypted using same secret key. encryptedValue2 which got a problem while decrypting using same secret key.
A new value (value4) being encrypted and decrypted using a secret key.
On decrypting encryptedValue2 I am getting the following exception:
Illegal Block Size Exception Input length must be multiple of 16 when decrypting with padded cipher
Following is what I have derived till now.
The problematic value seems to have a problem while decoding it, it returns 81 length array which is unable to get decrypted?
If this problem was to happen it should have happened to all the values.
Is this a value specific problem or it is something related to padding or it can have a different behavior on different browser, different os?
I was able to run the code without any problem. However, I used Apache's Base64 for encoding/decoding...maybe your Base64 has bugs. If you wrote it yourself, there is a big chance that you missed some cases. For real production code, use heavily tested libraries such as Apache's.
You can find the library that I used for Base64 here: http://commons.apache.org/proper/commons-codec/download_codec.cgi
Here is the full working code:
package security.symmatric;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;
public class AES {
public static String symmetricEncrypt(String text, String secretKey) {
byte[] raw;
String encryptedString;
SecretKeySpec skeySpec;
byte[] encryptText = text.getBytes();
Cipher cipher;
try {
raw = Base64.decodeBase64(secretKey);
skeySpec = new SecretKeySpec(raw, "AES");
cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
encryptedString = Base64.encodeBase64String(cipher.doFinal(encryptText));
}
catch (Exception e) {
e.printStackTrace();
return "Error";
}
return encryptedString;
}
public static String symmetricDecrypt(String text, String secretKey) {
Cipher cipher;
String encryptedString;
byte[] encryptText = null;
byte[] raw;
SecretKeySpec skeySpec;
try {
raw = Base64.decodeBase64(secretKey);
skeySpec = new SecretKeySpec(raw, "AES");
encryptText = Base64.decodeBase64(text);
cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, skeySpec);
encryptedString = new String(cipher.doFinal(encryptText));
} catch (Exception e) {
e.printStackTrace();
return "Error";
}
return encryptedString;
}
public static void main(String[] args) {
String secretKey = "XMzDdG4D03CKm2IxIWQw7g==";
String value1= "ABCD";
String enctypedValue1= "3uweh4pzoVyH1uODQmVNJA==";
String enctypedValue2= "37PTC20w4DMZYjG3f+GWepSvAbEJUccMXwS/lXilLav1qM/PrCTdontw5/82OdC1zzyhDEsFVRGo rV6gXAQcm+Zai15hliiUQ8l8KRMtUl4=";
String value4= "20000";
/** Ecnryption and decryption of value1 **/
String encryptedValue1= symmetricEncrypt(value1, secretKey);
String decryptedValue1 = symmetricDecrypt(encryptedValue1, secretKey);
System.out.println(decryptedValue1);
/** Decryption of enctypedValue1 **/
String decryptedValue2 = symmetricDecrypt(enctypedValue1, secretKey);
System.out.println(decryptedValue2);
/** Decryption of enctypedValue2 **/
String decryptedValue3 = symmetricDecrypt(enctypedValue2, secretKey);
System.out.println(decryptedValue3);
/** Ecnryption and decryption of value4 **/
String encryptedValue4= symmetricEncrypt(value4, secretKey);
String decryptedValue4 = symmetricDecrypt(encryptedValue4, secretKey);
System.out.println(decryptedValue4);
}
}