AES encryption in python and decryption in java not working - java

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.

Related

decrypt an encrypted file with 256bit block size in Java (encryption Rijndael C#)

I need to decrypt an file in Java.
The file was encrypted in C# using RijndaelManaged.
This are the C# settings for the encryption (summarized and not mentioned are default value):
var crypto = new RijndaelManaged();
crypto.GenerateKey();
crypto.BlockSize = crypto.KeySize;
crypto.IV = crypto.Key;
crypto.Padding = PaddingMode.Zeros;
my java code so far, which produces an error:
java.security.InvalidAlgorithmParameterException: Wrong IV length: must be 16 bytes long:
Key aesKey = new SecretKeySpec(key, "AES");
Cipher cipher2 = Cipher.getInstance("AES/CBC/noPadding");
byte[] iv = key;
IvParameterSpec ivspec = new IvParameterSpec(iv);
cipher2.init(Cipher.DECRYPT_MODE, aesKey, new IvParameterSpec(iv));
byte[] decryptedFile = cipher2.doFinal(buildFile);
I see 2 Problems concerning the C# code:
the generated Key is 256bit. Then the block size is set to to 256bit, but the standard is 128bit. As far as I know, Java is only able to use a 128bit block size, as the Error message suggests. At least I didn't find out how to set the block size.
Rijndael uses PaddingMode.Zeros, whats the equivalent Padding for Cipher?
please be aware that I can't change the C# code
Is there even a way to decrypt this in Java?
Update - Solution
I finally managed to decrypt the file using BouncyCastle suggested by Topaco in the comments.
BouncyCastle includes RijndaelEngine, same as the C# part. It allows to set the block size to 256bit.
To answer my 2. question: PaddinMode.Zeros equals new ZeroBytePadding()
Full code:
import org.bouncycastle.crypto.BlockCipher;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.engines.RijndaelEngine;
import org.bouncycastle.crypto.modes.CBCBlockCipher;
import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher;
import org.bouncycastle.crypto.paddings.ZeroBytePadding;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.crypto.params.ParametersWithIV;
...
private byte[] decryptPart(byte[] part, byte[] key) throws Exception {
BlockCipher engine = new RijndaelEngine(key.length * 8);
PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(new CBCBlockCipher(engine),
new ZeroBytePadding());
KeyParameter keyParam = new KeyParameter(key);
CipherParameters cipherParams = new ParametersWithIV(keyParam, key);
cipher.init(false, cipherParams);
byte[] output = new byte[cipher.getOutputSize(part.length)];
int tam = cipher.processBytes(part, 0, part.length, output, 0);
try {
cipher.doFinal(output, tam);
} catch (Exception ce) {
ce.printStackTrace();
}
return output;
}

Decrypt a Java AES encoded String in Dart

I need to decrypt an AES (PKCS#7) encoded string in my Flutter mobile application.
The string is got from a QR Code, which has been generated from a Java application and contains the AES encoded String.
The Java encoding :
import java.security.Security;
import java.nio.charset.StandardCharsets;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
public class MyClass {
public static void main(String[] args) throws Exception {
String toEncode = "firstname.lastname#mycompany.com;12";
String encoded = pleaseEncodeMe(toEncode);
System.out.println(encoded);
}
private static String pleaseEncodeMe(String plainText) throws Exception {
Security.addProvider(new BouncyCastleProvider());
final String encryptionAlgorithm = "AES/CBC/PKCS7PADDING";
final String encryptionKey = "WHatAnAWEsoMeKey";
final SecretKeySpec keySpecification = new SecretKeySpec(encryptionKey.getBytes(StandardCharsets.UTF_8), encryptionAlgorithm);
final Cipher cipher = Cipher.getInstance(encryptionAlgorithm, "BC");
cipher.init(Cipher.ENCRYPT_MODE, keySpecification);
final byte[] encryptedBytes = cipher.doFinal(plainText.getBytes());
return Base64.encodeBase64URLSafeString(encryptedBytes);
}
}
Output : AIRTEuNmSuQtYuysv93w3w83kJJ6sg7kaU7XzA8xrAjOp-lKYPp1brtDAPbhSJmT
The Dart decoding :
void main() {
print(decodeMeOrDie("AIRTEuNmSuQtYuysv93w3w83kJJ6sg7kaU7XzA8xrAjOp-lKYPp1brtDAPbhSJmT"));
}
String decodeMeOrDie(String encryptedString) {
final key = Key.fromUtf8("WHatAnAWEsoMeKey");
final iv = IV.fromLength(16);
final encrypter = Encrypter(AES(key, mode: AESMode.cbc, padding: "PKCS7"));
return encrypter.decrypt64(encryptedString, iv: iv);
}
Output : Y��=X�Rȑ�"Qme#mycompany.com;12
You can see that only a part of the string is decoded.
Two things must be taken into account:
1) For decryption, the IV used for encryption is required.
2) For security reasons, a new IV must be randomly generated for each encryption so that no IV is used more than once with the same key, here.
Therfore, the IV must be passed from the encryption-side to the decryption-side. This doesn't happen automatically, but has to be implemented.
One possibility is to concatenate the byte-arrays of IV and ciphertext. Usually the IV is placed before the ciphertext and the result is Base64-encoded (if required), e.g. in Java:
// Concatenate IV and ciphertext
byte[] iv = ...
byte[] ciphertext = ...
byte[] ivAndCiphertext = new byte[iv.length + ciphertext.length];
System.arraycopy(iv, 0, ivAndCiphertext, 0, iv.length);
System.arraycopy(ciphertext, 0, ivAndCiphertext, iv.length, ciphertext.length);
// If required: Base64-encoding
This data is transmitted to the decryption-side, which separates both parts after Base64-decoding. In the case of AES-CBC, the IV is 16 bytes long, so the first 16 bytes represent the IV and the rest the ciphertext. The IV doesn't need to be encrypted because it isn't secret.
Specifically for your case this means that you have to concatenate IV and ciphertext on the Java-side and to Base64-encode the result. On the Dart-side you have to Base64-decode first and then both parts, IV and ciphertext, can be separated and used for the following decryption.
There are two ways to generate the IV before encryption: Implicit generation by the Cipher-instance as in your example or explicit generation e.g. via SecureRandom. Both alternatives are discussed here. If the IV is generated implicitly (via the Cipher-instance), then this IV must be determined via the Cipher-instance, since it is later required for decryption:
// Determine IV from cipher for later decryption
byte[] iv = cipher.getParameters().getParameterSpec(IvParameterSpec.class).getIV();
If the IV is determined explicitly (e.g. using SecureRandom), it must be passed to the Cipher-instance so that it will be used in the running encryption. This is done using an IvParameterSpec.
// Assign IV to cipher so that it is used for current encryption
byte[] iv = ...
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
cipher.init(Cipher.ENCRYPT_MODE, secretkeySpec, ivParameterSpec);
A hard-coded key is in general not good practice (except for testing purposes perhaps). However, the topic of key generation/management is outside the scope of this answer. There are already a lot of questions and answers on this subject. If your question is not covered by these answers, please post a new question. A hard-coded IV doesn't occur within the above architecture and should only be used for testing purposes.
If it can help someone, here is the code I ended up with, in dart (it uses the encrypt package) :
/// Decode the specified QR code encrypted string
static String decodeQrCode(String encryptedString) {
try {
// pad the encrypted base64 string with '=' characters until length matches a multiple of 4
final int toPad = encryptedString.length % 4;
if (toPad != 0) {
encryptedString = encryptedString.padRight(encryptedString.length + toPad, "=");
}
// get first 16 bytes which is the initialization vector
final iv = encrypt.IV(Uint8List.fromList(base64Decode(encryptedString).getRange(0, 16).toList()));
// get cipher bytes (without initialization vector)
final encrypt.Encrypted encrypted = encrypt.Encrypted(Uint8List.fromList(
base64Decode(encryptedString).getRange(16, base64Decode(encryptedString).length).toList()));
// decrypt the string using the key and the initialization vector
final key = encrypt.Key.fromUtf8(YOUR_KEY);
final encrypter = encrypt.Encrypter(encrypt.AES(key, mode: encrypt.AESMode.cbc, padding: "PKCS7"));
return encrypter.decrypt(encrypted, iv: iv);
} catch (e) {
_log.severe("Error while decoding QR code : $e");
return null;
}
}

unable to decrypt in java if encrypted with aes-256-cfb nodejs [duplicate]

I'm using openssl's aes-256-cfb algorithm (from NodeJS's crypto module).
While both the NodeJS and the Java code successfully encrypt and decrypt data, the ciphertext is different, even when the iv, key and plaintext are all identical.
openssl/NodeJS cipherText:
05c2aad7bac42ed0846e9a52ce73df9ff9d7ff914feea49fed27d55ad690782a43107914c1b307ec92753227728c95b8e59c546d
Java cipherText:
05C2AAD7BAC42ED084739340D47CEC9F03D8E94AC7B1E11A56A6654F76AD2C8076BCA162303E39B44D043732E98FDD28C52D
I have guessed that openssl's aes-256-cfb translates to Java's AES/CFB/NoPadding.
The ciphertexts both share the same initial 9 bytes, which is odd - I would have expected them to share the first 16 bytes if there had been some difference in the common mode used. (I hope that "common mode" is the collective term for CFB/CBC/CTR/etc.)
Is Java's AES/CFB/NoPadding the correct translation of OpenSSL's aes-256-cfb?
Does the existence of the common first nine bytes imply that Java's AES/CFB/NoPadding is at least using AES256, as opposed to AES128?
If so, what else might account for the differing ciphertext?
Test cases follow:
OpenSSL/NodeJS:
var assert = require('assert');
var crypto = require('crypto');
describe('crypto', function() {
it('should work', function () {
var plainText = new Buffer('a secret plainText which has more than sixteen bytes');
var key = new Buffer('fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210', 'hex');
var iv = new Buffer('0123456789abcdef0123456789abcdef', 'hex');
var cipher = crypto.createCipheriv('aes-256-cfb', key, iv);
var decipher = crypto.createDecipheriv('aes-256-cfb', key, iv);
var cipherText = cipher.update(plainText);
cipher.final(); // no need to use this value with cfb, it seems to always be empty?
assert.equal(plainText.length, cipherText.length);
assert.equal('05c2aad7bac42ed0846e9a52ce73df9ff9d7ff914feea49fed27d55ad690782a43107914c1b307ec92753227728c95b8e59c546d', cipherText.toString('hex'));
var deciphered = decipher.update(cipherText);
decipher.final(); // no need to use value this with cfb, it seems to always be empty?
assert.equal(plainText, deciphered.toString('utf8'));
});
});
Java/Android:
import android.test.InstrumentationTestCase;
import com.google.protobuf.ByteString;
import bit.Twiddling;
import java.security.AlgorithmParameters;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class CryptoTest extends InstrumentationTestCase {
public void test_encrypt_and_decrypt() throws Exception {
byte[] plainText = "a secret message which has more than sixteen bytes".getBytes();
byte[] key = Twiddling.hexStringBEToBytes("fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210");
byte[] iv = Twiddling.hexStringBEToBytes("0123456789abcdef0123456789abcdef");//0123456789abcdef0123456789abcdef");
SecretKeySpec aesSecret = new SecretKeySpec(key, "AES");
Cipher cipher = Cipher.getInstance("AES/CFB/NoPadding");
Cipher decipher = Cipher.getInstance("AES/CFB/NoPadding");
IvParameterSpec ivps = new IvParameterSpec(iv);
assertEquals(16, iv.length);
cipher.init(Cipher.ENCRYPT_MODE, aesSecret, ivps);
byte[] cipherText = cipher.doFinal(plainText);
assertEquals(plainText.length, cipherText.length);
assertEquals("05C2AAD7BAC42ED084739340D47CEC9F03D8E94AC7B1E11A56A6654F76AD2C8076BCA162303E39B44D043732E98FDD28C52D", Twiddling.bytesToHexStringBE(cipherText));
decipher.init(Cipher.DECRYPT_MODE, aesSecret, ivps);
byte[] deciphered = decipher.doFinal(cipherText);
assertEquals(new String(plainText), new String(deciphered));
}
}
It was a find/replace error - the two plainTexts differ after the first nine bytes.
Java's name for OpenSSL's aes-256-cfb is AES/CFB/NoPadding.

Decrypting strings from node.js in Java?

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

Convert Java code to NodeJS - Encryption method

I want to duplicate the JAVA encryption code in NodeJS.
private String DEFAULT_KEY = "abcdwAYserXbzcSeqL/zPg==";
private String text = "abc";
Base64 base64decoder = new Base64();
byte[] raw = base64decoder.decode(key);
SecretKeySpec fSecretKeySpec = new SecretKeySpec(raw, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, fSecretKeySpec);
byte[] encrypted = cipher.doFinal(text.getBytes());
Base64 base64encoder = new Base64();
result = base64encoder.encodeToString(encrypted);
System.out.println("result: "+ result);
The above code generate the encrypted code as: ZkojvMTW+9EEK0owxMuA7A==
I have tried few ways in NodeJS. It is not generating same code for me.
I have tried the following code.
var bKey = new Buffer('abcdwAYserXbzcSeqL/zPg==', 'base64');
var cipher = crypto.createCipher('aes-128-ecb',bKey);
//cipher.setAutoPadding(auto_padding=false);
var crypted = cipher.update('abc',null,'base64');
crypted+=cipher.final('base64');
console.log(crypted);
Can someone help me out?
You probably are running into the issue that createCipher with two arguments takes a password, not a key. This password is first run through a key derivation function before it becomes a key.
Try to use the createCipheriv method instead, using any value for the IV. ECB mode doesn't take an IV, but at least you would be using a key instead of a password.

Categories

Resources