使用Node.js加密模块加密,使用Java解密(在Android应用程序中)
正在寻找一种在节点中加密数据(主要是字符串)并在android应用程序(java)中解密的方法 在每个节点中都成功地完成了加密/解密(在节点中加密/解密,在java中加密/解密),但似乎无法在它们之间工作 可能我没有用同样的方式加密/解密,但每种语言中的每个库对相同的东西都有不同的名称 谢谢你的帮助 下面是一些代码: Node.js 和爪哇使用Node.js加密模块加密,使用Java解密(在Android应用程序中),java,android,node.js,cryptography,Java,Android,Node.js,Cryptography,正在寻找一种在节点中加密数据(主要是字符串)并在android应用程序(java)中解密的方法 在每个节点中都成功地完成了加密/解密(在节点中加密/解密,在java中加密/解密),但似乎无法在它们之间工作 可能我没有用同样的方式加密/解密,但每种语言中的每个库对相同的东西都有不同的名称 谢谢你的帮助 下面是一些代码: Node.js 和爪哇 private static String decrypt(byte[] raw, byte[] encrypted) throws Exception {
private static String decrypt(byte[] raw, byte[] encrypted) throws Exception {
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, skeySpec );
byte[] decrypted = cipher.doFinal(encrypted);
return new String(decrypted);
}
原始密钥是这样创建的
private static byte[] getRawKey(String seed) throws Exception {
KeyGenerator kgen = KeyGenerator.getInstance("AES");
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
byte[] seedBytes = seed.getBytes()
sr.setSeed(seedBytes);
kgen.init(128, sr); // 192 and 256 bits may not be available
SecretKey skey = kgen.generateKey();
byte[] raw = skey.getEncoded();
return raw;
}
public static byte[] toByte(String hexString) {
int len = hexString.length()/2;
byte[] result = new byte[len];
for (int i = 0; i < len; i++)
result[i] = Integer.valueOf(hexString.substring(2*i, 2*i+2), 16).byteValue();
return result;
}
而加密的十六进制字符串被转换成如下字节
private static byte[] getRawKey(String seed) throws Exception {
KeyGenerator kgen = KeyGenerator.getInstance("AES");
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
byte[] seedBytes = seed.getBytes()
sr.setSeed(seedBytes);
kgen.init(128, sr); // 192 and 256 bits may not be available
SecretKey skey = kgen.generateKey();
byte[] raw = skey.getEncoded();
return raw;
}
public static byte[] toByte(String hexString) {
int len = hexString.length()/2;
byte[] result = new byte[len];
for (int i = 0; i < len; i++)
result[i] = Integer.valueOf(hexString.substring(2*i, 2*i+2), 16).byteValue();
return result;
}
公共静态字节[]toByte(字符串hexString){
int len=hexString.length()/2;
字节[]结果=新字节[len];
对于(int i=0;i
您需要确保正在使用
- 同一把钥匙
- 相同的算法、操作模式和填充
var cipher = crypto.createCipher('aes-128-cbc','somepass')
确实进行了一些关键推导,只是:
crypto.createCipher(算法、密码)
使用给定的算法和密码创建并返回密码对象
算法
依赖于OpenSSL,例如'aes192'
等。在最新版本中,OpenSSL列表密码算法
将显示可用的密码算法password
用于派生密钥和IV,它们必须是'binary'
编码的
字符串(有关详细信息,请参阅)
好的,这至少说明了如何对其进行编码,但不是这里要做什么。因此,我们可以使用另一种初始化方法(直接获取键和初始化向量,不做任何修改就使用它们),也可以查看源代码
<>代码> CurtCeCiffe<代码>将在NoDEIONFROC.CC中以某种方式调用C++函数。本质上,它使用EVP_BytesToKey
函数从提供的字符串(使用MD5、空salt和count 1)派生密钥,然后执行与cipheritiv
相同的操作(由createCipheriv
调用,并直接使用IV和key)
由于AES使用128位的密钥和初始化向量,而MD5有128位的输出,这实际上意味着
key = MD5(password)
iv = MD5(key + password)
(其中+表示串联,而不是加法)。如果需要,可以使用MessageDigest类在Java中重新实现这个密钥派生
更好的办法是使用一些缓慢的密钥派生算法,特别是如果你的密码是人类可以记住的。然后在node.js端使用函数生成此密钥,在Java端使用PBEKeySpec和SecretKeyFactory(使用算法PBKDF2WithHmacSHA1
)生成此密钥。(选择一个迭代次数,它不会让您的客户抱怨最常见设备的运行缓慢。)
对于您的密码算法,在Java端,您说的是“将AES算法与缺省操作模式和缺省填充模式一起使用”。不要这样做,因为它可能会随着提供者的不同而变化
相反,使用操作模式的明确指示(CBC
,在您的情况下),以及填充模式的明确指示。一个例子可能是:
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
请查看node.js文档,了解如何在其中指示填充模式(或者默认为哪一种,以便在Java端选择相同的填充模式)。(从中,看起来默认值也是PKCS5P添加到此处。)
此外,不要自己实现加密,而是考虑使用TLS进行传输加密。(当然,这只在双方都有实时连接的情况下才有效。)
显然,如果将密码短语传递给crypto.createCipher()
它将使用OpenSSL的EVP\u BytesToKey()
来派生密钥。您可以传递原始字节缓冲区并使用它初始化Java的SecretKey
,或者在Java代码中模拟EVP\u BytesToKey()
。使用$man EVP_BytesToKey
了解更多详细信息,但本质上它使用MD5多次散列密码短语并连接salt
至于使用原始密钥,类似这样的东西应该允许您使用原始密钥:
var c=crypto.createCipheriv(“aes-128-ecb”,新缓冲区(“0001020305060708A0B0C0D0F101112”,“十六进制”)。toString(“二进制”),“”)代码>
请注意,由于您使用的是CBC,因此需要使用相同的IV进行加密和解密(您可能希望将其附加到消息中,等等)
强制性警告:自己实现加密协议很少是个好主意。即使你能做到这一点,你会对所有消息使用相同的密钥吗?要多久?如果决定旋转关键点,请说明如何进行管理。等等等等等等谢谢大家。你的回答和评论为我指明了正确的方向,通过进一步的研究,我成功地得到了一个工作原型(粘贴在下面)。
事实证明,节点的加密使用MD5对密钥进行散列,而填充显然是使用PKCS7Padding完成的(通过反复试验得到的)
至于首先要做这件事的原因:
我的申请由三部分组成:
A.后端服务
B.第三方数据存储
C.作为客户端的android应用程序
后端服务准备数据并将其发布给第三方。
android应用程序获取和/或更新数据存储中的数据,该服务可能会根据这些数据进行操作
加密的需要是保持数据的私密性,即使来自第三方提供商
至于密钥管理,我想我可以让服务器创建一个新的密钥e
public static String decrypt(String seed, String encrypted) throws Exception {
byte[] keyb = seed.getBytes("UTF-8");
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] thedigest = md.digest(keyb);
SecretKeySpec skey = new SecretKeySpec(thedigest, "AES/ECB/PKCS7Padding");
Cipher dcipher = Cipher.getInstance("AES/ECB/PKCS7Padding");
dcipher.init(Cipher.DECRYPT_MODE, skey);
byte[] clearbyte = dcipher.doFinal(toByte(encrypted));
return new String(clearbyte);
}
public static byte[] toByte(String hexString) {
int len = hexString.length()/2;
byte[] result = new byte[len];
for (int i = 0; i < len; i++)
result[i] = Integer.valueOf(hexString.substring(2*i, 2*i+2), 16).byteValue();
return result;
}
var crypto = require('crypto')
var cipher = crypto.createCipher('aes-128-ecb','somepassword')
var text = "the big brown fox jumped over the fence"
var crypted = cipher.update(text,'utf-8','hex')
crypted += cipher.final('hex')
//now crypted contains the hex representation of the ciphertext
private static String decrypt(String seed, String encrypted) throws Exception {
byte[] keyb = seed.getBytes("UTF-8");
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] thedigest = md.digest(keyb);
SecretKeySpec skey = new SecretKeySpec(thedigest, "AES");
Cipher dcipher = Cipher.getInstance("AES");
dcipher.init(Cipher.DECRYPT_MODE, skey);
byte[] clearbyte = dcipher.doFinal(toByte(encrypted));
return new String(clearbyte);
}
private static byte[] toByte(String hexString) {
int len = hexString.length()/2;
byte[] result = new byte[len];
for (int i = 0; i < len; i++) {
result[i] = Integer.valueOf(hexString.substring(2*i, 2*i+2), 16).byteValue();
}
return result;
}