C #: ошибка при декодировании странной проблемы с заполнением OAEP

В настоящее время я работаю над классом, который шифрует большие объемы текста с помощью случайно сгенерированного ключа шифрования, зашифрованного сертификатом X509 со смарт-карты, используя RSACryptoServiceProvider для выполнения операций шифрования и дешифрования главного ключа. Однако, когда для параметра заполнения 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;
    }
}
}

person CitadelCore    schedule 08.06.2017    source источник
comment
в какой кодировке используется ваша строка ключа шифрования?   -  person Dai Bok    schedule 09.06.2017
comment
См. Также Плохая пара лет для индустрия криптографических токенов. Вы уверены, что это не заполнение PKCS?   -  person jww    schedule 09.06.2017
comment
Дай Бок: Это base64. См. Функцию GenerateEncryptionKey ().   -  person CitadelCore    schedule 09.06.2017
comment
Что, если вы измените строку DecryptWithCert RSACryptoServiceProvider csp = cert.PrivateKey как RSACryptoServiceProvider на RSACryptoServiceProvider csp = cert.PublicKey.Key как RSACryptoServiceProvider, кажется, что вы используете разные ключи для расшифровки / шифрования? Это помогает   -  person Dai Bok    schedule 11.06.2017
comment
Как я уже говорил, я использую правильную пару ключей. Проблема заключается только в заполнении OAEP. Если бы я не использовал правильную пару ключей, я бы не смог ее расшифровать, даже если бы использовал PKCS # 1.   -  person CitadelCore    schedule 11.06.2017


Ответы (3)


Сегодня я спорил с этим со смарт-картами (или, точнее, с Yubikey Neo с включенным PIV-апплетом смарт-карты); используя этот код:

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 все хорошо.

Даже если это отвечает тому, что вы уже знаете, это может быть полезно в качестве еще одной точки данных для всех, кто использует смарт-карты.

person Chris J    schedule 02.09.2017

Убедитесь, что размер вашего ключа не слишком маленький или не слишком большой.

См. Комментарии от MSDN

RSACryptoServiceProvider поддерживает размеры ключей от 384 бит до 16384 бит с шагом 8 бит, если у вас установлен Microsoft Enhanced Cryptographic Provider. Он поддерживает размеры ключей от 384 до 512 бит с шагом 8 бит, если у вас установлен Microsoft Base Cryptographic Provider.

Таким образом, вам может потребоваться дополнить короткие строки ключей некоторыми байтами, чтобы получить минимальную длину ключа.

person Dai Bok    schedule 09.06.2017
comment
Когда я пытаюсь это сделать, я получаю необработанное исключение: System.Security.Cryptography.CryptographicException: Bad Length. Сертификат смарт-карты поддерживает только 2048-битное шифрование. (256 байт - макс.) - person CitadelCore; 09.06.2017
comment
Кроме того, провайдер - это Microsoft Enhanced RSA и AES Cryptographic Provider. - person CitadelCore; 09.06.2017

Хорошо, мне удалось это проверить, и, насколько я могу судить, у меня проблемы с некоторыми сертификатами. Я не уверен, почему одни сертификаты работают, а другие - нет. Было бы хорошо узнать, почему в этом случае не работают некоторые сертификаты?

В любом случае, я создал новый самоподписанный сертификат, используя окна «Управление сертификатами шифрования файлов», и использовал этот сертификат, и, похоже, все работает.

Выход из вашего кода.

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

надеюсь, это поможет

person Dai Bok    schedule 10.06.2017
comment
Да, я знаю, что сертификаты машин работают, это просто сертификат на моей смарт-карте не работает. Я пробовал использовать другие сертификаты на той же смарт-карте с разными EKU. - person CitadelCore; 10.06.2017
comment
У вас есть закрытый ключ для этого сертификата? - person Dai Bok; 10.06.2017
comment
Да, как я уже упоминал ранее, заполнение OAEP - единственное, что препятствует работе дешифрования. Расшифровка отлично работает в режиме PKCS # 1. - person CitadelCore; 10.06.2017