Question about AES / CBC / PKCS5Padding Decryption in the Java Programming Language - java

I am developing a code in Java, in which when a user enters the key, the Initialization Vector and the ciphertext, the program returns the deciphered text, according to the AES / CBC / PKCS5Padding Mode.
This code is NOT working, and I would like someone to help me correct it, or to present a better code, please.
This Key, this Initialization Vector and this ciphertext were got from this website:
https://www.di-mgt.com.au/properpassword.html
That is, the plain text must return a simple "Hello World" message.
If you know of any Java code that does this, can you please post?
My code, which is experiencing a NullPointerException error:
package encryptdecryptvideo;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.ShortBufferException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
//import javax.crypto.*;
public class EncryptDecryptVideo {
byte[] input;
String inputString;
byte[] keyBytes = "9008873522F55634679EF64CC25E73354".getBytes();
byte[] ivBytes = "B8A112A270D9634EFF3818F6CCBDF5EC".getBytes();
SecretKeySpec key = new SecretKeySpec(keyBytes, "DES");
IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);
Cipher cipher;
byte[] cipherText = "625F094A1FB1677521B6014321A807EC".getBytes();
int ctLength;
public static void main(String args[]) throws InvalidKeyException, InvalidAlgorithmParameterException, ShortBufferException, IllegalBlockSizeException, BadPaddingException {
EncryptDecryptVideo decryptionobject = new EncryptDecryptVideo();
decryptionobject.decrypt();
}
public void decrypt() throws InvalidKeyException, InvalidAlgorithmParameterException, ShortBufferException, IllegalBlockSizeException, BadPaddingException {
cipher.init(Cipher.DECRYPT_MODE, key, ivSpec);
byte[] plainText = new byte[cipher.getOutputSize(ctLength)];
int ptLength = cipher.update(cipherText, 0, ctLength, plainText);
ptLength+= cipher.doFinal(plainText, ptLength);
System.out.println("Plain: "+new String(plainText));
}
}```

Some points that are obvious without deep in further:
First: there is no Cipher instantiation like ("AES/CBC/PKCS5Padding").
Second: Your "SecretKeySpec" will transform the input to a DES-key (and not "AES" as you are asking for in the title).
Third: the "cipher.doFinal" call usually returns a byte array and not any integer value.
Fourth: All of your input data seem to be a hexstring that should be converted to a byte array by something like "hexStringToByteArray" and not by ".getBytes" directly.
Fifth: the webpage you linked to does not use the "password" as direct input to the cipher but performs a password derivation (like PBKDF2) that needs to get replicated in Java code as well.
Sixth: please do not use "DES" anymore as it is broken and UNSECURE.
My recommendation is to use another source for your encryption/decryption than https://www.di-mgt.com.au/properpassword.html.

Related

How to create javax.crypto.SecretKey based on password and salt

I am not a security expert.
I have a requirement to generate javax.crypto.SecretKey based on password and salt.
In the existing code, we already have logic to generate javax.crypto.SecretKey but not based on password and salt`.
Also, in the existing code we already encrypt and decrypt using the javax.crypto.SecretKey.
There is already lot of data in DB which is encrypted using existing encrypt code and I dont think I can change existing encrypt and decrypt logic.
I am getting below the error when I try to decrypt data using the key generated based on password and salt with existing decrypt code.
key.getAlgorithm(): DESede
encryptedData: [B#31dc339b
Exception in thread "main" javax.crypto.BadPaddingException: Given final block not properly padded. Such issues can arise if a bad key is used during decryption.
at com.sun.crypto.provider.CipherCore.unpad(CipherCore.java:975)
at com.sun.crypto.provider.CipherCore.fillOutputBuffer(CipherCore.java:1056)
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:853)
at com.sun.crypto.provider.DESedeCipher.engineDoFinal(DESedeCipher.java:294)
at javax.crypto.Cipher.doFinal(Cipher.java:2168)
at com.arjun.mytest.PMAdminKeyTest.main(PMAdminKeyTest.java:41)
import java.security.KeyStore;
import java.security.Provider;
import java.security.spec.KeySpec;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
public class PMAdminKeyTest {
public static void main(String[] args) throws Exception {
// Requirement is to generate Key based on password and salt
SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
KeySpec keySpec = new PBEKeySpec("password".toCharArray(), "salt".getBytes(), 65536, 192);
SecretKey key = new SecretKeySpec(secretKeyFactory.generateSecret(keySpec).getEncoded(), "DESede");
System.out.println("key.getAlgorithm(): " + key.getAlgorithm());
byte[] data = "12345678".getBytes("UTF8");
// Existing encrypt and decrypt code. There is already lot of data in DB
// encrypted in this manner. I dont think I can change this code.
Cipher cipher = Cipher.getInstance(key.getAlgorithm() + "/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] encryptedData = cipher.doFinal(data);
System.out.println("encryptedData: " + encryptedData.toString());
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] decryptedData = cipher.doFinal(data);
System.out.println("decryptedData: " + decryptedData.toString());
}
}
The only issue I can see is that you pass the unencrypted data to the Cipher in decrypt mode, which won't work. (The cipher obviously cannot decrypt data which is not encrypted without getting odd results.)
So change
byte[] decryptedData = cipher.doFinal(data);
to
byte[] decryptedData = cipher.doFinal(encryptedData);
Then, everything works fine.
Altough I doubt this error exists in your productive code, so if you still have problems on that one, feel free to ask a new question.
You are not decrypting the encrypted data, you are simply trying to decrypt the original data.
Also while printing the data use UTF-8
import java.security.spec.KeySpec;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
public class PMAdminKeyTest {
public static void main(String[] args) throws Exception {
// Requirement is to generate Key based on password and salt
SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
KeySpec keySpec = new PBEKeySpec("password".toCharArray(), "salt".getBytes(), 65536, 192);
SecretKey key = new SecretKeySpec(secretKeyFactory.generateSecret(keySpec).getEncoded(), "DESede");
System.out.println("key.getAlgorithm(): " + key.getAlgorithm());
byte[] data = "12345678".getBytes("UTF8");
// Existing encrypt and decrypt code. There is already lot of data in DB
// encrypted in this manner. I dont think I can change this code.
Cipher cipher = Cipher.getInstance(key.getAlgorithm() + "/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] encryptedData = cipher.doFinal(data);
System.out.println("encryptedData: " + encryptedData.toString());
cipher.init(Cipher.DECRYPT_MODE, key);
// Notice this
byte[] decryptedData = cipher.doFinal(encryptedData);
// while printing the data use UTF-8
System.out.println("decryptedData: " + new String(decryptedData, "UTF-8"));
}
}

How can I decrypt using BouncyCastle, a string using a GCM Tag , IV string and Key string, all of them in HEX?

I amn trying to replicate the AES-Decrypt function from CyberChef, using GCM mode and HEX input.
Screenshot of the operation:
So far, I've written the following code:
package decryption;
import javax.crypto.Cipher;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.Security;
public class Main {
public static final int GCM_TAG_LENGTH = 32;
public static void main(String[] args) throws Exception {
String IV = "9092d522e11120919fce8492";
String input = "90fab0";
String GCMTag = "02883e111ad6f79cd53674b5f833abab";
String key = "8cda92dcb3283da821daa275359642c7a05d60a4badb5769618193a930c1cdec";
SecretKeySpec secretKey = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), 0, key.getBytes(StandardCharsets.UTF_8).length, "AES");
System.out.println(decrypt(input.getBytes(StandardCharsets.UTF_8), secretKey, IV.getBytes(StandardCharsets.UTF_8)));
}
public static String decrypt(byte[] cipherText, SecretKeySpec key, byte[] IV) throws Exception {
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding", "BC");
GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(GCM_TAG_LENGTH * 8, IV);
cipher.init(Cipher.DECRYPT_MODE, key, gcmParameterSpec);
byte[] decryptedText = cipher.doFinal(cipherText);
return new String(decryptedText);
}
}
For the above code I am getting:
Exception in thread "main" org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher$InvalidKeyOrParametersException: Invalid value for MAC size: 256
at org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher.engineInit(Unknown Source)
at java.base/javax.crypto.Cipher.init(Cipher.java:1442)
at java.base/javax.crypto.Cipher.init(Cipher.java:1375)
at decryption.Main.decrypt(Main.java:29)
at decryption.Main.main(Main.java:19)
Fairly certain I am way off, but I did not find any articles/tutorials on how the GCM Tag can be used as input.
The length of the GCM tag used here is not 32, but 16 bytes.
Furthermore, the BC provider expects ciphertext and tag in concatenated form (ciphertext|tag).
And you have to hex decode key, IV, ciphertext and tag. Since you are running BouncyCastle, you can use org.bouncycastle.util.encoders.Hex.decode(...).
Overall:
import org.bouncycastle.util.encoders.Hex;
...
public static final int GCM_TAG_LENGTH = 16;
...
SecretKeySpec secretKey = new SecretKeySpec(Hex.decode(key), "AES");
System.out.println(decrypt(Hex.decode(input + GCMTag), secretKey, Hex.decode(IV))); // 985
It is not clear from your code whether you are using a static IV/nonce. If so, you should be aware that using a static IV/nonce for GCM is a serious problem, s. e.g. here.
Instead, for each encryption, a random (non-secret) IV/nonce is generated, concatenated with the ciphertext (and tag), and sent together to the decrypting side, which can separate the IV/nonce based on the known IV/nonce size (12 bytes for GCM).
AES/GCM is also supported by the SunJCE provider (at least as of Java 8, s. e.g. here), so you may not need BouncyCastle. For hex decoding you can then use a solution from this post. Starting with Java 17 there is a built-in support.

Java Decryption creating additional symbols

I'm writing a encryption and decryption code as follows
import java.io.*;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.crypto.BadPaddingException;
import java.nio.file.Files;
import java.util.Scanner;
public class EncryptFile
{
public static void main(String args[]) throws IOException, NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, InvalidAlgorithmParameterException {
//Encrypt Mode
FileOutputStream outputStream = new FileOutputStream(new File("D:\\encryptedNewStringFile.txt"));
Key secretKey = new SecretKeySpec("encKey".getBytes(), "Blowfish");
Cipher cipher = Cipher.getInstance("Blowfish");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
byte[] getFileBytes = "writing a file using encryption ".getBytes();
byte[] outputBytes = cipher.doFinal(getFileBytes);
outputStream.write(outputBytes);
getFileBytes = "\n".getBytes();
outputBytes = cipher.doFinal(getFileBytes);
outputStream.write(outputBytes);
getFileBytes = "This is New Line 2 \nThis is NewLine 3".getBytes();
outputBytes = cipher.doFinal(getFileBytes);
outputStream.write(outputBytes);
outputStream.close();
//Decrypt Mode
File curFile = new File("D:\\encryptedNewStringFile.txt");
secretKey = new SecretKeySpec("encKey".getBytes(), "Blowfish");
cipher = Cipher.getInstance("Blowfish/ECB/NoPadding");
cipher.init(Cipher.DECRYPT_MODE, secretKey);
getFileBytes = Files.readAllBytes(curFile.toPath());
outputBytes = cipher.doFinal(getFileBytes);
InputStream bai = new ByteArrayInputStream(outputBytes);
BufferedReader bfReader = new BufferedReader(new InputStreamReader(bai));
Scanner scan = new Scanner(bfReader);
while(scan.hasNextLine())
{
System.out.println(scan.nextLine());
}
}
}
here i have a problem in output which is the printed output has some extra symbols (i.e question marks and box symbols)in it.
The output i received is
Any suggestions will be really helpful thanks in advance
Cipher cipher = Cipher.getInstance("Blowfish");
is equivalent to
Cipher cipher = Cipher.getInstance("Blowfish/ECB/PKCS5Padding");
which means that each time you call cipher.doFinal additional padding is produced.
In order to write a file without intermittent padding, you should be using
outputBytes = cipher.update(getFileBytes);
and use cipher.doFinal only when writing the last time to the file. Then you will be able to use PKCS5Padding instead of NoPadding during decryption in order to remove the valid padding at the end automatically.
Security considerations:
ECB mode is bad and should not be used. There are only very few use cases where this makes sense to use. At least use CBC mode with a randomly generated IV. The IV doesn't need to be secret but only unpredictable. We usually prepend it to the ciphertext and slice it off before decryption. Since it has always a predefined length, this is easy to do.
Use an authenticated mode of operation like GCM or use a message authentication code like HMAC-SHA256 in order to detect and react to (malicious) manipulation of the ciphertext.
Blowfish should not be used today. Although it has no direct vulnerability, its small block size may open you up to different protocol based vulnerabilities. It would be advisable to use a block cipher with a block size of 128-bit. AES comes to mind.
Combining the answers from #Artjom B. and #The 5th column mouse you get a file encryption program that will encrypt a file with Blowfish in CBC mode. The encryption and decryption is done in chunks so large files (up to some GB) could get encrypted and decrypted without "out of memory errors".
The key is generated randomly, and you should keep in mind - without knowledge of the key no decryption of the file is possible.
output:
file encryption with Blowfish CBC mode
used key (Base64): jsErS04so1NCC7Jmds6Grr+0tPkNoaj0hx/izLaW5H8=
result encryption: true
result decryption: true
Security warning: the code has no exception handling, no correct file handling (e.g. overwriting without notice) and is for educational purpose only:
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Base64;
public class BlowfishCbcFileEncryption {
public static void main(String[] args) throws NoSuchPaddingException, NoSuchAlgorithmException, IOException,
InvalidKeyException, InvalidAlgorithmParameterException {
System.out.println("file encryption with Blowfish CBC mode");
String uncryptedFilename = "uncrypted.txt";
String encryptedFilename = "encrypted.enc";
String decryptedFilename = "decrypted.txt";
// random blowfish 256 key
byte[] key = new byte[32];
SecureRandom secureRandom = new SecureRandom();
secureRandom.nextBytes(key);
System.out.println("used key (Base64): " + base64Encoding(key));
// random iv
byte[] iv = new byte[8]; // blowfish iv is 8 bytes long
secureRandom.nextBytes(iv);
boolean result;
result = encryptCbcFileBufferedCipherOutputStream(uncryptedFilename, encryptedFilename, key, iv);
System.out.println("result encryption: " + result);
result = decryptCbcFileBufferedCipherInputStream(encryptedFilename, decryptedFilename, key);
System.out.println("result decryption: " + result);
}
public static boolean encryptCbcFileBufferedCipherOutputStream(String inputFilename, String outputFilename, byte[] key, byte[] iv)
throws IOException, NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, InvalidAlgorithmParameterException {
Cipher cipher = Cipher.getInstance("Blowfish/CBC/PKCS5Padding");
try (FileInputStream in = new FileInputStream(inputFilename);
FileOutputStream out = new FileOutputStream(outputFilename);
CipherOutputStream encryptedOutputStream = new CipherOutputStream(out, cipher);) {
out.write(iv);
SecretKeySpec secretKeySpec = new SecretKeySpec(key, "Blowfish");
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
byte[] buffer = new byte[8096];
int nread;
while ((nread = in.read(buffer)) > 0) {
encryptedOutputStream.write(buffer, 0, nread);
}
encryptedOutputStream.flush();
}
if (new File(outputFilename).exists()) {
return true;
} else {
return false;
}
}
public static boolean decryptCbcFileBufferedCipherInputStream(String inputFilename, String outputFilename, byte[] key) throws
IOException, NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, InvalidAlgorithmParameterException {
byte[] iv = new byte[8]; // blowfish iv is 8 bytes long
Cipher cipher = Cipher.getInstance("Blowfish/CBC/PKCS5Padding");
try (FileInputStream in = new FileInputStream(inputFilename); // i don't care about the path as all is local
CipherInputStream cipherInputStream = new CipherInputStream(in, cipher);
FileOutputStream out = new FileOutputStream(outputFilename)) // i don't care about the path as all is local
{
byte[] buffer = new byte[8192];
in.read(iv);
SecretKeySpec secretKeySpec = new SecretKeySpec(key, "Blowfish");
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);
int nread;
while ((nread = cipherInputStream.read(buffer)) > 0) {
out.write(buffer, 0, nread);
}
out.flush();
}
if (new File(outputFilename).exists()) {
return true;
} else {
return false;
}
}
private static String base64Encoding(byte[] input) {
return Base64.getEncoder().encodeToString(input);
}
}
Each time you convert string into the byte array, you use default file encoding from your VM properties which is not UTF-8.
So, to fix this issue you have two options: to define the default encoding in java system properties:
System.setProperty("file.encoding", StandardCharsets.UTF_8.name());
or add the charset encoding by each converting of strings into bytes:
"writing a file using encryption ".getBytes(StandardCharsets.UTF_8);

Getting BadPaddingException due to byte[] too long to decipher

The following code is tested for short strings, in that case it decrypts the string nicely.
byte[] ciphertext = Base64.decode(myverylongstring,Base64.DEFAULT);
SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(Charset.forName("UTF-8")), "AES");
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv.getBytes(Charset.forName("UTF-8")));
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);
byte[] decryptedtextByte = cipher.doFinal(ciphertext);
String decryptedtext = new String(decryptedtextByte); //Successfully decrypts the string
System.out.println("decryptedtext: " + decryptedtext);
[...]
} catch (BadPaddingException e) {
e.printStackTrace();
}
But if the string is too large I get fired the exception I mention.
What could I do so it decrypts the string correctly maintaining the same large string to decrypt?
Edit, well technically what's decrypted is a byte[], changing title and adding code to not cause possible confussion.
As you did not show the encryption and we do not know what kind of key you are using there are many possible reasons for failure. As well you did not show the imports so I could just argue what Base64-encoder is in use.
Below you find a simple program that does the en- and decryption a byte array that is much larger than the size you are using.
Edit:
Security warning: The code below uses a stringified key and static IV, uses single part encryption / decryption for a huge byte array, uses the older CBC mode.
Please do not copy below code or use it in production - it is for educational purposes only.
The code does not have any proper exception handling !
It generates a (random filled) byte array with the plaintext and gets the key & iv from strings that are converted to byte[] using the Standard.Charset "UFT-8".
That it's doing the encryption, convert the ciphertext to a Base64 encoded string followed by the decoding to a new ciphertext byte[] and decryption with the same key and iv.
In the end there is simple comparison of the plaintext and decryptedtext byte arrays.
My advice it is to check for any forgotten charset-setting and correct (means identical) usage of key and iv.
code:
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.Base64;
public class Main {
public static void main(String[] args) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
System.out.println("https://stackoverflow.com/questions/63143007/getting-badpaddingexception-due-to-byte-too-long-to-decipher");
byte[] plaintext = new byte[100000];
SecureRandom secureRandom = new SecureRandom();
secureRandom.nextBytes(plaintext); // generate a random filled byte array
byte[] key = "12345678901234567890123456789012".getBytes(StandardCharsets.UTF_8);
byte[] iv = "1234567890123456".getBytes(StandardCharsets.UTF_8);
SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
byte[] ciphertextEnc = cipher.doFinal(plaintext);
String ciphertextBase64 = Base64.getEncoder().encodeToString(ciphertextEnc);
byte[] ciphertextDec = Base64.getDecoder().decode(ciphertextBase64);
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);
byte[] decryptedtextByte = cipher.doFinal(ciphertextDec);
System.out.println("decryptedtextByte equals plaintext: " + Arrays.equals(plaintext, decryptedtextByte));
}
}

Java code for mcrypt_encrypt PHP function with MCRYPT_RIJNDAEL_256 cipher

I have a PHP code for encrypting a string(token). I need to generate this token and call the legacy backend API. Backend API decrypts this and validates it before allowing access to the API. We are building the client application in Java. So this code needs to be implemented in Java.
$stringToEncode="****String which needs to be encrypted. This string length is multiple of 32********************";
$key="32ByteKey-asdcxzasdsadasdasdasda";
echo base64_encode(
mcrypt_encrypt(
MCRYPT_RIJNDAEL_256,
$key,
$stringToEncode,
MCRYPT_MODE_CBC,
$key
)
);
Here IV is same as key.
I tried with below Java code using "org.bouncycastle.crypto"
private static void encrypt(String key, String data) throws InvalidCipherTextException {
byte[] givenKey = key.getBytes(Charset.forName("ASCII"));
final int keysize = 256;
byte[] keyData = new byte[keysize / Byte.SIZE];
System.arraycopy(givenKey, 0, keyData, 0, Math.min(givenKey.length, keyData.length));
KeyParameter keyParameter = new KeyParameter(keyData);
BlockCipher rijndael = new RijndaelEngine(256);
ZeroBytePadding c = new ZeroBytePadding();
PaddedBufferedBlockCipher pbbc = new PaddedBufferedBlockCipher(rijndael, c);
CipherParameters ivAndKey = new ParametersWithIV(keyParameter, key.getBytes(Charset.forName("ASCII")));
pbbc.init(true, ivAndKey);
byte[] plaintext = data.getBytes(Charset.forName("UTF8"));
byte[] ciphertext = new byte[pbbc.getOutputSize(plaintext.length)];
int offset = 0;
offset += pbbc.processBytes(plaintext, 0, plaintext.length, ciphertext, offset);
offset += pbbc.doFinal(ciphertext, offset);
System.out.println("Encrypted: " + Base64.getEncoder().encodeToString(ciphertext));
}
But getting below exception -
Exception in thread "main" java.lang.IllegalArgumentException: invalid parameter passed to Rijndael init - org.bouncycastle.crypto.params.ParametersWithIV
I even tried with "javax.crypto" as shown below -
import java.nio.charset.Charset;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class aes{
public static void main(String[] args) throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
String key = "32ByteKey-asdcxzasdsadasdasdasda";
String data = "****String which needs to be encrypted. This string length is multiple of 32********************";
encrypt(key, data);
}
public static void encrypt(String key1, String data) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException{
byte[] key = key1.getBytes(Charset.forName("ASCII"));
byte[] decrypted = data.getBytes(Charset.forName("UTF8"));
IvParameterSpec ivSpec = new IvParameterSpec(key);
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "AES"), ivSpec);
byte[] encrypted = Base64.getEncoder().encodeToString(cipher.doFinal(decrypted)).getBytes();
System.out.println(encrypted);
}
}
But getting beow exception -
Exception in thread "main" java.security.InvalidAlgorithmParameterException: Wrong IV length: must be 16 bytes long
Can someone please suggest on how can I implement this?
The PHP code uses Rijndael with a blocksize of 256 bits. This isn't supported by the SunJCE Provider, only AES (which is a subset of Rindael with a blocksize of 128 bits). For this reason the second Java code snippet doesn't work either. Therefore a third party provider like BouncyCastle (BC) must be applied.
There are two bugs in the Java / BC code:
Contrary to the PHP code no CBC is applied. This must be changed using CBCBlockCipher.
The Zero padding variant of BC is different from the mcrypt variant. The BC variant always pads, i. e. even if the length of the plaintext already corresponds to an integer multiple of the blocksize (32 bytes here), the mcrypt variant doesn't (i. e. the latter only pads if the length of the plaintext doesn't correspond to an integer multiple of the blocksize).Since according to the description the plaintext already corresponds to an integer multiple of the blocksize, the mcrypt variant doesn't pad. In the Java / BC code, this can be achieved if simply no padding is used at all.But be careful: If the length of the plaintext doesn't correspond to an integer multiple of the blocksize, padding must of course be used (with regard to compatibility with the Zero padding variant used in the PHP code).
Both problems can be solved by replacing the lines
ZeroBytePadding c = new ZeroBytePadding();
PaddedBufferedBlockCipher pbbc = new PaddedBufferedBlockCipher(rijndael, c);
by
BufferedBlockCipher pbbc = new BufferedBlockCipher(new CBCBlockCipher(rijndael));
Then the ciphertexts produced by both codes are identical.
A few additional notes:
Using the key as IV is generally not useful. Since, for security reasons, an IV may only be used once for a given key, a new key must be used here for each encryption. See also here.Usually in practice a fresh, random IV is generated for each encryption.
It's generally more secure to use a standard, i.e. AES and not Rijndael with a block size of 256 bits, which isn't a standard.
Zero-padding isn't reliable (besides, there are several variants which, as you have experienced, can cause problems). More reliable is PKCS7 padding.

Categories

Resources