代码之家  ›  专栏  ›  技术社区  ›  Eric

保护数据。保护间歇性故障

  •  2
  • Eric  · 技术社区  · 16 年前

    我正在编写密码加密程序。我写了下面的应用程序来说明我的问题。大约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;
                }
            }
        }
    }
    
    6 回复  |  直到 7 年前
        1
  •  6
  •   Eric    16 年前

    在一位同事的建议下,我选择了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;
                }
            }
        }
    }
    
        2
  •  1
  •   Nir    16 年前

    问题在于到Unicode的转换以及加密方法encoding.unicode.getString的结尾,只有当您给它的字节构成有效的utf-16字符串时,它才起作用。

    我怀疑有时候protectedData.protect的结果不是有效的utf-16字符串,因此encoding.unicode.getString会从返回的字符串中删除一些没有意义的字节,从而导致无法将字符串转换回加密数据。

        3
  •  1
  •   Community T.Woody    7 年前

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

    您也可以使用拉丁语-1 aka iso-8859-1 aka codepage 28591来处理这个场景,因为它映射的值范围是0-255不变的。以下是可互换的:

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

    使用此编码,您将始终能够转换字节[->字符串->字节[],而不会丢失。

    this post 用于演示此编码用法的示例。

        4
  •  1
  •   Vladimir Arustamian    7 年前

    这门课应该有帮助:

    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);
        }
    }
    
        5
  •  1
  •   Michael come lately PhiLho    7 年前

    你不应该使用 System.Text.Encoding 密码文本类。你 遇到间歇性错误。您应该使用base64编码和 System.Convert 类方法。

    1. 获取加密的 string 来自加密的 byte[] ,您应该使用:

      Convert.ToBase64String(byte[] bytes)
      
    2. 获得生的 字节[] 从A 一串 要加密,应使用:

      Convert.FromBase64String(string data)
      

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

        6
  •  0
  •   Stu Mackellar    16 年前

    我强烈怀疑是对encoding.unicode.getString的调用导致了这个问题。您需要确保传递给unprotect调用的数据是 确切地 与从保护调用返回的相同。如果您将二进制数据编码为Unicode文本作为临时步骤,那么您不能保证这一点。为什么您无论如何都需要这个步骤-为什么不只存储字节[]?