使用Node.js加密模块加密并使用Java解密(在Android应用程序中)

2022-09-03 18:20:09

寻找一种在节点中加密数据(主要是字符串)并在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

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

原始密钥是按如下方式创建的

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

答案 1

谢谢大家。您的回答和评论为我指出了正确的方向,经过更多的研究,我设法获得了一个工作原型(粘贴在下面)。事实证明,节点的加密使用MD5来散列密钥,并且填充显然是使用PKCS7Padding完成的(通过反复试验得到了那个)。

至于首先这样做的原因:我有一个由三部分组成的应用程序:A.后端服务B.第三方数据存储C.Android应用程序作为客户端。

后端服务准备数据并将其发布到第三方。Android应用程序获取和/或更新数据存储中的数据,服务可能会对其执行操作。

加密的需要是保持数据的私密性,即使来自第三方提供商也是如此。

至于密钥管理 - 我想我可以让服务器在每个预配置的时间段内创建一个新密钥,用旧密钥对其进行加密并将其发布到数据存储中以供客户端解密并开始使用,但对于我的需求来说,这有点过分了。

我还可以创建一个密钥对,并用它来每隔一段时间传输新的对称密钥,但这更过分了(更不用说工作了)。

任何人,这是代码:在节点上加密.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

在 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;
}

答案 2

显然,如果您将密码传递给它,它将使用OpenSSL来派生密钥。您可以传递一个原始字节缓冲区,并使用相同的缓冲区来初始化 Java 的 ,或者在 Java 代码中进行模拟。用于更多详细信息,但实质上它与MD5多次散列密码短语并连接盐。crypto.createCipher()EVP_BytesToKey()SecretKeyEVP_BytesToKey()$ man EVP_BytesToKey

至于使用原始密钥,像这样的东西应该让你使用原始密钥:

var c = crypto.createCipheriv("aes-128-ecb", new Buffer("00010203050607080a0b0c0d0f101112", "hex").toString("binary"), "");

请注意,由于您使用的是CBC,因此您需要使用相同的IV进行加密和解密(您可能希望将其附加到您的消息等。

强制警告:自己实现加密协议很少是一个好主意。即使您让它工作,您是否要对所有消息使用相同的密钥?多长时间?如果决定轮换密钥,请了解如何管理。等等,等等。


推荐