如何正确地将加密(AES192)方法从nodejs移植到java



我正在尝试将此nodejs代码移植到java

const crypto = require("crypto");
const encrypt = (data, key) => {
const cipher = crypto.createCipher('aes192', key)
let crypted = cipher.update(data, 'utf8', 'hex')
crypted += cipher.final('hex')
return crypted;
}

我尝试过使用这个解决方案:

import org.springframework.security.crypto.codec.Hex;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
public String encrypt(String data, String key) {
try {
var cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key.getBytes(), "AES"));
var cipherText = cipher.update(data.getBytes());
cipherText = ArrayUtils.addAll(cipherText, cipher.doFinal());
return new String(Hex.encode(cipherText));
} catch (Exception e) {
e.printStackTrace();
}
return null;
}

缺点:

Java的encrypt方法返回的值与nodejs的方法不同(对于相同的数据和键(。
  • 在nodejs中,我可以放一个短键(5个字符长(,同时在java中我会捕获一个异常,例如";java.security.InvalidKeyException:无效的AES密钥长度:5字节">

  • 你能提出正确的解决方案或指出现有解决方案中的错误吗?提前谢谢!

    注意:我无法在nodejs中更改加密/解密方法,所以我需要将其正确地移植到java。

    encryptjava版本与javascript版本不使用相同的逻辑。

    encryptjavascript函数接受密码(参数名称key具有误导性(,然后将其传递给createCiphercreateCipher不直接使用密码,而是从中派生出一个密钥。它是用于加密消息的派生密钥。来自NodeJ的文档:

    The password is used to derive the cipher key and initialization vector (IV). The value must be either a 'latin1' encoded string, a Buffer, a TypedArray, or a DataView.
    

    另一方面,encryptjava函数需要一个现成的密钥,因此您必须自己从密码中派生密钥。

    AES密钥具有固定大小。它们只能是128、192或256位长。(以字节8、16、32为单位(。如果使用不同大小的密钥,则会得到异常InvalidKeyException。NodeJS没有抱怨一个";无效密钥长度";因为实际上你使用的是密码,而不是钥匙。NodeJS在加密数据之前从密码中派生一个密钥。

    (如文档中所述(NodeJs使用OpenSSL对数据进行加密,并使用OpenSSL特有的函数导出密钥:EVP_BytesToKey

    幸运的是,这个SO答案在Java中实现了EVP_BytesToKey。(代码来自此博客条目(

    我修改了你的代码以使用它。我在答案的末尾添加了最终结果。我很少写安全代码,在这种情况下,我只是调整了现有的解决方案,所以如果你决定使用它,你应该审查代码(或者让其他人审查,如果你的公司有安全团队的话(。

    最后一条评论:createCipher已弃用。但是您在问题中说,您不能更改javascriptencrypt版本的实现。(如果您不是encrypt的维护者(您应该与维护者讨论弃用问题,以了解他们仍然使用createCipher(使用EVP_BytesToKey(的原因。EVP_BytesToKey被认为是一个弱密钥派生函数,OpenSSL建议在较新的应用程序中使用更现代的函数。

    import org.apache.commons.lang3.ArrayUtils;
    import org.springframework.security.crypto.codec.Hex;
    import javax.crypto.Cipher;
    import javax.crypto.spec.IvParameterSpec;
    import javax.crypto.spec.SecretKeySpec;
    import java.nio.charset.StandardCharsets;
    import java.security.MessageDigest;
    public class Main {
    public static void main(String[] args){
    System.out.println("Result : " + encrypt("my secret message","pass"));
    }
    public static byte[][] EVP_BytesToKey(int key_len, int iv_len, MessageDigest md, byte[] salt, byte[] data, int count) {
    byte[][] both = new byte[2][];
    byte[] key = new byte[key_len];
    int key_ix = 0;
    byte[]  iv = new byte[iv_len];
    int iv_ix = 0;
    both[0] = key;
    both[1] = iv;
    byte[] md_buf = null;
    int nkey = key_len;
    int niv = iv_len;
    int i = 0;
    if(data == null) {
    return both;
    }
    int addmd = 0;
    for(;;) {
    md.reset();
    if(addmd++ > 0) {
    md.update(md_buf);
    }
    md.update(data);
    if(null != salt) {
    md.update(salt,0,8);
    }
    md_buf = md.digest();
    for(i=1;i<count;i++) {
    md.reset();
    md.update(md_buf);
    md_buf = md.digest();
    }
    i=0;
    if(nkey > 0) {
    for(;;) {
    if(nkey == 0) break;
    if(i == md_buf.length) break;
    key[key_ix++] = md_buf[i];
    nkey--;
    i++;
    }
    }
    if(niv > 0 && i != md_buf.length) {
    for(;;) {
    if(niv == 0) break;
    if(i == md_buf.length) break;
    iv[iv_ix++] = md_buf[i];
    niv--;
    i++;
    }
    }
    if(nkey == 0 && niv == 0) {
    break;
    }
    }
    for(i=0;i<md_buf.length;i++) {
    md_buf[i] = 0;
    }
    return both;
    }
    public static String encrypt(String data, String password) {
    try {
    MessageDigest md5 = MessageDigest.getInstance("MD5");
    var cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    var keySizeBits = 192 / Byte.SIZE; //AES with 192 bits key = 16 bytes
    var ivSize = cipher.getBlockSize();
    final byte[][] keyAndIV = EVP_BytesToKey(keySizeBits, ivSize, md5, null, password.getBytes(StandardCharsets.US_ASCII), 1);
    SecretKeySpec key = new SecretKeySpec(keyAndIV[0], "AES");
    IvParameterSpec iv = new IvParameterSpec(keyAndIV[1]);
    cipher.init(Cipher.ENCRYPT_MODE, key, iv);
    var cipherText = cipher.update(data.getBytes());
    cipherText = ArrayUtils.addAll(cipherText, cipher.doFinal());
    return new String(Hex.encode(cipherText));
    } catch (Exception e) {
    e.printStackTrace();
    }
    return null;
    }
    }
    

    最新更新