I got on this post a couple of weeks ago and worked perfectly:
Compatible AES algorithm for Java and Javascript
Now, I need to do the reverse operation, but once in java, I am getting this exception:
javax.crypto.BadPaddingException: Given final block not properly padded
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:966)
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:824)
at com.sun.crypto.provider.AESCipher.engineDoFinal(AESCipher.java:436)
at javax.crypto.Cipher.doFinal(Cipher.java:2165)
This is my "reverse" operation I did in JavaScript:
var rkEncryptionKey = CryptoJS.enc.Base64.parse('u/Gu5posvwDsXUnV5Zaq4g==');
var rkEncryptionIv = CryptoJS.enc.Base64.parse('5D9r9ZVzEYYgha93/aUK2w==');
function encryptString(stringToEncrypt) {
var utf8Stringified = CryptoJS.enc.Utf8.parse(stringToEncrypt);
var encrypted = CryptoJS.AES.encrypt(utf8Stringified.toString(), rkEncryptionKey, {mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7, iv: rkEncryptionIv});
return CryptoJS.enc.Base64.parse(encrypted.toString()).toString();
}
I though this would be all?
[EDIT]
Encrypted string is the following: {"company_name":"asdfasdfasd","customer_name":"asdfasdfasdfasdf","phone_number":"asdfasdfasdfasdf","email":"asdfasdfasdfasdfads"}
When doing the encrypt / decrypt from java to java it works, when doing so from java to javascript, also works, but from javascript to java, not working.
Java Code
public String toJson(final String encrypted) {
try {
SecretKey key = new SecretKeySpec(Base64.decodeBase64("u/Gu5posvwDsXUnV5Zaq4g=="), "AES");
AlgorithmParameterSpec iv = new IvParameterSpec(Base64.decodeBase64("5D9r9ZVzEYYgha93/aUK2w=="));
byte[] decodeBase64 = Base64.decodeBase64(encrypted);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, key, iv);
return new String(cipher.doFinal(decodeBase64), "UTF-8");
} catch (Exception e) {
throw new RuntimeException("This should not happen in production.", e);
}
}
Change
return CryptoJS.enc.Base64.parse(encrypted.toString()).toString();
to
return encrypted.ciphertext.toString(CryptoJS.enc.Base64);
The encrypted object usually stringifies into an OpenSSL format which might also contain the salt. If you're only interested in the actual ciphertext, then you need to stringify the ciphertext property.
Note that toString() takes an optional encoding function. Use the one that you need.
Related
I have Java based Web API which receives AES encrypt user credentials and let the user get the session token. This is working fine with AES encryption on client side with CryptoJS. But the same throws bad padding exception while trying to access this API from Python.
In Java I've used PKCS5Padding padding. I have tried this with NoPadding as well, but in that case the de-crypted text is simply empty. Also, when trying with NoPadding, CryptoJS code is not working properly. As, this API should work for any client side program (Later I'm planning to expand this to PHP, .Net and R as well), I want to have a common, working mechnism for encryption of passwords thru AES. I'm pretty new to python and I have tried various programs provided in stack overflow as well as in other blogs on this subject.
Here is the java part
public String decrypto(String cipherText, String secret) {
byte[] cipherData = java.util.Base64.getDecoder().decode(cipherText);
byte[] saltData = Arrays.copyOfRange(cipherData, 8, 16);
String decryptedText = null;
try {
MessageDigest md5 = MessageDigest.getInstance("MD5");
final byte[][] keyAndIV = GenerateKeyAndIV(32, 16, 1, saltData, secret.getBytes(StandardCharsets.UTF_8),
md5);
SecretKeySpec key = new SecretKeySpec(keyAndIV[0], "AES");
IvParameterSpec iv = new IvParameterSpec(keyAndIV[1]);
byte[] encrypted = Arrays.copyOfRange(cipherData, 16, cipherData.length);
Cipher aesCBC = Cipher.getInstance("AES/CBC/PKCS5Padding");
aesCBC.init(Cipher.DECRYPT_MODE, key, iv);
byte[] decryptedData = aesCBC.doFinal(encrypted);
decryptedText = new String(decryptedData, StandardCharsets.UTF_8);
} catch (Exception e) {
e.printStackTrace();
}
return decryptedText;
}
And this line send the encrypted password from CrptoJS without any issues which gets de-crypted properly in Java
encKey = CryptoJS.AES.encrypt(this.logingrp.value['loginpass'], this.localPublicKey).toString();
this.localLogin(encKey);
LocalLogin is an AJAX function to call the API
Python code is below. I'm using Pycryptodome
import requests, json
import base64
from Crypto.Cipher import AES
from Crypto.Util import Padding
from Crypto import Random
from urllib.parse import urlencode
from urllib.request import Request, urlopen
from hashlib import sha256
BS = 16
pad = lambda s: bytes(s + (BS - len(s) % BS) * chr(BS - len(s) % BS), 'utf-8')
unpad = lambda s : s[0:-ord(s[-1:])]
class AESCipher:
def __init__( self, key ):
self.key = bytes(key, 'utf-8')
def encrypt( self, raw ):
raw = pad(raw)
iv = Random.new().read( AES.block_size )
cipher = AES.new(self.key, AES.MODE_CBC )
return base64.b64encode( iv + cipher.encrypt( raw ) )
def decrypt( self, enc ):
enc = base64.b64decode(enc)
iv = enc[:16]
cipher = AES.new(self.key, AES.MODE_CBC, iv )
return unpad(cipher.decrypt( enc[16:] )).decode('utf8')
header = {"Content-type": "application/json",
"Authorization": "Bearer CT9797"
}
password = 'stone#123'
keyphrase = '9DA538D0HMQXQRGI';
cipher = AESCipher(keyphrase)
encrypted = cipher.encrypt(password).decode('utf8')
payload={'userId':'CT9797','userData':encrypted,'encKey':''}
response_decoded_json = requests.post(url=_base_URL+'eLogin', data=json.dumps(payload), headers=header)
print(response_decoded_json.status_code)
The below exception occurs while trying the above program on java side
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:10
Appreciate your time in reading and helping me out.
I have the following simple encryption code running in node.js:
var crypto = require('crypto');
var encKey = "FOO"; // Not the real key. Assume it works though.
var encrypt = function(str) {
var cipher = crypto.createCipher('aes-256-cbc', encKey);
var crypted = cipher.update(str, 'utf-8', 'hex');
crypted += cipher.final('hex');
return crypted;
};
Which I can also decrypt as below:
var crypto = require('crypto');
var encKey = "FOO"; // Not the real key. Assume it works though.
var decrypt = function(str) {
var decipher = crypto.createDecipher('aes-256-cbc', encKey);
var decrypted = decipher.update(str, 'hex', 'utf-8');
decrypted += decipher.final('utf-8');
return decrypted;
};
This all works fine. Strings are encrypting and decrypting as expected. But now I am faced with task of decrypting encrypted strings from this node.js code, in Java. And that is where things are going wrong and I am not sure why.
For decryption, My Java code looks like this:
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.security.MessageDigest;
import java.util.Arrays;
private static final String encKey = "FOO";
private static SecretKeySpec secretKey;
private static byte[] key;
public static String decrypt(String str) throws Exception {
String hexDecodedStr = new String(Hex.decodeHex(str.toCharArray()));
setKey(encKey);
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secretKey);
return new String(cipher.doFinal(hexDecodedStr.getBytes()));
}
private static void setKey(String myKey) throws Exception {
MessageDigest sha = null;
try {
key = myKey.getBytes("UTF-8");
sha = MessageDigest.getInstance("SHA-1");
key = sha.digest(key);
key = Arrays.copyOf(key, 16);
secretKey = new SecretKeySpec(key, "AES");
}
catch (Exception e) {
throw e;
}
}
And it doesn't work. It seems like no matter what I try, I end up with some exception on the cipher.doFinal() call, or the String I get back is totally wrong. I know the node.js code is using aes-256-cbc, while the Java code is using AES/ECB/PKCS5Padding instead of AES/CBC/PKCS5Padding, but when I tried to use AES/CBC/PKCS5Padding, it was requiring an InitVector which I didn't have in node.js so I was unsure of how to proceed. Is node making an InitVector under the hood if not provided with one? Am I missing something totally obvious?
You seems to have the same issue as others OpenSSL encryption failing to decrypt C#
As far I understood the docs, the crypto libeary uses openssl. The openssl creates IV and key from the password using its EVP_BytesToKey function and random salt (not just hash). As dave pointed out, the crypto library uses no salt.
the output of openssl is Salted_{8 bytes salt}{ciphertext} so check what is output of the cipher ( I am unable to do it now)
I wrote a small article how to encrypt properly in Java
I am having some troubles with the following code - I seem to be getting an IllegalBlocksizeException and am unsure what it is that I maybe doing incorrectly here? Would it be possible to get some advice / pointers?
Thanks
public class Encryption
{
private SecretKeyFactory factory;
private SecretKey tmp;
private SecretKey secret;
private Cipher cipher;
private byte[] iv;
private byte[] cipherText;
private final KeySpec spec = new PBEKeySpec("somepassword".toCharArray(), SALT, 65536, 256);
private static final byte[] SALT = {(byte)0xc3, (byte)0x23, (byte)0x71, (byte)0x1c, (byte)0x2e, (byte)0xc2, (byte)0xee, (byte)0x77};
public Encryption()
{
try
{
factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
tmp = factory.generateSecret(spec);
secret = new SecretKeySpec(tmp.getEncoded(), "AES");
cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secret);
iv = cipher.getParameters().getParameterSpec(IvParameterSpec.class).getIV();
}
catch (Exception e)
{
e.printStackTrace();
}
}
public String encrypt(String valueToEncrypt) throws Exception
{
cipher.init(Cipher.ENCRYPT_MODE, secret, new IvParameterSpec(iv));
cipherText = cipher.doFinal(Base64.decodeBase64(valueToEncrypt.getBytes()));
return Base64.encodeBase64String(cipherText);
}
public String decrypt(String encryptedValueToDecrypt) throws Exception
{
cipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(iv));
return new String(cipher.doFinal(new Base64().encode(encryptedValueToDecrypt.getBytes())));
}
public static void main(String[] args ) throws Exception
{
Encryption manager = new Encryption();
String encrypted = manager.encrypt("this is a string which i would like to encrypt");
System.out.println(encrypted);
String decrypted = manager.decrypt(encrypted);
System.out.println(decrypted);
System.out.println(encrypted.equals(decrypted));
}
}
The exception is as follows
Exception in thread "main" javax.crypto.IllegalBlockSizeException: Input length must be multiple of 16 when decrypting with padded cipher
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:750)
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:676)
at com.sun.crypto.provider.AESCipher.engineDoFinal(AESCipher.java:313)
at javax.crypto.Cipher.doFinal(Cipher.java:2087)
at encrypt.Encryption.decrypt(Encryption.java:52)
at encrypt.Encryption.main(Encryption.java:60)
The only approach to begin with the implementation of cryptographic algorithms, from the functional point of view (please keep in mind that a working code is not necessarily a secure one, and a lot of thought should come in that direction), is incremental: first try raw AES with a fixed key, then add the key generated by PBKDF2 and only later Base64. The latter is just an encoding tool and should be the easiest part of the process.
But let's take a look to the code:
1. The initialization seems fine, if your goal is to generate the key out of a password.
2. During the decryption, this line stands off:
cipherText = cipher.doFinal(Base64.decodeBase64(valueToEncrypt.getBytes()));
valueToEncrypt is a readable string, but you're trying to decrypt it. Since it has only lowercase letters and spaces, it might not trigger an error, but you're trying to base64-decode something that hasn't been base64-encoded. It would make more sense to try:
cipherText = cipher.doFinal(valueToEncrypt.getBytes());
Then the cipherText can be base64-encoded.
For the decryption part, undo the operations in encryption in the reverse order. If you encrypted and then base64-encoded, then base64-decode first and then decrypt.
As a final recommendation: think modular. Encode in one line and encrypt in another, so if you want to remove or add a layer you just have to toggle the comments on one line.
You have reversed the base-64 encoding and decoding operations. Base-64 takes raw bytes and makes them into printable text. You can encode the output of an encryption operation to make it printable. But then you will need to base‑64–decode that text before trying to decrypt it.
This part of your decrypt() method is causing the problem:
cipher.doFinal(new Base64().encode(encryptedValueToDecrypt.getBytes()))
That should be:
cipher.doFinal(Base64.decodeBase64(encryptedValueToDecrypt.getBytes()))
Asking for "pointers" is pretty open-ended. The best pointer I can give you: don't write this code yourself. Choose a package that provides a higher-level API, selecting high-security algorithms and applying them according to best practices. You don't know what you are doing, and you won't be able to write secure code. But using a high quality, open source library might help you begin to learn more about encryption.
You probably should base64 decode encryptedValueToDecrypt before you decrypt it.
I try to decrypt an encrypted data that I receive from a web service.
The encryption is done using AES 128.
I use the following code to decrypt the data:
public static String decrypt(String strToDecrypt)
{
try
{
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding"); //AES/CBC/PKCS7Padding
SecretKeySpec secretKey = new SecretKeySpec(AppConstants.AESEncryptionKey.getBytes("UTF8"), "AES");
int blockSize = cipher.getBlockSize();
cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(new byte[blockSize])); //new IvParameterSpec(new byte[16])
byte decBytes[] = cipher.doFinal(Base64.decode(strToDecrypt, 0));
// byte decBytes[] = cipher.doFinal(Base64.decodeBase64(strToDecrypt));
String decStr = new String(decBytes);
System.out.println("After decryption :" + decStr);
return decStr;
}
catch (Exception e)
{
System.out.println("Exception in decryption : " + e.getMessage());
}
return null;
}
At
cipher.doFinal()
I got the following Exception:
javax.crypto.badpaddingexception pad block corrupted
I went through my post but ended up with no solution. I am badly stuck over here.
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG","Crypto");
works perfectly
Note: This code works only on devices up to Android 6. Starting with Android 7.0 the "Crypto" provider has been removed, therefore this code will fail.
AES keys should consist of random data. If you store them as a String then you are likely to loose information, especially if you use encodings such as UTF-8. Your line:
AppConstants.AESEncryptionKey.getBytes("UTF8")
Makes it likely that you've lost data during conversion to/from a string. Use hexadecimals instead if you require a string, or simply store the key as a byte array.
Note that this answer doesn't indicate any security related hints. In general you only want to derive keys or store them in containers. You don't want to use CBC over an insecure channel either.
In my case issue is came because encrypted key and decrypted key both are different, when I check both key with same value then issue is not came
I was asked to encrypt some text from client side ( web ) before sending it to server side ( java )
So i try to use CryptoJS library for client side.
I encrypt it like this :
var key = "aaaaaaaaaaaaaaaaaaaaaaaa";
var value = "KF169841";
var encryptedString = CryptoJS.TripleDES.encrypt(value, key);
console.log(encryptedString.toString());
And i get something like this : U2FsdGVkX19eYFFHgYGCr3v9/skTOKVp0pLWRNK9JTg=
I use this encryptedString and key in other Decrypt tool online ( Which also use CryptoJS ) and got back exact value KF169841.
After sending this value and key to server ( well key isn't sending directly to server though but for test, it is ), i need to decrypt it using Java.
But i quite don't know how to decrypt it. I'm tried some code from google search but it end up wrong padding if use DESese or get wrong value if i use ECB/NoPadding.
I did try to something like setting sfg for CryptoJS side like:
mode: CryptoJS.mode.EBC,
padding: CryptoJS.pad.NoPadding
But they got javascript exception ( a is not define )
So any have any experience with CryptoJS can help me decrypt this one using java ?
=============================================================
UPDATE : Sorry here my server side code i'm using
/**
* Method To Decrypt An Ecrypted String
*/
public String decrypt(String encryptedString, String myEncryptionKey) {
String decryptedText = null;
try {
byte[] keyAsBytes = myEncryptionKey.getBytes("UTF8");
KeySpec myKeySpec = new DESedeKeySpec(keyAsBytes);
SecretKeyFactory mySecretKeyFactory =
SecretKeyFactory.getInstance("DESede");
Cipher cipher = Cipher.getInstance("DESede/ECB/NoPadding");
SecretKey key = mySecretKeyFactory.generateSecret(myKeySpec);
cipher.init(Cipher.DECRYPT_MODE, key);
// BASE64Decoder base64decoder = new BASE64Decoder();
// byte[] encryptedText = base64decoder.decodeBuffer(encryptedString);
byte[] encryptedText = org.apache.commons.codec.binary.Base64.decodeBase64(encryptedString);
byte[] plainText = cipher.doFinal(encryptedText);
decryptedText= bytes2String(plainText);
} catch (Exception e) {
e.printStackTrace();
}
return decryptedText;
}
According to the documentation, your encryptedString variable contains structured data that must be split apart to be sent to Java code. You will need to send encryptedString.iv and encryptedString.ciphertext to your Java code. If you continue to use passwords (see below), you will need to send encryptedString.salt as well.
If you pass your key as a string it will be interpreted as a password and a key will be derived from it. If you actually want to pass an explicit key, follow the documentation and specify the IV and key as suggested by the code snippet below. If you stick with supplying a password, then you must figure out the derivation scheme and use the same process in your Java code.
// Code snippet from http://code.google.com/p/crypto-js/#Custom_Key_and_IV
<script src="http://crypto-js.googlecode.com/svn/tags/3.1.2/build/rollups/aes.js"></script>
<script>
var key = CryptoJS.enc.Hex.parse('000102030405060708090a0b0c0d0e0f');
var iv = CryptoJS.enc.Hex.parse('101112131415161718191a1b1c1d1e1f');
var encrypted = CryptoJS.AES.encrypt("Message", key, { iv: iv });
</script>
Regarding your Java code, it looks mostly OK (although there is plenty of room for error with string conversions). However, you probably want to convert your key from hex to binary rather than grabbing the bytes:
byte[] keyAsBytes = DatatypeConverter.parseHexBinary(myEncryptionKey);
This assumes you alter your JavaScript code to pass the literal key value.
You will also need to switch to DESede/CBC/PKCS5Padding and pass an IVParameterSpec object to your Cipher.init call, specifying the IV value sent from your Java Script code.