为什么我总是在 nodejs 本机密码中得到错误的 des-ecb 结果?

Why did I always get wrong des-ecb result in nodejs native cipher?

这是我的代码:

const crypto = require('crypto')
let enterJS = 'h';
let enterStr = null;
enterStr = encrypt(enterJS, 'des-ecb').toUpperCase();
console.log("===============>>>> ENTER STR : " + enterStr);
function encrypt(plaintext, algorithm) {
    var keyStr = "imtestKey";
    var key = new Buffer(keyStr);
    var cipher = crypto.createCipher(algorithm, key);
    cipher.setAutoPadding(true);
    var ciph = cipher.update(plaintext, 'ascii');
    var ciphf = cipher.final();
    return ciph.toString('hex') + ciphf.toString('hex');
}

但是我得到的结果是:

===============>>>> ENTER STR : 16CE7F2DEB9BB56D

我在这个网站上测试的正确结果:http://tool.chacuo.net/cryptdes

des-mode:ecb

fill-mode:pkcs7padding

password:imtestKey

output:hex

正确的结果(与我的java代码相同)是

832e52ebd3fb9059

我的节点版本是v8.9.0,如何才能得到正确的结果?

这是我的 java 代码:

import java.lang.StringBuilder;
import javax.crypto.Cipher;
import java.security.SecureRandom;
import javax.crypto.spec.DESKeySpec;
import javax.crypto.SecretKeyFactory;
import javax.crypto.SecretKey;

public class Test {
    public static void main(String[] args) {
        String js = "h";
        try {
            byte[] bs = encrypt(js.getBytes(), "imtestKey".getBytes());
            System.out.println(byte2hex(bs));
        } catch(Exception ex) {

        }
    }

    public static byte[] encrypt(byte[] src, byte[] key) throws Exception {
        SecureRandom sr = new SecureRandom();
        DESKeySpec dks = new DESKeySpec(key);
        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
        SecretKey securekey = keyFactory.generateSecret(dks);
        Cipher cipher = Cipher.getInstance("DES");
        cipher.init(Cipher.ENCRYPT_MODE, securekey, sr);
        return cipher.doFinal(src);
    }


    public static String byte2hex(byte[] b) {
        StringBuilder sb = new StringBuilder();
        String stmp = "";

        for(int n = 0; b != null && n < b.length; ++n) {
            stmp = Integer.toHexString(b[n] & 255);
            if (stmp.length() == 1) {
                sb.append("0").append(stmp);
            } else {
                sb.append(stmp);
            }
        }

        return sb.toString().toUpperCase();
    }
}

除了安全方面(正如已经指出的那样,DES 和 ECB,以及没有密钥派生是不安全的),您使用的是已弃用的 crypto.createCipher() 函数,该函数 派生 来自提供的密码的密钥。

The implementation of crypto.createCipher() derives keys using the OpenSSL function EVP_BytesToKey with the digest algorithm set to MD5, one iteration, and no salt. The lack of salt allows dictionary attacks as the same password always creates the same key. The low iteration count and non-cryptographically secure hash algorithm allow passwords to be tested very rapidly.

改用 crypto.createCipheriv(),它按原样使用提供的密钥:

const crypto = require('crypto')
let enterJS = 'h';
let enterStr = null;
function encrypt(plaintext, algorithm) {
    var keyStr = "imtestKey";
    var key = Buffer.alloc(8, keyStr);
    var cipher = crypto.createCipheriv(algorithm, key, Buffer.alloc(0));
    cipher.setAutoPadding(true);
    var ciph = cipher.update(Buffer.from(plaintext));
    var ciphf = cipher.final();
    return Buffer.concat([ciph, ciphf]).toString('hex');
}
enterStr = encrypt(enterJS, 'des-ecb').toUpperCase();
console.log("===============>>>> ENTER STR : " + enterStr);

createCipheriv API 将拒绝您的 9 字节长密钥,因为 DES 需要 8 字节密钥。我做了一个解决方法,将提供的密码的前 8 个字节作为密钥,现在它正在打印您想要的结果。

输出:

===============>>>> ENTER STR : 832E52EBD3FB9059