获取 CryptoJS 的 TripleDES 加密字符串的子字符串

Getting substring of a CryptoJS's TripleDES encrypted string

我有一个遗留 php 代码,它使用 TripleDES 方法加密,作为银行流程的一部分来加密购买操作。这是代码:

/******  3DES Function  ******/
function encrypt_3DES($message, $key){
    $l = ceil(strlen($message) / 8) * 8;
    return substr(openssl_encrypt($message . str_repeat("[=10=]", $l - strlen($message)), 'des-ede3-cbc', $key, OPENSSL_RAW_DATA, "[=10=][=10=][=10=][=10=][=10=][=10=][=10=][=10=]"), 0, $l);
}

我想使用 CriptoJS 的 TripleDES 在 javascript 中重现相同的代码。这是我目前的试用:

<script src=https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/rollups/tripledes.js></script>
<script>

    function encrypt_3DES(message, key) {
        // Parse input parameters
        message = CryptoJS.enc.Utf8.parse(message);
        key = CryptoJS.enc.Utf8.parse(key);
        // Build an iv with zero values
        let iv = CryptoJS.lib.WordArray.create(64/8);
        let encrypted = CryptoJS.TripleDES.encrypt(message, key, {iv: iv});
        // Encrypt with TripleDES
        return encrypted.toString();
    }

    let message = "testMessage";
    let key = "testKey";

    let encrypted = encrypt_3DES(message, key);
    // encrypted: "HLu4p18KtFfy5VjwjFkDHA=="
    
</script>

取示例值,结果是字符串 "HLu4p18KtFfy5VjwjFkDHA==".

问题是 jsphp 之间的结果略有不同,我的怀疑指向 php 端完成的 substr 操作,因为当我转换 open_ssl 操作的结果(没有子字符串)时,我的 javascript 代码返回相同的 base64 字符串:

<?php

/******  3DES Function  ******/
function encrypt_3DES($message, $key)
{
    $l = ceil(strlen($message) / 8) * 8;
    return substr(openssl_encrypt($message . str_repeat("[=12=]", $l - strlen($message)), 'des-ede3-cbc', $key, OPENSSL_RAW_DATA, "[=12=][=12=][=12=][=12=][=12=][=12=][=12=][=12=]"), 0, $l);
}

$message = "testMessage";
$key = "testKey";

$res = base64_encode(encrypt_3DES($message, $key));
$res2 = base64_encode(openssl_encrypt($message, 'des-ede3-cbc', $key, OPENSSL_RAW_DATA, "[=12=][=12=][=12=][=12=][=12=][=12=][=12=][=12=]"));

/* $res: HLu4p18KtFfTr47KvI/WEw== (My goal is to get this in javascript) */
/* $res2: HLu4p18KtFfy5VjwjFkDHA== (without the substr operation, the results match) */
?>

所以,我的问题是:有没有办法将相同的子字符串应用于 javascript 的 CryptoJS 操作的加密结果?

PHP 代码实现了自定义零填充。但是,默认的 PKCS#7 填充没有被禁用,因此除了显式零填充之外,一个完整的块不必要地附加了隐式 PKCS#7 填充。
使用 substr() 删除最后一个不必要的块。底线是简单地使用零填充。

更有效的 PHP 实现是使用 OPENSSL_ZERO_PADDING 禁用默认填充,而不是截断最后一个块:

return openssl_encrypt($message . str_repeat("[=10=]", $l - strlen($message)), 'des-ede3-cbc', $key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, "[=10=][=10=][=10=][=10=][=10=][=10=][=10=][=10=]"). 

无论如何,为了使C#代码return与PHP代码相同的密文,需要将C#代码中的填充更改为CryptoJS.pad.ZeroPadding


请注意,静态 IV(如此处使用的零 IV)不安全,TripleDES 速度慢(更好:AES)并且零填充不可靠(更好:PKCS#7)。
此外,TripleDES 的有效密钥大小为 24 bytes and 16 bytes.


测试:

下面的PHP代码:

$key = '012345678901234567890123';
$message = 'The quick brown fox jumps over the lazy dog';
print(base64_encode(encrypt_3DES($message, $key)) . PHP_EOL);

生成以下 Base64 编码的密文:

HNQad59lGkM83byQY2pwhluVkB7YOyIx/kGh/ibcIXQdFoiODkD1kTIIEHhqcoFm

固定的JavaScript密码给出了相同的密文。

function encrypt_3DES(message, key) {
    // Parse input parameters
    message = CryptoJS.enc.Utf8.parse(message);
    key = CryptoJS.enc.Utf8.parse(key);
    // Build an iv with zero values
    let iv = CryptoJS.lib.WordArray.create(64/8);
    let encrypted = CryptoJS.TripleDES.encrypt(message, key, {iv: iv, padding: CryptoJS.pad.ZeroPadding});
    // Encrypt with TripleDES
    return encrypted.toString();
}

let message = "The quick brown fox jumps over the lazy dog";
let key = "012345678901234567890123";

let encrypted = encrypt_3DES(message, key);
document.getElementById("ct").innerHTML = encrypted;
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js"></script>
<p style="font-family:'Courier New', monospace;" id="ct"></p>