【问题标题】:ProtectedData.Protect intermittent failureProtectedData.Protect 间歇性故障
【发布时间】:2008-10-29 13:41:38
【问题描述】:

我正在编写密码加密例程。我写了下面的应用程序来说明我的问题。大约 20% 的时间,此代码按预期工作。其余时间,解密会抛出一个加密异常——“数据无效”。

我相信问题出在加密部分,因为解密部分每次都一样。也就是说,如果加密例程产生一个解密例程可以解密的值,它总是可以解密它。但是,如果加密例程产生一个阻塞解密例程的值,它总是阻塞。所以解密程序是一致的;加密例程不是。

我怀疑我对 Unicode 编码的使用不正确,但我尝试过其他人的结果相同。

我做错了什么?

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Security.Cryptography;

namespace DataProtectionTest
{
    public partial class Form1 : Form
    {
        private static readonly byte[] entropy = { 1, 2, 3, 4, 1, 2, 3, 4 };
        private string password;
        public Form1()
        {
            InitializeComponent();
        }

        private void btnEncryptIt_Click(object sender, EventArgs e)
        {
            Byte[] pw = Encoding.Unicode.GetBytes(textBox1.Text);
            Byte[] encryptedPw = ProtectedData.Protect(pw, entropy, DataProtectionScope.LocalMachine);
            password = Encoding.Unicode.GetString(encryptedPw);     
        }

        private void btnDecryptIt_Click(object sender, EventArgs e)
        {
            Byte[] pwBytes = Encoding.Unicode.GetBytes(password);
            try
            {
                Byte[] decryptedPw = ProtectedData.Unprotect(pwBytes, entropy, DataProtectionScope.LocalMachine);
                string pw = Encoding.Unicode.GetString(decryptedPw);
                textBox2.Text = pw;
            }
            catch (CryptographicException ce)
            {
                textBox2.Text = ce.Message;
            }
        }
    }
}

【问题讨论】:

    标签: c# .net security encoding cryptography


    【解决方案1】:

    在同事的建议下,我选择了 Convert.ToBase64String。效果很好。更正了下面的程序。

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Text;
    using System.Windows.Forms;
    using System.Security.Cryptography;
    
    namespace DataProtectionTest
    {
        public partial class Form1 : Form
        {
            private static readonly byte[] entropy = { 1, 2, 3, 4, 1, 2, 3, 4 };
            private string password;
            public Form1()
            {
                InitializeComponent();
            }
    
            private void btnEncryptIt_Click(object sender, EventArgs e)
            {
                Byte[] pw = Encoding.Unicode.GetBytes(textBox1.Text);
                Byte[] encryptedPw = ProtectedData.Protect(pw, entropy, DataProtectionScope.LocalMachine);
                //password = Encoding.Unicode.GetString(encryptedPw);       
                password = Convert.ToBase64String(encryptedPw);
            }
    
            private void btnDecryptIt_Click(object sender, EventArgs e)
            {
                //Byte[] pwBytes = Encoding.Unicode.GetBytes(password);
                Byte[] pwBytes = Convert.FromBase64String(password);
                try
                {
                    Byte[] decryptedPw = ProtectedData.Unprotect(pwBytes, entropy, DataProtectionScope.LocalMachine);
                    string pw = Encoding.Unicode.GetString(decryptedPw);
                    textBox2.Text = pw;
                }
                catch (CryptographicException ce)
                {
                    textBox2.Text = ce.Message;
                }
            }
        }
    }
    

    【讨论】:

    • 这段代码仍然有一个错误......请在下面查看我的答案。
    • 将它与 Base64 字符串相互转换的行为解决了我的问题。以前我正在转换为 unicode 字符串。真的很有用。谢谢
    【解决方案2】:

    您不应该将任何System.Text.Encoding 类用于密文。您遇到间歇性错误。您应该使用 Base64 编码和 System.Convert 类方法。

    1. 要从加密的byte[] 中获取加密的string,您应该使用:

      Convert.ToBase64String(byte[] bytes)
      
    2. 要从string 获取要加密的原始byte[],您应该使用:

      Convert.FromBase64String(string data)
      

    更多信息,请参考MS Security guru Shawn Fanning's post.

    【讨论】:

      【解决方案3】:

      问题在于转换为 unicode 和加密方法的结束,Encoding.Unicode.GetString 仅在您给它的字节构成有效的 UTF-16 字符串时才有效。

      我怀疑有时 ProtectedData.Protect 的结果不是有效的 UTF-16 字符串 - 因此 Encoding.Unicode.GetString 会从返回的字符串中删除没有意义的字节,从而导致无法转换回的字符串进入加密数据。

      【讨论】:

        【解决方案4】:

        最好的解决办法是convert the byte array to a base 64 string

        对于这种情况,您还可以使用 Latin-1 aka ISO-8859-1 aka codepage 28591,因为它映射 0-255 范围内的值不变。以下是可以互换的:

        Encoding.GetEncoding(28591)
        Encoding.GetEncoding("Latin1")
        Encoding.GetEncoding("iso-8859-1")
        

        使用这种编码,您将始终能够转换 byte[] -> string -> byte[] 而不会丢失。

        请参阅this post 以获取说明如何使用此编码的示例。

        【讨论】:

          【解决方案5】:

          这个课程应该会有所帮助:

          public static class StringEncryptor
          {
              private static readonly byte[] key = { 0x45, 0x4E, 0x3A, 0x8C, 0x89, 0x70, 0x37, 0x99, 0x58, 0x31, 0x24, 0x98, 0x3A, 0x87, 0x9B, 0x34 };
          
              public static string EncryptString(this string sourceString)
              {
                  if (string.IsNullOrEmpty(sourceString))
                  {
                      return string.Empty;
                  }
          
                  var base64String = Base64Encode(sourceString);
                  var protectedBytes = ProtectedData.Protect(Convert.FromBase64String(base64String), key, DataProtectionScope.CurrentUser);
                  return Convert.ToBase64String(protectedBytes);
              }
          
              public static string DecryptString(this string sourceString)
              {
                  if (string.IsNullOrEmpty(sourceString))
                  {
                      return string.Empty;
                  }
          
                  var unprotectedBytes = ProtectedData.Unprotect(Convert.FromBase64String(sourceString), key, DataProtectionScope.CurrentUser);
                  var base64String = Convert.ToBase64String(unprotectedBytes);
                  return Base64Decode(base64String);
              }
          
              private static string Base64Encode(string plainText)
              {
                  var plainTextBytes = Encoding.UTF8.GetBytes(plainText);
                  return Convert.ToBase64String(plainTextBytes);
              }
          
              private static string Base64Decode(string base64EncodedData)
              {
                  var base64EncodedBytes = Convert.FromBase64String(base64EncodedData);
                  return Encoding.UTF8.GetString(base64EncodedBytes);
              }
          }
          

          【讨论】:

            【解决方案6】:

            我强烈怀疑是对 Encoding.Unicode.GetString 的调用导致了问题。您需要确保传递给 Unprotect 调用的数据与从 Protect 调用返回的数据完全相同相同。如果您将二进制数据编码为 Unicode 文本作为临时步骤,那么您无法保证这一点。为什么你仍然需要这一步 - 为什么不只存储字节[]?

            【讨论】:

            • 我正在转换回字符串,因为我需要将它作为 varchar 存储在数据库中。
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2017-04-13
            • 2011-10-27
            • 1970-01-01
            • 2012-02-20
            • 1970-01-01
            • 1970-01-01
            • 2018-10-25
            相关资源
            最近更新 更多