C#:解码OAEP填充奇怪的问题时出错

时间:2017-06-08 13:33:17

标签: c# encryption cryptography smartcard

我目前正致力于使用RSACryptoServiceProvider执行主密钥加密和解密操作,使用来自智能卡的X509证书加密的随机生成的加密密钥对大量文本进行加密。但是,当我将fOEAP填充选项设置为true时,我在解码OAEP填充时会出现"错误"每次解密时出错。我检查了密钥大小,它在可接受的范围内。我已经通过断点来确保从加密函数返回的Base64字符串与加密的Base64字符串完全相同,后者在文件再次加载时会传递回解密函数。

密钥对绝对正确,因为没有OAEP它可以正常工作。我也检查了文本编码。

编辑:事实证明这可能是智能卡特定问题,当我尝试使用本地X509证书进行解密时,解密成功。

编辑:这是失败的解密代码:

string TestString = "Hello World!";
X509Certificate2 cert = DRXEncrypter.GetCertificate("Select a test certificate", "Select a certificate to use for this test from the local store.");
string key = DRXEncrypter.GenerateEncryptionKey(214);
Console.WriteLine("Encryption Key: " + key);

string encrypted = DRXEncrypter.EncryptBody(TestString, key);
Console.WriteLine("Encrypted Body: " + encrypted);

string cryptokey = DRXEncrypter.EncryptWithCert(cert, key);
Console.WriteLine("Encrypted Decryption Key: " + cryptokey);

string decrypted = DRXEncrypter.DecryptBody(encrypted, cryptokey, cert);
Console.WriteLine("Decrypted Body: " + decrypted);

Console.WriteLine("Output String: " + decrypted + ".");

以下是我编写的加密提供程序类的代码。我已经被困在这个问题上好几个小时了,所以如果有人可以帮助我的话会很棒。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.IO;

namespace CoreDRXEditor
{
public class DRXEncrypter
{
    private byte[] Salt = Encoding.ASCII.GetBytes("81PO9j8I1a94j");
    private string EncryptionKey;
    private const bool UseOAEP = true;

    public DRXEncrypter(string EncryptionKey)
    {
        this.EncryptionKey = EncryptionKey;
    }

    public static string EncryptBody(string body, string encryptionkey)
    {
        // Use the plaintext master key to encrypt the body.
        DRXEncrypter enc = new DRXEncrypter(encryptionkey);

        // Encrypt the body.
        return enc.Encrypt(body);
    }

    public static int GetMaxKeySize(X509Certificate2 cert)
    {
        RSACryptoServiceProvider csp = cert.PublicKey.Key as RSACryptoServiceProvider;

        return csp.KeySize;
    }

    public static string DecryptBody(string body, string encryptionkey, X509Certificate2 cert)
    {
        // Decrypt the encrypted encryption key with the certificate.
        string DecryptedKey = Convert.ToBase64String(DecryptWithCert(cert, encryptionkey));

        // Create a new DRXEncrypter using the decrypted encryption key to decrypt the body.
        DRXEncrypter enc = new DRXEncrypter(DecryptedKey);

        // Return the decrypted body.
        return enc.Decrypt(body);
    }

    public static string GenerateEncryptionKey(int KeyLength)
    {
        using (RandomNumberGenerator rng = new RNGCryptoServiceProvider())
        {
            byte[] CryptoBytes = new byte[KeyLength];
            rng.GetBytes(CryptoBytes);

            return Convert.ToBase64String(CryptoBytes);
        }
    }

    public static X509Certificate2 GetCertificate(string title, string message)
    {
        X509Store cstore = new X509Store(StoreLocation.CurrentUser);
        cstore.Open(OpenFlags.ReadOnly);

        X509CertificateCollection certs = X509Certificate2UI.SelectFromCollection(cstore.Certificates, title, message, X509SelectionFlag.SingleSelection);

        if (certs.Count == 1)
        {
            X509Certificate2 mcert = certs[0] as X509Certificate2;
            return mcert;
        }
        else
        {
            return null;
        }
    }

    public static string EncryptWithCert(X509Certificate2 cert, string PlainText)
    {
        RSACryptoServiceProvider csp = cert.PublicKey.Key as RSACryptoServiceProvider;

        byte[] PlainBytes = Convert.FromBase64String(PlainText);

        // This converts the plain text into a byte array and then encrypts the raw bytes.
        byte[] CryptoBytes = csp.Encrypt(PlainBytes, UseOAEP);

        // This converts the encrypted bytes into a Base64 string.
        string ReturnString = Convert.ToBase64String(CryptoBytes);

        return ReturnString;
    }

    public static byte[] DecryptWithCert(X509Certificate2 cert, string EncryptedText)
    {
        RSACryptoServiceProvider csp = cert.PrivateKey as RSACryptoServiceProvider;

        //CspParameters csps = new CspParameters();

        byte[] EncryptedBytes = Convert.FromBase64String(EncryptedText);

        // This converts the encrypted, Base64 encoded byte array from EncryptWithCert() to a byte[] and decrypts it.
        byte[] CryptoBytes = csp.Decrypt(EncryptedBytes, UseOAEP);

        return CryptoBytes;
    }

    public string Encrypt(string PlainText)
    {
        RijndaelManaged Algorithm = null;
        string Output = null;

        try
        {
            Rfc2898DeriveBytes PrivateKey = new Rfc2898DeriveBytes(this.EncryptionKey, this.Salt);


            Algorithm = new RijndaelManaged();
            Algorithm.Key = PrivateKey.GetBytes(Algorithm.KeySize / 8);
            Algorithm.Padding = PaddingMode.PKCS7;

            ICryptoTransform Encryption = Algorithm.CreateEncryptor(Algorithm.Key, Algorithm.IV);

            using (MemoryStream msa = new MemoryStream())
            {
                msa.Write(BitConverter.GetBytes(Algorithm.IV.Length), 0, sizeof(int));
                msa.Write(Algorithm.IV, 0, Algorithm.IV.Length);
                using (CryptoStream csa = new CryptoStream(msa, Encryption, CryptoStreamMode.Write))
                {
                    using (StreamWriter swa = new StreamWriter(csa))
                    {
                        swa.Write(PlainText);
                    }
                }
                Output = Convert.ToBase64String(msa.ToArray());
            }
        }
        finally
        {
            if (Algorithm != null)
            {
                Algorithm.Clear();
            }
        }

        return Output;
    }

    public string Decrypt(string EncryptedText)
    {
        RijndaelManaged Algorithm = null;
        string Output = null;

        try
        {
            Rfc2898DeriveBytes PrivateKey = new Rfc2898DeriveBytes(this.EncryptionKey, this.Salt);

            byte[] KeyBytes = Convert.FromBase64String(EncryptedText);
            using (MemoryStream msb = new MemoryStream(KeyBytes))
            {
                Algorithm = new RijndaelManaged();
                Algorithm.Key = PrivateKey.GetBytes(Algorithm.KeySize / 8);
                Algorithm.IV = ReadByteArray(msb);
                Algorithm.Padding = PaddingMode.PKCS7;
                ICryptoTransform Decryption = Algorithm.CreateDecryptor(Algorithm.Key, Algorithm.IV);
                using (CryptoStream csb = new CryptoStream(msb, Decryption, CryptoStreamMode.Read))
                {
                    using (StreamReader srb = new StreamReader(csb))
                    {
                        Output = srb.ReadToEnd();
                    }
                }

            }
        }
        finally
        {
            if (Algorithm != null)
            {
                Algorithm.Clear();
            }
        }

        return Output;
    }

    public static string Sha512(string ToHash)
    {
        using (SHA512 SHA = new SHA512Managed())
        {
            byte[] HashByte = Encoding.UTF8.GetBytes(ToHash);
            byte[] HashBytes = SHA.ComputeHash(HashByte);
            string Hash = System.Text.Encoding.UTF8.GetString(HashBytes, 0, HashBytes.Length);
            return Hash;
        }
    }

    public static string Base64Encode(string data)
    {
        byte[] str = Encoding.UTF8.GetBytes(data);
        return Convert.ToBase64String(str);
    }

    public static string Base64Decode(string data)
    {
        byte[] str = Convert.FromBase64String(data);
        return Encoding.UTF8.GetString(str);
    }

    private byte[] ReadByteArray(Stream st)
    {
        byte[] Length = new byte[sizeof(int)];
        st.Read(Length, 0, Length.Length);
        byte[] Buffer = new byte[BitConverter.ToInt32(Length, 0)];
        st.Read(Buffer, 0, Buffer.Length);

        return Buffer;
    }
}
}

3 个答案:

答案 0 :(得分:0)

确保您的钥匙尺寸不会太小或太大。

查看MSDN

的评论
  

RSACryptoServiceProvider支持从384位到16384的密钥大小   如果您有Microsoft增强,则以8位为增量的位   已安装加密提供程序。它支持384位的密钥大小   如果您有Microsoft Base,则以512位为增量,以8位为单位   已安装加密提供程序。

因此,您可能需要使用一些字节填充短键字符串以获得最小密钥长度

答案 1 :(得分:0)

好的,我设法检查了这个,从我能看到的,我有一些证书的问题。我不确定为什么有些证书有效,而有些证书不适用。在这种情况下,知道为什么某些证书失败会很好吗?

无论如何,我使用Windows"管理文件加密证书"创建了一个新的自签名证书。并使用此证书,似乎一切正常。

您的代码输出。

Encryption Key: aUc/GXWDoh2LktaEGeCJfju1dHP118yD/fzfT0iJLuhOq2QeyGpG6m3aBHaxvdH0ufeXRHbMjmlmPgIL/bhABzkT2C5Oa6ZhY3IFXb5t7JXZ3AtUunvtNAnRyFJ7MzklrSZGgQ
vF67DSNfIVE17doKt6j6mkCpSco56ooZCrOs2Mp3vSXqNjvjiwMEfQbk41aYUNVNVNlBGhdNQCIZIAKezQCUpWqzn2II27FIDfqDIEW4ieyzpXC05GzUlGXDxFOiFUPk3n0Y94vgeF8AlCD74eyZtz
WQ==
Encrypted Body: EAAAANS/W7+GGRbT1q5NCYvZlDZYtxaA8g55HzUqP5qxhenn
Encrypted Decryption Key: vc/tcsApmY1503BFi7oSu/RDvZivA1Ed58KJuLoEC6eE8q0BIa6ye2JvtXyxkVbzzL0MA51pZ2ZhMIsfCnBsEDjCgy+JLTZTGM1Mv+em9frFUKb0zHbICnPUa/3H
yd1yOWsdn5ws19QN2dzC6eau+ExhT2T/vyZO4Nf9NdHKnB8n2yB1rrQ/T+N2EYCNH/AVPDAsme6JG7k9Od2XIipBXMyCgXgWYZmQusq+JQjA9d3c4CrQYcg/ERF+K3oZv/gPicBkAR5taxwSxAajGg
bpkJNsbhTMHTN9bOn333qZ6ojlo5e882baZXuZWPr9qtj1b7ONoOyuSx/OvGKjt93BQg==
Decrypted Body: Hello World!
Output String: Hello World!.

希望有所帮助

答案 2 :(得分:0)

我今天用智能卡(或者更确切地说,启用了智能卡PIV小程序的Yubikey Neo)与此争论不休。使用此代码:

var encryptor = (RSACryptoServiceProvider)c.PublicKey.Key;
var decryptor = (RSACryptoServiceProvider)c.PrivateKey;

var encrypt = encryptor.Encrypt(bytes, RSAEncryptionPadding.Pkcs1);
var decrypt = decryptor.Decrypt(encrypt, RSAEncryptionPadding.Pkcs1);

我发现使用填充算法很重要。如果我使用PKCS1填充,一切正常。如果我使用OaepSHA1,则会出现Error while decoding [...]错误。如果我使用其他任何东西(例如,OaepSHA256),我会收到Not supported错误。

我只能得出结论,我的智能卡并不能正确支持OAEP SHA1,但使用PKCS#1填充一切都很好。

即使这回答了您已经知道的内容,但对于其他使用智能卡的人来说,它可能是另一个数据点。