首页 文章

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

提问于
浏览
5

我目前正致力于使用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

    今天我一直在用智能卡(或更准确地说,启用了智能卡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填充一切都很好 .

    即使这回答了你已经知道的内容,它也可能是另一个使用智能卡的其他数据点 .

  • 0

    确保您的密钥大小不是太小或太大 .

    MSDN的评论

    如果安装了Microsoft增强加密提供程序,RSACryptoServiceProvider支持密钥大小从384位到16384位,增量为8位 . 如果安装了Microsoft Base Cryptographic Provider,它支持密钥大小从384位到512位,增量为8位 .

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

  • 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!.
    

    希望有所帮助

相关问题