2
votes

I was trying encryption in android and decryption in nodejs server. I generated an AES 128bit key and encrypt it using AES algorithm and then encrypt this generated key using RSA algorithm. Then send both to the server. But while decrypting on the server side, I think the RSA decryption works fine but have a problem in AES decryption. I'm not getting the string in server side that I encrypted on the client side.

This is the code for the encryption on android side:

String encryptedSecretKey;
String cipherTextString;

// 1. generate secret key using AES
KeyGenerator keyGenerator = null;
keyGenerator = KeyGenerator.getInstance("AES");
keyGenerator.init(128);

// 2. get string which needs to be encrypted
String text = "This is the message to be encrypted";

// 3. encrypt string using secret key
byte[] raw = secretKey.getEncoded();
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
cipherTextString = Base64.encodeToString(cipher.doFinal(text.getBytes(Charset.forName("UTF-8"))), Base64.DEFAULT);

// 4. get public key
X509EncodedKeySpec publicSpec = new X509EncodedKeySpec(Base64.decode(publicKeyString, Base64.DEFAULT));
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey publicKey = keyFactory.generatePublic(publicSpec);

// 5. encrypt secret key using public key
Cipher cipher2 = Cipher.getInstance("RSA/ECB/OAEPWithSHA1AndMGF1Padding");
cipher2.init(Cipher.ENCRYPT_MODE, publicKey);
encryptedSecretKey = Base64.encodeToString(cipher2.doFinal(secretKey.getEncoded()), Base64.DEFAULT);

Then send this to the server side.

The code for server side is given below:

var encryptedMessage = req.body.cipherText;
var encryptedAesKey = req.body.secretKey;

//printing those values
console.log("\nEncryptedMessage: \n" + encryptedMessage);
console.log("\nEncrypted key: \n" + encryptedAesKey);

var privateKey = fs.readFileSync('././Keys/privkey_server.pem', "utf8");
var bufferForAesKey = new Buffer(encryptedAesKey, "base64");
var obj = {
        key: privateKey
        // , padding: constants.RSA_PKCS1_PADDING
        // , padding: constants.RSA/ECB/OAEPWithSHA-1
};
var decryptedAes = crypto.privateDecrypt(obj, bufferForAesKey);

console.log("Decrypted AES: " + decryptedAes);

var decryptedAesKeyString = decryptedAes.toString("base64");
console.log("Decrypted AES Key: " + decryptedAesKeyString);
var bufferForAES = new Buffer(decryptedAes, "base64");

//decrypting using AES
var bufferForEncryptedMsg = new Buffer(encryptedMessage, "base64");

var decipher = crypto.createDecipher('aes-128-cbc',bufferForAES);
decipher.setAutoPadding(false);
var dec = decipher.update(bufferForEncryptedMsg,"base64", "utf8");
dec += decipher.final("utf8");
console.log(dec);

Here the final result 'dec' is not giving the correct result but the intermediate results are same in client and server. That means, RSA works fine but have problem in AES.

The output is given below:

EncryptedMessage: 
SfosHg+cTrQXYUdF0FuqCJMHgfcP13ckp2L0B9QqOcl8UtWnnl8fLi5lxgR2SKOj


Encrypted key: 
C/pa52PZda3xShrPXkHZx8OL6sW4JBEhG/ggNAoHhSVXIGt+iDq/B1ByG5yStBGF3GFJRQT0aGsG
+bZJydP7j9gTivmt99H/bxiZan4CHZnqfGKG1dJCI7ILAYZMCw7JhIcRC8qHMM4LMdF+rxRhENDe
alUfnsLWpcrX9J6fKejJ7EWnWQ1VadKqCDmrJ5xw0lBbsOpwN/vY09+VhF4WkOz8Y3cQGk+FVdz5
tr4L9/jgXlTZdOC2KVBLSH+9pvtHwMWFKKoDSAzvkil4htBjbWTqlBuEINC4I/J/4P3RX2riT5Pv
xHQi/Dv7vdBlo9AEdvWe3Ek8oBleIpmIQHXwQWknPOYghhBAVmACG/tbEQcAtbcmRLruT/XzjPJt
HNBt2HeG9JHYKNoHC3kOuJdnlGe8mv8k0Nzwj04RhEGKSmPIiu/oDgYwS0l96KIlS2ELqBlS5O0L
AJ+RBG7m0WwC9dfrufsuwu0+SPUmg5/ElXRmA3T81lXtQqQbGg8G6r/bAVFGduy4a49s/VWoylx+
/sI079IwyY0IOfwQTVGZRyDC5O1ZBjoYv2+TRo3bjG8GXNQoybkmWkhgotcqVD9mXO67D2NBsFPT
EJnw+1ApSqR7ggIAF+qsMxejFKBICBL/4J8FP4+obA07J1pWiciTRKX+G130qzIBKM08Zdaf/50=

Decrypted AES: %Kp[ϪS�/�W l��9ӊ˽��~��
B�A�
Decrypted AES Key: JUtwW8+qU6Mv/FcgbMbkOdOKy72pun4B490KQrRB4QQ=
T�Ϝ��u��q�
          ���w�p���u`�̗r[`H0[tW��=��~i-�W

Here the Decrypted AES key is same as the key that we generate in android. But the final output is not giving the desired result. Is there any error in my code??

1
Are you doing this as a learning experience or do you intend on using it in production? If the latter, you're going about this all wrong... You should be using HTTPS.Luke Joshua Park
@LukeJoshuaPark Right now I'm trying to just encrypt the communication. Not for production.Neron Joseph
Is there a reason you can't use HTTPS? Even if what you had worked, it would be insecure in multiple ways. No authentication, no forward secrecy.Luke Joshua Park

1 Answers

2
votes

Neardupe Decrypting strings from node.js in Java? which is the same thing in the opposite direction.

[In Java] I generated an AES 128bit key and encrypt [with] it using AES algorithm and then encrypt this generated key using RSA algorithm.

No you didn't. Your Java code instantiates a KeyGenerator for AES-128, but doesn't use it to generate any key. The key you actually used (and as you say the server correctly decrypted from RSA-OAEP) is 32 bytes, corresponding to AES-256.

But your main problem is that createDecipher takes a password NOT the key. Per the doc

crypto.createDecipher(algorithm, password[, options])

The implementation of crypto.createDecipher() derives keys using the OpenSSL function EVP_BytesToKey with the digest algorithm set to MD5, one iteration, and no salt.

You passed what is actually a key as a password; this results in nodejs using a key that is completely different from the one used in Java and thus getting completely wrong results. You should instead use createDecipheriv which does take the key, and IV (Initialization Vector).

And that is your other problem. To decrypt you must use the same IV as encrypt did, normally by including the IV with the ciphertext sent from the sender to receiver, but you don't. As a result the following (simplified) code cannot decrypt the first 16 bytes of your data, but does the rest.

const crypto = require('crypto');
msg = Buffer.from('SfosHg+cTrQXYUdF0FuqCJMHgfcP13ckp2L0B9QqOcl8UtWnnl8fLi5lxgR2SKOj','base64');
aeskey = Buffer.from('JUtwW8+qU6Mv/FcgbMbkOdOKy72pun4B490KQrRB4QQ=','base64');
dec = crypto.createDecipheriv('aes-256-cbc',aeskey,Buffer.alloc(16)/*this should be the IV*/);
console.log(dec.update(msg,'','latin1')+dec.final('latin1'));
// I used latin1 instead of utf8 because the garbaged first block 
// isn't valid UTF-8, and the rest is ASCII which works as either.
->
Y;øï«*M2WÚâeage to be encrypted
// some garbaged chars are control chars and Stack (or browser?) 
// may not display them but there really are 16 in total

As an aside, the statement in the doc that 'Initialization vectors [must] be unpredictable and unique ... [but not secret]' is correct for CBC mode, but not some other modes supported by OpenSSL (thus nodejs) and Java. However, that's not a programming Q and thus offtopic here; it belongs on crypto.SX or possibly security.SX where it has already been answered many times.