NodeJS“createDecipheriv”方法不适用于 Java 中的加密文本

问题描述 投票:0回答:1

我正在尝试使用 NodeJS 解密遗留数据库,但没有成功。该代码最初是用 Java 编写的。

已编辑

用于加密数据的Java源代码。

import java.util.*;

import javax.crypto.Cipher;
import javax.crypto.SecretKeyFactory;
import javax.crypto.SecretKey;
import javax.crypto.spec.DESKeySpec;
import java.util.Base64;

public class Main {
    public static void main(String[] args) {

        String encrypted = "";
      
        String secretCredentials = "abcdefghijklmnop";

        String plainText = "Text to encrypt!";
      
        String encoding = "UTF8";

        String strategy = "DES";
      
        try {
        
            byte[] secretCredentialsBytes = secretCredentials.getBytes(encoding);
        
            SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance(strategy);
        
            SecretKey secretKey = secretKeyFactory.generateSecret(new DESKeySpec(secretCredentialsBytes));
        
            Cipher cipher = Cipher.getInstance(strategy);
            
            cipher.init(Cipher.ENCRYPT_MODE, secretKey);
            
            byte[] plainTextBytes = plainText.getBytes();
              
            byte[] outputBytes = cipher.doFinal(plainTextBytes);
        
            byte[] encryptedTextBytes = Base64.getEncoder().encode(outputBytes);
  
            encryptedText = new String(encryptedTextBytes);
            
            System.out.println(encryptedText); //=> AIGBXYEWXz5w2Z3Fjqe5YiQbyR5eVbPW
          
        } catch (Exception e) {
            e.printStackTrace();
        }
      
    }
}

解密数据的NodeJS源码:

const {
  createDecipheriv
} = await import('crypto');

const encrypted = 'AIGBXYEWXz5w2Z3Fjqe5YiQbyR5eVbPW';

const encryptedAsBuffer = Buffer.from(encrypted, 'base64');

const credentials = 'abcdefghijklmnop';

let credentialsAsBuffer  = Buffer.from(credentials, 'utf-8');

const strategy = 'des-ede-cbc';

const iv = credentialsAsBuffer.subarray(credentialsAsBuffer.length - (credentialsAsBuffer.length - 8));

const decipher = createDecipheriv(strategy, credentialsAsBuffer, iv);

let decrypted = decipher.update(encryptedAsBuffer, 'base64', 'utf-8');
decrypted += decipher.final('utf-8');

console.log(decrypted);

但是返回错误...

node:internal/crypto/cipher:193
  const ret = this[kHandle].final();
                            ^

Error: error:1C800064:Provider routines::bad decrypt
    at Decipheriv.final (node:internal/crypto/cipher:193:29)
    at file:///... {
  library: 'Provider routines',
  reason: 'bad decrypt',
  code: 'ERR_OSSL_BAD_DECRYPT'
}

Node.js v20.11.0

我知道“DES”算法遇到了漏洞,但它是一个遗留系统。

node.js encryption des node-crypto
1个回答
0
投票

Java代码只指定了算法,没有指定模式,也没有指定填充。在这种情况下,将使用依赖于提供商的默认值。
对于 SunJCE 提供程序,这意味着 ECB 用作模式,PKCS#7 用作填充。反过来,欧洲央行意味着根本不涉及 IV。请注意,指定模式和填充更加透明,例如:

...
Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
...

您使用的密钥材料 (

abcdefghijklmnop
) 大小为 128 位(16 字节)。 DES仅应用64位密钥(其中仅使用56位,其余8位是奇偶校验位)。 Java 实现仅使用密钥材料的前 8 个字节,其余部分将被忽略。因此,Java 代码相当于:

...
secretCredentialsBytes = Arrays.copyOf(secretCredentialsBytes, 8);
...

此外,应在Java代码中指定

String
/
byte[]
转换的编码,否则将使用与平台相关的默认编码。关于您的测试数据,这很可能是适合您环境的 UTF-8。等效,但更透明:

...
byte[] secretCredentialsBytes = secretCredentials.getBytes(StandardCharsets.UTF_8);
...
String encryptedText = new String(encryptedTextBytes, StandardCharsets.UTF_8);
...

NodeJS 端口的主要问题似乎是您的 NodeJS 环境不支持已弃用的 DES。然而,与 DES 相比,根据您的测试,支持三重 DES
TripleDES 是 DES 的三次连续执行(作为加密/解密/加密)以提高安全性(但性能相应下降),使用至少两个不同的 DES 密钥 (2TDEA),或者更安全地使用三个不同的 DES 密钥( 3TDEA),s。 键控选项了解更多详细信息。
如果使用相同 DES 密钥而不是不同的 DES 密钥,则三重 DES 会简化为 DES。这样,可以用 Triple DES 来模仿 DES。对于 2TDEA 变体,8 字节 DES 密钥必须与其自身连接成 16 字节密钥(DES 密钥 | DES 密钥):

const encrypted = 'AIGBXYEWXz5w2Z3Fjqe5YiQbyR5eVbPW';

const stringKey = 'abcdefghijklmnop';
let keyAsBuffer = Buffer.from(stringKey, 'utf-8').subarray(0,8);
keyAsBuffer = Buffer.concat([keyAsBuffer, keyAsBuffer]); // DES key | DES key

const strategy = 'des-ede-ecb'; // Triple DES in 2TDEA variant, ECB mode 
const decipher = crypto.createDecipheriv(strategy, keyAsBuffer, null);
let decrypted = decipher.update(encrypted, 'base64', 'utf-8');
decrypted += decipher.final('utf-8');

console.log(decrypted); // Text to encrypt!

请注意,在指定算法时也可以使用

des-ede
,因为这是
des-ede-ecb
的别名,尽管模式的显式指定更加透明(注意:对于 DES,
des
des-cbc 的别名) 
)。有关说明符的概述,请参见例如openssl-enc,秒。 支持的密码
为了完整起见:3TDEA 变体中的三重 DES(对于 ECB)用
des-ede3-ecb
(或
des-ede3
)指定。在这种情况下,密钥必须连接到 24 字节密钥(DES 密钥 | DES 密钥 | DES 密钥)。


安全:
正如您自己已经提到的:DES 已经过时(大约 20 年前已弃用)并且不安全。 3TDEA 变体中的三重 DES 更安全(现在也已弃用),但性能相对较差。当前对称加密的标准是 AES。
欧洲央行也缺乏安全感。具有 IV 的模式(例如 CBC)更安全,经过身份验证的加密(例如 GCM)更安全。
另一个漏洞是仅使用字符集编码从字符串(由于其熵通常较低)直接派生密钥。如果应用密码,则应使用密钥导出函数(例如 Argon2 或至少 PBKDF2)与随机盐结合来导出密钥。

© www.soinside.com 2019 - 2024. All rights reserved.