android 中的 AES 加密和 nodejs 中的解密

AES Encryption in android and decryption in nodejs

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

这是 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);

然后发送到服务器端。

服务端代码如下:

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

这里的最终结果'dec'没有给出正确的结果,但客户端和服务器的中间结果是相同的。这意味着,RSA 工作正常但在 AES 中有问题。

输出如下:

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

此处解密的 AES 密钥与我们在 android 中生成的密钥相同。但最终的输出并没有给出预期的结果。我的代码有什么错误吗??

Neardupe Decrypting strings from node.js in 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.

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

但是您的主要问题是 createDecipher 使用密码而不是密钥。 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.

您传递了实际上是密钥的密码;这导致 nodejs 使用与 Java 中使用的密钥完全不同的密钥,从而得到完全错误的结果。您应该改用 createDecipheriv 来获取密钥,以及 IV(初始化向量)。

那是你的另一个问题。要解密,您必须使用与加密相同的 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

顺便说一句,文档中的声明 'Initialization vectors [must] be unpredictable and unique ... [but not secret]' 对于 CBC 模式是正确的,但对于 OpenSSL(因此是 nodejs)和 Java 支持的其他一些模式则不正确。但是,这不是编程问题,因此在这里是题外话;它属于 crypto.SX 或可能 security.SX 已经回答了很多次。