首页 > 解决方案 > 在 Angular 中加密并在 C# 中解密

问题描述

我有在 Angular 上加密数据的代码,但我不知道如何在服务器端解密

  var panno = CryptoJS.AES.encrypt("FEAPS8905Q", "myPassword").toString();

以角度加密,U2FsdGVkX19mi5mXlJ14Lj0XcJBbqMPDzi/UeNXK4Cw=在使用 Http.post 方法发送加密后,我没有得到确切的数据,而是得到楀뢖᷈鍩ԏ건뫨샞일䜍钚䁞

我也使用了这个参考Decrypting on C#,但我得到了一些数据,比如 壓섢⻫捼笺ﵑ戛ꔉ됒퍿誁累♟꘶콒ꚦ

public string Decrypt(string cipherText)
    {
        string EncryptionKey = "myPassword";
        cipherText = cipherText.Replace(" ", "+");
        byte[] cipherBytes = Convert.FromBase64String(cipherText);
        using (Aes encryptor = Aes.Create())
        {
            Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(EncryptionKey, new byte[] {  
            0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76  });
            encryptor.Key = pdb.GetBytes(32);
            encryptor.Padding = PaddingMode.None;
            encryptor.IV = pdb.GetBytes(16);
            using (MemoryStream ms = new MemoryStream())
            {
                using (CryptoStream cs = new CryptoStream(ms, encryptor.CreateDecryptor(), CryptoStreamMode.Write))
                {
                    cs.Write(cipherBytes, 0, cipherBytes.Length);
                    cs.Close();
                }
                cipherText = Encoding.Unicode.GetString(ms.ToArray());
            }
        }
        return cipherText;
    }

标签: c#angularencryptioncryptographycryptojs

解决方案


CryptoJS.AES.encrypt(text, password)使用派生算法从您的密码中隐式派生加密密钥和 iv,这对于 C# 来说不是原生的。与其依赖隐式推导 - 最好自己明确地这样做,使用众所周知的算法,如 PBKDF2。

需要密钥派生,因为您的密码可以具有任意大小,但给定算法 (AES) 需要特定大小的密钥,例如 256 位。所以我们需要从任意长度的密码变成固定大小的密钥(以不可逆的方式)。

示例 JavaScript 代码:

function encrypt (msg, pass) {
  // random salt for derivation
  var keySize = 256;
  var salt = CryptoJS.lib.WordArray.random(16);
  // well known algorithm to generate key
  var key = CryptoJS.PBKDF2(pass, salt, {
      keySize: keySize/32,
      iterations: 100
    });
  // random IV
  var iv = CryptoJS.lib.WordArray.random(128/8);      
  // specify everything explicitly
  var encrypted = CryptoJS.AES.encrypt(msg, key, { 
    iv: iv, 
    padding: CryptoJS.pad.Pkcs7,
    mode: CryptoJS.mode.CBC        
  });
  // combine everything together in base64 string
  var result = CryptoJS.enc.Base64.stringify(salt.concat(iv).concat(encrypted.ciphertext));
  return result;
}

在 C# 中解密它现在很容易:

public static string Decrypt(string cipherText, string password) {
    byte[] cipherBytes = Convert.FromBase64String(cipherText);
    using (Aes encryptor = Aes.Create()) {
        // extract salt (first 16 bytes)
        var salt = cipherBytes.Take(16).ToArray();
        // extract iv (next 16 bytes)
        var iv = cipherBytes.Skip(16).Take(16).ToArray();
        // the rest is encrypted data
        var encrypted = cipherBytes.Skip(32).ToArray();
        Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(password, salt, 100);
        encryptor.Key = pdb.GetBytes(32);
        encryptor.Padding = PaddingMode.PKCS7;
        encryptor.Mode = CipherMode.CBC;
        encryptor.IV = iv;
        // you need to decrypt this way, not the way in your question
        using (MemoryStream ms = new MemoryStream(encrypted)) {
            using (CryptoStream cs = new CryptoStream(ms, encryptor.CreateDecryptor(), CryptoStreamMode.Read)) {
                using (var reader = new StreamReader(cs, Encoding.UTF8)) {
                    return reader.ReadToEnd();
                }
            }
        }
    }
}    

如果您了解后果,您可以使用固定盐(或例如应用程序中每个用户的固定盐),并减少 PB​​KDF2 中的迭代次数。不过不要使用固定的 IV,也不要使用部分密钥作为 IV。


推荐阅读