Reputation: 4270
Code in angular using crypto-js:
let key = '12345123451234512345123451234509';// actual keys are different and has same length of 32 char
let iv = '12345123451234512345123451234509';
let secret_key = CryptoJS.enc.Hex.parse(key);
let secret_iv = CryptoJS.enc.Hex.parse(iv);
let encryptedString = CryptoJS.AES.encrypt(
JSON.stringify(data),
secret_key,
{
iv: secret_iv,
padding: CryptoJS.pad.ZeroPadding
}
).toString();
let requestObj = {
input: encryptedString.trim()
}
I am not able to do same encryption in android. Android Code
String key32Char = "12345123451234512345123451234509";
String iv32Char = "12345123451234512345123451234509";
byte[] srcBuff = jsonString.getBytes("UTF-8");
//SecretKeySpec secretKeySpec = new SecretKeySpec(key32Char.getBytes(), "AES");
//IvParameterSpec ivParameterSpec = new IvParameterSpec(iv32Char.getBytes());
SecretKeySpec secretKeySpec = new SecretKeySpec(Base64.decode(key32Char, Base64.NO_WRAP), "AES");
IvParameterSpec ivParameterSpec = new IvParameterSpec(Base64.decode(iv32Char, Base64.NO_WRAP));
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
byte[] dstBuff = cipher.doFinal(srcBuff);
String encryptedString = Base64.encodeToString(dstBuff, Base64.NO_WRAP);
JSONObject requestObj = new JSONObject();
requestObj.put("input", encryptedString);
Upvotes: 1
Views: 1116
Reputation: 9173
As name suggests, CryptoJS.enc.Hex.parse(key) parses a Hex String and uses it as key. So you need to do the same for your java code.
In addition, you need to select correct encryption mode and padding too. Your CryptoJs code uses CBC mode so you need to do same in Java Code. Your are using zero padding in CryptoJs side which is not available in java, so you need to do it manually. But in general, using zero padding is a bad idea and it is better to use PKCS5 padding for example which is default for CryptoJs.
With these things, these 2 codes match:
let key = '12345123451234512345123451234509';// actual keys are different and has same length of 32 char
let iv = '12345123451234512345123451234509';
let secret_key = CryptoJS.enc.Hex.parse(key);
let secret_iv = CryptoJS.enc.Hex.parse(iv);
let encryptedString = CryptoJS.AES.encrypt(
"0123456789012345x",
secret_key,
{
iv: secret_iv,
}
).toString();
let requestObj = {
input: encryptedString.trim()
}
Java:
public void doit()
{
byte[] key32Char = hexStringToByteArray("12345123451234512345123451234509");
byte[] iv32Char = hexStringToByteArray("12345123451234512345123451234509");
byte[] srcBuff = "0123456789012345x".getBytes();
SecretKeySpec secretKeySpec = new SecretKeySpec(key32Char, "AES");
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv32Char);
try {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
byte[] dstBuff = cipher.doFinal(srcBuff);
String encryptedString = new String(Base64.getEncoder().encode(dstBuff));
System.out.print(encryptedString);
}
catch(Exception e) {
System.out.print(e.toString());
return;
}
}
public byte[] hexStringToByteArray(String s)
{
int len = s.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
+ Character.digit(s.charAt(i+1), 16));
}
return data;
}
Update:
If you are forced to use bad idea of zero padding, you need to keep real size of data and do padding manually:
public void doitZeroPadding()
{
...
// For the simplicity, I assume that data size is smaller than 128.
// You need to change this part as needed.
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
int dsize = srcBuff.length + 1; // 1 is for plain buffer size
// This line align size to the multiple of block size.
int newBufSize = ((dsize + cipher.getBlockSize() - 1) / cipher.getBlockSize()) * cipher.getBlockSize();
byte[] newSrcBuf = new byte[newBufSize];
// You need real buffer size, or you don't know how long is decrypted buffer.
// I add it inside encrypting buffer to prevent other to see real decrypted buffer size.
// But if you want to have exact same encrypted buffer on both sides, you must remove it.
newSrcBuf[0] = (byte)(srcBuff.length);
System.arraycopy(srcBuff, 0, newSrcBuf, 1, srcBuff.length);
// Now use newSrcBuf/newBufSize
...
}
on the decryption side, check real size from decrypted buffer and use that size starting byte 1 for creating string.
Upvotes: 1
Reputation: 5636
IV and Key:To match the key and IV part both must use either base64 or hex decodings.
In Hex encoded string there are 32 hex char that makes 128-bit. However, the same string can be rejected by base64 decode and if not rejected the output will not be 128-bit. You need to use
byte[] bytes = new BigInteger("7F" + str, 16).toByteArray();
SecretKeySpec key = new SecretKeySpec(bytes, 1, bytes.length-1, "AES");
to convert the hex string into byte array.
padding: CryptoJS.pad.ZeroPadding
is useful if your data size is an exact multiple of 128. Otherwise, you need to use this parameter to say that I'll use this for testing my new padding scheme. You need to better use Pkcs7
that was the default.
In Java you need getInstance("AES/CBC/PKCS5Padding");
Mode of operation: The default in JS is CBC, therefor you need the same in Java, as above getInstance("AES/CBC/PKCS5Padding");
Output: To compare the outputs you need to see the same result. In Java you convert the output into base64, so you need the same for JS.
As you can see, you must do the same steps, parameters for both.
Note that: CBC mode is archaic and you should prefer authenticated encryption modes like AES-GCM or ChaCha20-Poly1305. They not only provides confidentiality but also integrity and authentication. Unfortunately, crypto-js doesn't have them. But you can use some other JS libraries for that.
Upvotes: 1