Расшифровка EnvelopedCms с нестандартными AlgorithmIdentifier

Я пытаюсь расшифровать EnvelopedCms это было зашифровано с использованием не-default AlgorithmIdentifier такой:

ContentInfo contentInfo = new ContentInfo(data);
EnvelopedCms envelopedCms = new EnvelopedCms(contentInfo, new AlgorithmIdentifier(new System.Security.Cryptography.Oid("2.16.840.1.101.3.4.1.42")));
CmsRecipientCollection recipients = new CmsRecipientCollection(SubjectIdentifierType.IssuerAndSerialNumber, certificates);
envelopedCms.Encrypt(recipients);
byte[] encryptedData = envelopedCms.Encode();

шифрование работает, как ожидалось. Теперь, когда я пытаюсь расшифровать envelopedCms использовать что-то вроде этого:

EnvelopedCms envelopedCms = new EnvelopedCms();
envelopedCms.Decode(encryptedData );
envelopedCms.Decrypt(certificates);
byte[] decryptedData = envelopedCms.ContentInfo.Content;

я замечаю, что a.) доступ к сертификату занимает довольно много времени (больше, чем при использовании алгоритма по умолчанию) и b.) Я получаю эту ошибку сообщение:

System.Security.Cryptography.CryptographicException: Access was denied because of a security violation.

который, глядя на источник, где это не удается, вероятно, не проблема. Может ли кто-нибудь получить код дешифрования выше работы [с Смарт-Картой]?

//EDIT1 Обратите внимание, что эта проблема возникает только в том случае, если используемый сертификат помещен на смарт-карту и если был указан алгоритм идентификации другой, чем по умолчанию (3DES), как в примере кода. Все работает нормально, если используется AlgorithmIdentifier по умолчанию или сертификат Не помещается на смарт-карту. Это не похоже на СК вопрос как таковой, поскольку он работает по умолчанию AlgorithmIdentifier. Это скорее комбинация SC и используемого алгоритма AES, который вызывает проблему, но я не смог найти рабочее решение.

//EDIT2 Полный пример, демонстрирующий проблему, читайте комментарии для деталей:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading.Tasks;
using System.Security.Cryptography;
using System.IO;
using System.Reflection;
using System.Diagnostics;
using System.Runtime.Serialization;
using System.Security.Cryptography.Pkcs;

namespace ConsoleApp
{

    class Program
    {
        static void Main(string[] args)
        {
            // Select the (smartcard) certificate to use it for encryption
            X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
            store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);
            X509Certificate2Collection collection = (X509Certificate2Collection)store.Certificates;
            X509Certificate2Collection fcollection = (X509Certificate2Collection)collection.Find(X509FindType.FindByTimeValid, DateTime.Now, false);
            X509Certificate2Collection scollection = X509Certificate2UI.SelectFromCollection(fcollection, "Certificate Select", "Select your smartcard certificate", X509SelectionFlag.MultiSelection);

            // Output which certificate will be used
            Console.WriteLine("Using Certificate:");
            int i = 0;
            foreach (X509Certificate2 x509 in scollection)
            {
                byte[] rawdata = x509.RawData;
                Console.WriteLine("---------------------------------------------------------------------");
                Console.WriteLine("1.tFull DN: {0}", x509.Subject);
                Console.WriteLine("tThumbprint: {0}", x509.Thumbprint);
                Console.WriteLine("---------------------------------------------------------------------");
                i++;
            }
            store.Close();

            // Wait
            Console.WriteLine("Press any key to continue...");
            Console.ReadKey(true);

            // Create data for encryption
            string message = "THIS IS OUR SECRET MESSAGE";
            byte[] data = System.Text.Encoding.ASCII.GetBytes(message);

            // Encrypt
            Console.WriteLine("Encrypting message...");

            // ContentInfo contentInfo = new ContentInfo(data); // will use default ContentInfo Oid, which is "DATA"
            // Explicitly use ContentInfo Oid 1.2.840.113549.1.7.1, "DATA", which is the default.
            ContentInfo contentInfo = new ContentInfo(new System.Security.Cryptography.Oid("1.2.840.113549.1.7.1"), data);

            // If using OID 1.2.840.113549.3.7 (the default one used if empty constructor is used) or 1.2.840.113549.1.9.16.3.6  everything works
            // If using OID 2.16.840.1.101.3.4.1.42 (AES CBC) it breaks
            AlgorithmIdentifier encryptionAlgorithm = new AlgorithmIdentifier(new System.Security.Cryptography.Oid("1.2.840.113549.3.7"));
            // EnvelopedCms envelopedCms = new EnvelopedCms(contentInfo); // this will use default encryption algorithm (3DES)
            EnvelopedCms envelopedCms = new EnvelopedCms(contentInfo, encryptionAlgorithm);
            Console.WriteLine("Encyption Algorithm:" + envelopedCms.ContentEncryptionAlgorithm.Oid.FriendlyName);
            Console.WriteLine("Encyption Algorithm:" + envelopedCms.ContentEncryptionAlgorithm.Oid.Value);
            CmsRecipientCollection recipients = new CmsRecipientCollection(SubjectIdentifierType.IssuerAndSerialNumber, scollection);
            /*Console.WriteLine("Receipientinfo count: " + encryptionEnvelopedCms.RecipientInfos.Count.ToString());
            foreach (var i in encryptionEnvelopedCms.RecipientInfos)
            {
                Console.Write("RecipientInfo Encryption Oid: " + i.KeyEncryptionAlgorithm.Oid);
            }
            */
            envelopedCms.Encrypt(recipients);
            byte[] encryptedData = envelopedCms.Encode();
            Console.WriteLine("Message encrypted!");

            // Decrypt
            envelopedCms.Decode(encryptedData);
            Console.WriteLine("Decryption Algorithm:" + envelopedCms.ContentEncryptionAlgorithm.Oid.FriendlyName);
            Console.WriteLine("Decryption Algorithm:" + envelopedCms.ContentEncryptionAlgorithm.Oid.Value);
            // Next line will fail if both conditions are true: 
            // 1. A non-default AlgorithmIdentifier was used for encryption, in our case AES
            // 2. The private key required for decryption is placed on a smartcard that requires a manual action, such as entering a PIN code, before releasing the private key
            // Note that everything works just fine when the default AlgorithmIdentifier is used (3DES) or the private key is available in the X509Store
            envelopedCms.Decrypt(scollection);
            byte[] decryptedData = envelopedCms.ContentInfo.Content;
            Console.WriteLine("Message decrypted!");
            Console.WriteLine("Decrypted message: " + System.Text.Encoding.ASCII.GetString(decryptedData));
            Console.WriteLine("Press any key to exit.");
            Console.ReadKey(true);
        }
    }
}

2 ответов


 [TestMethod]
    public void TestEnvelopedCMS()
    {
        X509Store store = new X509Store("MY", StoreLocation.CurrentUser);
        store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);

        X509Certificate2Collection collection = (X509Certificate2Collection)store.Certificates;
        X509Certificate2Collection fcollection = (X509Certificate2Collection)collection.Find(X509FindType.FindByTimeValid, DateTime.Now, false);

        byte[] data = new byte[256];
        //lets change data before we encrypt
        data[2] = 1;

        ContentInfo contentInfo = new ContentInfo(data);
        EnvelopedCms envelopedCms = new EnvelopedCms(contentInfo, new AlgorithmIdentifier(new System.Security.Cryptography.Oid("2.16.840.1.101.3.4.1.42")));
        CmsRecipientCollection recipients = new CmsRecipientCollection(SubjectIdentifierType.IssuerAndSerialNumber, fcollection);
        envelopedCms.Encrypt(recipients);
        byte[] encryptedData = envelopedCms.Encode();

        //lets decrypt now
        envelopedCms.Decode(encryptedData);
        envelopedCms.Decrypt(fcollection);
        byte[] decryptedData = envelopedCms.ContentInfo.Content;

         //grab index from byte[]
        var item = decryptedData.Skip(2).Take(1).FirstOrDefault();
        var item2 = data.Skip(2).Take(1).FirstOrDefault();

        Assert.IsTrue(item == item2);
    }

Итак, наконец я нашел причину, почему это не работает. Это действительно зависит от SC, который я использую (Yubikey 4). В моем случае я создал свои ключи RSA с помощью openssl, а затем передал их в SC с помощью официального инструмента Yubico PIV Manager / PIV. Это, похоже, еще не поддерживается официальным драйвером SC от Yubico (Yubikey Smart Card Minidriver (YKMD)). Официальный драйвер, однако, кажется единственным, который поддерживает все расширенные функции Yubikey и в настоящее время вроде бы необходим, если вы хотите использовать AES в качестве алгоритма шифрования. Я использовал драйвер OpenSC, прежде чем он будет работать нормально для 3DES, но не будет работать для более продвинутых функций. Таким образом, если кто-то сталкивается с этой проблемой с Yubikey:

  1. убедитесь, что вы используете официальный драйвер (Yubikey Smart Card Minidriver (YKMD)) вместо базового драйвера Windows или драйвера OpenSC
  2. для работы официального драйвера вам необходимо импортировать сертификаты с помощью certutil на Windows,в этой статье.
  3. Если вы получаете ошибку по строке "NTE_BAD_KEYSET" при попытке импорта с помощью certutil, это, вероятно, потому, что вы инициализировали функцию PIV с помощью инструментов Yubico (инструмент PIV и/или менеджер PIV). Это также не поддерживается в этом случае, таким образом, вам придется сначала сбросить конфигурацию Yubikey PIV (в основном введите неправильный PIN x раз, затем неправильный PUK x раз, а затем вы можете сбросить конфигурацию PIV-все это делается с помощью инструмента PIV от Yubico как показано здесь в нижней части страницы)
  4. Теперь вы можете установить свой пользовательский PIN, PUK, Management-Key и так далее с помощью инструментов Yubico. Кажется, что" только " init конфигурации PIV не разрешается делать с помощью этих инструментов. Также обратите внимание, что вы найдете больше деталей, таких как" как установить политику касания " (по умолчанию отключено, что вроде su***) в руководстве по развертыванию SC от Yubico.