4

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

6 回答 6

9

在一位同事的建议下,我选择了 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;
            }
        }
    }
}
于 2008-10-29T14:45:38.570 回答
2

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

  1. 要从 encrypted 中获取stringencrypted byte[],您应该使用:

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

    Convert.FromBase64String(string data)
    

有关更多信息,请参阅MS 安全大师 Shawn Fanning 的帖子。

于 2011-12-30T18:29:53.850 回答
1

最好的解决方案是将字节数组转换为 base 64 字符串

对于这种情况,您还可以使用 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[] 而不会丢失。

有关说明此编码使用的示例,请参阅此帖子。

于 2008-10-29T14:31:30.143 回答
1

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

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

于 2008-10-29T15:13:36.097 回答
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);
    }
}
于 2017-03-03T13:54:30.697 回答
0

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

于 2008-10-29T13:56:09.747 回答