node.js - 使用crypto模块将java编写的DESede/ECB/NoPadding算法转换为Nodejs

标签 node.js des cryptojs

我正在致力于将代码从 java 迁移到 Nodejs。我有一个要求,即使用“DESede/ECB/NoPadding”算法用私钥加密文本。目前代码是用 Java 编写的,现在我需要迁移到 Nodejs。由于加密的 key 被发送到其他应用程序,因此我无法在此处更改算法或 key 。以下是java中使用的方法

1. Stored the private key in hex string. I.e. 48 chars hex string as below which is equivalent to 24 bytes reuquired for 3des
73AD9CEC99816AA6A4D82FB273AD9CEC99816AA6A4D82FB2
2. Following is code written in java 
https://github.com/dilipkumar2k6/3des/blob/master/TripleDes.java
import java.security.GeneralSecurityException;

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;


public class TripleDes {
    // Crypto library related keys
    private static final String ALGO_NAME = "DESede/ECB/NoPadding";
    private static final int PADDING_BLOCK = 8;

    // Test Data
    private static final String PLAIN_TEXT = "Hello World";
    private static final String SHARED_KEY = "73AD9CEC99816AA6A4D82FB273AD9CEC99816AA6A4D82FB2";

    public static void main(String[] arg) {

        try {
            // Get Algorithm name
            String desAlgoName = getDESAlgorithmName(ALGO_NAME);
            // Create Cipher object
            Cipher cipher = Cipher.getInstance(ALGO_NAME);
            //Actual DES algo needs 56 bits key, which is equivalent to 1byte (0 at 0th position)  Get 8*3 byets key
            byte [] key = hexFromString(SHARED_KEY);
            System.out.println("DES Algorithm  shared key size in bytes >> "+key.length);
            // Create SecretKeySpec
            SecretKeySpec secretKeySpec = new SecretKeySpec(key, desAlgoName);
            //Encrypt bytes
            byte [] encryptedBytes = encryptIntoBytes(cipher, secretKeySpec, PLAIN_TEXT.getBytes(), 0, PLAIN_TEXT.getBytes().length);
            String encryptedString=  hexToString(encryptedBytes);
            System.out.println(encryptedString);

        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    public static byte[] encryptIntoBytes(Cipher cipher, SecretKeySpec secretKeySpec, byte[] dct, int offset, int len) throws GeneralSecurityException {
        cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
        byte[] ect = cipher.doFinal(addPadding(dct, offset, len));
        return ect;
    }

    public static String getDESAlgorithmName(String algoName) {
        System.out.println("getDESAlgorithmName algoName >> "+algoName);
        String desAlgoName = null;
        int i = algoName.indexOf("/");
        if (i != -1)
            desAlgoName = algoName.substring(0, i);
        else
            desAlgoName = algoName;
        return desAlgoName;
    }

    /**
     * Adds padding characters to the data to be encrypted. Also adds random
     * Initial Value to the beginning of the encrypted data when using Triple
     * DES in CBC mode (DES-EDE3/CBC).
     * 
     * @param inData
     *            Array of bytes to be padded
     * @param offset
     *            Offset to starting point within array
     * @param len
     *            Number of bytes to be encrypted
     * @return Padded array of bytes
     */
    public static byte[] addPadding(byte[] inData, int offset, int len) {
        System.out.println("addPadding offset >> "+offset+", len >> "+len);
        byte[] bp = null;
        int padChars = PADDING_BLOCK; // start with max padding value
        int partial = (len + 1) % padChars; // calculate how many extra bytes
                                            // exist
        if (partial == 0) {
            padChars = 1; // if none, set to only pad with length byte
        } else {
            padChars = padChars - partial + 1; // calculate padding size to
                                                // include length
        }
        System.out.println("addPadding >> Add padding of "+padChars);
        /*
         * Create a byte array large enough to hold data plus padding bytes The
         * count of padding bytes is placed in the first byte of the data to be
         * encrypted. That byte is included in the count.
         */
        bp = new byte[len + padChars];
        bp[0] = Byte.parseByte(Integer.toString(padChars));
        System.arraycopy(inData, offset, bp, 1, len);
        return bp;
    }

    public static byte[] hexFromString(String hex) {
        int len = hex.length();
        byte[] buf = new byte[((len + 1) / 2)];

        int i = 0, j = 0;
        if ((len % 2) == 1)
            buf[j++] = (byte) fromDigit(hex.charAt(i++));

        while (i < len) {
            buf[j++] = (byte) ((fromDigit(hex.charAt(i++)) << 4) | fromDigit(hex
                    .charAt(i++)));
        }
        return buf;
    }

    public static int fromDigit(char ch) {
        if (ch >= '0' && ch <= '9')
            return ch - '0';
        if (ch >= 'A' && ch <= 'F')
            return ch - 'A' + 10;
        if (ch >= 'a' && ch <= 'f')
            return ch - 'a' + 10;

        throw new IllegalArgumentException("invalid hex digit '" + ch + "'");
    }

    public static String hexToString(byte[] ba) {
        return hexToString(ba, 0, ba.length);
    }

    public static final char[] hexDigits = { '0', '1', '2', '3', '4', '5',
            '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };

    public static String hexToString(byte[] ba, int offset, int length) {
        char[] buf = new char[length * 2];
        int j = 0;
        int k;

        for (int i = offset; i < offset + length; i++) {
            k = ba[i];
            buf[j++] = hexDigits[(k >>> 4) & 0x0F];
            buf[j++] = hexDigits[k & 0x0F];
        }
        return new String(buf);
    }

}

我需要将此代码迁移到 Nodejs 并面临多个问题。我引用了http://mygo.iteye.com/blog/2018882了解 Nodejs 在 des3 中进行加密的基本思路。但是我发现 JAVA 方式和 Nodejs 方式存在以下差异。

1. JAVA is using Hex string of 48 lenght as key, since one char in hex is 4 bits therfore final size is equivalent to 24 bytes length which meets DES3 requirement. 
2. In Java code, final key is being used as bytes (as needed by DES) which made indpendent of the way we store the key
3. In node js, key is stored as character i.e. to use des3 I have to use 24 bytes which is equivalent to 24 chars key as  73AD9CEC99816AA6A4D82FB2. Here this is string of 24 chars and since one char is one byte thereofore total length is 24 bytes which meets DES3 requirement.
4. Following is nodejs code for reference
https://github.com/dilipkumar2k6/3des/blob/master/Crypto.js
'use strict';
/*
 *  Offers related services.
 */
var crypto = require("crypto");

module.exports = {
    encrypt: function (plainText) {
        return encrypt({
            alg: 'des-ede3', //3des-ecb  
            autoPad: true,
            key: '73AD9CEC99816AA6A4D82FB2',
            plaintext: 'Hello World',
            iv: null
        });
    }
};

function encrypt(param) {
    var key = new Buffer(param.key);
    var iv = new Buffer(param.iv ? param.iv : 0);
    var plaintext = param.plaintext;
    var alg = param.alg;
    var autoPad = param.autoPad;

    //encrypt  
    var cipher = crypto.createCipheriv(alg, key, iv);
    cipher.setAutoPadding(autoPad);  //default true  
    var ciph = cipher.update(plaintext, 'utf8', 'hex');
    ciph += cipher.final('hex');
    console.log(alg, ciph);
    return ciph;

}

function decrypt(param) {
    var key = new Buffer(param.key);
    var iv = new Buffer(param.iv ? param.iv : 0)
    var alg = param.alg;
    var autoPad = param.autoPad;

    //decrypt  
    var decipher = crypto.createDecipheriv(alg, key, iv);
    cipher.setAutoPadding(autoPad);
    var txt = decipher.update(ciph, 'hex', 'utf8');
    txt += decipher.final('utf8');
    console.log(alg, txt);
    return txt;
}

以下是我的问题。

1. How can i convert my existing hex code into string? I used "hexToString" method (please check the java code)to convert hex into string. However getting weired character (this is also expected but problem is how i can use this transformed key in nodejs.
2. Can I pass byte array as key to Nodejs? It will make problem easy as I can easily convert my hex key into bytes array and I store my bytes array key in nodejs code.
3. In my javacode, I have custom padding logic, how can i write same logic in nodejs?
4. Most importantly, can I achieve same encryption logic in nodejs (similar to java)?

Artjom B.帮助我深入了解了nodejs和des3算法。我编辑了我的帖子以澄清我的确切要求。

我认为我的主要问题是,我如何将 byte[] 作为 DES3 的 Nodejs 加密的 key ?

我有点陷入困境。请帮忙。

最佳答案

运行crypto.getCiphers()会显示可用的密码。 ECB 模式下的三重 DES (EDE) 具有两个 key (16 字节 key ),可用作 des-ede。如果您有三部分 key (24 字节 key ),您应该使用 des-ede3ecb 可能不会出现在密码描述中,因为它是最基本的形式。

三重 DES-EDE 有不同的方式使用 key 。 EDE 表示使用三个不同 key 进行加密-解密-加密。例如,如果您只有一个 8 字节 key ,则这表明您对 EDE 的每个阶段使用相同的 key 。从您的 Java 代码中可以清楚地看出您有一个 24 字节 key (48 个十六进制编码字符)。您必须使用相同的 key

加密模块默认使用 PKCS7 填充,因此您需要设置 auto padding to false并自己做填充。我把这个任务留给你了。

module.exports = {
    encrypt: function (plainText) {
        return encrypt({
            alg: 'des-ede3', //3des-ecb  
            autoPad: false,
            key: '73AD9CEC99816AA6A4D82FB273AD9CEC99816AA6A4D82FB2',
            plaintext: 'Hello World',
            iv: null
        });
    }
};

function mypad(buf){
    // TODO: do the padding
    // replicate padding as in Java
    return buf;
}

function myunpad(buf){
    // TODO: do the unpadding
    // read the first *byte* and remove as many trailing *bytes*
    return buf;
}

function encrypt(param) {
    var key = new Buffer(param.key);
    var iv = new Buffer(param.iv ? param.iv : 0);
    var plaintext = mypad(new Buffer(param.plaintext));
    var alg = param.alg;
    var autoPad = param.autoPad;

    //encrypt  
    var cipher = crypto.createCipheriv(alg, key, iv);
    cipher.setAutoPadding(autoPad);  //default true  
    var ciph = cipher.update(plaintext, 'utf8', 'hex');
    ciph += cipher.final('hex');
    console.log(alg, ciph);
    return ciph;
}

function decrypt(param) {
    var key = new Buffer(param.key);
    var iv = new Buffer(param.iv ? param.iv : 0)
    var alg = param.alg;
    var autoPad = param.autoPad;

    //decrypt  
    var decipher = crypto.createDecipheriv(alg, key, iv);
    cipher.setAutoPadding(autoPad);
    var txt = decipher.update(ciph, 'hex', 'utf8');
    txt += decipher.final('utf8');
    console.log(alg, txt);
    return myunpad(new Buffer(txt, 'hex'));
}
<小时/>

警告:

不要使用 (3)DES,尤其是只有一个 8 字节 key 时!不要使用 ECB 模式!不要将 NoPadding 用于 block 模式!将 AES-256 与 GCM 模式结合使用(无填充,因为它是流模式)。-

关于node.js - 使用crypto模块将java编写的DESede/ECB/NoPadding算法转换为Nodejs,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29831300/

相关文章:

ruby-on-rails - Rails/execjs 找不到 Node.js

c# - C# 中的 DES 初始化向量

node.js - Nodejs `crypto.publicEncrypt` 不会使用 `ssh-keygen rsa` 生成的公钥

node.js - 如何使用请求使用转换流?

mysql - 更新突变时返回 null GraphQL 和 Sequelize

node.js - Node.exe 卡在 100% CPU

java - 如何使用 ExoPlayer 播放 DES 加密文件

c - 使用 crypt 函数时 C 中的段错误

javascript - 在 CryptoJS 中创建共享 key 签名失败

javascript - 从长字符串生成短哈希