none
System.Security.Cryptography.CryptographicException: Access denied when trying to call System.Security.Cryptography.Pkcs.EnvelopedCms.DecryptContent RRS feed

  • 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();
    			certs.Clear();
    			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.



    Wednesday, November 13, 2013 1:27 PM

Answers

  • Hi Alex,

    For your question:

    "Does it mean for us that we should always use the PKI to store certificates and should not use another storage, for example our database?"

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

    I think you can use the exported new pfx with your previous code.

    In other word, we only need to make sure the pfx is set to valid KeySpec for encrytion/decryption. The workaround I used in my previous email is only a way can make your pfx work for encryption, but if you already have a valid one, it should work as well.


    <THE CONTENT IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, WHETHER EXPRESS OR IMPLIED>
    Thanks
    Alan Yao
    MSDN Community Support

    Please remember to "Mark as Answer" the responses that resolved your issue. It is a common way to recognize those who have helped you, and makes it easier for other visitors to find the resolution later.



    Tuesday, December 3, 2013 9:09 AM

All replies

  • Hi alexkostyanikov,

      Welcome to MSDN Forum Support.

       I am trying to involve someone familiar with this topic to further look at this issue. 

      Sincerely,

      Jason Wang


    We are trying to better understand customer views on social support experience, so your participation in this interview project would be greatly appreciated if you have time. Thanks for helping make community forums a great place.
    Click HERE to participate the survey.

    Thursday, November 14, 2013 9:08 AM
  • Hi Alex,

    The error message shows that the EnvelopedCms.Decrypt method requires KeyContainerPermissionFlags.Decrypt permission. We could try to add permission into your code. Code like below:

    public static byte[] Decrypt(byte[] encryptedContent, X509Certificate2Collection decryptingCertificates)

            {

                if (decryptingCertificates == null || decryptingCertificates.Count == 0)

                {

                    throw new ApplicationException("NoCertificates");

                }

                // Create a KeyContainerPermission with the right 

                // to open the key container.

                KeyContainerPermission keyContainerPerm = new

                     KeyContainerPermission(KeyContainerPermissionFlags.Decrypt);

                keyContainerPerm.Assert();

                EnvelopedCms dataEnvelope = new EnvelopedCms();

                dataEnvelope.Decode(encryptedContent);

                dataEnvelope.Decrypt(decryptingCertificates);

                ContentInfo contentInfo = dataEnvelope.ContentInfo;

                return contentInfo.Content;

            }

    You could see permission requirement in this page’s Remark tips. http://msdn.microsoft.com/en-us/library/ms148294(v=vs.110).aspx.

    Assert method http://msdn.microsoft.com/en-us/library/system.security.codeaccesspermission.assert(v=vs.110).aspx.

    Per my understanding, your computer account has no right to do decrypt method on your Win 8. Could you please try it on your administrator account? Please let me know the result.

    Best Regards,


    We are trying to better understand customer views on social support experience, so your participation in this interview project would be greatly appreciated if you have time. Thanks for helping make community forums a great place.
    Click HERE to participate the survey.

    Tuesday, November 19, 2013 1:54 AM
    Moderator
  • Hi, Hetro!

    I saw this permission requirement.

    Unfortunately, this does not help. And I have tried the administrator account.



    Tuesday, November 19, 2013 3:13 PM
  • Hi Alex,

    I have reproduced this issue and I've found the error was reported when some native code was called.

    To further check this issue, I need some time to prepare debugging envrionement and perform deep debugging.

    I will update on this thread, once I got more info.


    <THE CONTENT IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, WHETHER EXPRESS OR IMPLIED>
    Thanks
    Alan Yao
    MSDN Community Support

    Please remember to "Mark as Answer" the responses that resolved your issue. It is a common way to recognize those who have helped you, and makes it easier for other visitors to find the resolution later.


    Wednesday, November 20, 2013 9:03 AM
  • Hi, Alan

    Thank you very much that you are spending your time for me

    Thursday, November 21, 2013 9:12 AM
  • Hi Alan,

    Have you been able to make any progress with this?

    Thanks,

    Andy

    Tuesday, November 26, 2013 4:43 PM
  • Sorry for the late response, since I were waiting for other team's confirmation.

    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 we used 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 <pw> -importpfx MY <pfxfile> AT_KEYEXCHANGE

    Then I were able to get the following code work by selecting the imported cert from cert store:

    static void Main(string[] args)
            {
                X509Certificate2Collection xc = GetCertificates(@"", "");
                            //  Original message.
                const String msg = "Here is your personal identification number:";
    
                //  Convert message to an array of Unicode bytes for signing.
                UnicodeEncoding unicode = new UnicodeEncoding();
                byte[] msgBytes = unicode.GetBytes(msg);
                
    
                byte[] enstream = Encrypt(msgBytes, xc);
                byte[] destream = Decrypt(enstream, xc);
            }
    
            public static X509Certificate2Collection GetCertificates(string certPath, string password)
            {
                X509Certificate2Collection certs = null;
                certs = new X509Certificate2Collection();
                certs.Clear();
                //var flags = X509KeyStorageFlags.DefaultKeySet;
                //certs.Import(certPath, password, flags);
                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);
                certs = X509Certificate2UI.SelectFromCollection(fcollection, "Test Certificate Select", "Select a certificate from the following list to get information on that certificate", X509SelectionFlag.MultiSelection);
                
    
                return certs;
            }
            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);
                CmsRecipientCollection recipients = new CmsRecipientCollection(SubjectIdentifierType.IssuerAndSerialNumber, decryptingCertificates);
                dataEnvelope.Decrypt(decryptingCertificates);
    
                ContentInfo contentInfo = dataEnvelope.ContentInfo;
    
                return contentInfo.Content;
            }

    Let me know if you have any further concerns.


    <THE CONTENT IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, WHETHER EXPRESS OR IMPLIED>
    Thanks
    Alan Yao
    MSDN Community Support

    Please remember to "Mark as Answer" the responses that resolved your issue. It is a common way to recognize those who have helped you, and makes it easier for other visitors to find the resolution later.


    Friday, November 29, 2013 1:00 PM
  • Hi, Alan.

    Great work. This information will help us.

    One more detail i want do clarify. 

    You wrote:

    "This is because the imported cert by the code we used 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."

    Does it mean for us that we should always use the PKI to store certificates and should not use another storage, for example our database?

    Thank you for help


    Monday, December 2, 2013 11:36 AM
  • Hi Alex,

    For your question:

    "Does it mean for us that we should always use the PKI to store certificates and should not use another storage, for example our database?"

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

    I think you can use the exported new pfx with your previous code.

    In other word, we only need to make sure the pfx is set to valid KeySpec for encrytion/decryption. The workaround I used in my previous email is only a way can make your pfx work for encryption, but if you already have a valid one, it should work as well.


    <THE CONTENT IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, WHETHER EXPRESS OR IMPLIED>
    Thanks
    Alan Yao
    MSDN Community Support

    Please remember to "Mark as Answer" the responses that resolved your issue. It is a common way to recognize those who have helped you, and makes it easier for other visitors to find the resolution later.



    Tuesday, December 3, 2013 9:09 AM
  • Thanks a lot! It works.
    Monday, December 9, 2013 12:04 PM