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);
}
}
Related
I want print specific index of an Array of split text but it gives error, some one can help please
TEXT.txt contains A,B,3,D,5,1
FileReader Alphabets_and_Numbers = new FileReader("TEXT.txt");
Scanner read = new Scanner(Alphabets_and_Numbers);
read.useDelimiter(",\\s*");
while(read.hasNext()) {
String data = read.next();
String[] Alphabets = data.split("\\d");
/*
A
B
D
*/
String[] Numbers = data.split("[^0-6]");
/*
3
5
1
*/
when printed with for loop is Ok it prints ABD
for(int c = 0; c<Alphabets.length; c++)
{
System.out.println(Alphabets[c]);
}
while to print single Element D, it gives
ArrayIndexOutOfBoundsException: 2
System.out.println(Alphabets[2]);
}
Your code is using a random salt for the password deriving but the salt is never stored so when later decrypting (in a second run) you don't have the salt again.
Second - there is a similar issue with the initialization vector that is used for AES encryption. You don't define one on encryption side so Java is (internally)
generating one and does the encryption. Now you have to grab this iv from the cipher and save it as well for later decryption. As you don't provide the iv you run
into the exception "Missing parameter type: IV expected".
As I'm too lazy to check your code you find below the code (provided by #Topaco, all credits go to him ! link: https://stackoverflow.com/a/63719246/8166854) for
a complete file encryption / decryption that is suitable even for larger files as it uses buffered CipherInput-/OutputStreams.
Security warning: the codehas no exception handling and is for educational purpose only!.
code:
import javax.crypto.*;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.security.SecureRandom;
public class MainFileEncryption {
public static void main(String[] args) throws Exception {
System.out.println("File encryption with PBEWithHmacSHA256AndAES_256");
String uncryptedFilename = "plaintext.txt";
String encryptedFilename = "plaintext.enc";
String decryptedFilename = "decrypted.txt";
String password = "mySecretPassword";
byte[] iv = new byte[16];
SecureRandom secureRandom = new SecureRandom();
secureRandom.nextBytes(iv);
encrypt(password, iv, uncryptedFilename, encryptedFilename);
decrypt(password, encryptedFilename, decryptedFilename);
}
// by Topaco https://stackoverflow.com/a/63719246/8166854
private static void encrypt(String key, byte[] initVector, String inputFile, String outputFile) throws Exception {
// Key
PBEKeySpec pbeKeySpec = new PBEKeySpec(key.toCharArray());
SecretKeyFactory keyFac = SecretKeyFactory.getInstance("PBEWithHmacSHA256AndAES_256");
SecretKey pbeKey = keyFac.generateSecret(pbeKeySpec);
// IV
IvParameterSpec iv = new IvParameterSpec(initVector);
// Salt
SecureRandom rand = new SecureRandom();
byte[] salt = new byte[16];
rand.nextBytes(salt);
// ParameterSpec
int count = 10000;
PBEParameterSpec pbeParamSpec = new PBEParameterSpec(salt, count, iv);
// Cipher
Cipher cipher = Cipher.getInstance("PBEWithHmacSHA256AndAES_256");
cipher.init(Cipher.ENCRYPT_MODE, pbeKey, pbeParamSpec);
try (FileInputStream fin = new FileInputStream(inputFile);
FileOutputStream fout = new FileOutputStream(outputFile);
CipherOutputStream cipherOut = new CipherOutputStream(fout, cipher)) {
// Write IV, Salt
fout.write(initVector);
fout.write(salt);
// Encrypt
final byte[] bytes = new byte[1024];
for (int length = fin.read(bytes); length != -1; length = fin.read(bytes)) {
cipherOut.write(bytes, 0, length);
}
}
}
private static void decrypt(String key, String inputFile, String outputFile) throws Exception {
try (FileInputStream encryptedData = new FileInputStream(inputFile);
FileOutputStream decryptedOut = new FileOutputStream(outputFile)) {
// Key
PBEKeySpec pbeKeySpec = new PBEKeySpec(key.toCharArray());
SecretKeyFactory keyFac = SecretKeyFactory.getInstance("PBEWithHmacSHA256AndAES_256");
SecretKey pbeKey = keyFac.generateSecret(pbeKeySpec);
// Read IV
byte[] initVect = new byte[16];
encryptedData.read(initVect);
IvParameterSpec iv = new IvParameterSpec(initVect);
// Read salt
byte[] salt = new byte[16];
encryptedData.read(salt);
// ParameterSpec
int count = 10000;
PBEParameterSpec pbeParamSpec = new PBEParameterSpec(salt, count, iv);
// Cipher
Cipher cipher = Cipher.getInstance("PBEWithHmacSHA256AndAES_256");
cipher.init(Cipher.DECRYPT_MODE, pbeKey, pbeParamSpec);
try (CipherInputStream decryptStream = new CipherInputStream(encryptedData, cipher)) {
// Decrypt
final byte[] bytes = new byte[1024];
for (int length = decryptStream.read(bytes); length != -1; length = decryptStream.read(bytes)) {
decryptedOut.write(bytes, 0, length);
}
}
}
}
}
I referred this and trying to do file decryption with base64 decoding
My requirement is to encode data with base64 during encryption and decode data with base64 during decryption.
But im facing below error:
javax.crypto.IllegalBlockSizeException: Input length must be multiple of 16 when decrypting with padded cipher
at java.base/com.sun.crypto.provider.CipherCore.doFinal(Unknown Source)
at java.base/com.sun.crypto.provider.CipherCore.doFinal(Unknown Source)
at java.base/com.sun.crypto.provider.AESCipher.engineDoFinal(Unknown Source)
at java.base/javax.crypto.Cipher.doFinal(Unknown Source)
at aes.DecryptNew.decryptNew(DecryptNew.java:124)
at aes.DecryptNew.main(DecryptNew.java:32)
Also im confused on how to perform decryption in chunks.
Please suggest me issues in this code.
import javax.crypto.Cipher;
import javax.crypto.CipherOutputStream;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.BufferedWriter;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.*;
import java.util.Arrays;
import java.util.Base64;
public class DecryptNew {
public static void main(String[] args) {
String plaintextFilename = "D:\\\\plaintext.txt";
String ciphertextFilename = "D:\\\\plaintext.txt.crypt";
String decryptedtextFilename = "D:\\\\plaintextDecrypted.txt";
String password = "testpass";
writeToFile();
String ciphertext = encryptfile(plaintextFilename, password);
System.out.println("ciphertext: " + ciphertext);
decryptNew(ciphertextFilename, password, decryptedtextFilename);
}
static void writeToFile() {
BufferedWriter writer = null;
try
{
writer = new BufferedWriter( new FileWriter("D:\\\\plaintext.txt"));
byte[] data = Base64.getEncoder().encode("hello\r\nhello".getBytes(StandardCharsets.UTF_8));
writer.write(new String(data));
}
catch ( IOException e)
{
}
finally
{
try
{
if ( writer != null)
writer.close( );
}
catch ( IOException e)
{
}
}
}
public static String encryptfile(String path, String password) {
try {
FileInputStream fis = new FileInputStream(path);
FileOutputStream fos = new FileOutputStream(path.concat(".crypt"));
final byte[] pass = Base64.getEncoder().encode(password.getBytes());
final byte[] salt = (new SecureRandom()).generateSeed(8);
fos.write(Base64.getEncoder().encode("Salted__".getBytes()));
fos.write(salt);
final byte[] passAndSalt = concatenateByteArrays(pass, salt);
byte[] hash = new byte[0];
byte[] keyAndIv = new byte[0];
for (int i = 0; i < 3 && keyAndIv.length < 48; i++) {
final byte[] hashData = concatenateByteArrays(hash, passAndSalt);
final MessageDigest md = MessageDigest.getInstance("SHA-1");
hash = md.digest(hashData);
keyAndIv = concatenateByteArrays(keyAndIv, hash);
}
final byte[] keyValue = Arrays.copyOfRange(keyAndIv, 0, 32);
final byte[] iv = Arrays.copyOfRange(keyAndIv, 32, 48);
final SecretKeySpec key = new SecretKeySpec(keyValue, "AES");
final Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(iv));
CipherOutputStream cos = new CipherOutputStream(fos, cipher);
int b;
byte[] d = new byte[8];
while ((b = fis.read(d)) != -1) {
cos.write(d, 0, b);
}
cos.flush();
cos.close();
fis.close();
System.out.println("encrypt done " + path);
} catch (IOException | NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | InvalidAlgorithmParameterException e) {
e.printStackTrace();
}
return path;
}
static void decryptNew(String path,String password, String outPath) {
byte[] SALTED_MAGIC = Base64.getEncoder().encode("Salted__".getBytes());
try{
FileInputStream fis = new FileInputStream(path);
FileOutputStream fos = new FileOutputStream(outPath);
final byte[] pass = password.getBytes(StandardCharsets.US_ASCII);
final byte[] inBytes = Files.readAllBytes(Paths.get(path));
final byte[] shouldBeMagic = Arrays.copyOfRange(inBytes, 0, SALTED_MAGIC.length);
if (!Arrays.equals(shouldBeMagic, SALTED_MAGIC)) {
throw new IllegalArgumentException("Initial bytes from input do not match OpenSSL SALTED_MAGIC salt value.");
}
final byte[] salt = Arrays.copyOfRange(inBytes, SALTED_MAGIC.length, SALTED_MAGIC.length + 8);
final byte[] passAndSalt = concatenateByteArrays(pass, salt);
byte[] hash = new byte[0];
byte[] keyAndIv = new byte[0];
for (int i = 0; i < 3 && keyAndIv.length < 48; i++) {
final byte[] hashData = concatenateByteArrays(hash, passAndSalt);
MessageDigest md = null;
md = MessageDigest.getInstance("SHA-1");
hash = md.digest(hashData);
keyAndIv = concatenateByteArrays(keyAndIv, hash);
}
final byte[] keyValue = Arrays.copyOfRange(keyAndIv, 0, 32);
final SecretKeySpec key = new SecretKeySpec(keyValue, "AES");
final byte[] iv = Arrays.copyOfRange(keyAndIv, 32, 48);
final Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv));
final byte[] clear = cipher.doFinal(inBytes, 16, inBytes.length - 16);
String contentDecoded = new String(Base64.getDecoder().decode(clear));
fos.write(contentDecoded.getBytes());
fos.close();
System.out.println("Decrypt is completed");
}catch(Exception e){
e.printStackTrace();
}
}
public static byte[] concatenateByteArrays(byte[] a, byte[] b) {
return ByteBuffer
.allocate(a.length + b.length)
.put(a).put(b)
.array();
}
}
As mentioned in my first comment: Encryption and decryption use different encodings for the password (Base64 in encryption, ASCII in decryption).
In addition the prefix is Base64 encoded in both encryption and decryption, so prefix plus salt are larger than one block (16 bytes), and therefore the subsequent length determination of the ciphertext during decryption fails, as it is assumed that the ciphertext starts with the 2nd block.
Both issues can be solved if the same encoding is used for the password in encryption and decryption (e.g. ASCII) and the prefix is ASCII encoded, e.g. for encryption:
...
byte[] pass = password.getBytes(StandardCharsets.US_ASCII);
byte[] SALTED_MAGIC = "Salted__".getBytes(StandardCharsets.US_ASCII);
byte[] salt = (new SecureRandom()).generateSeed(8);
fos.write(SALTED_MAGIC);
fos.write(salt);
...
and for decryption:
...
byte[] pass = password.getBytes(StandardCharsets.US_ASCII);
byte[] SALTED_MAGIC = "Salted__".getBytes(StandardCharsets.US_ASCII);
byte[] prefix = fis.readNBytes(8);
byte[] salt = fis.readNBytes(8);
...
However the current encryption method does not Base64 encode (only the prefix is Base64 encoded which is rather counterproductive, s. above).
Even a Base64 encoded plaintext does not change this, as the ciphertext itself is not Base64 encoded.
Considering your statement My requirement is to encode data with base64 during encryption and decode data with base64 during decryption and the OpenSSL format used, I assume that you want to decrypt a ciphertext that has been encrypted with the -base64 option, analogous to OpenSSL, i.e. with
openssl enc -aes-256-cbc -base64 -pass pass:testpass -p -in sample.txt -out sample.crypt
Here, prefix, salt and ciphertext are concatenated on byte level and the result is then Base64 encoded. To achieve this, your implementation posted in the question could be changed as follows:
static void decrypt(String path, String password, String outPath) {
try (FileInputStream fis = new FileInputStream(path);
Base64InputStream bis = new Base64InputStream(fis, false, 64, "\r\n".getBytes(StandardCharsets.US_ASCII))) { // from Apache Commons Codec
// Read prefix and salt
byte[] SALTED_MAGIC = "Salted__".getBytes(StandardCharsets.US_ASCII);
byte[] prefix = bis.readNBytes(8);
if (!Arrays.equals(prefix, SALTED_MAGIC)) {
throw new IllegalArgumentException("Initial bytes from input do not match OpenSSL SALTED_MAGIC salt value.");
}
byte[] salt = bis.readNBytes(8);
// Derive key and IV
byte[] pass = password.getBytes(StandardCharsets.US_ASCII);
byte[] passAndSalt = concatenateByteArrays(pass, salt);
byte[] hash = new byte[0];
byte[] keyAndIv = new byte[0];
for (int i = 0; i < 3 && keyAndIv.length < 48; i++) {
byte[] hashData = concatenateByteArrays(hash, passAndSalt);
MessageDigest md = null;
md = MessageDigest.getInstance("SHA-1"); // Use digest from encryption
hash = md.digest(hashData);
keyAndIv = concatenateByteArrays(keyAndIv, hash);
}
byte[] keyValue = Arrays.copyOfRange(keyAndIv, 0, 32);
SecretKeySpec key = new SecretKeySpec(keyValue, "AES");
byte[] iv = Arrays.copyOfRange(keyAndIv, 32, 48);
// Decrypt
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv));
try (CipherInputStream cis = new CipherInputStream(bis, cipher);
FileOutputStream fos = new FileOutputStream(outPath)) {
int length;
byte[] buffer = new byte[1024];
while ((length = cis.read(buffer)) != -1) {
fos.write(buffer, 0, length);
}
}
System.out.println("Decrypt is completed");
} catch (Exception e) {
e.printStackTrace();
}
}
As I already mentioned in the comments to the linked question, the processing in chunks can easily be implemented with the class CipherInputStream. The Base64 decoding can be achieved with the class Base64InputStream of the Apache Commons Codec, as also addressed by Michael Fehr. This is a convenient way to perform Base64 decoding and decryption together. If the data does not need to be Base64 decoded (e.g. if the -base64 option was not used during encryption) the Base64InputStream class can simply be omitted.
As previously stated in the comments, small files do not need to be processed in chunks. This is only necessary when the files become large relative to the memory. However, since your encryption method processes the data in chunks, it is consistent that the decryption does the same.
Note that the encryption posted in the question is not compatible with the result of the above OpenSSL statement, i.e. the encryption would have to be adapted if necessary (analogous to the decryption posted above).
Edit: The encryptfile() method posted in the question creates a file containing the Base64 encoded prefix, the raw salt and the raw ciphertext. For key derivation the Base64 encoded password is applied. The method is used to encrypt a Base64 encoded plaintext. The following method is the counterpart of encryptfile() and allows the decryption and Base64 decoding of the plaintext:
static void decryptfile(String path, String password, String outPath) {
try (FileInputStream fis = new FileInputStream(path)) {
// Read prefix and salt
byte[] SALTED_MAGIC = Base64.getEncoder().encode("Salted__".getBytes());
byte[] prefix = new byte[SALTED_MAGIC.length];
fis.readNBytes(prefix, 0, prefix.length);
if (!Arrays.equals(prefix, SALTED_MAGIC)) {
throw new IllegalArgumentException("Initial bytes from input do not match OpenSSL SALTED_MAGIC salt value.");
}
byte[] salt = new byte[8];
fis.readNBytes(salt, 0, salt.length);
// Derive key and IV
final byte[] pass = Base64.getEncoder().encode(password.getBytes());
byte[] passAndSalt = concatenateByteArrays(pass, salt);
byte[] hash = new byte[0];
byte[] keyAndIv = new byte[0];
for (int i = 0; i < 3 && keyAndIv.length < 48; i++) {
byte[] hashData = concatenateByteArrays(hash, passAndSalt);
MessageDigest md = null;
md = MessageDigest.getInstance("SHA-1"); // Use digest from encryption
hash = md.digest(hashData);
keyAndIv = concatenateByteArrays(keyAndIv, hash);
}
byte[] keyValue = Arrays.copyOfRange(keyAndIv, 0, 32);
SecretKeySpec key = new SecretKeySpec(keyValue, "AES");
byte[] iv = Arrays.copyOfRange(keyAndIv, 32, 48);
// Decrypt
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv));
try (CipherInputStream cis = new CipherInputStream(fis, cipher);
Base64InputStream bis = new Base64InputStream(cis, false, -1, null); // from Apache Commons Codec
FileOutputStream fos = new FileOutputStream(outPath)) {
int length;
byte[] buffer = new byte[1024];
while ((length = bis.read(buffer)) != -1) {
fos.write(buffer, 0, length);
}
}
System.out.println("Decrypt is completed");
} catch (Exception e) {
e.printStackTrace();
}
}
The decryptfile()-method writes the Base64 decoded plaintext to the file in outPath. To get the Base64 encoded plaintext simply remove the Base64InputStream instance from the stream hierarchy.
As already explained in the comment, this method is incompatible to OpenSSL, i.e. it can't decrypt ciphertexts generated with the OpenSSL statement posted above (with or without -base64 option). To decrypt a ciphertext generated with OpenSSL with -base64 option use the decrypt() method.
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 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();
}
I am new to encryption and decryption. I was given a PSKC file and asked for decryption. I was given the password for decryption. The PSKC file doenot have initialization vector value.
I wrote the code trying to decrypt it. But i am unsuccessful in achieving the outcome.
below is the PSKC file example
<?xml version="1.0"?>
<pskc:KeyContainer xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:pkcs5="http://www.rsasecurity.com/rsalabs/pkcs/schemas/pkcs-5v2-0#" xmlns:xenc="http://www.w3.org/2001/04/xmlenc#" xmlns:xenc11="http://www.w3.org/2009/xmlenc11#" xmlns:pskc="urn:ietf:params:xml:ns:keyprov:pskc">
<pskc:EncryptionKey>
<xenc11:DerivedKey xmlns:xenc="http://www.w3.org/2001/04/xmlenc#" xmlns:pkcs5="http://www.rsasecurity.com/rsalabs/pkcs/schemas/pkcs-5v2-0#" xmlns:pskc="urn:ietf:params:xml:ns:keyprov:pskc" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:xenc11="http://www.w3.org/2009/xmlenc11#">
<xenc11:KeyDerivationMethod Algorithm="http://www.rsasecurity.com/rsalabs/pkcs/schemas/pkcs-5v2-0#pbkdf2">
<pkcs5:PBKDF2-params xmlns:xenc="http://www.w3.org/2001/04/xmlenc#" xmlns:pskc="urn:ietf:params:xml:ns:keyprov:pskc" xmlns:xenc11="http://www.w3.org/2009/xmlenc11#" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:pkcs5="http://www.rsasecurity.com/rsalabs/pkcs/schemas/pkcs-5v2-0#">
<Salt>
<Specified>EW0h0yUcDX72WU9UiKiCwDpXsJg=</Specified>
</Salt>
<IterationCount>128</IterationCount>
<KeyLength>16</KeyLength>
<PRF />
</pkcs5:PBKDF2-params>
</xenc11:KeyDerivationMethod>
<xenc:ReferenceList>
<xenc:DataReference URI="#ED" />
</xenc:ReferenceList>
<xenc11:MasterKeyName>Passphrase1</xenc11:MasterKeyName>
</xenc11:DerivedKey>
</pskc:EncryptionKey>
<pskc:MACMethod Algorithm="http://www.w3.org/2000/09/xmldsig#hmac-sha1">
<pskc:MACKey>
<xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes128-cbc" />
<xenc:CipherData>
<xenc:CipherValue>jq/NdikC7AZf0Z+HEL5NrCICV8XW+ttzl/8687hVGHceoyJAaFws+111plQH6Mlg</xenc:CipherValue>
</xenc:CipherData>
</pskc:MACKey>
</pskc:MACMethod>
<pskc:KeyPackage>
<pskc:DeviceInfo>
<pskc:Manufacturer>Gemalto</pskc:Manufacturer>
<pskc:SerialNo>GAKT000047A5</pskc:SerialNo>
</pskc:DeviceInfo>
<pskc:CryptoModuleInfo>
<pskc:Id>CM_ID_007</pskc:Id>
</pskc:CryptoModuleInfo>
<pskc:Key Id="GAKT000047A5" Algorithm="urn:ietf:params:xml:ns:keyprov:pskc:totp">
<pskc:Issuer>Issuer0</pskc:Issuer>
<pskc:AlgorithmParameters>
<pskc:ResponseFormat Encoding="DECIMAL" Length="6" />
</pskc:AlgorithmParameters>
<pskc:Data>
<pskc:Secret>
<pskc:EncryptedValue>
<xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes128-cbc" />
<xenc:CipherData>
<xenc:CipherValue>pM7VB/KomPjq2cKaxPr5cKT1tUZN5tGMI+u1XKJTG1la+ThraPpLKlL2plKk6vQE</xenc:CipherValue>
</xenc:CipherData>
</pskc:EncryptedValue>
<pskc:ValueMAC>lbu+9OcLArnj6mS7KYOKDa4zRU0=</pskc:ValueMAC>
</pskc:Secret>
<pskc:Time>
<pskc:PlainValue>0</pskc:PlainValue>
</pskc:Time>
<pskc:TimeInterval>
<pskc:PlainValue>30</pskc:PlainValue>
</pskc:TimeInterval>
</pskc:Data>
</pskc:Key>
</pskc:KeyPackage>
</pskc:KeyContainer>
below is the java code which i have written for decryption.
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import javax.crypto.Cipher;
import javax.crypto.Mac;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;
import org.bouncycastle.crypto.generators.PKCS5S2ParametersGenerator;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.crypto.params.ParametersWithIV;
import com.sun.org.apache.xerces.internal.impl.dv.util.HexBin;
public class test {
/**
* #param args
*/
public static void main(String[] args) {
test te = new test();
try {
te.decryptSeedValue();
} catch (Exception e) {
// TODO Auto-generated catch block
System.out.println(e.getMessage());
e.printStackTrace();
}
// TODO Auto-generated method stub
}
public static HashMap decryptSeedValue()throws Exception{
String password = "G?20R+I+3-/UcWIN";
String pbesalt ="EW0h0yUcDX72WU9UiKiCwDpXsJg=";
String iv = "aaaaaaaaaaaaaaaaaaaaaaaa";
int iteration = 128;
String value = "pM7VB/KomPjq2cKaxPr5cKT1tUZN5tGMI+u1XKJTG1la+ThraPpLKlL2plKk6vQE";
String valueDigest = "lbu+9OcLArnj6mS7KYOKDa4zRU0=";
byte[] cipherText =null;
//some parameters need to decode from Base64 to byte[]
byte[] data = base64Decode(value.getBytes());
//System.out.println("data(hex string) = " + HexBin.encode(data));//debug
byte[] salt = base64Decode(pbesalt.getBytes());
//System.out.println("salt(hex string) = " + HexBin.encode(salt));//debug
byte[] initVec = base64Decode(iv.getBytes());
//System.out.println("iv(hex string) = " + HexBin.encode(initVec));//debug
//perform PBE key generation and AES/CBC/PKCS5Padding decrpyption
HashMap hs = myFunction(data, password, initVec, salt, iteration);
String seedValue = (String)hs.get("DECRYPTED_SEED_VALUE");
byte[] temp = (byte[])hs.get("HASH_OUTPUT");
//System.out.println("hashed output(hex string) = " + HexBin.encode(temp));//debug
//perform Base64 Encode
byte[] out = base64Encode(temp);
String output = new String((out));
System.out.println("output = "+output);
System.out.println("valueD = "+valueDigest);
//System.out.println("hashed output(base64) = " + output);
//compare the result
if(output.equals(valueDigest)){
System.out.println("Hash verification successful for:-->" );
System.out.println("\n");
//hs.put("SEED_VALUE", HexBin.encode(temp));
hs.put("SEED_VALUE", seedValue);
return hs;
}
else{
System.out.println("Hash verification failed for :-->");
return null;
}
}
public static HashMap myFunction(byte[] data, String password, byte[] initVec,
byte[] salt, int iteration) throws Exception{
PKCS5S2ParametersGenerator generator = new PKCS5S2ParametersGenerator();
byte[] pBytes = password.getBytes();
generator.init(pBytes, salt, iteration);
int keysize = 128;//fixed at AES key of 16 bytes
int ivsize = initVec.length;
ParametersWithIV params = (ParametersWithIV) generator.generateDerivedParameters(keysize, ivsize);
KeyParameter keyParam = (KeyParameter) params.getParameters();
//System.out.println("derived key = " + HexBin.encode(keyParam.getKey()));
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
IvParameterSpec paramSpec = new IvParameterSpec(initVec);
SecretKeySpec key = new SecretKeySpec(keyParam.getKey(), "AES");
cipher.init(Cipher.DECRYPT_MODE, key, paramSpec);
//perform decryption
byte[] secret = cipher.doFinal(data);
//display the 20 bytes secret of the token
//System.out.println("token secret(hex string) = " + HexBin.encode(secret));
//perform HMAC-SHA-1
byte[] output = hmac_sha1(secret, keyParam.getKey());
HashMap hs = new HashMap();
hs.put("ENCRYPTION_KEY", HexBin.encode(keyParam.getKey()));
hs.put("HASH_OUTPUT", output);
hs.put("DECRYPTED_SEED_VALUE", HexBin.encode(secret));
return hs;
}
public static byte[] base64Encode(byte[] passwordBytes) throws NoSuchAlgorithmException {
Base64 base64 = new Base64();
byte[] hashBytes2 = base64.encode(passwordBytes);
return hashBytes2;
}
public static byte[] base64Decode(byte[] passwordBytes) throws NoSuchAlgorithmException {
Base64 base64 = new Base64();
byte[] hashBytes2 = base64.decode(passwordBytes);
return hashBytes2;
}
public static byte[] hmac_sha1(byte[] dataByte, byte[] keyByte) throws Exception{
Mac hmacSha1;
hmacSha1 = Mac.getInstance("HmacSHA1");
SecretKeySpec macKey = new SecretKeySpec(keyByte, "HmacSHA1");
hmacSha1.init(macKey);
byte[] result = hmacSha1.doFinal(dataByte);
return result;
}
/**
* Convert a byte array of 8 bit characters into a String.
*
* #param bytes the array containing the characters
* #param length the number of bytes to process
* #return a String representation of bytes
*/
private static String toString(
byte[] bytes,
int length)
{
char[] chars = new char[length];
for (int i = 0; i != chars.length; i++)
{
chars[i] = (char)(bytes[i] & 0xff);
}
return new String(chars);
}
}
it doesn't throw any exception, but it prints "Hash verification failed for" which is defined in my code when decryption fails.
Can some one please help me out.
As per the pskc standard http://www.rfc-editor.org/rfc/rfc6030.txt the IV is prepended to the ciphervalue. This is aes128, so it'll be the first 16 bytes once it's been base64 decoded.
Adding onto what bcharlton is describing; what you are not doing is check the hmac_sha1 for the encrypted data (which has the iv prepended in encrypted form), using the MACKey described in the xml document.
With AES-128 CBC the initialization vector is explicitly defined, and since there is no verification built into it, it uses HMAC for it.
So given your example the following will work:
public static HashMap decryptSeedValue() throws Exception
{
String password = "G?20R+I+3-/UcWIN";
String pbesalt = "EW0h0yUcDX72WU9UiKiCwDpXsJg=";
String iv = "aaaaaaaaaaaaaaaaaaaaaaaa";
int iteration = 128;
String value = "pM7VB/KomPjq2cKaxPr5cKT1tUZN5tGMI+u1XKJTG1la+ThraPpLKlL2plKk6vQE";
String valueDigest = "lbu+9OcLArnj6mS7KYOKDa4zRU0=";
//YOU NEED THIS GUY BELOW TO VERIFY
String macKey = "jq/NdikC7AZf0Z+HEL5NrCICV8XW+ttzl/8687hVGHceoyJAaFws+111plQH6Mlg";
byte[] cipherText = null;
//some parameters need to decode from Base64 to byte[]
byte[] data = base64Decode(value.getBytes());
//System.out.println("data(hex string) = " + HexBin.encode(data));//debug
byte[] salt = base64Decode(pbesalt.getBytes());
//System.out.println("salt(hex string) = " + HexBin.encode(salt));//debug
byte[] initVec = base64Decode(iv.getBytes());
//System.out.println("iv(hex string) = " + HexBin.encode(initVec));//debug
//perform PBE key generation and AES/CBC/PKCS5Padding decrpyption
HashMap hs = myFunction(data, password, base64Decode(macKey.getBytes()), salt, iteration);
String seedValue = (String) hs.get("DECRYPTED_SEED_VALUE");
byte[] temp = (byte[]) hs.get("HASH_OUTPUT");
//System.out.println("hashed output(hex string) = " + HexBin.encode(temp));//debug
//perform Base64 Encode
byte[] out = base64Encode(temp);
String output = new String((out));
System.out.println("output = " + output);
System.out.println("valueD = " + valueDigest);
//System.out.println("hashed output(base64) = " + output);
//compare the result
if (output.equals(valueDigest)) {
System.out.println("Hash verification successful for:-->");
System.out.println("\n");
//hs.put("SEED_VALUE", HexBin.encode(temp));
hs.put("SEED_VALUE", seedValue);
return hs;
} else {
System.out.println("Hash verification failed for :-->");
return null;
}
}
public static HashMap myFunction(byte[] data, String password, byte[] macData,
byte[] salt, int iteration) throws Exception
{
PKCS5S2ParametersGenerator generator = new PKCS5S2ParametersGenerator();
byte[] pBytes = password.getBytes();
generator.init(pBytes, salt, iteration);
byte[] iv = new byte[16];
int ivsize = iv.length;
byte[] encryptedData = new byte[data.length - ivsize];
System.arraycopy(data, 0, iv, 0, iv.length);
System.arraycopy(data, ivsize, encryptedData, 0, encryptedData.length);
byte[] maciv = new byte[16];
byte[] encryptedMac = new byte[macData.length - maciv.length];
System.arraycopy(macData, 0, maciv, 0, maciv.length);
System.arraycopy(macData, maciv.length, encryptedMac, 0, encryptedMac.length);
int keysize = 128;//fixed at AES key of 16 bytes
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
KeySpec spec = new PBEKeySpec(password.toCharArray(), salt, iteration, keysize);
SecretKey tmp = factory.generateSecret(spec);
SecretKey key = new SecretKeySpec(tmp.getEncoded(), "AES");
Cipher dcipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
dcipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv));
byte[] decryptedData = dcipher.doFinal(encryptedData);
// decryptedData is your token value!
dcipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(maciv));
byte[] decryptedMac = dcipher.doFinal(encryptedMac);
//display the 20 bytes secret of the token
//System.out.println("token secret(hex string) = " + HexBin.encode(secret));
//perform HMAC-SHA-1
//Use the decrypted MAC key here for hashing!
byte[] output = hmac_sha1(data, decryptedMac);
HashMap hs = new HashMap();
hs.put("ENCRYPTION_KEY", password);
hs.put("HASH_OUTPUT", output);
hs.put("DECRYPTED_SEED_VALUE", HexBin.encode(decryptedData));
return hs;
}
Keep in mind that as https://www.rfc-editor.org/rfc/rfc6030#section-6.2 describes, a different iv can be used for the MAC and the token key.