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

2022-01-10 00:00:00 node.js cryptography android java

寻找一种在节点中加密数据(主要是字符串)并在安卓应用程序(java)中解密的方法.

Looking for a way to encrypt data (mainly strings) in node and decrypt in an android app (java).

在每一个中都已成功完成(在节点中加密/解密,在 java 中加密/解密),但似乎无法在它们之间工作.

Have successfully done so in each one (encrypt/decrypt in node, and encrypt/decrypt in java) but can't seem to get it to work between them.

可能我没有以相同的方式加密/解密,但是每种语言的每个库对相同的事物都有不同的名称...

Possibly I'm not encrypting/decrypting in the same way, but each library in each language has different names for same things...

任何帮助表示赞赏.

这里有一些代码:Node.js

here's some code: 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;
}

而加密的十六进制字符串被转换成这样的字节

while the encrypted hex string is converted to bytes like this

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

推荐答案

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

Apparently if you pass a passphrase to crypto.createCipher() it uses OpenSSL's EVP_BytesToKey() to derive the key. You can either pass a raw byte buffer and use the same to initialize Java's SecretKey, or emulate EVP_BytesToKey() in your Java code. Use $ man EVP_BytesToKey for more details, but essentially it hashes the passphrase multiple times with MD5 and concatenates a salt.

至于使用原始密钥,这样的事情应该可以让您使用原始密钥:

As for using a raw key, something like this should let you use a raw key:

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

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

Note that since you are using CBC, you need to use the same IV for encryption and decryption (you might want to append it to your message, etc.)

强制警告:自己实施加密协议很少是一个好主意.即使你让它工作,你会为所有消息使用相同的密钥吗?多长时间?如果您决定轮换密钥,您将如何管理它.等等,等等.

Mandatory warning: implementing a crypto protocol yourself is rarely a good idea. Even if you get this to work, are you going to use the same key for all messages? For how long? If you decide to rotate the key, how to you manage this. Etc, .etc.

相关文章