在节点中加密,在java中解密

在节点中加密,在java中解密,java,node.js,encryption,openssl,aes,Java,Node.js,Encryption,Openssl,Aes,我有一个Java加密代码。我正在尝试将加密部分移植到节点。基本上,node将使用加密模块进行加密,然后Java将进行解密 以下是我如何使用Java进行加密: protected static String encrypt(String plaintext) { final byte[] KEY = { 0x6d, 0x79, 0x56, 0x65, 0x72, 0x79, 0x54, 0x6f, 0x70, 0x53, 0x65, 0x63

我有一个Java加密代码。我正在尝试将加密部分移植到节点。基本上,node将使用加密模块进行加密,然后Java将进行解密

以下是我如何使用Java进行加密:

protected static String encrypt(String plaintext) {
    final byte[] KEY = {
            0x6d, 0x79, 0x56, 0x65, 0x72, 0x79, 0x54, 0x6f, 0x70,
            0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x4b
    };

    try {
        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
        final SecretKeySpec secretKey = new SecretKeySpec(KEY, "AES");
        cipher.init(Cipher.ENCRYPT_MODE, secretKey);
        final String encryptedString = Base64.encodeToString(
            cipher.doFinal(plaintext.getBytes()), Base64.DEFAULT);

        return encryptedString;
    } catch (Exception e) {
        return null;
    }
}
以下是我如何在节点中进行加密:

var crypto = require('crypto'),
    key = new Buffer('6d7956657279546f705365637265744b', 'hex'),
    cipher = crypto.createCipher('aes-128-ecb', key),
    chunks = [];

cipher.setAutoPadding(true);
chunks.push(cipher.update(
    new Buffer(JSON.stringify({someKey: "someValue"}), 'utf8'),
    null, 'base64'));
chunks.push(cipher.final('base64'));

var encryptedString = chunks.join('');
在Java中,我得到字符串
t4rljo5env8h1uvmohzz1kjyxzbobuqvlsthspplja=
。这将被正确解密。然而,在节点中,我得到了
al084heptk7goygqrsgsgxf+WWKvNYhT4SC7MukrzHieM=
,这显然是不同的,因此无法正确解密

我试图寻找和我有同样问题的人,github问题是我能找到的最接近的问题。正如在那个问题中所建议的,我试着像这样运行openssl:

$ echo -e '{"someKey": "someValue"}' | openssl enc -a -e -aes-128-ecb -K "6d7956657279546f705365637265744b"
T4RlJo5ENV8h1uvmOHzz1MY2bhoFRHZ+ClxsV24l2BU=
我得到的结果与java生成的结果非常接近,但仍然不同:

T4RlJo5ENV8h1uvmOHzz1MY2bhoFRHZ+ClxsV24l2BU=  // openssl
T4RlJo5ENV8h1uvmOHzz1KjyXzBoBuqVLSTHsPppljA=  // java
al084hEpTK7gOYGQRSGxF+WWKvNYhT4SC7MukrzHieM=  // node

这就引出了一个问题,如何使节点输出与java代码相同的加密字符串?我只能在node中更改代码,但不能在java中更改代码。

最后,我找到了问题的解决方案。多亏了盖伊。解决方案的关键是初始化向量。引述要点:

//ECB模式不需要IV,所以保持这样,它会很好地工作

以下是解决方案的外观:

var crypto = require('crypto'),
    iv = new Buffer(''),
    key = new Buffer('6d7956657279546f705365637265744b', 'hex'),
    cipher = cypto.createCipheriv('aes-128-ecb', key, iv),
    chunks = [];

chunks.push(cipher.update(
    new Buffer(JSON.stringify({someKey: "someValue"}), 'utf8'),
    'buffer', 'base64'));
chunks.push(cipher.final('base64'));
var encryptedString = chunks.join('');

在Node.Js中加密和在Java中解密的工作示例:

要加密:

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;
}
私有静态字符串解密(字符串种子,字符串加密)引发异常{
byte[]keyb=seed.getBytes(“UTF-8”);
MessageDigest md=MessageDigest.getInstance(“MD5”);
字节[]thedigest=md.digest(keyb);
SecretKeySpec skey=新的SecretKeySpec(最常用的“AES”);
Cipher dcipher=Cipher.getInstance(“AES”);
dcipher.init(Cipher.DECRYPT_模式,skey);
byte[]clearbyte=dcipher.doFinal(toByte(加密));
返回新字符串(clearbyte);
}
专用静态字节[]toByte(字符串hexString){
int len=hexString.length()/2;
字节[]结果=新字节[len];
对于(int i=0;i
我认为我从节点和java两方面发布了完整的CBC示例(256而不是128): 如果获得java.security.InvalidKeyException,则必须安装java加密扩展(JCE)无限强度权限策略文件:

Java加密和解密。

在以下两个方向进行节点切换:

var crypto=require('crypto');
var iv=新缓冲区('0000000000000000');
//参考在缓冲区之间转换http://nodejs.org/api/buffer.html#buffer_new_buffer_str_encoding
//参考节点加密apihttp://nodejs.org/api/crypto.html#crypto_crypto_createcipheriv_algorithm_key_iv
//参考ECB与CBC密码方法http://crypto.stackexchange.com/questions/225/should-i-use-ecb-or-cbc-encryption-mode-for-my-block-cipher
var encrypt=函数(数据、密钥){
var decodeKey=crypto.createHash('sha256').update(key'utf-8').digest();
var cipher=crypto.createCipheriv('aes-256-cbc',decodeKey,iv);
返回cipher.update(数据'utf8','hex')+cipher.final('hex');
};
var decrypt=函数(数据、密钥){
var encodeKey=crypto.createHash('sha256').update(key,'utf-8').digest();
var cipher=crypto.createDecipheriv('aes-256-cbc',encodeKey,iv);
返回cipher.update(数据'hex','utf8')+cipher.final('utf8');
};
var decrypt128=函数(数据、密钥){
var encodeKey=crypto.createHash('sha256').update(key,'utf-8').digest();
var cipher=crypto.createDecipheriv('aes-128-cbc',新缓冲区(密钥,'hex'),
新缓冲区(
iv);;
返回cipher.update(数据'hex','utf8')+cipher.final('utf8');
};
var data='这是我的字符串'
变量键='1234567891123456';
var cipher=加密(数据、密钥);
var decipher=解密(密码、密钥);
console.log(密码);
控制台日志(解密);
//下面的字符串是从java端的“main”生成的
console.log(解密)(
“79D78BEFC0627B118A2ABC6BD9D544E83F929301432F202A6909EF18E0FDD1”,键);
console.log(解密)(
“3EB7CF373E108ACA93E85D170C000938A6B3DCCED53A4BFC0F5A18B7DDC02499”,
“d7900701209d3fbac4e214dfeb5f230f”);

重要信息:Matthew Payne answer只适用于“在节点中加密,在java中解密”而不是同时使用这两种格式,因此如果您想要两种格式,请不要复制和粘贴

每种格式都使用什么填充格式?您如何知道每个平台上加密的内容实际上是相同的(我认为没有理由假设节点的
JSON.stringify
输出与您作为
明文
传递给Java的内容相同)?Java正在使用PKCS5pAdd。据我所知,openssl也使用相同的方法(很抱歉,我丢失了链接)。对于节点,我不确定如何指定填充,这就是为什么我选择使用
cipher.setAutoPadding(true)
。编辑:找到链接。请参阅接受答案。两个输入都相同。当我再次测试时,我将
JSON.stringify
的输出(手动)传递给java
encrypt
,并使用一些转义字符:
encrypt(“{\'someKey\':\'someValue\'”)
您需要比较字节数组。相同外观的文本可以在不同的字符集中以不同的字节进行编码,并且
String#getBytes
明确地取决于平台字符集。转储字节数组。如果没有包含数组,java转储如下所示:
7B22736F6D654B6579223A22736F6D65566616C7565227D
,与节点中的外观相同。问题可能出在其他地方。这并不是真正的问题,问题是
createCipher
使用一个密码来派生密钥(IV也是派生的,但ECB忽略了这一点)。
createCipheriv
方法
    import java.security.MessageDigest;
    import javax.crypto.spec.SecretKeySpec;
    import javax.crypto.spec.IvParameterSpec;
    import javax.crypto.Cipher;
    import java.util.Base64;
    import javax.xml.bind.DatatypeConverter;

    public class AESExample {
        private static byte[] iv = "0000000000000000".getBytes();
        private static String decrypt(String encrypted, String seed)
                throws Exception {
            byte[] keyb = seed.getBytes("utf-8");
            MessageDigest md = MessageDigest.getInstance("SHA-256");
            byte[] thedigest = md.digest(keyb);
            SecretKeySpec skey = new SecretKeySpec(thedigest, "AES");
            Cipher dcipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            dcipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(seed.getBytes("UTF-8"), "AES"), new IvParameterSpec(iv));
            byte[] clearbyte = dcipher.doFinal(DatatypeConverter
                    .parseHexBinary(encrypted));
            return new String(clearbyte);
        }
        public static String encrypt(String content, String key) throws Exception {
            byte[] input = content.getBytes("utf-8");
            MessageDigest md = MessageDigest.getInstance("SHA-256");
            byte[] thedigest = md.digest(key.getBytes("utf-8"));
            SecretKeySpec skc = new SecretKeySpec(thedigest, "AES");
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key.getBytes("UTF-8"), "AES"), new IvParameterSpec(iv));
            byte[] cipherText = new byte[cipher.getOutputSize(input.length)];
            int ctLength = cipher.update(input, 0, input.length, cipherText, 0);
            ctLength += cipher.doFinal(cipherText, ctLength);
            return DatatypeConverter.printHexBinary(cipherText);
        }

public static String encrypt128(String content, String key) throws Exception {
        byte[] input = content.getBytes("utf-8");
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(DatatypeConverter.parseHexBinary(key), "AES"), new IvParameterSpec(iv));
         byte[] encrypted = cipher.doFinal(content.getBytes("UTF-8"));
        return DatatypeConverter.printHexBinary(encrypted);
    }

        public static void main(String[] args) throws Exception {
            String data = "Here is my string";
            String key = "1234567891123456";
            String cipher = AESExample.encrypt(data, key);
            String decipher = AESExample.decrypt(cipher, key);
            System.out.println(cipher);
            System.out.println(decipher);
            System.out.println(AESExample.encrypt(data, "1234567891123456"));
            System.out.println(AESExample.encrypt128(data, "d7900701209d3fbac4e214dfeb5f230f"));
        }
    }