I searched a lot but I haven't found a good solution how to solve this. I have an app which has to decrypt a long hex string with AES 256.
In order to test it, I created a test method which encrypts a long text into a hex and then convert it back and decrypt it.
If I run this method, I always get the following error: Given final block not properly padded. I receive this error in the decryption method.
The test method looks like so:
#Test
public void testEncAndDecRequestWithHexString() throws UnsupportedEncodingException {
CryptoHelper cryptoHelper = new CryptoHelper("AES256");
String paramStr = "ABCB28BCEE5947B8AECE3386871EC0DF&{D5CA99D2-506B-4864-8971-E87821D6B105}&7523429";
//encrypt the param string
byte[] paramByteEnc = cryptoHelper.encryptBytesToBytes(paramStr.getBytes("ASCII"), PARAM_KEY, PARAM_IV);
//convert it to hex
String encryptedHexStr = cryptoHelper.byteArrayToHexStr(paramByteEnc);
//convert it back to a byte array
byte[] encryptedHexBytes = cryptoHelper.hexStrToByteArray(encryptedHexStr);
// decrypt it
byte[] paramByteDecrypted = cryptoHelper.decryptBytesToBytes(encryptedHexBytes, encryptedHexBytes.length, PARAM_KEY, PARAM_IV);
String decryptedStr = new String(paramByteDecrypted);
assertEquals("ABCB28BCEE5947B8AECE3386871EC0DF&{D5CA99D2-506B-4864-8971-E87821D6B105}&7523429", decryptedStr);
}
The CryptHelper class has following methods:
#Override
public byte[] encryptBytesToBytes(byte[] plainData, byte[] key, byte[] iv) {
try {
initCipher(Cipher.ENCRYPT_MODE, key, iv);
return aesCipher.doFinal(plainData);
} catch (IllegalBlockSizeException | BadPaddingException e) {
log.severe(e.getMessage());
}
return null;
}
#Override
public byte[] decryptBytesToBytes(byte[] encryptedBytes, int length,
byte[] key, byte[] iv) {
try {
initCipher(Cipher.DECRYPT_MODE, key, iv);
return aesCipher.doFinal(encryptedBytes, 0, length);
} catch (IllegalBlockSizeException | BadPaddingException e) {
e.printStackTrace();
}
return null;
}
private void initCipher(int mode, byte[] keyBytes, byte[] ivBytes) {
try {
// create shared secret and init cipher mode
SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, "AES");
aesCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
aesCipher.init(mode == Cipher.ENCRYPT_MODE ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE, secretKeySpec, new IvParameterSpec(ivBytes));
} catch (InvalidAlgorithmParameterException | NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException e) {
e.printStackTrace();
}
}
public String byteArrayToHexStr(byte[] encrypted) {
StringBuilder hex = new StringBuilder();
for (byte b : encrypted) {
hex.append(String.format("%02X", b));
}
return new String(hex.toString());
}
public byte[] hexStrToByteArray(String hex) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < hex.length() - 1; i += 2) {
String output = hex.substring(i, (i + 2));
int decimal = Integer.parseInt(output, 16);
sb.append((char) decimal);
}
String temp = sb.toString();
return temp.getBytes();
}
I used the same key and initialization vector for the decryption process so the problem is not the wrong key or initialization vector. I am also sure that every function here is doing their job correctly. If you don't use the functions hexStrToByteArray() and byteArrayToHexStr() and just use the encrypted byte for decrypting, it works no problem. I think there is a encoding/decoding problem but I have no idea how to handle it in java. If I use getBytes("UTF-8") and new String(byte[], "UTF-8") I get an IllegalBlockSizeException.
I hope you can help me finding out if I am on the right way and what I did wrong.
This is a clear indication that you shouldn't write library functions if they have already been defined. Use a hex codec from Bouncy Castle, Guava or Apache codec instead (until Oracle finally sees the light and provides one in a java.util package).
If you do implement it yourself, please don't mistake characters for bytes:
public byte[] hexStrToByteArray(String hex) {
ByteArrayOutputStream baos = new ByteArrayOutputStream(hex.length() / 2);
for (int i = 0; i < hex.length(); i += 2) {
String output = hex.substring(i, i + 2);
int decimal = Integer.parseInt(output, 16);
baos.write(decimal);
}
return baos.toByteArray();
}
Related
As I am trying to encrypt the text to PKCS8 private key and decrypt by X509 public key.
public static String decryptByPublicKey(String data, String keyHash) {
final String KEY_ALGORITHM = "RSA";
final int MAX_DECRYPT_BLOCK = 128;
try {
byte[] keyBytes = Base64.getDecoder().decode(keyHash);
X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
Key publicKey = keyFactory.generatePublic(publicKeySpec);
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, publicKey);
ByteArrayOutputStream out = new ByteArrayOutputStream();
int offSet = 0;
byte[] cache;
byte[] buffer =data.getBytes();
int inputLen = buffer.length;
int i = 0;
while (inputLen - offSet > 0) {
if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
cache = cipher.doFinal(buffer, offSet, MAX_DECRYPT_BLOCK);
} else {
cache = cipher.doFinal(buffer, offSet, inputLen - offSet);
}
out.write(cache, 0, cache.length);
i++;
offSet = i * MAX_DECRYPT_BLOCK;
}
byte[] decryptedData = out.toByteArray();
out.close();
return new String(decryptedData, "UTF-8");
} catch (Exception e) {
logger.error("decryptByPublicKey - Exception: ", e);
e.printStackTrace();
}
return null;
}
It shows BadPaddingException.
javax.crypto.BadPaddingException: Decryption error
at sun.security.rsa.RSAPadding.unpadV15(RSAPadding.java:380)
at sun.security.rsa.RSAPadding.unpad(RSAPadding.java:291)
at com.sun.crypto.provider.RSACipher.doFinal(RSACipher.java:356)
at com.sun.crypto.provider.RSACipher.engineDoFinal(RSACipher.java:389)
at javax.crypto.Cipher.doFinal(Cipher.java:2223)
Since, I have tried to reverse the method (encrypt by public key and decrypt by private key), it is okay.
Do anyone can tell me what's the problem is?
From the error given
It shows BadPaddingException.javax.crypto.BadPaddingException: Decryption error
Please check the size of your buffer on (int inputLen = buffer.length). You need to pad out the data to be the required block size.
You should only call doFinal() once, the last time. Every other time you should be calling update().
But it doesn't make sense to do this. Encrypting with a private key isn't encryption, it is digital signing.
I wrote this simple Java program which encrypt a string and output the hex value of the iv, salt, derived key and cipher text.
public class tmp{
static Cipher encryptionCipher;
static String RANDOM_ALGORITHM = "SHA1PRNG";
static String PBE_ALGORITHM = "PBEWithSHA256And256BitAES-CBC-BC";
static String CIPHER_ALGORITHM = "AES/CBC/PKCS7Padding";
static String SECRET_KEY_ALGORITHM = "AES";
static int PBE_ITERATION_COUNT = 2048;
static String PROVIDER = "BC";
public static byte[] generateIv() {
try{
SecureRandom random;
random = SecureRandom.getInstance(RANDOM_ALGORITHM);
byte[] iv = new byte[16];
random.nextBytes(iv);
return iv;
} catch(Exception e){
return null; // Always must return something
}
}
public static byte[] generateSalt() {
try {SecureRandom random;
random = SecureRandom.getInstance(RANDOM_ALGORITHM);
byte[] salt = new byte[32];
random.nextBytes(salt);
return salt;
} catch(Exception e){
return null; // Always must return something
}
}
public static SecretKey getSecretKey(String password, byte[] salt){
try {
PBEKeySpec pbeKeySpec = new PBEKeySpec(password.toCharArray(), salt, PBE_ITERATION_COUNT, 256);
SecretKeyFactory factory = SecretKeyFactory.getInstance(PBE_ALGORITHM, PROVIDER);
SecretKey tmp = factory.generateSecret(pbeKeySpec);
return new SecretKeySpec(tmp.getEncoded(), SECRET_KEY_ALGORITHM);
} catch(Exception e){
System.out.println(e); // Always must return something
return null;
}
}
public static String encrypt(String plaintext, Key key, byte[] iv) {
try {
AlgorithmParameterSpec ivParamSpec = new IvParameterSpec(iv);
encryptionCipher = Cipher.getInstance(CIPHER_ALGORITHM, PROVIDER);
encryptionCipher.init(Cipher.ENCRYPT_MODE, key, ivParamSpec);
byte[] ciphertext = encryptionCipher.doFinal(plaintext.getBytes("UTF-8"));
String cipherHexString = DatatypeConverter.printHexBinary(ciphertext);
return cipherHexString;
}
catch (Exception e) {
System.out.println(e);
return null;
}
}
public static void main (String[] Args){
SecretKey key;
//sha512(ciao)
String encami = "This is a test pharse. Thanks!!";
String password = "a0c299b71a9e59d5ebb07917e70601a3570aa103e99a7bb65a58e780ec9077b1902d1dedb31b1457beda595fe4d71d779b6ca9cad476266cc07590e31d84b206";
byte[] iv = new byte[16];
byte[] salt = new byte[32];
iv = generateIv();
salt = generateSalt();
String ll = DatatypeConverter.printHexBinary(iv);
String lp = DatatypeConverter.printHexBinary(salt);
System.out.println(ll);
System.out.println(lp);
key = getSecretKey(password, salt);
byte tt[] = new byte[32];
tt = key.getEncoded();
String lo = DatatypeConverter.printHexBinary(tt);
System.out.println(lo);
String outenc = encrypt(encami, key, iv);
System.out.println(outenc);
}
}
In the following C program iv and salt are initialized with the values given by the above Java program. No padding needed since the length of the text is 32 bytes.
#include <stdio.h>
#include <gcrypt.h>
#include <stdlib.h>
#include <string.h>
int
main (void)
{
int i;
char *encami = "This is a test pharse. Thanks!!";
char *pwd = "a0c299b71a9e59d5ebb07917e70601a3570aa103e99a7bb65a58e780ec9077b1902d1dedb31b1457beda595fe4d71d779b6ca9cad476266cc07590e31d84b206";
unsigned char iv[] = {};
unsigned char salt[] = {};
int algo = gcry_cipher_map_name("aes256");
unsigned char *devkey = NULL;
unsigned char *enc_buf = NULL;
enc_buf = gcry_malloc(32);
devkey = gcry_malloc_secure (32);
gcry_cipher_hd_t hd;
gcry_cipher_open(&hd, algo, GCRY_CIPHER_MODE_CBC, 0);
gcry_kdf_derive (pwd, strlen(pwd)+1, GCRY_KDF_PBKDF2, GCRY_MD_SHA256, salt, 32, 2048, 32, devkey);
for (i=0; i<32; i++)
printf ("%02x", devkey[i]);
printf("\n");
gcry_cipher_setkey(hd, devkey, 32);
gcry_cipher_setiv(hd, iv, 16);
gcry_cipher_encrypt(hd, enc_buf, strlen(encami)+1, encami, strlen(encami)+1);
for (i=0; i<32; i++)
printf("%02x", enc_buf[i]);
printf("\n");
gcry_cipher_close(hd);
gcry_free(enc_buf);
gcry_free (devkey);
return 0;
}
My problem is that the derived key is not the same in those two programs. Why?
Is the bouncy castle deriving function not working in the same way as gcry_kdf_derive?
Thanks!
I've now looked into the PBEWithSHA256And256BitAES-CBC-BC algorithm in the BC provider, and found that it is not compatible with GCRY_KDF_PBKDF2. The gcrypt algorithm is PKCS#5 2.0 Scheme 2, whereas the BC one is actually implementing PKCS#12.
Actually, I've so far not found a named algorithm in the provider that matches the gcrypt one, however I was able to use the BC API directly to get matching results b/w them, as follows.
Bouncy Castle:
byte[] salt = new byte[8];
Arrays.fill(salt, (byte)1);
PBEParametersGenerator pGen = new PKCS5S2ParametersGenerator(new SHA256Digest());
pGen.init(Strings.toByteArray("password"), salt, 2048);
KeyParameter key = (KeyParameter)pGen.generateDerivedParameters(256);
System.out.println(Hex.toHexString(key.getKey()));
gcrypt:
unsigned char salt[8];
memset(salt, 1, 8);
unsigned char key[32];
gcry_kdf_derive("password", 8, GCRY_KDF_PBKDF2, GCRY_MD_SHA256, salt, 8, 2048, 32, key);
for (int i = 0; i < 32; ++i)
printf("%02x", key[i]);
printf("\n");
which both output:
4182537a153b1f0da1ccb57971787a42537e38dbf2b4aa3692baebb106fc02e8
You appear to be including a terminating NULL character in your count of 32 bytes (encami), which explains the differing outputs. The java version sees a 31-character input and provides a single PKCS#7-padded output block (PKCS#7 will pad the input with a single '1' byte). The C version is passed 32 bytes, including the final '0' byte. So the inputs are different.
I recommend you stop treating the NULL terminator as part of the input; instead apply PKCS#7 padding, as the Java version is doing. I'm not familiar with gcrypt, so I don't know what the typical method is for doing this, but PKCS#7 padding is a quite simple concept in any case.
I am trying to write an encryption algorithm using RSA in Java, I am getting a
"javax.crypto.BadPaddingException: Data must start with zero"; I do not know what is this exception for.
This is the example I used here
and Here is my code; please help.
public byte[] getEncryptedValue(byte[] bytes, PublicKey key) {
try {
cipher.init(Cipher.ENCRYPT_MODE, key);
return blockCipher(bytes, Cipher.ENCRYPT_MODE);
} catch (Exception ex) {
Logger.getLogger(SecurityUtil.class.getName()).log(Level.SEVERE, null, ex);
}
return null;
}
public byte[] getDecryptedValue(byte[] bytes, PrivateKey key) {
try {
cipher.init(Cipher.DECRYPT_MODE, key);
return blockCipher(bytes, Cipher.DECRYPT_MODE);
} catch (Exception ex) {
Logger.getLogger(SecurityUtil.class.getName()).log(Level.SEVERE, null, ex);
}
return null;
}
private byte[] append(byte[] prefix, byte[] suffix) {
byte[] toReturn = new byte[prefix.length + suffix.length];
System.arraycopy(prefix, 0, toReturn, 0, prefix.length);
System.arraycopy(suffix, 0, toReturn, prefix.length, suffix.length);
return toReturn;
}
private byte[] blockCipher(byte[] bytes, int mode) throws IllegalBlockSizeException, BadPaddingException {
byte[] scrambled = new byte[0];
byte[] toReturn = new byte[0];blocks (because of RSA)
int length = (mode == Cipher.ENCRYPT_MODE) ? 100 : 128;
int n = 0;
byte[] buffer = new byte[length];
for (int i = 0; i < bytes.length; i++) {
if ((i > 0) && (i % length == 0)) {
n = 0;
scrambled = cipher.doFinal(buffer);
toReturn = append(toReturn, scrambled);
}
buffer[i % length] = bytes[i];
n++;
}
***scrambled = cipher.doFinal(buffer, 0, n);*** <-- the exception is caught here
toReturn = append(toReturn, scrambled);
return toReturn;
}
The problem could be the data sent over the network using sockets may be corrupted due to some encoding problems. I had the same problem while developing a simple client/server chat program that encrypts/decrypts using asymmetric key the messages between the server and client and vise versa, instead of sending the message as a string, I sent it as a byte array which is the encrypted message.
check if keys are matching
check if data returned by getEncryptedValue are the same that you pass to getDecryptedValue
check corectness of loop in blockCipher method
How can I hash some String with SHA-256 in Java?
SHA-256 isn't an "encoding" - it's a one-way hash.
You'd basically convert the string into bytes (e.g. using text.getBytes(StandardCharsets.UTF_8)) and then hash the bytes. Note that the result of the hash would also be arbitrary binary data, and if you want to represent that in a string, you should use base64 or hex... don't try to use the String(byte[], String) constructor.
e.g.
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] hash = digest.digest(text.getBytes(StandardCharsets.UTF_8));
I think that the easiest solution is to use Apache Common Codec:
String sha256hex = org.apache.commons.codec.digest.DigestUtils.sha256Hex(stringText);
Another alternative is Guava which has an easy-to-use suite of Hashing utilities. For example, to hash a string using SHA256 as a hex-string you would simply do:
final String hashed = Hashing.sha256()
.hashString("your input", StandardCharsets.UTF_8)
.toString();
Full example hash to string as another string.
public static String sha256(final String base) {
try{
final MessageDigest digest = MessageDigest.getInstance("SHA-256");
final byte[] hash = digest.digest(base.getBytes("UTF-8"));
final StringBuilder hexString = new StringBuilder();
for (int i = 0; i < hash.length; i++) {
final String hex = Integer.toHexString(0xff & hash[i]);
if(hex.length() == 1)
hexString.append('0');
hexString.append(hex);
}
return hexString.toString();
} catch(Exception ex){
throw new RuntimeException(ex);
}
}
If you are using Java 8 you can encode the byte[] by doing
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] hash = digest.digest(text.getBytes(StandardCharsets.UTF_8));
String encoded = Base64.getEncoder().encodeToString(hash);
import java.security.MessageDigest;
public class CodeSnippets {
public static String getSha256(String value) {
try{
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update(value.getBytes());
return bytesToHex(md.digest());
} catch(Exception ex){
throw new RuntimeException(ex);
}
}
private static String bytesToHex(byte[] bytes) {
StringBuffer result = new StringBuffer();
for (byte b : bytes) result.append(Integer.toString((b & 0xff) + 0x100, 16).substring(1));
return result.toString();
}
}
String hashWith256(String textToHash) {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] byteOfTextToHash = textToHash.getBytes(StandardCharsets.UTF_8);
byte[] hashedByetArray = digest.digest(byteOfTextToHash);
String encoded = Base64.getEncoder().encodeToString(hashedByetArray);
return encoded;
}
I traced the Apache code through DigestUtils and sha256 seems to default back to java.security.MessageDigest for calculation. Apache does not implement an independent sha256 solution. I was looking for an independent implementation to compare against the java.security library. FYI only.
In Java 8
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Scanner;
import javax.xml.bind.DatatypeConverter;
Scanner scanner = new Scanner(System.in);
String password = scanner.nextLine();
scanner.close();
MessageDigest digest = null;
try {
digest = MessageDigest.getInstance("SHA-256");
} catch (NoSuchAlgorithmException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
byte[] hash = digest.digest(password.getBytes(StandardCharsets.UTF_8));
String encoded = DatatypeConverter.printHexBinary(hash);
System.out.println(encoded.toLowerCase());
This was my approach using Kotlin:
private fun getHashFromEmailString(email : String) : String{
val charset = Charsets.UTF_8
val byteArray = email.toByteArray(charset)
val digest = MessageDigest.getInstance("SHA-256")
val hash = digest.digest(byteArray)
return hash.fold("", { str, it -> str + "%02x".format(it)})
}
This is what i have been used for hashing:
String pass = "password";
MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
byte hashBytes[] = messageDigest.digest(pass.getBytes(StandardCharsets.UTF_8));
BigInteger noHash = new BigInteger(1, hashBytes);
String hashStr = noHash.toString(16);
Output: 5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8
Here is a slightly more performant way to turn the digest into a hex string:
private static final char[] hexArray = "0123456789abcdef".toCharArray();
public static String getSHA256(String data) {
StringBuilder sb = new StringBuilder();
try {
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update(data.getBytes());
byte[] byteData = md.digest();
sb.append(bytesToHex(byteData);
} catch(Exception e) {
e.printStackTrace();
}
return sb.toString();
}
private static String bytesToHex(byte[] bytes) {
char[] hexChars = new char[bytes.length * 2];
for ( int j = 0; j < bytes.length; j++ ) {
int v = bytes[j] & 0xFF;
hexChars[j * 2] = hexArray[v >>> 4];
hexChars[j * 2 + 1] = hexArray[v & 0x0F];
}
return String.valueOf(hexChars);
}
Does anyone know of a faster way in Java?
This method return a left padded String with zero:
Java 10 and after:
public static String sha256(String text) {
try {
var messageDigest = MessageDigest.getInstance("SHA-256");
var hash = messageDigest.digest(text.getBytes(StandardCharsets.UTF_8));
return String.format("%064x", new BigInteger(1, hash));
}
catch (NoSuchAlgorithmException e) {
e.printStackTrace();
return null;
}
}
Java 8:
public static String sha256(String text) {
try {
MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
byte[] hash = messageDigest.digest(text.getBytes(StandardCharsets.UTF_8));
return String.format("%064x", new BigInteger(1, hash));
}
catch (NoSuchAlgorithmException e) {
e.printStackTrace();
return null;
}
}
BTW, you can use "%064X" for an uppercase result.
Example:
System.out.println(sha256("hello world 1"));
063dbf1d36387944a5f0ace625b4d3ee36b2daefd8bdaee5ede723637efb1cf4
Comparison to Linux cmd:
$ echo -n 'hello world 1' | sha256sum
063dbf1d36387944a5f0ace625b4d3ee36b2daefd8bdaee5ede723637efb1cf4 -
You can use MessageDigest in the following way:
public static String getSHA256(String data){
StringBuffer sb = new StringBuffer();
try{
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update(data.getBytes());
byte byteData[] = md.digest();
for (int i = 0; i < byteData.length; i++) {
sb.append(Integer.toString((byteData[i] & 0xff) + 0x100, 16).substring(1));
}
} catch(Exception e){
e.printStackTrace();
}
return sb.toString();
}
Here's a method that shows how to hash a String with the sha-256
algorithm and encode the result in hex format. This is an often used format to hash and store passwords in a database:
public static String sha256(final String data) {
try {
final byte[] hash = MessageDigest.getInstance("SHA-256").digest(data.getBytes(StandardCharsets.UTF_8));
final StringBuilder hashStr = new StringBuilder(hash.length);
for (byte hashByte : hash)
hashStr.append(Integer.toHexString(255 & hashByte));
return hashStr.toString();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
return null;
}
}
public static String sha256(String s) {
try {
return DatatypeConverter.printHexBinary(MessageDigest.getInstance("SHA-256").digest(s.getBytes(StandardCharsets.UTF_8))).toLowerCase();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return null;
}
In Java, MessageDigest class is used to calculate cryptographic hashing value. This class provides cryptographic hash function ( MD5, SHA-1 and SHA-256) to find hash value of text.
Code example for using SHA-256 algorithm.
public void printHash(String str) throws NoSuchAlgorithmException {
MessageDigest md=MessageDigest.getInstance("SHA-256");
byte[] sha256=md.digest(str.getBytes(StandardCharsets.UTF_8));
for(byte b : sha256){
System.out.printf("%02x",b);
}
}
private static String getMessageDigest(String message, String algorithm) {
MessageDigest digest;
try {
digest = MessageDigest.getInstance(algorithm);
byte data[] = digest.digest(message.getBytes("UTF-8"));
return convertByteArrayToHexString(data);
} catch (NoSuchAlgorithmException | UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
You can call above method with different algorithms like below.
getMessageDigest(message, "MD5");
getMessageDigest(message, "SHA-256");
getMessageDigest(message, "SHA-1");
You can refer this link for complete application.
I am working on AES algorithm, and I have this exception which I couldn't solve.
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*..)
the exception happens in the decryption part.
I initialize the key in a different place from where the decryption algorithm is
KeyGenerator kgen = KeyGenerator.getInstance("AES");//key generation for AES
kgen.init(128); // 192 and 256 bits may not be available
then I pass it with the cipher text which I read from file to the following method
public String decrypt(String message, SecretKey skey) {
byte[] raw = skey.getEncoded();
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
// Instantiate the cipher
Cipher cipher;
byte[] original = null;
try {
cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, skeySpec);
System.out.println("Original string: "
+ message);
original = cipher.doFinal(message.trim().getBytes()); //here where I got the exception
String originalString = new String(original);
}
//catches
EDIT
here's the encryption method.
public String encrypt(String message, SecretKey skey) {
byte[] raw = skey.getEncoded();
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
// Instantiate the cipher
Cipher cipher;
byte[] encrypted = null;
try {
cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
encrypted = cipher.doFinal(message.getBytes());
System.out.println("raw is " + encrypted);
} catches
return asHex(encrypted);
}
and here's the asHex method
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();
}
Here's where I read the cipher text form the file
static public String readFile(String filePath) {
StringBuilder file = new StringBuilder();
String line = null;
try {
FileReader reader = new FileReader(filePath);
BufferedReader br = new BufferedReader(reader);
if (br != null) {
line = br.readLine();
while (line != null) {
file.append(line);
// System.out.println("line is " + line);
line = br.readLine();
}
}
br.close();
reader.close();
} catch (IOException ex) {
Logger.getLogger(FileManagement.class.getName()).log(Level.SEVERE, null, ex);
}
System.out.println("line is " + file.toString());
return String.valueOf(file);
}
can someone help?
Ok, so the problem is that you are converting the encrypted bytes to a hex string (using the asHex method) but are not converting the hex string back to a byte array correctly for decryption. You can't use getBytes.
You can use the following method to convert a hex string to a byte array:
public static byte[] fromHexString(String s) {
int len = s.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
+ Character.digit(s.charAt(i+1), 16));
}
return data;
}
and then change your decrypt method to use:
original = cipher.doFinal(fromHexString(message));
I did have a Bad Padding Exception and have not been able to find on the internet a solution to my problem. Since I found it after some hard-working hours, I give it here.
My problem was, I was reading a file on my hard drive, and encrypting it through a buffer, always calling the doFinal() method instead of update() method. So when decrypting it, I had padding errors
input = new FileInputStream(file);
output = new FileOutputStream(newFile);
Cipher cipher = Cipher.getInstance("DES");
cipher.init(Cipher.ENCRYPT_MODE, mySecretKey);
byte[] buf = new byte[1024];
count = input.read(buf);
while (count >= 0) {
output.write(cipher.update(buf, 0, count)); // HERE I WAS DOING doFinal() method
count = input.read(buf);
}
output.write(cipher.doFinal()); // AND I DID NOT HAD THIS LINE BEFORE
output.flush();
And when decrypting, with the same method, but with a Cipher init with DECRYPT_MODE
input = new FileInputStream(file);
output = new FileOutputStream(newFile);
Cipher cipher = Cipher.getInstance("DES");
cipher.init(Cipher.DECRYPT_MODE, mySecretKey);
byte[] buf = new byte[1024];
count = input.read(buf);
while (count >= 0) {
output.write(cipher.update(buf, 0, count)); // HERE I WAS DOING doFinal() method
//AND HERE WAS THE BadPaddingExceotion -- the first pass in the while structure
count = input.read(buf);
}
output.write(cipher.doFinal()); // AND I DID NOT HAD THIS LINE BEFORE
output.flush();
With the code written, I no longer have any BadPaddingException.
I may precise that this exception only appears when the original clear file length (obtained through file.length()) is bigger than the buffer. Else, we do not need to pass several times in the while structure, and we can encrypt in one pass with a doFinal() call. That justify the random character of the exception following the size of the file you try to encrypt.
I hope you had a good reading!
I guess the expression message.trim().getBytes() does not return the same bytes which are generated when you encrypted the message. Specially the trim() method could delete the bytes which were added as padding in the encrypted message.
Verify that both the returned array of the doFinal() method during the encryption and the returned array of message.trim().getBytes():
got the same number of bytes (array length)
got the same bytes in the array
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(512);
KeyPair rsaKeyPair = kpg.genKeyPair();
byte[] txt = "This is a secret message.".getBytes();
System.out.println("Original clear message: " + new String(txt));
// encrypt
Cipher cipher;
try
{
cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, rsaKeyPair.getPublic());
txt = cipher.doFinal(txt);
}
catch (Throwable e)
{
e.printStackTrace();
return;
}
System.out.println("Encrypted message: " + new String(txt));
// decrypt
try
{
cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.DECRYPT_MODE, rsaKeyPair.getPrivate());
txt = cipher.doFinal(txt);
}
catch (Throwable e)
{
e.printStackTrace();
return;
}
System.out.println("Decrypted message: " + new String(txt));
Here is a solution I was able to piece together using a jks keystore with RSA encryption
import javax.crypto.Cipher;
import javax.xml.bind.DatatypeConverter;
import java.security.Key;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.KeyStore;
import java.security.cert.Certificate;
public class Main {
public static void main(String[] args) {
byte[] txt = "This is a secret message for your own eyes only".getBytes();
byte[] encText;
try{
// Load the keystore
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
char[] password = "keystorePassword".toCharArray();
java.io.FileInputStream fis = new java.io.FileInputStream("/path/to/keystore/myKeyStore.jks");
ks.load(fis, password);
fis.close();
Key rsakey = ks.getKey("mykeyalias", password);
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
// Encrypt
Certificate cert = ks.getCertificate("mykeyalias");
try
{
cipher.init(Cipher.ENCRYPT_MODE, cert.getPublicKey());
encText = cipher.doFinal(txt);
System.out.println(encText.toString());
}
catch (Throwable e)
{
e.printStackTrace();
return;
}
// Decrypt
cipher.init(Cipher.DECRYPT_MODE, rsakey);
String decrypted = new String(cipher.doFinal(encText));
System.out.println(decrypted);
} catch (Exception e) {
System.out.println("error" + e);
}
}