I have a server running in JAVA and I am creating a client with HTML and Javascript (about which I am less knowledgeable). The server accesses log-in credentials stored in a database with AES encryption.
I need the user to enter credentials into the HTML page, then javascript to encrypt them and send them to the server, where I need to decrypt them in Java. I have found many threads online but cannot solve my problem: the encryption with JS and Java produce different results.
This is my Javascript code.
var key = "0123456789ABCDEF";
function encrypt() {
const ivMd5Hash = CryptoJS.MD5(CryptoJS.enc.Utf8.parse(key));
console.log("Hash: "+ivMd5Hash)
const cipher = this.cipher(CryptoJS.enc.Utf8.parse(value), CryptoJS.enc.Utf8.parse(key), ivMd5Hash);
return cipher.ciphertext.toString();
}
function cipher(value, key, iv) {
const cipher = CryptoJS.AES.encrypt(value, key, {
iv: iv,
mode: CryptoJS.mode.CBC,
keySize: 128,
padding: CryptoJS.pad.Pkcs7
});
return cipher;
}
console.log("Encrypted value: "+encrypt("testuser", key);
The result from the console log is
HASH: e43df9b5a46b755ea8f1b4dd08265544
Encrypted value: fc88ab1f7821b9f8c5b46ab1d41250a2
Now the JAVA part.
public class MainClass {
private static final String key = "0123456789ABCDEF";
private static String initVector = "";
public static void main(String[] args) throws Exception {
initVector=md5(key); // md5 is an external method, which works
System.out.println("HASH: "+initVector);
System.out.println("Encrypted value: "+encrypt("testuser"));
}
public static String encrypt(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 md5(String str) {
MessageDigest messageDigest = null;
try {//from w w w . j a v a 2 s. c o m
messageDigest = MessageDigest.getInstance("MD5");
messageDigest.reset();
messageDigest.update(str.getBytes("UTF-8"));
} catch (NoSuchAlgorithmException e) {
System.out.println("NoSuchAlgorithmException caught!");
System.exit(-1);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
byte[] byteArray = messageDigest.digest();
StringBuffer md5StrBuff = new StringBuffer();
for (int i = 0; i < byteArray.length; i++) {
if (Integer.toHexString(0xFF & byteArray[i]).length() == 1)
md5StrBuff.append("0").append(
Integer.toHexString(0xFF & byteArray[i]));
else
md5StrBuff.append(Integer.toHexString(0xFF & byteArray[i]));
}
return md5StrBuff.toString();
}
The result from the console is
HASH: e43df9b5a46b755ea8f1b4dd08265544 OK
Encrypted value does not appear – I get instead an InvalidAlgorithmParameterException saying the IV length must be 16 bytes long.
Can anyone help me understand why the MD5 works on JS and not on JAVA ?
I would need to have in Java the same encrypted value I get from JS..
Thanks!!
2
Answers
Well vicinl, Your Java code has few issues.
The first fact I want to point you out is Whenever we convert bytes to Hex String. The length of the Hex String will be doubled. i.e. 16 bytes will produce 32 lengths of worth Hexadecimal string in their equivalent representation.
Not to mention in your Java code you are converting something which is inherently in bytes to a hex string and giving input using tobytes of the hexadecimal string which doesn’t makes sense as those are not the same. This is the unnecessary overhead of conversion (That too incorrect one).
Instead, I can rewrite my md5 method as follow:
Basic reference:
How to convert a byte array to a hex string in Java?
Reference for AES:
https://www.baeldung.com/java-aes-encryption-decryption
Hope this helps, Thanks.
Just use proper TLS/SSL. Do not reinvent the wheel please. The SSL will properly encrypt all traffic between the client and server. You cannot do better yourself.
The code provided (both Java and JS) has multiple issues (from hardcoded iv/key, no integrity validation for cbc,.. to potentially different encoding). Yet for the defined use case it is completely not needed.
IV must be 128 bit (16 bytes) long. JS quietly and unfortunately truncaces or zero-pads input with invalid length.
The md5 fn returns hex encoded digest, which leads to 32 byte value. When using crypto, always use byte array (or buffer for JS) and encode/decode to text for final input/output.
I wrote some notes for crypto basics. Hard part in crypto is that all pieces must be present and fit. Just copy/paste code without understanding will provide false feeling for security.
We can just hope the credentials stored in DB are properly encrypted.
p.s.: why the ***** are people writing own hex encode/decode functions?? (except for homework)