EnvelopedCms how to properly identify certificate to decrypt and not request a password?

ぐ巨炮叔叔 提交于 2019-12-11 16:57:48

问题


I am decrypting SMIME.P7M attachments in emails. I currently have the following

                EnvelopedCms envDate = new EnvelopedCms(new ContentInfo(data));
                envDate.Decode(data);
                RecipientInfoCollection recips = envDate.RecipientInfos;
                RecipientInfo recipin = recips[0];
                X509Certificate2 x509_2 = LoadCertificate2(StoreLocation.CurrentUser, (SubjectIdentifier)recipin.RecipientIdentifier);

And the load certificates looks like this

public static X509Certificate2 LoadCertificate2(StoreLocation storeLocation, SubjectIdentifier identifier)
        {
            X509Store store = new X509Store(storeLocation);
            store.Open(OpenFlags.ReadOnly);
            X509Certificate2Collection certCollection = store.Certificates;
            X509Certificate2 x509 = null;
            X509IssuerSerial issuerSerial;

            if (identifier.Type == SubjectIdentifierType.IssuerAndSerialNumber)
            {
                issuerSerial = (X509IssuerSerial)identifier.Value;
            }

            foreach (X509Certificate2 c in certCollection)
            {
                Console.WriteLine("{0}Valid Date: {1}{0}", Environment.NewLine, c.NotBefore);
                if (c.SerialNumber == issuerSerial.SerialNumber && c.Issuer == issuerSerial.IssuerName)
                {
                    x509 = c;
                    break;
                }
            }
            if (x509 == null)
                Console.WriteLine("A x509 certificate for  was not found");
            store.Close();
            return x509;
        }

The above code only get the first recipient RecipientInfo recipin = recips[0]; however is the most efficient method to get the appropriate certificate to loop through each recipient and check the store for the SubjectIdentifier?

Once obtaining the correct certificate I use this

                X509Certificate2Collection col = new X509Certificate2Collection(x509_2);
                envDate.Decrypt(col);
               decData = envDate.ContentInfo.Content;

This prompts for the PIN associated with the privatekey of the certificate, how can I add the PIN prior to calling decrypt so there is no prompt?


回答1:


The EnvelopedCms class in .NET Framework doesn't have any way to let you easily programmatically apply a PIN (or other unlocking mechanism); particularly so if the certificate exists in the CurrentUser\My or LocalMachine\My stores (because those are searched prior to any certificates in the extraStore collection).

On .NET Framework 4.7+ you could accomplish it in a very roundabout way for CNG-accessible keys provided that the certificate is not also in the CurrentUser\My or LocalMachine\My stores:

CngKey key = ExerciseLeftToTheReader();
key.SetProperty(new CngProperty("SmartCardPin", pin, CngPropertyOptions.None));
X509Certificate2 cert = DifferentExerciseLeftToTheReader();

// You need to use tmpCert because this won't do good things if the certificate
// already knows about/how-to-find its associated private key

using (key)
using (X509Certificate2 tmpCert = new X509Certificate2(cert.RawData))
{
   // Need to NOT read the HasPrivateKey property until after the property set.  Debugger beware.
   NativeMethods.CertSetCertificateContextProperty(
       tmpCert.Handle,
       CERT_NCRYPT_KEY_HANDLE_PROP_ID,
       CERT_SET_PROPERTY_INHIBIT_PERSIST_FLAG,
       key.Handle);

   envelopedCms.Decrypt(new X509Certificate2Collection(tmpCert));
}

(And, of course, you need to define the P/Invoke for CertSetCertificateContextProperty)

In .NET Core 3.0 this becomes easier (preview 2 is currently available with this feature)... though it does leave you with the burden of determining which RecipientInfo is you, and which key goes with it:

RecipientInfo recipientInfo = FigureOutWhichOneYouCanMatch();
CngKey key = ExerciseLeftToTheReader();
key.SetProperty(new CngProperty("SmartCardPin", pin, CngPropertyOptions.None));

using (key)
using (RSA rsa = new RSACng(key))
{
    envelopedCms.Decrypt(recipientInfo, rsa);
}


来源:https://stackoverflow.com/questions/54801472/envelopedcms-how-to-properly-identify-certificate-to-decrypt-and-not-request-a-p

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!