java - android中的AES加密和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从 Java 中的 node.js 中解密字符串?这是相反方向的同一件事。
[在 Java 中] 我生成了一个 AES 128 位密钥并使用 AES 算法对其进行加密,然后使用 RSA 算法加密这个生成的密钥。
不,你没有。您的 Java 代码KeyGenerator
为 AES-128 实例化 a,但不使用它来生成任何密钥。您实际使用的密钥(正如您所说的从 RSA-OAEP 正确解密的服务器)是 32 个字节,对应于 AES-256。
但是您的主要问题是createDecipher
需要密码而不是密钥。根据文档
crypto.createDecipher(算法,密码[,选项])
crypto.createDecipher() 的实现使用 OpenSSL 函数 EVP_BytesToKey 派生密钥,摘要算法设置为 MD5,一次迭代,无盐。
您传递了实际上是密钥的密码;这导致 nodejs 使用的密钥与 Java 中使用的密钥完全不同,从而得到完全错误的结果。您应该改用createDecipheriv
which does take key 和 IV (Initialization Vector)。
那是你的另一个问题。要解密,您必须使用与 encrypt 相同的 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
顺便说一句,文档中关于“初始化向量 [必须] 不可预测且唯一...... [但不是秘密]”的声明对于 CBC 模式是正确的,但对于 OpenSSL(因此 nodejs)和 Java 支持的其他一些模式则不正确。然而,这不是一个编程问题,因此这里离题了;它属于 crypto.SX 或可能是 security.SX ,它已经被回答了很多次。
推荐阅读
- firebase - Flutter:Firebase 实时数据库 orderByChild 对查询结果没有影响
- javascript - ExtJS 6.0.2:如何强制自定义进度条网格列在排序时更新
- python - 如何从输入的值中列出一个列表?
- sqlite - 为什么只执行 delete 语句?
- ruby - RVM 安装 Ruby 错误“文件名太长”
- javascript - 正确加载 Googe API 后未创建 gapi.client
- r - 在 R Shiny 中检测到 selectInput 值更改为 NULL
- exception - 从redis缓存反序列化java.time.Instant期间出现异常
- npm - npm script onchange/watch 仅对实际更改的文件而不是文件夹中的所有文件运行命令
- javascript - 尝试检查 beforeinstallprompt 是否未定义