I'm trying to read a base64 encoded and AES 128-bit encrypted string from PHP, but I'm getting IllegalBlockSizeException.
PHP encrypt:
encrypt("My f awesome test !");
function encrypt($string){
$td = mcrypt_module_open('rijndael-128', '', 'cbc', "1cc251f602cf49f2");
mcrypt_generic_init($td, "f931c96c4a4e7e47", "1cc251f602cf49f2");
$enc = mcrypt_generic($td, $string);
mcrypt_generic_deinit($td);
mcrypt_module_close($td);
return base64_encode($enc);
}
And the returned value is:
McBeY73GQ5fawxIunVKpqUupipeRlt9ntyMRzjbPfTI=
Now I want to read it in Java:
static public String decrypt(String data) throws Exception {
data = new String( Base64.decode(data, Base64.NO_WRAP) );
byte[] keyByte = "f931c96c4a4e7e47".getBytes("UTF-8");
byte[] ivByte = "1cc251f602cf49f2".getBytes("UTF-8");
Key key = new SecretKeySpec(keyByte, "AES");
IvParameterSpec iv = new IvParameterSpec(ivByte);
Cipher c = Cipher.getInstance("AES/CBC/NoPadding");
c.init(Cipher.DECRYPT_MODE, key, iv);
byte[] bval = c.doFinal( data.getBytes("UTF-8") );
return new String( bval );
}
And I'm getting an Exception:
javax.crypto.IllegalBlockSizeException: data not block size aligned
This might be caused by padding?
EDIT
Your error was caused by the conversion of the plaintext to and from a string. It's not necessary anyway - just use byte arrays:
byte[] data = Base64
.decodeBase64("McBeY73GQ5fawxIunVKpqUupipeRlt9ntyMRzjbPfTI=");
byte[] keyByte = "f931c96c4a4e7e47".getBytes("UTF-8");
byte[] ivByte = "1cc251f602cf49f2".getBytes("UTF-8");
Key key = new SecretKeySpec(keyByte, "AES");
IvParameterSpec iv = new IvParameterSpec(ivByte);
Cipher c = Cipher.getInstance("AES/CBC/NoPadding");
c.init(Cipher.DECRYPT_MODE, key, iv);
byte[] bval = c.doFinal(data);
System.out.println(new String(bval)); // Prints My f awesome test !
I recommend you use padding in your encryption, otherwise you cannot cope with arbitrarily-sized input.
the IllegalBlockSizeException thrown on call to doFinal() if: "cipher is a block cipher, no padding has been requested (only in encryption mode), and the total input length of the data processed by this cipher is not a multiple of block size; or if this encryption algorithm is unable to process the input data provided." -http://docs.oracle.com/javase/6/docs/api/javax/crypto/Cipher.html#doFinal%28%29. So its either bad input data or block size.
This is a working version of encryption/decryption between
https://github.com/chaudhuri-ab/CrossPlatformCiphers
Hope this helps someone as it took me a while to work through the little details between the platforms.
Related
I have a java code working perfectly
public static String encrypt(String message, String sercretKey)
{
String base64EncryptedString = "";
MessageDigest md = MessageDigest.getInstance("SHA-256");
byte[] digestOfPassword = md.digest(sercretKey.getBytes("utf-8"));
byte[] keyBytes = Arrays.copyOf(digestOfPassword, 24);
byte[] iv = Arrays.copyOf(digestOfPassword, 16);
SecretKey key = new SecretKeySpec(keyBytes, "AES");
javax.crypto.Cipher cipher = javax.crypto.Cipher.getInstance("AES/CBC/PKCS5Padding");
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
cipher.init(javax.crypto.Cipher.ENCRYPT_MODE, key, ivParameterSpec);
byte[] plainTextBytes = message.getBytes("utf-8");
byte[] buf = cipher.doFinal(plainTextBytes);
byte[] base64Bytes = Base64.getEncoder().encode(buf);
base64EncryptedString = new String(base64Bytes);
return base64EncryptedString;
}
I have tried using below code to recreate this above code in PHP
function encryptTest($sSecretKey,$sValue)
{
$key = hash('sha256', $sSecretKey,false);
$key = utf8_encode($key);
$key = substr($key, 0, 24);
$iv = substr($key, 0, 16);
$data = $sValue;
$outEnc = openssl_encrypt($data, "AES-256-CBC", $key, OPENSSL_RAW_DATA, $iv);
return base64_encode($outEnc);
}
But showing different results. What I have missed.
(Same types of questions are available in StackOverflow, but pointing my issues)
There are the following issues:
In the PHP code, the key is currently returned hex encoded. Instead, it must be returned as bytes string. To do this, the third parameter in hash() must be switched from false to true.
In the Java code a 192 bits key is used, i.e. AES-192. Accordingly, in the PHP code "AES-192-CBC" must be applied (and not "AES-256-CBC").
The utf8_encode() call in the PHP code is to be removed, as this corrupts the key.
With these changes, both codes provide the same ciphertext.
Security:
Using SHA256 as key derivation is insecure. Instead apply a dedicated algorithm like Argon2 or PBKDF2. Also, using the key (or a part of it) as IV is insecure as it results in the reuse of key/IV pairs. Instead, a randomly generated IV should be applied for each encryption.
javax.crypto.BadPaddingException: Given final block not properly padded - This is the error which I've got. I do not know why. My code seems to be alright. The key is the same during encrypting and decrypting. What I can say is that none of the already answered questions contain solution for my problem. Here is my code:
public String decrypt(String text) throws Exception {
String key = "SiadajerSiadajer"; // 128 bit key
// Create key and cipher
Key aesKey = new SecretKeySpec(key.getBytes(), "AES");
Cipher cipher = Cipher.getInstance("AES");
// encrypt the text
byte[]encrypted = text.getBytes();
cipher.init(Cipher.DECRYPT_MODE, aesKey);
String decrypted = new String(cipher.doFinal(encrypted)); //Here is the error
return decrypted;
}
And the encryption mechanism in php
function encrypt($data, $size)
{
$length = $size - strlen($data) % $size;
return $data . str_repeat(chr($length), $length);
}
function decrypt($data)
{
return substr($data, 0, -ord($data[strlen($data) - 1]));
}
$key = "SiadajerSiadajer";
$iv_size = 16; // 128 bits
$iv = openssl_random_pseudo_bytes($iv_size, $strong);
$name = openssl_encrypt(encrypt($name, 16), 'AES-256-CBC', $key, 0, $iv);
EDIT
Now in the php part my code is:
$key = "SiadajerSiadajer";
$iv_size = 16; // 128 bits
$iv = openssl_random_pseudo_bytes($iv_size, $strong);
$EIV = base64_encode($iv);
$name = openssl_encrypt(encrypt($name, 16), 'AES-256-CBC', $key, 0, $EIV);
And it gives me warning:Warning: openssl_encrypt(): IV passed is 24 bytes long which is longer than the 16 expected by selected cipher, truncating in C:\xampp2\htdocs\standardfinalinserting.php on line 68
And in Java part my decryption method is exactly like in the answer for my question but after running it gives me an error: java.security.InvalidKeyException: Illegal key size on the line:
cipher.init(Cipher.DECRYPT_MODE, aesKey, ivParameterSpec);
EDIT 2:
So this is my whole Main class. It contains your whole Java code example
public class Main {
private byte[] padKey(byte[] key) {
byte[] paddedKey = new byte[32];
System.arraycopy(key, 0, paddedKey, 0, key.length);
return paddedKey;
}
private byte[] unpad(byte[] data) {
byte[] unpaddedData = new byte[data.length - data[data.length - 1]];
System.arraycopy(data, 0, unpaddedData, 0, unpaddedData.length);
return unpaddedData;
}
public String decrypt(String encodedJoinedData) throws Exception {
// Base64-decode the joined data
byte[] joinedData = Base64.decode(encodedJoinedData);
// Get IV and encrypted data
byte[] iv = new byte[16];
System.arraycopy(joinedData, 0, iv, 0, iv.length);
byte[] encryptedData = new byte[joinedData.length - iv.length];
System.arraycopy(joinedData, iv.length, encryptedData, 0, encryptedData.length);
// Pad key
byte[] key = padKey("SiadajerSiadajer".getBytes());
Key aesKey = new SecretKeySpec(key, "AES");
// Specify CBC-mode
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
cipher.init(Cipher.DECRYPT_MODE, aesKey, ivParameterSpec); //HERE IS THE ERROR
// Decrypt data
byte[] decryptedData = cipher.doFinal(encryptedData);
// Remove custom padding
byte[] unpaddedData = unpad(decryptedData);
return new String(unpaddedData);
}
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
String encodedJoinedData = "RB6Au33KGOF1/z3BQKqievUmh81Y8q4uw7s4vEs9xurQmNZAKwmhRQtS9wGYKQj8cJaPpK2xaDzx3RppQ8PsM/rQ9/p0Lme+x75dozBEbmFn6Q9eCXOCiCivVsKzZ8Vz";
String decryptedData = new Main().decrypt(encodedJoinedData);
System.out.println(decryptedData + " - " + decryptedData.length());
}
}
Running the code results in error:
Exception in thread "main" java.security.InvalidKeyException: Illegal key size
at javax.crypto.Cipher.checkCryptoPerm(Cipher.java:1039)
at javax.crypto.Cipher.implInit(Cipher.java:805)
at javax.crypto.Cipher.chooseProvider(Cipher.java:864)
at javax.crypto.Cipher.init(Cipher.java:1396)
at javax.crypto.Cipher.init(Cipher.java:1327)
at com.dd.escuel.Main.decrypt(Main.java:43)
at com.dd.escuel.Main.main(Main.java:57)
There are a few issues with the Java-code:
In the PHP-code AES-256 is used, thus, the key must have a length of 32 Byte. Shorter keys are automatically right padded with zeros. This happens in your PHP-code since your key SiadajerSiadajer has a length of only 16 Byte. The key-padding must also be done in the Java-code. For this, e.g. the following Java-method can be used:
private byte[] padKey(byte[] key) {
byte[] paddedKey = new byte[32];
System.arraycopy(key, 0, paddedKey, 0, key.length);
return paddedKey;
}
With Cipher.getInstance("AES") the ECB-mode and PKCS5Padding are chosen by default. The CBC-mode must be explicitly specified in the Java-code with
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
In the PHP-method openssl_encrypt the fourth parameter $options is set to 0 which means that the returned data are Base64-encoded. Thus, in the Java-code the data have to be Base64-decoded before decryption:
byte[]encryptedData = Base64.decode(text);
Since the CBC-mode is used the IV of the encryption must be considered. A possible approach is to Base64-encode the IV in the PHP-code (subsequent to the encryption) with
$encodedIV = base64_encode($iv);
and pass that value to the Java-method decrypt as second parameter. Here, the IV must be decoded and used for the decryption:
byte[] iv = Base64.decode(ivEncoded);
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
...
cipher.init(Cipher.DECRYPT_MODE, aesKey, ivParameterSpec);
In the PHP-method openssl_encrypt the fourth parameter $options is set to 0 which means that the default padding (PKCS7) is used. Moreover, in the PHP-method encrypt a kind of custom padding is implemented (btw: the name of the method is unsuitable since it doesn't encrypt) and thus, it's padded twice. Therefore, after the decryption the custom padding (which may consist of white spaces) must be removed in the Java-code:
byte[] unpaddedData = unpad(decryptedData);
with
private byte[] unpad(byte[] data) {
byte[] unpaddedData = new byte[data.length - data[data.length - 1]];
System.arraycopy(data, 0, unpaddedData, 0, unpaddedData.length);
return unpaddedData;
}
Altogether:
public String decrypt(String text, String ivEncoded) throws Exception {
// Pad key
byte[] key = padKey("SiadajerSiadajer".getBytes());
Key aesKey = new SecretKeySpec(key, "AES");
// Base64 decode data
byte[]encryptedData = Base64.decode(text);
// Base64 decode iv
byte[] iv = Base64.decode(ivEncoded);
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
// Specify CBC-mode
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, aesKey, ivParameterSpec);
// Decrypt
byte[] decryptedData = cipher.doFinal(encryptedData);
// Remove custom padding
byte[] unpaddedData = unpad(decryptedData);
return new String(unpaddedData);
}
Test:
PHP-code input: Plain text ($data), key ($key):
$data = 'This is a plain text which needs to be encrypted...';
$key = "SiadajerSiadajer";
PHP-code output: Base64 encoded IV ($encodedIV), encrypted data ($name):
$encodedIV = 'Dg+Zs3mqIJeDOOEPMT5F4Q==';
$name = '8dXjeQhx2WSswQOQXLcyMKNpa5s413yI2Ku8WiIB/xtA2pEjrKcl5kWtrOh9k4A12Jl0N/z6tH67Wybhp/OwTi1NtiJOZxl3w6YQufE29oU=';
If that output is used as input for the Java-method decrypt the decrypted data equals the plain text.
Concerning the PHP-code I would suggest to remove either the custom or the default (PKCS7) padding (if you have the choice). The latter can be achieved by using the flag OPENSSL_ZERO_PADDING as fourth parameter in the openssl_encrypt method (note: that flag doesn't mean "pad with zero-values", but "no padding"). If the custom padding is kept, you should at least rename the PHP-methods encrypt and decrypt to pad and unpad (or something similar), respectively.
As stated already in the comments, the GCM-mode may be a better choice than the-CBC mode. However, it would be useful to get informed about the basics before coding, e.g. difference between CBC- and GCM-mode,
explanation of the GCM-mode here and here and the pitfalls coming along with the GCM-mode (GCM is secure, but only if you follow certain guidelines, e.g. a uniqe IV/nonce for each message encrypted with the same key).
You can use the PHP-method openssl_get_cipher_methods to find out the permissible AES-modes supported in PHP. This is explained in more detail here. For AES-256 and the AES-mode GCM you have to specify aes-256-gcm (with lowercase(!) letters). Presumably, that's why you get the "Unknown cipher algorithm"-error.
EDIT:
You can use for the encryption the following PHP-code (which is a slightly modified version of your PHP-code from the question):
<?php
function pad($data, $size) {
$length = $size - strlen($data) % $size;
return $data . str_repeat(chr($length), $length);
}
function unpad($data) {
return substr($data, 0, -ord($data[strlen($data) - 1]));
}
$data = 'This is a plain text which needs to be encrypted...';
$key = "SiadajerSiadajer";
$iv_size = 16;
$iv = openssl_random_pseudo_bytes($iv_size, $strong);
$encryptedData = openssl_encrypt(pad($data, 16), 'AES-256-CBC', $key, 0, $iv);
print base64_encode($iv)."\n".$encryptedData."\n";
EDIT 2:
The IV and the encrypted data can be joined before or after the Base64-encoding. The first is more efficient and thus, I've implemented this variant. However, some changes are needed in the PHP- and Java-code and therefore, I post all methods which have changed.
The PHP-code becomes:
<?php
function pad($data, $size) {
$length = $size - strlen($data) % $size;
return $data . str_repeat(chr($length), $length);
}
function unpad($data) {
return substr($data, 0, -ord($data[strlen($data) - 1]));
}
$data = 'This is a plain text which needs to be encrypted...';
$key = "SiadajerSiadajer";
$iv_size = 16;
$iv = openssl_random_pseudo_bytes($iv_size, $strong);
$encryptedData = openssl_encrypt(pad($data, 16), 'AES-256-CBC', $key, OPENSSL_RAW_DATA, $iv);
$joinedData = $iv.$encryptedData;
$encodedJoinedData = base64_encode($joinedData);
print $encodedJoinedData."\n";
And the Jave-method decrypt becomes:
public String decrypt(String encodedJoinedData) throws Exception {
// Base64-decode the joined data
byte[] joinedData = Base64.decode(encodedJoinedData);
// Get IV and encrypted data
byte[] iv = new byte[16];
System.arraycopy(joinedData, 0, iv, 0, iv.length);
byte[] encryptedData = new byte[joinedData.length - iv.length];
System.arraycopy(joinedData, iv.length, encryptedData, 0, encryptedData.length);
// Pad key
byte[] key = padKey("SiadajerSiadajer".getBytes());
Key aesKey = new SecretKeySpec(key, "AES");
// Specify CBC-mode
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
cipher.init(Cipher.DECRYPT_MODE, aesKey, ivParameterSpec);
// Decrypt data
byte[] decryptedData = cipher.doFinal(encryptedData);
// Remove custom padding
byte[] unpaddedData = unpad(decryptedData);
return new String(unpaddedData);
}
and an example for the Java-method main is:
public static void main(String[] args) throws Exception {
String encodedJoinedData = "RB6Au33KGOF1/z3BQKqievUmh81Y8q4uw7s4vEs9xurQmNZAKwmhRQtS9wGYKQj8cJaPpK2xaDzx3RppQ8PsM/rQ9/p0Lme+x75dozBEbmFn6Q9eCXOCiCivVsKzZ8Vz";
String decryptedData = new Main().decrypt(encodedJoinedData);
System.out.println(decryptedData + " - " + decryptedData.length());
}
The usage is as follows:
Encrypt your plain text with the PHP-code. In the example above (PHP-code) the plain text is
$data = 'This is a plain text which needs to be encrypted...';
Then, pass the string contained in $encodedJoinedData to the Java-method decrypt. In the example above (main-method) the string is
String encodedJoinedData = "RB6Au33KGOF1/z3BQKqievUmh81Y8q4uw7s4vEs9xurQmNZAKwmhRQtS9wGYKQj8cJaPpK2xaDzx3RppQ8PsM/rQ9/p0Lme+x75dozBEbmFn6Q9eCXOCiCivVsKzZ8Vz";
The Java-method decrypt will provide the initial plain text.
A final note: If you decide to remove the (redundant) custom padding replace in the PHP-code the line
$encryptedData = openssl_encrypt(pad($data, 16), 'AES-256-CBC', $key, OPENSSL_RAW_DATA, $iv);
with
$encryptedData = openssl_encrypt($data, 'AES-256-CBC', $key, OPENSSL_RAW_DATA, $iv);
and remove the pad- and the unpad-method.
In the Java-code replace the lines
// Remove custom padding
byte[] unpaddedData = unpad(decryptedData);
return new String(unpaddedData);
with
return new String(decryptedData);
and remove the unpad-method.
Edit 3:
The InvalidKeyException (Illegal key size) mentioned in the Edit2-section of the question is already discussed e.g. here InvalidKeyException Illegal key size and here Java Security: Illegal key size or default parameters?.
I'm trying to make an encryption-decryption app. I've got two classes - one with functions to generate the key, encrypt and decrypt, second one for JavaFX GUI. In the GUI class I've got 4 textareas: 1st to write text to encrypt, 2nd for encrypted text, 3rd for the key (String encodedKey = Base64.getEncoder().encodeToString(klucz.getEncoded());) and 4th for decrypted text.
The problem is, I am not able to decrypt the text. I'm trying to recreate the SecretKey like this:
String encodedKey = textAreaKey.getText();
byte[] decodedKey = Base64.getDecoder().decode(encodedKey);
SecretKey klucz = new SecretKeySpec(decodedKey, "DESede");
When I encrypt the key looks like this: com.sun.crypto.provider.DESedeKey#4f964d80 and when I try to recreate it: javax.crypto.spec.SecretKeySpec#4f964d80 and I'm getting javax.crypto.IllegalBlockSizeException: Input length must be multiple of 8 when decrypting with padded cipher
Here is my 1st class:
public class Encryption {
public static SecretKey generateKey() throws NoSuchAlgorithmException {
Security.addProvider(new com.sun.crypto.provider.SunJCE());
KeyGenerator keygen = KeyGenerator.getInstance("DESede");
keygen.init(168);
SecretKey klucz = keygen.generateKey();
return klucz;
}
static byte[] encrypt(byte[] plainTextByte, SecretKey klucz)
throws Exception {
Cipher cipher = Cipher.getInstance("DESede/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, klucz);
byte[] encryptedBytes = cipher.doFinal(plainTextByte);
return encryptedBytes;
}
static byte[] decrypt(byte[] encryptedBytes, SecretKey klucz)
throws Exception {
Cipher cipher = Cipher.getInstance("DESede/ECB/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, klucz);
byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
return decryptedBytes;
}
}
edit
btnEncrypt.setOnAction((ActionEvent event) -> {
try {
String plainText = textAreaToEncrypt.getText();
SecretKey klucz = Encryption.generateKey();
byte[] plainTextByte = plainText.getBytes();
byte[] encryptedBytes = Encryption.encrypt(plainTextByte, klucz);
String encryptedText = Base64.getEncoder().encodeToString(encryptedBytes);
textAreaEncryptedText.setText(encryptedText);
byte[] byteKey = klucz.getEncoded();
String stringKey = Base64.getEncoder().encodeToString(byteKey);
textAreaKey.setTextstringKey
} catch (Exception ex) {
ex.printStackTrace();
}
});
btnDecrypt.setOnAction((ActionEvent event) -> {
try {
String stringKey = textAreaKey.getText();
byte[] decodedKey = Base64.getDecoder().decode(encodedKey);
SecretKey klucz2 = new SecretKeySpec(decodedKey, "DESede");
String encryptedText = textAreaEncryptedText.getText();
byte[] encryptedBytes = Base64.getDecoder().decode(encryptedText.getBytes());
byte[] decryptedBytes = Encryption.decrypt(encryptedBytes, klucz2;
String decryptedText = Base64.getEncoder().encodeToString(decryptedBytes);
textAreaDecryptedText.setText(decryptedText);
} catch (Exception ex) {
ex.printStackTrace();
}
});
One of your problems is here:
String encryptedText = new String(encryptedBytes, "UTF8");
Generally, many byte sequences in cipher text are not valid UTF-8–encoded characters. When you try to create a String, this malformed sequences will be replaced with the "replacement character", and then information from the the cipher text is irretrievably lost. When you convert the String back to bytes and try to decrypt it, the corrupt cipher text raises an error.
If you need to represent the cipher text as a character string, use base-64 encoding, just as you do for the key.
The other principal problem is that you are aren't specifying the full transformation. You should specify the "mode" and "padding" of the cipher explicitly, like "DESede/ECB/PKCS5Padding".
The correct mode will depend on your assignment. ECB is generally not secure, but more secure modes add a bit of complexity that may be outside the scope of your assignment. Study your instructions and clarify the requirements with your teacher if necessary.
There are two main issues:
You should not use user entered password as a key (there are difference between them). The key must have specific size depending on the cipher (16 or 24 bytes for 3des)
Direct 3DES (DESede) is a block cipher encrypting 8 bytes at once. To encrypt multiple blocks, there are some methods defined how to do that properly. It is calls Block cipher mode.
For proper encryption you need to take care of a few more things
Creating a key from the password
Let's assume you want to use DESede (3des). The key must have fixed size - 16 or 24 bytes. To properly generate a key from password you should use PBKDF. Some people are sensitive to "must use", however neglecting this step really compromises the encryption security mainly using user-entered passwords.
For 3DES you can use :
int keySize = 16*8;
int iterations = 800000;
char[] password = "password".toCharArray();
SecureRandom random = new SecureRandom();
byte[] salt = random.generateSeed(8);
SecretKeyFactory secKeyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512");
KeySpec spec = new PBEKeySpec(password, salt, iterations, keySize);
SecretKey pbeSecretKey = secKeyFactory.generateSecret(spec);
SecretKey desSecret = new SecretKeySpec(pbeSecretKey.getEncoded(), "DESede");
// iv needs to have block size
// we will use the salt for simplification
IvParameterSpec ivParam = new IvParameterSpec(salt);
Cipher cipher = Cipher.getInstance("DESEde/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, desSecret, ivParam);
System.out.println("salt: "+Base64.getEncoder().encodeToString(salt));
System.out.println(cipher.getIV().length+" iv: "+Base64.getEncoder().encodeToString(cipher.getIV()));
byte[] ciphertext = cipher.doFinal("plaintext input".getBytes());
System.out.println("encrypted: "+Base64.getEncoder().encodeToString(ciphertext));
if you can ensure that your password has good entropy (is long and random enough) you may be good with a simple hash
MessageDigest dgst = MessageDigest.getInstance("sha-1");
byte[] hash = dgst.digest("some long, complex and random password".getBytes());
byte[] keyBytes = new byte[keySize/8];
System.arraycopy(hash, 0, keyBytes, 0, keySize/8);
SecretKey desSecret = new SecretKeySpec(keyBytes, "DESede");
The salt serves to randomize the output and should be used.
The output of the encryption should be salt | cipthertext | tag (not necessarily in this order, but you will need all of these for proper encryption).
To decrypt the output, you will need to split the output to salt, ciphertext and the tag.
I see zero vectors ( static salt or iv ) very often in examples from StackOverflow, but in many cases it may lead to broken ciphers revelaling key or plaintext.
The initialization vector iv is needed for block chain modes (encrypting longer input than a single block), we could use the salt from the key as well
when having the same size ( 8 bytes in our case). For really secure solution the password salt should be longer.
The tag is an authentication tag, to ensure that nobody has manipulated with the ciphertext. You could use HMAC of the plaintext or ciphertext. It is important you should use different key for HMAC than for encryption. However - I believe in your case your homework will be ok even without the hmac tag
Hello guys,
I'm working on a project that need to encrypt file content in Android and upload it to a server and then decrypt it with NodeJS service.
I have looked over a few projects that based on AES in Java and NodeJS and found something that worked on Java and not on NodeJS.
Here is the code in Java:
public static String encrypt(String encodeKey, String inputFile) throws Exception {
byte[] input = getStringFromFile(inputFile).toString().getBytes("utf-8");
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] thedigest = md.digest(encodeKey.getBytes("UTF-8"));
SecretKeySpec skc = new SecretKeySpec(thedigest, "AES/ECB/PKCS5Padding");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, skc);
byte[] cipherText = new byte[cipher.getOutputSize(input.length)];
int ctLength = cipher.update(input, 0, input.length, cipherText, 0);
String data = Base64.encodeToString(cipherText, Base64.DEFAULT);
Log.d("Crypto | Length", String.valueOf(ctLength));
Log.d("Crypto | Keypass", encodeKey);
return data;
}
And the code in NodeJS:
var
decipher = crypto.createDecipher('aes-128-ecb', encryption_key),
chunks = [];
chunks.push( decipher.update( new Buffer(fullBuffer, "base64").toString("binary")) );
chunks.push( decipher.final('binary') );
var decrypted = chunks.join("");
As you can see, for each of the files i'm generating a new key-hash for salt (it's the same for decrypt and encrypt);
My problem is that when i'm trying to decrypt it i'm getting this error from NodeJS:
digital envelope routines:EVP_DecryptFinal_ex:wrong final block length
You aren't using the Java Cipher class correctly. You need to call the doFinal method. Since you aren't doing piecemeal encryption anyway you can dispense with the pre-sizing of the ciphertext, eliminate the call to update, and simply call
byte [] cipherText = cipher.doFinal(input);
This is the code in question:
decrypt(self):
"""Decrypt one block"""
data = self.source.read(1024)
if not data:
return ""
iv = data[:16]
encrypted = data[16:]
counter = Crypto.Util.Counter.new(64, prefix=iv[:8], initial_value=struct.unpack(">Q", iv[8:])[0])
cipher = Crypto.Cipher.AES.new(self.info["CpData"], Crypto.Cipher.AES.MODE_CTR, counter=counter)
return cipher.decrypt(encrypted)
This is the line I have problems understanding:
counter = Crypto.Util.Counter.new(64, prefix=iv[:8], initial_value=struct.unpack(">Q", iv[8:])[0])
What does it do and how do I replicate it in Java? Currently I have this, but the result is not what I expect:
public static byte[] decrypt(byte[] encryptedData) throws Exception {
Key key = new SecretKeySpec(keyBytes, "AES");
Cipher c = Cipher.getInstance("AES/CTR/NoPadding");
byte[] iv = Arrays.copyOfRange(encryptedData, 0, 16) ; // first 16 bytes
byte[] data = Arrays.copyOfRange(encryptedData, 16, 1024); // rest
IvParameterSpec ivSpec = new IvParameterSpec(iv);
c.init(Cipher.DECRYPT_MODE, key, ivSpec);
byte[] decValue = c.doFinal(data);
return decValue;
}
Hours of googling have not yielded a useable result. How do I use this counter thing in Java?
Thanks!
On a quick look it appears that there is 8 bytes of IV prepended to the incoming cyphertext. Extract the first 8 bytes and use them as an IV to set up AES-CTR. Then decrypt the rest of the cyphertext.