I have a java backend application that serves jsp for the frontend. This application has several form pages. On submission of a form, the frontend form data would be encrypted with aes 256 gcm and the aes key (generated using pbkdf2 on frontend) would be encrypted with RSA public key. The encrypted key, salt, iv, encryptedContent and tag would be added to buffer and converted to Base64 string. The RSA key pair is generated by Java using KeyPairGenerator class and the public key is sent to frontend.
This is more of a conceptual question perhaps because encryption seems to work sometimes and sometimes it fails, that to it can happen on any of the form page randomly which makes the user restart whole process. It would be immensely helpful if someone can please tell me what am I missing here exactly to make it work 100% of the time.
Frontend code:
const crypto = require('crypto');
const cryptoConfig = {
cipherAlgorithm: 'aes-256-gcm',
iterations: 64000,
keyLength: 32,
saltLength: 16,
ivLength: 12,
tagLength: 16,
digest: 'sha512'
};
function aesGCMEncrypt(content, windowId) {
const salt = crypto.randomBytes(cryptoConfig.saltLength);
const iv = crypto.randomBytes(cryptoConfig.ivLength);
const aesKey = crypto.pbkdf2Sync(windowId, salt, cryptoConfig.iterations, cryptoConfig.keyLength, cryptoConfig.digest);
const aesKeyB64 = aesKey.toString('base64');
const cipher = crypto.createCipheriv(cryptoConfig.cipherAlgorithm, aesKey, iv);
const encrypted = Buffer.concat([cipher.update(content, 'utf8'), cipher.final()]);
const tag = cipher.getAuthTag();
return { encrypted, salt, iv, tag, aesKeyB64 };
}
function createBuffer(key, salt, iv, encrypted, tag){
const concatenatedBuffer = Buffer.concat([key, salt, iv, encrypted, tag]);
return concatenatedBuffer.toString('base64');
}
function convertBase64ToPem(base64PublicKey, keyType) {
const prefix = `-----BEGIN ${keyType} KEY-----\n`;
const postfix = `\n-----END ${keyType} KEY-----`;
const base64Lines = base64PublicKey.match(/.{1,64}/g);
if (base64Lines) {
const pem = base64Lines.join('\n');
return `${prefix}${pem}${postfix}`;
}
return null;
}
function encryptWithPublicKey (toEncrypt, pubKey) {
console.log("toEncrypt -> ",toEncrypt)
console.log("pubKey -> ",pubKey)
var buffer = Buffer.from(toEncrypt, 'utf8');
var encrypted = crypto.publicEncrypt({key:pubKey, padding : crypto.constants.RSA_PKCS1_PADDING}, buffer)
return encrypted.toString("base64");
}
const b64Check = (b64String) => {
return b64String === (btoa(atob(b64String)))
}
const aesGCMWithRSAEncryption = (content, windowId, publicKey) => {
let result = null;
while (!result){debugger;
try {
const { encrypted, salt, iv, tag, aesKeyB64 } = aesGCMEncrypt(content, windowId);
let encryptedAesKey = encryptWithPublicKey(aesKeyB64, convertBase64ToPem(publicKey, "PUBLIC"))
encryptedAesKey = Buffer.from(encryptedAesKey, 'base64')
const b64EncString = createBuffer(encryptedAesKey, salt, iv, encrypted, tag)
if (b64Check(b64EncString)){
result = b64EncString
}
}
catch(err){
continue
}
}
return result
}
const aesGCMWithRSAEncryptionTest = (str) => {
let i = 0
while (i == 0){
try {
if (b64Check(str)){
i++
}
}
catch(err){
console.log
continue
}
}
return result
}
const publicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCmx4wNRE9D/qg/aeYBTBNzTIFptEQBVU9+DOqOBf4ohNAYjWWIUk7fM3HJD2gSQHcP3zwHOF4mfGTVS7ejw7ip1sCL1qzpBMKlJw0XaEwuyigXeshTGbUVCyIXcavdvxuqiko3R92bDoAIpylOAvLtAXjjEJbwVcnAm95v5qF04wIDAQAB"
const content = "{\"windowID\":\"MTIz5NDQwOQ==\"}";
const windowId = "123456789";
const str = aesGCMWithRSAEncryption(content, windowId, publicKey)
console.log("result -> ",str)
module.exports = {
aesGCMWithRSAEncryption: aesGCMWithRSAEncryption
}
What I tried to do above is post encryption, if the encrypted string turns out to not to be a valid base64 string then go through the whole encryption process again. But during testing, I got below base64 string which apparently reached backend and caused the application to fail giving an error
java.lang.IllegalArgumentException: Last unit does not have enough valid bits
Invalid base64 string
i82BsXc8DRypceSt44pLm5jWxuzFXcW5er50Rr0wkxIvXcoj
AAJ0wpsfRL7SUcIfOnJIcLwcU5/9KM3DXP7m0piOCRon0xlUNUn7G KBFQs2JRNH6ZwR3v
/7lQZWQ0MMSGxvey87pJvGxQUhfdkf4466HMSdtAY5p6kHaKaz4mRKD8UnLFJ
3m487kVtE5UGrblbrtSGczQK/ zvFNwAEIHpegbt298hQ
YbsYF1stCe7YbK7oi3QRebBstwl OyaPAkgeuno9EMrUBMrVBLSD1sT3bFeA5O8NF/3
k/Wyj2dLIbIBhe4ge2ZlyQJNKAMkQ==
I tried to run the string on the test function aesGCMWithRSAEncryptionTest(str) and the base64Check() did throw an exception which got caught by catch block so the application during actual testing time should have had been caught also.
I can provide more details if required but can anyone please explain me what's going wrong here? How can I resolve this?