ProtectedData.Protect间歇性失败

时间:2008-10-29 13:41:39

标签: c# .net security encoding cryptography

我正在编写密码加密例程。我写了下面的应用来说明我的问题。大约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 个答案:

答案 0 :(得分:6)

根据同事的建议,我选择了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;
            }
        }
    }
}

答案 1 :(得分:1)

最佳解决方案是convert the byte array to a base 64 string

对于此场景,您还可以使用Latin-1 aka ISO-8859-1 aka代码页28591,因为它将0-255范围内的值映射不变。以下是可以互换的:

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

使用此编码,您始终可以转换byte [] - > string - > byte []不丢失。

有关说明此编码使用情况的示例,请参阅this post

答案 2 :(得分:1)

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

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

答案 3 :(得分:1)

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

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

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

    Convert.FromBase64String(string data)
    
  3. 有关其他信息,请参阅MS Security guru Shawn Fanning's post.

答案 4 :(得分:1)

这堂课应该有所帮助:

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 :(得分:0)

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