c# - 来自 C# 的 crypto-js DES 解密

标签 c# encryption cryptojs cryptostream

我有一个使用 DES 加密的 C# 加密方法。我需要在 node.js api 中解密该值,我正在创建。我已经设法在 api 中重新创建了大部分解密方法,但是当我传入要解密的 secret 和值时,我得到了不同的结果。
加密.cs

public static string Encrypt(string toEncrypt, string key)
{
    var des = new DESCryptoServiceProvider();
    var ms = new MemoryStream();

    des.Key = HashKey(key, des.KeySize / 8);
    des.IV = HashKey(key, des.KeySize / 8);
    string s = Encoding.UTF8.GetString (des.Key);
    des.IV = Encoding.UTF8.GetBytes (key);
    byte[] inputBytes = Encoding.UTF8.GetBytes(toEncrypt);

    var cs = new CryptoStream(ms, des.CreateEncryptor(), CryptoStreamMode.Write);
    cs.Write(inputBytes, 0, inputBytes.Length);
    cs.FlushFinalBlock();

    return HttpServerUtility.UrlTokenEncode(ms.ToArray());
}

public static string Decrypt(string toDecrypt, string key)
{
    var des = new DESCryptoServiceProvider();
    var ms = new MemoryStream();

    des.Key = HashKey(key, des.KeySize / 8);
    des.IV = HashKey(key, des.KeySize / 8);
    byte[] inputBytes = HttpServerUtility.UrlTokenDecode(toDecrypt);

    var cs = new CryptoStream(ms, des.CreateDecryptor(), CryptoStreamMode.Write);
    cs.Write(inputBytes, 0, inputBytes.Length);
    cs.FlushFinalBlock();

    var encoding = Encoding.UTF8;
    return encoding.GetString(ms.ToArray());
}

public static byte[] HashKey(string key, int length)
{
    var sha = new SHA1CryptoServiceProvider();
    byte[] keyBytes = Encoding.UTF8.GetBytes(key);
    byte[] hash = sha.ComputeHash(keyBytes);
    byte[] truncateHash = new byte[length];
    Array.Copy(hash, 0, truncateHash, 0, length);
    return truncateHash;
}
这是我继承的代码,到目前为止我已经设法重新创建了这个:
应用程序.js
var keyHex = 'Secret'
var ciphertext = 'EncryptedValue'          
// Decrypt
var keyBytes = CryptoJS.enc.Utf8.parse(keyHex)
var sh1KeyVal = CryptoJS.SHA1(keyBytes)
var trunc = convertWordArrayToUint8Array(sh1KeyVal).slice(0, 8)
var decoded = decodeURI(ciphertext)
var key = trunc.toString(CryptoJS.enc.Utf8)
var bytes  = CryptoJS.DES.decrypt(decoded, key, { iv: key });
var originalText = bytes.toString(CryptoJS.enc.Utf8);
console.log('Message: ', originalText);
secret 的散列过程是我能够重新创建的,并且我已经确认值 trunc api 中的字节数组与 HashKey 相同方法输出。
但是,当我使用 var bytes = CryptoJS.DES.decrypt(decoded, key, { iv: key }); 进行简单加密时它提供了与 C# 方法不同的加密值,我认为这就是解密失败的原因。
我发现了一些东西,但我不确定如何解决,当我传递 key 的值和解密的值时,它们需要是字符串,但是在 C# 版本中,CryptoStream 采用字节数组,所以我必须做什么将值作为字符串传递给解密,我不确定这是否有效。
key 也是如此,DESCryptoServiceProvider接受 key 和 iv 作为字节数组,但是当我转换加密 js 截断数组时,它只是转换字节数组的文字文本。
我目前正在尝试使用以下方法进行转换:
var key = trunc.toString(CryptoJS.enc.Utf8)
我错过了这个过程中的一个步骤,我错过了什么吗?

最佳答案

在C#代码的加解密部分,IV是用des.IV = HashKey(key, des.KeySize / 8)确定的。 .在加密部分,这个值后来被 des.IV = Encoding.UTF8.GetBytes(key) 覆盖。 .
因此,加密和解密使用不同的 IV,这在 CBC 模式的上下文中会在解密后产生损坏的明文开头。除此之外,key 会抛出异常,因此 IV 长度不等于 8 字节。
据推测,IV 的覆盖是复制/粘贴错误。因此该行 des.IV = Encoding.UTF8.GetBytes(key)下面忽略。
除了这个问题,这两个代码在以下方面有所不同:

  • 在 C# 代码中, HttpServerUtility.UrlTokenEncode() 执行 Base64url编码,附加 Base64 填充字节 0、1 或 2 的数量,而不是通常的填充字节 ( = )。这不能被 decodeURI() 解码在 JavaScript 代码中使用,因为 decodeURI()解码 URL encoding .此外,由于 CryptoJS 可以处理 Base64 编码,因此转换为 Base64 比 Base64url 解码更有效。可以转换为 Base64,例如和:
    function toBase64(b64url){
        var b64withoutPadding = b64url.substring(0, b64url.length - 1).replace(/-/g, '+').replace(/_/g, '/')
        var numberPaddingBytes = parseInt(b64url.substring(b64url.length - 1))
        var b64 = b64withoutPadding.padEnd(b64withoutPadding.length + numberPaddingBytes, '=');
        return b64 
    }
    
  • 在 C# 代码中,SHA1 哈希的前 8 个字节用作 DES key 。 CryptoJS 代码需要 DES key 作为 WordArray ,这在 CryptoJS 代码中实现不正确。一个可能的实现是:
    var keyDES = CryptoJS.lib.WordArray.create(sh1KeyVal.words.slice(0, 8 / 4));
    

  • 通过这些更改,可以使用 CryptoJS 代码进行解密。在以下示例中,密文是使用 C# 代码生成的:

    var key = 'my passphrase'
    var ciphertextB64url = 'jUtdTa7mUnBrL1yW5uA85GrD2mwUFLOzzsiZH0chPWo1'
    var ciphertextB64 = toBase64(ciphertextB64url);
    
    var keyUtf8 = CryptoJS.enc.Utf8.parse(key)
    var sha1KeyVal = CryptoJS.SHA1(keyUtf8)
    var keyDES = CryptoJS.lib.WordArray.create(sha1KeyVal.words.slice(0, 8 / 4));
    
    var bytes  = CryptoJS.DES.decrypt(ciphertextB64, keyDES, { iv: keyDES });
    var originalText = bytes.toString(CryptoJS.enc.Utf8);
    console.log('Message: ', originalText); // Message:  The quick brown fox jumps...
    
    function toBase64(b64url){
        var b64withoutPadding = b64url.substring(0, b64url.length - 1).replace(/-/g, '+').replace(/_/g, '/')
        var numberPaddingBytes = parseInt(b64url.substring(b64url.length - 1))
        var b64 = b64withoutPadding.padEnd(b64withoutPadding.length + numberPaddingBytes, '=');
        return b64 
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js"></script>

    请注意,这两个代码都有严重的漏洞(也是注释):
  • DES 是一种不安全的算法。更好地使用例如AES。
  • SHA1 也不安全。最好选择例如SHA256
  • 从摘要中获取 key 是不安全的。最好使用可靠的 key 派生函数,如 Argon2 或 PBKDF2。
  • 使用 key 作为 IV 通常是不安全的。正确的方法是为每个加密生成一个随机 IV。非 secret IV 与密文(通常是串联的)一起传递。
  • 关于c# - 来自 C# 的 crypto-js DES 解密,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67617148/

    相关文章:

    node.js - 带有 AESEngine 和 PKCS7 填充的 bouncycaSTLe 的 PlatedBufferedBlockCipher 的等效密码是什么?

    javascript - NodeJs Crypto - MD5 哈希不正确

    c# - 绑定(bind) EasingColorKeyframe 值

    c# - 如何将颜色转换为代码,反之亦然

    c# - WMP 声音在其他计算机上不起作用

    javascript - CryptoJS 和 key /IV 长度

    java - AES/CBC/NoPadding 解密中的乱码输出

    c# - InputBinding 和 WebBrowser 控件

    java - 3DES 加密 Oracle/JAVA 等效

    java - 外部引用java安全策略