Question

I am writing a simple example code to demonstrate how encryption and decryption with the X509 certificates works.

public static byte[] Encrypt(byte[] content, X509Certificate2Collection encryptingCertificates)
    {
        if (content == null)
        {
            throw new ApplicationException("NullContent");
        }
        if (encryptingCertificates == null || encryptingCertificates.Count == 0)
        {
            throw new ApplicationException("NoCertificates");
        }

        CmsRecipientCollection recipients = new CmsRecipientCollection(SubjectIdentifierType.IssuerAndSerialNumber, encryptingCertificates);
        EnvelopedCms dataEnvelope = new EnvelopedCms(new ContentInfo(new Oid("1.2.840.113549.1.7.1"), content), new AlgorithmIdentifier(new Oid("2.16.840.1.101.3.4.1.2")));
        dataEnvelope.Encrypt(recipients);

        return dataEnvelope.Encode();
    }

public static byte[] Decrypt(byte[] encryptedContent, X509Certificate2Collection decryptingCertificates)
    {
        if (decryptingCertificates == null || decryptingCertificates.Count == 0)
        {
            throw new ApplicationException("NoCertificates");
        }

        EnvelopedCms dataEnvelope = new EnvelopedCms();

        dataEnvelope.Decode(encryptedContent);
        dataEnvelope.Decrypt(decryptingCertificates);

        ContentInfo contentInfo = dataEnvelope.ContentInfo;

        return contentInfo.Content;
    }

And i have encountered with a problem - the code which have to decrypt (dataEnvelope.Decrypt(decryptingCertificates)) throw CryptographicException: Access denied.

CryptographicException: Access denied.
   at System.Security.Cryptography.Pkcs.EnvelopedCms.DecryptContent(RecipientInfoCollection recipientInfos, X509Certificate2Collection extraStore)
   at CertificateTestingTool.CertificateResolver.Decrypt(Byte[] encryptedContent, X509Certificate2Collection decryptingCerti
ficates)
   at CertificateTestingTool.Program.Main(String[] args)

It happens on the windows server 2012 and windows 8. I have checked this code on the win server 2008 and win 7 it works fine.

Additional information: I don’t use PKI, I import *.pfx file with the private key from a folder (X509Certificate2Collection.Import(…)) and it have imported successfully.

public static X509Certificate2Collection GetCertificates(string certPath, string password)
    {
        X509Certificate2Collection certs = null;
        var logger = Log.Logger;
        certs = new X509Certificate2Collection();
        var flags = X509KeyStorageFlags.DefaultKeySet;
        certs.Import(certPath, password, flags);

        return certs;
    }

Could anybody help me with this? As I understand some permission rules were introduced at the new OS version.

Was it helpful?

Solution

This issue was caused because an exception occurred when API CryptMsgControl was called.

Why it works on win7 but doesn't on win8?

This is because the imported cert by the code is using KeySpec AT_SIGNATURE. This is not the proper spec for encryption/decryption. It is meant for signing. AT_KEYEXCHANGE is the proper one for this purpose.

In Win7 the improper spec works due to the specific implementation for parsing the legacy CAPI keys not strictly enforcing key usages from the KeySpec. Hence, AT_SIGNATURE allowed encrypt/decrypt usages.

To solve this problem, we need to import the cert using certutil command tool with following command:

certutil -user –p -importpfx MY AT_KEYEXCHANGE

We just need to make sure the pfx file has KeySpec set to 1 (AT_KEYEXCHANGE). To check this, we can use the command certutil.exe -dump -v xyz.pfx

When we import a pfx using certutil with KeySpec AT_KEYEXCHANGE specified, we are modifying the KeySpec property of the pfx file. After that, we can export it to a pfx file again, then we can check the KeySpec again with the command mentioned above, and we will see the KeySpec change to AT_KEYEXCHANGE.

Some more details you can find here

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top