使用非默认的AlgorithmIdentifier解密EnvelopedCms

mas*_*asi 9 .net c# encryption cryptography

我正在尝试解密使用非默认AlgorithmIdentifier加密的EnvelopedCms,如下所示:

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();
Run Code Online (Sandbox Code Playgroud)

加密按预期工作.现在,当我尝试解密envelopedCms使用这样的东西时:

EnvelopedCms envelopedCms = new EnvelopedCms();
envelopedCms.Decode(encryptedData );
envelopedCms.Decrypt(certificates);
byte[] decryptedData = envelopedCms.ContentInfo.Content;
Run Code Online (Sandbox Code Playgroud)

我注意到.)对证书的访问需要很长时间(比使用默认的AlgorithmIdentifier时更长)和b.)我收到此错误消息:

System.Security.Cryptography.CryptographicException: Access was denied because of a security violation.
Run Code Online (Sandbox Code Playgroud)

其中,查看失败的来源可能不是问题.任何人都可以使用智能卡获得上述解密代码吗?

// EDIT1请注意,只有在使用的证书放在智能卡上并且如果指定了默认值(3DES)之外的AlgorithmIdentifier时,才会出现此问题,如示例代码中所示.如果使用默认的AlgorithmIdentifier或者未将证书放在智能卡上,一切正常.它本身似乎不是SC问题,因为它使用默认的AlgorithmIdentifier.更确切地说,SC和AES AlgorithmIdentifier的组合引起了问题,但我无法找到可行的解决方案.

// 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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

mas*_*asi 0

好吧,最后我找到了为什么这不起作用的原因。这实际上取决于我正在使用的 SC (Yubikey 4)。就我而言,我使用 openssl 创建了 RSA 密钥,然后使用官方 Yubico PIV Manager / PIV Tool 将它们传输到 SC。Yubico 的官方 SC 驱动程序(YubiKey 智能卡微型驱动程序 (YKMD))似乎尚不支持此功能。然而,官方驱动程序似乎是唯一支持 Yubikey 所有高级功能的驱动程序,目前如果您想使用 AES 作为加密算法,似乎需要它。我之前使用的是 OpenSC 驱动程序,该驱动程序对于 3DES 工作得很好,但对于更高级的功能会失败。因此,如果有人在使用 Yubikey 时遇到此问题:

  1. 确保您使用官方驱动程序(YubiKey 智能卡微型驱动程序 (YKMD))而不是 Windows 基本驱动程序或 OpenSC 驱动程序
  2. 为了使官方驱动程序正常工作,您必须在 Windows 上使用 certutil 导入证书,如本文所示
  3. 如果您在尝试使用 certutil 导入时遇到“NTE_BAD_KEYSET”行错误,这可能是因为您使用 Yubico 工具(PIV 工具和/或 PIV 管理器)初始化了 PIV 函数。在这种情况下也不支持此功能,因此,您必须首先重置 Yubikey PIV 配置(基本上输入错误的 PIN x 次,然后输入错误的 PUK x 次,然后您可以重置 PIV 配置 - 所有这些都是使用 Yubico 的 PIV 工具完成,如页面底部所示
  4. 现在您可以使用 Yubico 工具设置自定义 PIN、PUK、管理密钥等。似乎“仅”不允许使用此工具完成 PIV 配置的初始化。另请注意,您会在 Yubico 的 SC 部署指南中找到更多详细信息,例如“如何设置触摸策略”(默认情况下关闭,有点 su***)。