Shh*_*Shh 14 java android cryptography node.js
寻找一种方法来加密节点中的数据(主要是字符串)并在android应用程序(java)中解密.
已成功地在每个中完成(在节点中加密/解密,在java中加密/解密)但似乎无法使它们在它们之间工作.
可能我不是以相同的方式加密/解密,但是每种语言中的每个库对于相同的事物都有不同的名称......
任何帮助赞赏.
这是一些代码:Node.js
var crypto = require('crypto')
var cipher = crypto.createCipher('aes-128-cbc','somepass')
var text = "uncle had a little farm"
var crypted = cipher.update(text,'utf8','hex')
crypted += cipher.final('hex')
//now crypted contains the hex representation of the ciphertext
Run Code Online (Sandbox Code Playgroud)
和java
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);
}
Run Code Online (Sandbox Code Playgroud)
原始密钥是这样创建的
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;
}
Run Code Online (Sandbox Code Playgroud)
而加密的十六进制字符串转换为这样的字节
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;
}
Run Code Online (Sandbox Code Playgroud)
Shh*_*Shh 11
感谢大家.你的回答和评论指出了我正确的方向,并通过一些更多的研究我设法得到一个工作原型(粘贴在下面).事实证明节点的加密使用MD5来对密钥进行散列,并且使用PKCS7Padding显然填充(使用试验和错误得到填充)
至于首先要做的原因:我有一个由三部分组成的应用程序:A.后端服务B.第三方数据存储C.作为客户端的Android应用程序.
后端服务准备数据并将其发布给第三方.Android应用程序获取和/或更新数据存储中的数据,服务可以对其进行操作.
加密的需要是将数据保密,即使是来自第三方提供商也是如此.
至于密钥管理 - 我想我可以让服务器在每个预先配置的时间段内创建一个新密钥,用旧密钥对其进行加密并将其发布到数据存储区,以便客户端解密并开始使用,但这有点过头了.我的需要.
我也可以创建一个密钥对,并使用它来每隔一段时间传输一个新的对称密钥,但这更是矫枉过正(更不用说工作了)
Anywho,这是代码:在Node.js上加密
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
Run Code Online (Sandbox Code Playgroud)
解密Java:
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;
}
Run Code Online (Sandbox Code Playgroud)
显然,如果您传递密码短语,crypto.createCipher()
则使用OpenSSL EVP_BytesToKey()
来获取密钥.您可以传递原始字节缓冲区并使用它来初始化Java SecretKey
,也可以EVP_BytesToKey()
在Java代码中进行模拟.使用$ man EVP_BytesToKey
更多的细节,但它本质上的密码多次使用MD5哈希并连接盐.
至于使用原始密钥,这样的东西应该让你使用原始密钥:
var c = crypto.createCipheriv("aes-128-ecb", new Buffer("00010203050607080a0b0c0d0f101112", "hex").toString("binary"), "");
请注意,由于您使用的是CBC,因此您需要使用相同的IV进行加密和解密(您可能希望将其附加到您的消息中等)
强制警告:自己实施加密协议很少是个好主意.即使你让它工作,你是否会对所有消息使用相同的密钥?多长时间?如果您决定旋转密钥,请如何管理.等等,等等.
您需要确保正在使用
在连接的两侧。
对于key,在Java方面,您正在使用大量工作从字符串派生密钥-在node.js方面没有做任何事情。在此使用标准的密钥派生算法(并且两侧使用相同的密钥)。
再看一遍
var cipher = crypto.createCipher('aes-128-cbc','somepass')
Run Code Online (Sandbox Code Playgroud)
确实确实进行了一些关键的派生,只是文档对它的确切作用保持沉默:
crypto.createCipher(算法,密码)
使用给定的算法和密码创建并返回一个密码对象。
algorithm
取决于OpenSSL,示例为'aes192'
,等等。在最新版本中,openssl list-cipher-algorithms
将显示可用的密码算法。password
用于导出密钥和IV,后者必须是已'binary'
编码的字符串(有关更多信息,请参见缓冲区)。
好的,这至少说明了如何对其进行编码,但没有说明在此做什么。因此,我们可以使用其他初始化方法crypto.createCipheriv
(直接获取键和初始化向量,而无需进行任何修改即可使用它们),或者查看源代码。
createCipher
将以某种方式调用node_crypto.cc中的C ++函数CipherInit。本质上,这使用EVP_BytesToKey
函数从提供的字符串(带有MD5,空盐和1)中导出密钥,然后执行与CipherInitiv
(由调用createCipheriv
,并直接使用IV和密钥)相同的功能。
由于AES使用128位密钥和初始化向量,而MD5具有128位输出,因此实际上意味着
key = MD5(password)
iv = MD5(key + password)
Run Code Online (Sandbox Code Playgroud)
(其中+表示串联,而不是加法)。如果需要,可以使用MessageDigest类在Java中重新实现此密钥派生。
一个更好的主意是使用一些慢速密钥派生算法,特别是如果您的密码可以被人们记住的话。然后,使用pbkdf2函数在node.js端生成此密钥,并PBKDF2WithHmacSHA1
在Java端使用PBEKeySpec和SecretKeyFactory(带有algorithm )一起生成。(选择一个迭代计数就不会让您的客户抱怨最常见的设备运行缓慢。)
对于密码算法,在Java方面,您说的是“使用AES算法以及此处的默认操作模式和默认填充模式”。不要这样做,因为它可能会因提供程序而异。
而是使用操作模式的显式指示(CBC
在您的情况下为),以及填充模式的显式指示。一个示例可能是:
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
Run Code Online (Sandbox Code Playgroud)
请查看node.js文档,以了解如何在此处指示填充模式(或在Java端选择填充模式的默认模式)。(从OpenSSL EVP文档中,这里看起来也默认为PKCS5Padding。)
另外,可以考虑使用TLS进行传输加密,而不是自己实施加密。(当然,这仅在双方之间存在实时连接时才有效。)
小智 5
尝试使用Java SE时,之前答案的示例对我不起作用,因为Java 7抱怨不能使用"AES/ECB/PKCS7Padding".
然而这有效:
加密:
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
Run Code Online (Sandbox Code Playgroud)
解密:
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;
}
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
19135 次 |
最近记录: |