AES在android中加密,nodejs中解密

时间:2022-08-22 18:24:00

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.

我在android上尝试加密,在nodejs服务器上进行解密。我生成了一个AES 128bit密钥并使用AES算法对其进行加密,然后使用RSA算法对生成的密钥进行加密。然后将两者都发送到服务器。但是在服务器端进行解密时,我认为RSA解密工作得很好,但是在AES解密时存在问题。我没有在服务器端获得我在客户端加密的字符串。

This is the code for the encryption on android side:

这是android端的加密代码:

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.

这里的最终结果'dec'并没有给出正确的结果,但是在客户端和服务器中,中间结果是相同的。这意味着RSA运行良好,但在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??

在这里,解密的AES密钥与我们在android中生成的密钥相同。但是最终的输出并没有给出期望的结果。我的代码有错误吗?

1 个解决方案

#1


2  

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

对节点上的字符串进行解密。js在Java中?方向相反。

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

我生成了一个AES 128bit密钥,并使用AES算法对其进行加密,然后使用RSA算法对生成的密钥进行加密。

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.

不,你没有。您的Java代码为AES-128实例化一个KeyGenerator,但不使用它生成任何密钥。您实际使用的密钥(正如您所说,服务器正确地从RSA-OAEP解密)是32字节,对应于AES-256。

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

但是您的主要问题是createDecipher采用密码而不是密钥。每一个医生

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

加密。createDecipher(算法、密码[选项])

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.

createdecipher()的实现使用OpenSSL函数evp_by睾酮来获取密钥,并将摘要算法设置为MD5,一次迭代,不加盐。

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).

你传递的实际上是一个密钥作为密码;这导致nodejs使用的键与Java中使用的键完全不同,从而得到完全错误的结果。您应该使用createDecipheriv,它确实使用了密钥和IV(初始化向量)。

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.

这是另一个问题。要解密,您必须使用与加密相同的IV,通常是将IV包含从发送方发送到接收方的密文,但您没有。因此,下面的(简化的)代码不能解密数据的前16个字节,但是可以解密其余的字节。

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.

顺便说一句,doc中关于“初始化向量(必须)是不可预测的和唯一的……”对CBC模式来说是正确的,但并不是OpenSSL(这样的nodejs)和Java支持的其他模式。但是,这并不是一个编程Q,因此在这里是偏离主题的;它属于加密。SX或安全。它已经被回答了很多次了。

#1


2  

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

对节点上的字符串进行解密。js在Java中?方向相反。

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

我生成了一个AES 128bit密钥,并使用AES算法对其进行加密,然后使用RSA算法对生成的密钥进行加密。

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.

不,你没有。您的Java代码为AES-128实例化一个KeyGenerator,但不使用它生成任何密钥。您实际使用的密钥(正如您所说,服务器正确地从RSA-OAEP解密)是32字节,对应于AES-256。

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

但是您的主要问题是createDecipher采用密码而不是密钥。每一个医生

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

加密。createDecipher(算法、密码[选项])

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.

createdecipher()的实现使用OpenSSL函数evp_by睾酮来获取密钥,并将摘要算法设置为MD5,一次迭代,不加盐。

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).

你传递的实际上是一个密钥作为密码;这导致nodejs使用的键与Java中使用的键完全不同,从而得到完全错误的结果。您应该使用createDecipheriv,它确实使用了密钥和IV(初始化向量)。

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.

这是另一个问题。要解密,您必须使用与加密相同的IV,通常是将IV包含从发送方发送到接收方的密文,但您没有。因此,下面的(简化的)代码不能解密数据的前16个字节,但是可以解密其余的字节。

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.

顺便说一句,doc中关于“初始化向量(必须)是不可预测的和唯一的……”对CBC模式来说是正确的,但并不是OpenSSL(这样的nodejs)和Java支持的其他模式。但是,这并不是一个编程Q,因此在这里是偏离主题的;它属于加密。SX或安全。它已经被回答了很多次了。