I generate 128bit AES/CBC/PKCS5Padding key using Java javax.crypto API. Here is the algorithm that I use:
public static String encryptAES(String data, String secretKey) {
try {
byte[] secretKeys = Hashing.sha1().hashString(secretKey, Charsets.UTF_8)
.toString().substring(0, 16)
.getBytes(Charsets.UTF_8);
final SecretKey secret = new SecretKeySpec(secretKeys, "AES");
final Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secret);
final AlgorithmParameters params = cipher.getParameters();
final byte[] iv = params.getParameterSpec(IvParameterSpec.class).getIV();
final byte[] cipherText = cipher.doFinal(data.getBytes(Charsets.UTF_8));
return DatatypeConverter.printHexBinary(iv) + DatatypeConverter.printHexBinary(cipherText);
} catch (Exception e) {
throw Throwables.propagate(e);
}
}
public static String decryptAES(String data, String secretKey) {
try {
byte[] secretKeys = Hashing.sha1().hashString(secretKey, Charsets.UTF_8)
.toString().substring(0, 16)
.getBytes(Charsets.UTF_8);
// grab first 16 bytes - that's the IV
String hexedIv = data.substring(0, 32);
// grab everything else - that's the cipher-text (encrypted message)
String hexedCipherText = data.substring(32);
byte[] iv = DatatypeConverter.parseHexBinary(hexedIv);
byte[] cipherText = DatatypeConverter.parseHexBinary(hexedCipherText);
final Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(secretKeys, "AES"), new IvParameterSpec(iv));
return new String(cipher.doFinal(cipherText), Charsets.UTF_8);
} catch (BadPaddingException e) {
throw new IllegalArgumentException("Secret key is invalid");
}catch (Exception e) {
throw Throwables.propagate(e);
}
}
I can easily encrypt and decrypt messages using secretKey with these methods. Since Java has 128bit AES encryption by default, it generates a hash of the original secret key with SHA1 and takes the first 16-bytes of the hash to use it as secret key in AES. Then it dumps the IV and cipherText in HEX format.
For example encryptAES("test", "test") generates CB5E759CE5FEAFEFCC9BABBFD84DC80C0291ED4917CF1402FF03B8E12716E44C and I want to decrypt this key with CryptoJS.
Here is my attempt:
var str = 'CB5E759CE5FEAFEFCC9BABBFD84DC80C0291ED4917CF1402FF03B8E12716E44C';
CryptJS.AES.decrypt(
CryptJS.enc.Hex.parse(str.substring(32)),
CryptJS.SHA1("test").toString().substring(0,16),
{
iv: CryptJS.enc.Hex.parse(str.substring(0,32)),
mode: CryptJS.mode.CBC,
formatter: CryptJS.enc.Hex,
blockSize: 16,
padding: CryptJS.pad.Pkcs7
}).toString()
However it returns an empty string.
The problem is that you're using a 64 bit key as a 128 bit. Hashing.sha1().hashString(secretKey, Charsets.UTF_8) is an instance of HashCode and its toString method is described as such:
Returns a string containing each byte of asBytes(), in order, as a two-digit unsigned hexadecimal number in lower case.
It is a Hex-encoded string. If you take only 16 characters of that string and use it as a key, you only have 64 bits of entropy and not 128 bits. You really should be using HashCode#asBytes() directly.
Anyway, the problem with the CryptoJS code is manyfold:
The ciphertext must be a CipherParams object, but it is enough if it contains the ciphertext bytes as a WordArray in the ciphertext property.
The key must be passed in as a WordArray instead of a string. Otherwise, an OpenSSL-compatible (EVP_BytesToKey) key derivation function is used to derive the key and IV from the string (assumed to be a password).
Additional options are either unnecessary, because they are defaults, or they are wrong, because the blockSize is calculated in words and not bytes.
Here is CryptoJS code that is compatible with your broken Java code:
var str = 'CB5E759CE5FEAFEFCC9BABBFD84DC80C0291ED4917CF1402FF03B8E12716E44C';
console.log("Result: " + CryptoJS.AES.decrypt({
ciphertext: CryptoJS.enc.Hex.parse(str.substring(32))
}, CryptoJS.enc.Utf8.parse(CryptoJS.SHA1("test").toString().substring(0,16)),
{
iv: CryptoJS.enc.Hex.parse(str.substring(0,32)),
}).toString(CryptoJS.enc.Utf8))
<script src="https://cdn.rawgit.com/CryptoStore/crypto-js/3.1.2/build/rollups/sha1.js"></script>
<script src="https://cdn.rawgit.com/CryptoStore/crypto-js/3.1.2/build/rollups/aes.js"></script>
Here is CryptoJS code that is compatible with the fixed Java code:
var str = 'F6A5230232062D2F0BDC2080021E997C6D07A733004287544C9DDE7708975525';
console.log("Result: " + CryptoJS.AES.decrypt({
ciphertext: CryptoJS.enc.Hex.parse(str.substring(32))
}, CryptoJS.enc.Hex.parse(CryptoJS.SHA1("test").toString().substring(0,32)),
{
iv: CryptoJS.enc.Hex.parse(str.substring(0,32)),
}).toString(CryptoJS.enc.Utf8))
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/rollups/sha1.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/rollups/aes.js"></script>
The equivalent encryption code in CryptoJS would look like this:
function encrypt(plaintext, password){
var iv = CryptoJS.lib.WordArray.random(128/8);
var key = CryptoJS.enc.Hex.parse(CryptoJS.SHA1(password).toString().substring(0,32));
var ct = CryptoJS.AES.encrypt(plaintext, key, { iv: iv });
return iv.concat(ct.ciphertext).toString();
}
console.log("ct: " + encrypt("plaintext", "test"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/rollups/sha1.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/rollups/aes.js"></script>
This one perfectly worked for me
import * as CryptoJS from 'crypto-js';
const SECRET_CREDIT_CARD_KEY = '1231231231231231' // 16 digits key
decrypt(cipherText) {
const iv = CryptoJS.enc.Hex.parse(this.SECRET_CREDIT_CARD_KEY);
const key = CryptoJS.enc.Utf8.parse(this.SECRET_CREDIT_CARD_KEY);
const result = CryptoJS.AES.decrypt(cipherText, key,
{
iv,
mode: CryptoJS.mode.ECB,
}
)
const final = result.toString(CryptoJS.enc.Utf8)
return final
}
console.log(decrypt('your encrypted text'))
using this library in Angular 8
https://www.npmjs.com/package/crypto-js
Related
I need to convert java code for encryption and decryption using AES/CBC/PKCS5Padding algorithm to dart code.
The java code of AES/CBC/PKCS5Padding encryption and decryption:
package test_Terminal.classes;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
/**
*
* #author jeena
*/
public class IOTEncodingDecoding {
SecretKeySpec secretKeySpec;
IvParameterSpec ivSpec;
String EncryptionKey = "733D3A17-D8A0-454B-AD22-88608FD0C46A";
String saltString = "FA9A4D0F-5523-4EEF-B226-9A3E8F14FEF8";
String algorithm = "AES/CBC/PKCS5Padding";
int encoding_mode;
test_Terminal.classes.general General = new test_Terminal.classes.general();
void setSecretKey() {
try {
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
PBEKeySpec pbeKeySpec = new PBEKeySpec(EncryptionKey.toCharArray(), saltString.getBytes(StandardCharsets.UTF_16LE), 1000, 384);
byte[] derivedData = factory.generateSecret(pbeKeySpec).getEncoded();
byte[] key = new byte[32];
byte[] iv = new byte[16];
System.arraycopy(derivedData, 0, key, 0, key.length);
System.arraycopy(derivedData, key.length, iv, 0, iv.length);
secretKeySpec = new SecretKeySpec(key, "AES");
ivSpec = new IvParameterSpec(iv);
} catch (Exception e) {
General.LogException("setSecretKey", e);
}
}
public String encrypt(String input) {
try {
Cipher cipher = Cipher.getInstance(algorithm);
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivSpec);
byte[] cipherText ;
if(encoding_mode==1)
cipherText = cipher.doFinal(input.getBytes(StandardCharsets.UTF_16LE));
else
cipherText = cipher.doFinal(input.getBytes());
return Base64.getEncoder().encodeToString(cipherText);
} catch (Exception e) {
General.LogException("encrypt", e);
}
return "";
}
public String decrypt(String cipherText) {
try {
Cipher cipher = Cipher.getInstance(algorithm);
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivSpec);
byte[] plainText = cipher.doFinal(Base64.getDecoder().decode(cipherText));
if(encoding_mode==1)
return new String(plainText, StandardCharsets.UTF_16LE);
else
return new String(plainText);
} catch (Exception e) {
General.LogException("decrypt", e);
General.LogActivity("decrypt", e.getMessage());
}
return "Ticket format error";
}
public void setMode() {
setSecretKey();
}
}
I need to get the following result:
Input(PlainText):C123492349C1CT20230206130645.
Output(Encrypted string):8tyHRaQCsxmmGW2xPBFYx/PALmvHkmjx/TzaXC2rIv0=
This is the dart code that I've got so far for decryption, but I'm getting error.
Uint8List? decrypt(String ciphertext, String password) {
Uint8List rawCipher = base64.decode(ciphertext);
var salt = rawCipher.sublist(0, 0 + 8);
var iv = rawCipher.sublist(8, 8 + 16);
var encrypted = rawCipher.sublist(8 + 16);
Uint8List key = generateKey(password, salt);
print('key => $key');
CBCBlockCipher cipher = CBCBlockCipher(AESEngine());
ParametersWithIV<KeyParameter> params =
ParametersWithIV<KeyParameter>(KeyParameter(key), iv);
PaddedBlockCipherParameters<ParametersWithIV<KeyParameter>, Null>
paddingParams =
PaddedBlockCipherParameters<ParametersWithIV<KeyParameter>, Null>(
params, null);
PaddedBlockCipherImpl paddingCipher =
PaddedBlockCipherImpl(PKCS7Padding(), cipher);
paddingCipher.init(false, paddingParams);
var val = paddingCipher.process(encrypted);
String res = String.fromCharCodes(val);
debugPrint('res => $res');
return val;
}
Uint8List generateKey(String passphrase, Uint8List salt) {
final derivator = PBKDF2KeyDerivator(HMac(SHA1Digest(), 64))
..init(Pbkdf2Parameters(salt, 1024, 16));
return derivator.process(utf8.encode(passphrase) as Uint8List);
}
I got this code from
The Exception that I'm getting is:
Exception has occurred.
ArgumentError (Invalid argument(s): Input data length must be a multiple of cipher's block size)
I think the values inside rawCipher.sublist() function is wrong. I'm stuck on this problem for few days, please help.
Both codes differ:
Regarding encodings: The Dart code does not consider the UTF-16 LE encoding of the salt. Furthermore, the encoding of the plaintext is unclear. For encoding_mode==1 it is UTF-16LE, otherwise it corresponds to the platform encoding in your environment (which only you know).
Regarding PBKDF2: The Java code derives key and IV from a static salt (note that a static salt is a vulnerability), while the Dart code assumes a concatenation in the order salt|IV|ciphertext during encryption (using a random 8 bytes salt and a random IV).
Also, different iteration counts are used: 1000 in the Java code, 1024 in the Dart code (note that both values are generally much too small for PBKDF2).
The differences can be fixed as follows:
Regarding encodings: In the Dart code, the salt must first be UTF-16 LE encoded: Since the utf package is deprecated, see e.g. here for a UTF-16 LE encoding and here for the decoding. The encoding can be adapted to:
Uint8List encodeUtf16LE(String salt) {
var byteData = ByteData(salt.codeUnits.length * 2);
for (var i = 0; i < salt.codeUnits.length; i += 1) {
byteData.setUint16(i * 2, salt.codeUnits[i], Endian.little);
}
return byteData.buffer.asUint8List();
}
Moreover, from the sample data it can be concluded (by testing) that the plaintext in the Java code has been encoded with UTF-8.
Regarding PBKDF2: In the Dart code, key and IV must be derived from the static salt applied in the Java code.
Also, the parameters from the Java code must be applied (digest: SHA-1, iteration count: 1000, keysize: 32 + 16 = 48 bytes):
Uint8List generateKey(String passphrase, Uint8List salt) {
final derivator = PBKDF2KeyDerivator(HMac(SHA1Digest(), 64))
..init(Pbkdf2Parameters(salt, 1000, 32 + 16));
return derivator.process(utf8.encode(passphrase) as Uint8List);
}
With these changes, key and IV can be derived as follows:
var salt = "FA9A4D0F-5523-4EEF-B226-9A3E8F14FEF8";
var passphrase = "733D3A17-D8A0-454B-AD22-88608FD0C46A";
var saltBytes = encodeUtf16LE(salt);
var keyIv = generateKey(passphrase, saltBytes);
var key = keyIv.sublist(0, 32);
var iv = keyIv.sublist(32, 32 + 16);
The decryption code can be applied unchanged, for decoding use utf8.decode() instead of String.fromCharCodes().
import 'dart:convert';
import 'dart:typed_data';
import 'package:pointycastle/export.dart';
...
var ciphertext = "8tyHRaQCsxmmGW2xPBFYx/PALmvHkmjx/TzaXC2rIv0=";
var encrypted = base64.decode(ciphertext);
var paddingCipher = PaddedBlockCipherImpl(PKCS7Padding(), CBCBlockCipher(AESEngine()))
..init(false, PaddedBlockCipherParameters(ParametersWithIV(KeyParameter(key), iv), null));
var decryptedBytes = paddingCipher.process(encrypted);
var decrypted = utf8.decode(decryptedBytes); // C123492349C1CT20230206130645
I Have an AES encryption and decrypt both in java and nodejs (cryptojs).
but when I tried to encrypt and decrypt in both have different result.
This is my java code :
import java.*;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.crypto.Cipher;
import org.apache.commons.codec.binary.Base64;
public class enc {
public static void main(String[] args){
String key = "1234567890123456"; // 128 bit key: must 16 character
String initVector = "1234567890123456"; // 16 bytes IV : must 16 character
System.out.println("Key : "+key);
System.out.println("Init Vector : "+initVector);
String encrypted = encryptAES(key, initVector, "Hello World");
System.out.println("encrypted : "+encrypted);
}
public static String encryptAES(String key, String initVector, String value) {
try {
IvParameterSpec iv = new IvParameterSpec(initVector.getBytes("UTF-8"));
SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
byte[] encrypted = cipher.doFinal(value.getBytes());
return Base64.encodeBase64String(encrypted);
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}
public static String decryptAES(String key, String initVector, String encrypted) {
try {
IvParameterSpec iv = new IvParameterSpec(
initVector.getBytes("UTF-8"));
SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
byte[] original = cipher.doFinal(Base64.decodeBase64(encrypted));
return new String(original);
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}
}
and java result :
Key : 1234567890123456
Init Vector : 1234567890123456
encrypted : ZyODokM33Io1ZKIA8h7owA==
decrypted : Hello World
My Nodejs using crypto js :
var CryptoJS = require("crypto-js");
var atob = require('atob');
var btoa = require('btoa');
var message = "Hello World";
var key = "1234567890123456";
var iv = "1234567890123456";
console.log("Key : "+key);
console.log("Init Vector : "+iv);
key = CryptoJS.enc.Base64.parse(key);
iv = CryptoJS.enc.Base64.parse(iv);
var chiperData = CryptoJS.AES.encrypt(message, key, { iv: iv });
console.log('encrypted : ',chiperData.toString());
var data = CryptoJS.AES.decrypt(chiperData, key, { iv: iv });
console.log('decrypted : ',hex2a(data.toString()));
// Convert hex string to ASCII.
// Thanks to https://stackoverflow.com/questions/11889329/word-array-to-string
function hex2a(hex) {
var str = '';
for (var i = 0; i < hex.length; i += 2)
str += String.fromCharCode(parseInt(hex.substr(i, 2), 16));
return str;
}
and the result :
Key : 1234567890123456
Init Vector : 1234567890123456
encrypted : tHtu5jUs0ZotjjJIHgy0eQ==
decrypted : Hello World
as you can see encrypted in java and cryptojs is different. ZyODokM33Io1ZKIA8h7owA== and tHtu5jUs0ZotjjJIHgy0eQ==
Where is my code wrong? and how to solve this case?
so they have same encruption so can decrypt each other encrypted string
thanks
This is a typical problem that occurs due to the mismatch of encodings. For example, here, it seems like the problem lies in the following lines:
key = CryptoJS.enc.Base64.parse(key);
iv = CryptoJS.enc.Base64.parse(iv);
Here you are treating key and iv as Base64 encoded string, where previously (in Java) you treated them as UTF-8 encoded string.
If you want the same result as Java you should change it to,
key = CryptoJS.enc.Utf8.parse(key);
iv = CryptoJS.enc.Utf8.parse(iv);
Edit:
As #erickson suggested, you can also change your Java implementation as well so that it interprets your key and iv as Base64.
I get a encrypted base64 string from Python.
The format is AES 256 CBC, but when I try to decrypt using Android it return decrypted string as nil.
Python
# coding=utf-8
import base64
from random import choice
from string import letters
try:
from Crypto import Random
from Crypto.Cipher import AES
except ImportError:
import crypto
import sys
sys.modules['Crypto'] = crypto
from crypto.Cipher import AES
from crypto import Random
class AESCipher(object):
def __init__(self, key):
self.bs = 32
self.key = key
def encrypt(self, raw):
_raw = raw
raw = self._pad(raw)
print raw, ';'
print _raw, ';'
iv = "".join([choice(letters[:26]) for i in xrange(16)])
print " iv :", iv
cipher = AES.new(self.key, AES.MODE_CBC, iv)
return base64.b64encode(iv + cipher.encrypt(raw))
def decrypt(self, enc):
enc = base64.b64decode(enc)
iv = enc[:AES.block_size]
cipher = AES.new(self.key, AES.MODE_CBC, iv)
return self._unpad(cipher.decrypt(enc[AES.block_size:])).decode('utf-8')
def _pad(self, s):
a = (self.bs - len(s) % self.bs)
b = chr(self.bs - len(s) % self.bs)
return s + a * b
#staticmethod
def _unpad(s):
return s[:-ord(s[len(s) - 1:])]
def encrypt(k, t):
o = AESCipher(k)
return o.encrypt(t)
def decrypt(k, t):
o = AESCipher(k)
return o.decrypt(t)
def main():
k = "qwertyuiopasdfghjklzxcvbnmqwerty"
s1 = "Hello World!"
d2 = encrypt(k, s1)
print " Password :", k
print "Encrypted :", d2
print " Plain :", decrypt(k, d2)
if __name__ == '__main__':
main()
Java
Here I use https://github.com/fukata/AES-256-CBC-Example
final String aEcodedSting = "aWVnZWphbnBleWJlemdteeAal+cw04QPYRuuIC3J1/zbkZZSCqxGLo/a26ZiieOk";
String decrypted = AESUtil.decrypt(aEcodedSting);
When I try to decrypt I got this
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.vinu.aessamble/com.example.vinu.aessamble.MainActivity}:
java.lang.RuntimeException: javax.crypto.BadPaddingException: error:1e06b065:Cipher functions:EVP_DecryptFinal_ex:BAD_DECRYPT
This is the Python encryption output:
Password : qwertyuiopasdfghjklzxcvbnmqwerty
Encrypted : aWVnZWphbnBleWJlemdteeAal+cw04QPYRuuIC3J1/zbkZZSCqxGLo/a26ZiieOk
iv : iegejanpeybezgmy
plainText : ser456&*(
Please notify me when anyone can solve this using another library.
There are 4 problems:
Difference between python output and java input
Different IV and key
Different key creation
Padding
1) Currently your python code output is a base64 encoding of iv + encrypted_data
return base64.b64encode(iv + cipher.encrypt(raw))
But in java you're directly decrypting raw data.
You should fix this way
// Decode base64
byte[] array = Base64.decode(src);
// Get only encrypted data (removing first 16 byte, namely the IV)
byte[] encrypted = Arrays.copyOfRange(array, 16, array.length);
// Decrypt data
decrypted = new String(cipher.doFinal(encrypted));
2) You must use same IV and key for input and output, so you should copy them from python console output:
iv : qbmocwtttkttpqvv
Password : qwertyuiopasdfghjklzxcvbnmqwerty
Encrypted : anZxZHVpaWJpb2FhaWdqaCK0Un7H9J4UlXRizOJ7s8lchAWAPdH4GRf5tLAkCmm6
Plain : Hello World!
and paste in java code:
private static final String ENCRYPTION_KEY = "qwertyuiopasdfghjklzxcvbnmqwerty";
private static final String ENCRYPTION_IV = "qbmocwtttkttpqvv";
3) In python you're using the key as string, but in java library it is hashed before being used for decrypting, so you should change your makeKey() method:
static Key makeKey() {
try {
byte[] key = ENCRYPTION_KEY.getBytes("UTF-8");
return new SecretKeySpec(key, "AES");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return null;
}
4) Finally, you don't need to specify a padding in java with "AES/CBC/PKCS5Padding", because this way you force Cipher to pad automatically.
You can simply use "AES/CBC/NoPadding" in your decrypt() method, so it should look like this:
public static String decrypt(String src) {
String decrypted = "";
try {
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
cipher.init(Cipher.DECRYPT_MODE, makeKey(), makeIv());
byte[] array = Base64.decode(src);
byte[] encrypted = Arrays.copyOfRange(array, 16, array.length);
decrypted = new String(cipher.doFinal(encrypted));
} catch (Exception e) {
throw new RuntimeException(e);
}
return decrypted;
}
Java output with your base64 and IV:
encrypted: aWVnZWphbnBleWJlemdteeAal+cw04QPYRuuIC3J1/zbkZZSCqxGLo/a26ZiieOk
decrypted: ser456&*(
Edit:
As suggested by Artjom B. (thank you), it would be better to read IV directly from ciphertext instead of hardcoding in AESUtil.
Your input consists of the IV in first 16 bytes and encrypted text in last 16 bytes, so you could take advantage of this.
public static String decrypt(String src) {
String decrypted = "";
try {
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
// Decode input
byte[] array = Base64.decode(src);
// Read first 16 bytes (IV data)
byte[] ivData = Arrays.copyOfRange(array, 0, 16);
// Read last 16 bytes (encrypted text)
byte[] encrypted = Arrays.copyOfRange(array, 16, array.length);
// Init the cipher with decrypt mode, key, and IV bytes array (no more hardcoded)
cipher.init(Cipher.DECRYPT_MODE, makeKey(), new IvParameterSpec(ivData));
// Decrypt same old way
decrypted = new String(cipher.doFinal(encrypted));
} catch (Exception e) {
throw new RuntimeException(e);
}
return decrypted;
}
Moreover, as said here
Python code uses a 32 byte block size for padding which means that Java will still not be able to decrypt half of all possible ciphertexts. AES block size is 16 bytes and this should be changed in the Python implementation
You could change your Python class as below (AES.block_size is equal to 16):
class AESCipher(object):
def __init__(self, key):
self.bs = AES.block_size
self.key = key
The encrypted text is done in JAVA (which we have no JAVA background at all)
The decryption will be in C#, and here is the code
public static string DecryptString(string Message, string Passphrase)
{
byte[] Results;
UTF8Encoding UTF8 = new UTF8Encoding();
MD5CryptoServiceProvider HashProvider = new MD5CryptoServiceProvider();
byte[] TDESKey = HashProvider.ComputeHash(UTF8.GetBytes(Passphrase));
// byte[] TDESKey = UTF8.GetBytes(Passphrase);
TripleDESCryptoServiceProvider TDESAlgorithm = new TripleDESCryptoServiceProvider();
TDESAlgorithm.Key = TDESKey;
// TDESAlgorithm.Mode = CipherMode.CTS;
TDESAlgorithm.Padding = PaddingMode.Zeros;
byte[] DataToDecrypt = Convert.FromBase64String(Message);
try
{
ICryptoTransform Decryptor = TDESAlgorithm.CreateDecryptor();
Results = Decryptor.TransformFinalBlock(DataToDecrypt, 0, DataToDecrypt.Length);
}
finally
{
TDESAlgorithm.Clear();
HashProvider.Clear();
}
return Encoding.UTF8.GetString(Results);
}
Encrypted Java code is
public String encryptData(String privateKey, String rawData)
{
Cipher cipher = null;
try
{
cipher = Cipher.getInstance(DESEDE_ENCRYPTION_SCHEME);
cipher.init(Cipher.ENCRYPT_MODE, getSecretKey(privateKey));
byte[] plainText = rawData.getBytes(UNICODE_FORMAT);
byte[] encryptedText = cipher.doFinal(plainText);
return new String(Base64.encodeBase64(encryptedText));
}
}
However, when tried to decrypt, got the error message: BAD DATA
Where am I missing here?
You are not using MD5 in Java, so you should not be using it in your .NET for computing the hash.
Your key should have been generated using a specific encoding and same you should use in .NET.
Please note, there is some fundamental difference in java KeySpec and the Key being used for TripleDESCryptoServiceProvider. As mentioned by Microsfot https://msdn.microsoft.com/en-us/library/system.security.cryptography.tripledescryptoserviceprovider.aspx
Triple DES only supports "key lengths from 128 bits to 192 bits in increments of 64 bits"
So you need to convert your key appropriately before assigning. To do this you can use the Array.Resize method as following.
byte[] TDESKey = Encoding.UTF8.GetBytes(Passphrase);
System.Array.Resize(ref TDESKey , 192 / 8);
Hope this will help.
I need to encrypt values in my Java code with user choice key and decrypt the values in Java script module.
Below is my Java code to encrypt the values. Here I am generating 128 bit key value from the user choice key and same is using to encrypt the values.
String plainText = "Hello, World! This is a Java/Javascript AES test.";
try {
byte[] rawKey = getRawKey("12345".getBytes());
SecretKey key = new SecretKeySpec(rawKey, "AES");
AlgorithmParameterSpec iv = new IvParameterSpec(
Base64.decodeBase64("5D9r9ZVzEYYgha93/aUK2w=="));
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, key, iv);
System.out.println(Base64.encodeBase64String(cipher
.doFinal(plainText.getBytes("UTF-8"))));
} catch (Exception e) {
e.printStackTrace();
System.out.println("Exception in crypto...");
}
public static byte[] getRawKey(byte[] seed) throws Exception {
KeyGenerator kgen = KeyGenerator.getInstance("AES");
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
sr.setSeed(seed);
kgen.init(128, sr); // 192 and 256 bits may not be available
SecretKey skey = kgen.generateKey();
byte[] raw = skey.getEncoded();
String s = new String(raw);
System.out.println("raw key.." + raw);
return raw;
}
Above code printing the below values:
raw key..[B#45b9ce4b
vN2GouJcVli/rFMDHEwCNZejraO5cQxBtlo5D64qkaRTkxxRTIo+Vm38H4fUZp7ABxj7ul0Ha6bO5aFxMzMY0g==
When I use the above values to decrypt in JS code , I am not getting any response.
<html>
<head>
<script src="http://crypto-js.googlecode.com/svn/tags/3.1.2/build/rollups/aes.js"></script>
<script >
var encrypted = CryptoJS.enc.Base64.parse('vN2GouJcVli/rFMDHEwCNZejraO5cQxBtlo5D64qkaRTkxxRTIo+Vm38H4fUZp7ABxj7ul0Ha6bO5aFxMzMY0g==');
var key = CryptoJS.enc.Base64.parse('[B#45b9ce4b');
var iv = CryptoJS.enc.Base64.parse('5D9r9ZVzEYYgha93/aUK2w==');
document.write(CryptoJS.enc.Utf8.stringify(CryptoJS.AES.decrypt(
{ ciphertext: encrypted },
key,
{ mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7, iv: iv, })));
</script>
</head>
<body>
<h1>hsd h </h1>
</body>
</html>
Please point me If there's anything missing from my code or please suggest me if any alternative to produce the same result.
Your key is incorrect in your JS code. In your Java code you called System.out.println() using a byte[] as an argument which will not give you meaningful output. [B#45b9ce4b is not valid Base64 data.
To fix this you need take the byte[] representing the key and Base64 encode it into a String and then print the string.
A comment on key generation:
You should avoid using a random number generator to derive key material from user input (i.e. a password). The correct way to create key material from user input is by using a key stretching algorithm.
You should absolutely use a standard algorithm for this such as PBKDF2. In Java you can get a key factory for this via SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");