【问题标题】:Symmetric encrypt/decrypt in .NET.NET 中的对称加密/解密
【发布时间】:2011-01-10 04:16:33
【问题描述】:

我正在使用 C# 中的对称加密/解密例程。我知道以前关于这个话题有几个问题,但大多数答案似乎是关于加密的哲学,而不是给出实际的代码。

更新:我真的很想看一些代码,而不仅仅是链接。非常感谢!

【问题讨论】:

  • 我建议使用 Rijndael,然后 :)
  • 切勿将加密技术用于非预期用途。加密并非旨在保护完全信任的用户的秘密,因此不能安全地用于此目的。如果您的用户可以运行解密数据的程序,则用户可以解密数据。你试图做的是一个非常非常糟糕的主意,我强烈建议你不要这样做。
  • 埃里克,你有什么建议?
  • 使服务器在面对敌对客户端时更加健壮。
  • 当然有。用户没有在他们的家用计算机上执行交易,也没有向他们的家用计算机报告审计日志。至少涉及两台服务器:代理服务器和审计服务器。这些是您可以信任的,因此您的安全系统应该依赖它们,而不是假定的敌对用户。 (如果经纪人对你怀有敌意,客户和经纪人合谋敲诈你,那你的问题就更大了。)

标签: c# encryption


【解决方案1】:

查看this page底部的示例代码。

复制粘贴到这里:

int Rfc2898KeygenIterations= 100;
int AesKeySizeInBits = 128;
String Password = "VerySecret!";
byte[] Salt = new byte[16];
System.Random rnd = new System.Random(); 
rnd.NextBytes(Salt);
byte[] rawPlaintext = System.Text.Encoding.Unicode.GetBytes("This is all clear now!");
byte[] cipherText= null;
byte[] plainText= null;
using (Aes aes = new AesManaged())
{
    aes.Padding = PaddingMode.PKCS7;
    aes.KeySize = AesKeySizeInBits;
    int KeyStrengthInBytes= aes.KeySize/8;
    System.Security.Cryptography.Rfc2898DeriveBytes rfc2898 =
        new System.Security.Cryptography.Rfc2898DeriveBytes(Password, Salt, Rfc2898KeygenIterations);
    aes.Key = rfc2898.GetBytes(KeyStrengthInBytes);
    aes.IV = rfc2898.GetBytes(KeyStrengthInBytes);
    using (MemoryStream ms = new MemoryStream())
    {
        using (CryptoStream cs = new CryptoStream(ms, aes.CreateEncryptor(), CryptoStreamMode.Write))
        {
            cs.Write(rawPlaintext, 0, rawPlaintext.Length);
        }
        cipherText= ms.ToArray();
    }

    using (MemoryStream ms = new MemoryStream())
    {
        using (CryptoStream cs = new CryptoStream(ms, aes.CreateDecryptor(), CryptoStreamMode.Write))
        {
            cs.Write(cipherText, 0, cipherText.Length);
        }
        plainText = ms.ToArray();
    }
}
string s = System.Text.Encoding.Unicode.GetString(plainText);
Console.WriteLine(s);

【讨论】:

  • Ewww,使用非安全随机创建盐?坏主意。
  • @blowdart - 不。 Salt 和 IV 需要尽可能独特,但安全地生成它们毫无意义。安全随机意味着难以预测的随机,您并不关心您的 IV 或 salt 是否被预测 - 无论如何它们都会以明文形式传递/存储。
  • @blowdart - 我承认你关于 CBC 模式下 IV 的观点。但它不适用于 PBKDF2 中的盐。
  • 很抱歉解决这个问题,但是如果没有原始 aes 对象,您应该如何解密 cipherText?看起来你输了 IV。
  • @ginkner 密文包含IV
【解决方案2】:

这是我在 VB.NET 论坛上找到并转换为 C# 的简单解决方案。它确实帮助我更好地理解了这个主题。

// Shamelessly lifted from http://discuss.itacumens.com/index.php?topic=62872.0, 
// then converted to C# (http://www.developerfusion.com/tools/convert/vb-to-csharp/) and
// changed where necessary.
public class Encryptor
{
    private static SymmetricAlgorithm _cryptoService = new TripleDESCryptoServiceProvider(); 
    // maybe use AesCryptoServiceProvider instead?

    // vector and key have to match between encryption and decryption
    public static string Encrypt(string text, byte[] key, byte[] vector)
    {
        return Transform(text, _cryptoService.CreateEncryptor(key, vector));
    }

    // vector and key have to match between encryption and decryption
    public static string Decrypt(string text, byte[] key, byte[] vector)
    {
        return Transform(text, _cryptoService.CreateDecryptor(key, vector));
    }

    private static string Transform(string text, ICryptoTransform cryptoTransform)
    {
        MemoryStream stream = new MemoryStream();
        CryptoStream cryptoStream = new CryptoStream(stream, cryptoTransform, CryptoStreamMode.Write);

        byte[] input = Encoding.Default.GetBytes(text);

        cryptoStream.Write(input, 0, input.Length);
        cryptoStream.FlushFinalBlock();

        return Encoding.Default.GetString(stream.ToArray());
    }
}

【讨论】:

  • 如果系统的默认编码是不允许某些字符的编码,或者如果系统的默认编码在加密和描述之间发生变化,这可能会偶尔失败。要解决此问题,请将Encoding.Default 替换为Encoding.GetEncoding(437)
  • 另外,建议使用Create 方法创建加密提供者,如Aes.Create
【解决方案3】:

对于初学者来说,键不是字符串,键是二进制 blob。 PlainText 也是一样,它实际上不是文本,它还是一个二进制 blob。

现在当然可以使用Encoding.UTF8.GetBytes(message) 将字符串转换为字节数组,但是在来回转换键时会稍微复杂一些,通常使用Convert.ToBase64StringConvert.FromBase64String

不要忘记分组密码还需要一个东西,初始化向量,所以你的方法签名应该是

byte[] Encrypt(byte[] plainText, byte[] key, byte[] iv)

byte[] Decrypt(byte[] cipherText, byte[] key, byte[] iv)

密钥和 IV必须是加密安全的随机数,不要只键入它们,也不要使用 C# 的 Random 函数。密钥和 IV 的大小取决于使用的密码算法,并且可以通过类的属性访问。

要生成 CSRPNG,您可以执行以下操作

RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
byte[] key = new byte[algorithm.KeySizeValue / 8];
rng.GetBytes(key);
byte[] iv = new byte[algorithm.BlockSizeValue / 8];
rng.GetBytes(iv);

您还可以使用 Rfc2898DeriveBytes 类从密码和盐派生密钥和 IV,但盐应该是加密安全的随机数。您还应该注意,当您创建对称算法时,会为您生成安全密钥和 IV。

这样您就可以为您的文本选择正确的编码,无论是 UTF8、ASCII 还是其他。链接有足够的样本,所以在这里剪切和粘贴是毫无意义的。

【讨论】:

  • IV 不需要加密安全,只有密钥可以。
  • 不正确,在CBC模式下,IV必须在加密时不可预测
  • SymmetricAlgorithm 类也有方法 GenerateIV()GenerateKey()
【解决方案4】:

你想要的是类库中的加密服务提供者

喜欢这个用于 AES 的

http://msdn.microsoft.com/en-us/library/system.security.cryptography.aescryptoserviceprovider_members.aspx

【讨论】:

    【解决方案5】:

    首先使用您选择的编码将您的文本、密钥和初始化向量转换为字节。然后使用三重 DES 提供程序,如下所示:

    http://msdn.microsoft.com/en-us/library/system.security.cryptography.tripledes.aspx

    或者 AES,如果您认为三重 DES 太老了,或者其他什么。

    出于好奇,您打算如何传递密钥?

    【讨论】:

    • 所以你想加密数据以阻止用户阅读?
    • 对于 DES,key 和 IV 可以是任意字符串,对吧?
    • 在 .NET 应用程序的源代码中硬编码密钥是非常不安全的。用户所要做的就是下载 Reflector,密钥就在眼前。
    • @WillVousden,那我们该怎么办?
    • @WillVousden 用户如何下载 dll?
    【解决方案6】:

    发自MSDN documetation 的 AES 对称算法:

    using System;
    using System.IO;
    using System.Security.Cryptography;
    
    namespace Aes_Example
    {
        class AesExample
        {
            public static void Main()
            {
                string original = "Here is some data to encrypt!";
    
                // Create a new instance of the AesCryptoServiceProvider
                // class.  This generates a new key and initialization
                // vector (IV).
                using (AesCryptoServiceProvider myAes = new AesCryptoServiceProvider())
                {
                    // Encrypt the string to an array of bytes.
                    byte[] encrypted = EncryptStringToBytes_Aes(original, myAes.Key, myAes.IV);
    
                    // Decrypt the bytes to a string.
                    string roundtrip = DecryptStringFromBytes_Aes(encrypted, myAes.Key, myAes.IV);
    
                    //Display the original data and the decrypted data.
                    Console.WriteLine("Original:   {0}", original);
                    Console.WriteLine("Round Trip: {0}", roundtrip);
                }
            }
            static byte[] EncryptStringToBytes_Aes(string plainText, byte[] Key, byte[] IV)
            {
                // Check arguments.
                if (plainText == null || plainText.Length <= 0)
                    throw new ArgumentNullException("plainText");
                if (Key == null || Key.Length <= 0)
                    throw new ArgumentNullException("Key");
                if (IV == null || IV.Length <= 0)
                    throw new ArgumentNullException("IV");
                byte[] encrypted;
    
                // Create an AesCryptoServiceProvider object
                // with the specified key and IV.
                using (AesCryptoServiceProvider aesAlg = new AesCryptoServiceProvider())
                {
                    aesAlg.Key = Key;
                    aesAlg.IV = IV;
    
                    // Create an encryptor to perform the stream transform.
                    ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
    
                    // Create the streams used for encryption.
                    using (MemoryStream msEncrypt = new MemoryStream())
                    {
                        using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
                        {
                            using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
                            {
                                //Write all data to the stream.
                                swEncrypt.Write(plainText);
                            }
                            encrypted = msEncrypt.ToArray();
                        }
                    }
                }
    
                // Return the encrypted bytes from the memory stream.
                return encrypted;
            }
    
            static string DecryptStringFromBytes_Aes(byte[] cipherText, byte[] Key, byte[] IV)
            {
                // Check arguments.
                if (cipherText == null || cipherText.Length <= 0)
                    throw new ArgumentNullException("cipherText");
                if (Key == null || Key.Length <= 0)
                    throw new ArgumentNullException("Key");
                if (IV == null || IV.Length <= 0)
                    throw new ArgumentNullException("IV");
    
                // Declare the string used to hold
                // the decrypted text.
                string plaintext = null;
    
                // Create an AesCryptoServiceProvider object
                // with the specified key and IV.
                using (AesCryptoServiceProvider aesAlg = new AesCryptoServiceProvider())
                {
                    aesAlg.Key = Key;
                    aesAlg.IV = IV;
    
                    // Create a decryptor to perform the stream transform.
                    ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
    
                    // Create the streams used for decryption.
                    using (MemoryStream msDecrypt = new MemoryStream(cipherText))
                    {
                        using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
                        {
                            using (StreamReader srDecrypt = new StreamReader(csDecrypt))
                            {
    
                                // Read the decrypted bytes from the decrypting stream
                                // and place them in a string.
                                plaintext = srDecrypt.ReadToEnd();
                            }
                        }
                    }
                }
    
                return plaintext;
            }
        }
    }
    

    【讨论】:

      【解决方案7】:
      【解决方案8】:

      这种方法使用字符串作为加密密码和加密文本。它对于与需要字符串的非 .NET 代码进行互操作很有用。

      它使用 OEM 美国编码在二进制数据和字符串之间进行转换,因此唯一的转换是在每个 unicode 字符中添加或删除一个额外的 0 字节(相对于 UTF8 编码,它动态解释字符的字节)。

      public class AesEncryptor {
          readonly Encoding encoding = Encoding.GetEncoding(437); // Using the OEM United States encoding since it provides symmetric transform for 0..255.
          readonly AesManaged aes;
      
          /// <param name="password">Should be at least 32 characters.</param>
          public AesEncryptor(string password) {
              const int keySize = 256;
              const int saltSize = 16;
              var derivedBytes = new Rfc2898DeriveBytes(password, saltSize);
              aes = new AesManaged { Padding = PaddingMode.PKCS7, KeySize = keySize, Key = derivedBytes.GetBytes(keySize / 8), IV = derivedBytes.Salt };
          }
      
          public string Encrypt(string text) => CryptoTransform(text, aes.CreateEncryptor());
          public string Decrypt(string text) => CryptoTransform(text, aes.CreateDecryptor());
      
          string CryptoTransform(string text, ICryptoTransform cryptoTransform) {
              byte[] input = encoding.GetBytes(text);
              var stream = new MemoryStream();
              using (var cryptoStream = new CryptoStream(stream, cryptoTransform, CryptoStreamMode.Write))
                  cryptoStream.Write(input, 0, input.Length);
              return encoding.GetString(stream.ToArray());
          }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2013-04-22
        • 1970-01-01
        • 2010-12-13
        • 2013-06-04
        • 2011-01-10
        • 2011-08-19
        相关资源
        最近更新 更多