Reputation: 1168
We have a java spring backend that encrypts some columns using:
Encryptors.queryableText(secretKey, new String(Hex.encode(salt.getBytes(Charsets.UTF_8))));
This creates an object of type TextEncryptor in a specific configuration which then can encrypt with textEncryptor.encrypt(msg)
and decrypt with textEncryptor.decrypt(msg)
.
This method of encryption provided by spring security is used to make encrypted columns queryable. The same text will always lead to the same encrypted String.
Another node.js backends now needs to access some of the encrypted columns for generating aggregated reports.
I found some article that seems to address that problem quite well: https://stackanswers.net/questions/spring-4-encryptors-vs-cryptojs
I changed that code to fit my needs. IV and salt had to be fix like specified by Encryptors.queryableText()
:
/*!
* Author: flohall
* date: 2019-11-05
* file: module/textEncryptor.js
*/
var CryptoJS = require("crypto-js");
var config = require('../config.json');
//keySize and iteration like specified in spring for AesBytesEncryptor
const keySize = 256;
const iterations = 1024;
//see config.json
const salt = CryptoJS.enc.Hex.parse(config.textEncryptor.hexEncodedSalt);
const secretKey = config.textEncryptor.secretKey;
const key = CryptoJS.PBKDF2(secretKey, salt, {
keySize: keySize / 32,
iterations: iterations
});
//same as NULL_IV_GENERATOR of AesBytesEncryptor - so encryption creates always same cipher text for same input
const iv = {words: [0, 0, 0, 0, 0, 0, 0, 0], sigBytes: 0}
const cfg = {
iv: iv,
padding: CryptoJS.pad.Pkcs7,
mode: CryptoJS.mode.CBC
}
exports.encrypt = function (msg) {
const encrypted = CryptoJS.AES.encrypt(msg, key, cfg);
return encrypted.ciphertext.toString();
}
exports.decrypt = function (encryptedMessage) {
var decrypted = CryptoJS.AES.decrypt(encryptedMessage, key, cfg)
return decrypted.toString(CryptoJS.enc.Utf8);
}
So far so good - important to know is that the config.textEncryptor.hexEncodedSalt given to the node.js function is the already hexencoded salt. I generated it using:
//salt is read from application.yml and not hexencoded yet
System.out.println(new String(Hex.encode(salt.getBytes(Charsets.UTF_8))));
The node.js textEncryptor.encrypt(msg)
method does generate the same encrypted message as textEncryptor.encrypt(msg)
in java, but still textEncryptor.decrypt(msg)
in node.js isn't able to decrypt in the same way as textEncryptor.decrypt(msg)
in java does.
This test code in node.js does not work:
var textEncryptor = require('./modules/textEncryptor.js');
var encrypted = textEncryptor.encrypt("helloWorld")
var decrypted = textEncryptor.decrypt(encrypted)
console.log(encrypted);
console.log(decrypted);
It prints out just the decrypted text followed by an empty line.
Any idea - what I am missing?
Upvotes: 2
Views: 1175
Reputation: 1168
Funny shortly after asking this I found out, what I was missing. It works, but I didn't expect it that way, because this is different than described in the article I linked in my question. The encryptedMessage
has to be hex parsed first and put into an object.
exports.decrypt = function (encryptedMessage) {
var encrypted = { ciphertext: CryptoJS.enc.Hex.parse(encryptedMessage)};
var decrypted = CryptoJS.AES.decrypt(encrypted, key, cfg)
return decrypted.toString(CryptoJS.enc.Utf8);
}
/*!
* Author: flohall
* date: 2019-11-05
* file: module/textEncryptor.js
*/
var CryptoJS = require("crypto-js");
var config = require('../config.json');
//keySize and iteration like specified in spring for AesBytesEncryptor
const keySize = 256;
const iterations = 1024;
//see config.json
const salt = CryptoJS.enc.Hex.parse(config.textEncryptor.hexEncodedSalt);
const secretKey = config.textEncryptor.secretKey;
const key = CryptoJS.PBKDF2(secretKey, salt, {
keySize: keySize / 32,
iterations: iterations
});
//same as NULL_IV_GENERATOR of AesBytesEncryptor - so encryption creates always same cipher text for same input
const iv = {words: [0, 0, 0, 0, 0, 0, 0, 0], sigBytes: 0}
const cfg = {
iv: iv,
padding: CryptoJS.pad.Pkcs7,
mode: CryptoJS.mode.CBC
}
exports.encrypt = function (msg) {
const encrypted = CryptoJS.AES.encrypt(msg, key, cfg);
return encrypted.ciphertext.toString();
}
exports.decrypt = function (encryptedMessage) {
var encrypted = { ciphertext: CryptoJS.enc.Hex.parse(encryptedMessage)};
var decrypted = CryptoJS.AES.decrypt(encrypted, key, cfg)
return decrypted.toString(CryptoJS.enc.Utf8);
}
Upvotes: 2