37

I have site in Azure Websites (not Hosted Service) and I need processing .pfx certificates with private key there.

var x509Certificate2 = new X509Certificate2(certificate, password);

But I was faced with follow exception:

System.Security.Cryptography.CryptographicException: The system cannot find the file specified.

   at System.Security.Cryptography.CryptographicException.ThrowCryptographicException(Int32 hr)
   at System.Security.Cryptography.X509Certificates.X509Utils._LoadCertFromBlob(Byte[] rawData, IntPtr password, UInt32 dwFlags, Boolean persistKeySet, SafeCertContextHandle& pCertCtx)
   at System.Security.Cryptography.X509Certificates.X509Certificate.LoadCertificateFromBlob(Byte[] rawData, Object password, X509KeyStorageFlags keyStorageFlags)
   at System.Security.Cryptography.X509Certificates.X509Certificate..ctor(Byte[] rawData, String password, X509KeyStorageFlags keyStorageFlags)
   at System.Security.Cryptography.X509Certificates.X509Certificate2..ctor(Byte[] rawData, String password, X509KeyStorageFlags keyStorageFlags)

In article http://blog.tylerdoerksen.com/2013/08/23/pfx-certificate-files-and-windows-azure-websites/ I have found that it happens because by default the system uses a local directory of user to store the key. But Azure Websites have no local user profile directory. In the same article author propose to use X509KeyStorageFlags.MachineKeySet flag.

var x509Certificate2 = new X509Certificate2(certificate, password, X509KeyStorageFlags.MachineKeySet);

But now I have other exception:

System.Security.Cryptography.CryptographicException: Access denied.

   at System.Security.Cryptography.CryptographicException.ThrowCryptographicException(Int32 hr)
   at System.Security.Cryptography.X509Certificates.X509Utils._LoadCertFromBlob(Byte[] rawData, IntPtr password, UInt32 dwFlags, Boolean persistKeySet, SafeCertContextHandle& pCertCtx)
   at System.Security.Cryptography.X509Certificates.X509Certificate.LoadCertificateFromBlob(Byte[] rawData, Object password, X509KeyStorageFlags keyStorageFlags)
   at System.Security.Cryptography.X509Certificates.X509Certificate..ctor(Byte[] rawData, String password, X509KeyStorageFlags keyStorageFlags)
   at System.Security.Cryptography.X509Certificates.X509Certificate2..ctor(Byte[] rawData, String password, X509KeyStorageFlags keyStorageFlags)

Can anybody help me to understand why it happens and how to fix it?

abatishchev
  • 98,240
  • 88
  • 296
  • 433
Roman Oleynik
  • 619
  • 1
  • 5
  • 12

6 Answers6

98

I guess you found a workaround, but if others are struggling with this, I found the answer to this in another SO question:

How can constructing an X509Certificate2 from a PKCS#12 byte array throw CryptographicException("The system cannot find the file specified.")?

The magic is specifying the X509KeyStorageFlags storage flags. Example:

var myCertificae = new X509Certificate2(
    certificateData,
    securePasswordString,
    X509KeyStorageFlags.MachineKeySet | 
    X509KeyStorageFlags.PersistKeySet | 
    X509KeyStorageFlags.Exportable);
Community
  • 1
  • 1
Jon Odgård
  • 1,096
  • 8
  • 4
  • 1
    Thank you. This helped me within a webjob instance too. I was receiving error: Job failed due to exit code -1073740940 – Thomas Edmondson Mar 12 '15 at 01:02
  • This is the only search result that came up for this error number 1073740940. There was no error attached when I was running it as a webjob just that exist code. Thanks a lot @ThomasEdmondson that was driving me nuts. – Yodacheese May 29 '15 at 02:52
  • This did the trick for me on an Azure Web App, thanks! It should probably be the accepted answer. – Ryan Killeen May 05 '16 at 18:38
  • 1
    This should be the answer – Maxim Balaganskiy May 13 '16 at 06:47
  • This used to work for me a few months ago - but ever since I started a new Azure Website today I'm getting the same error, except I *am* specifying all three `X509KeyStorageFlags` values. I wonder if there's a change in Azure's configuration. – Dai Aug 10 '17 at 08:01
  • Worked for me as well. Why is this necessary anyway? – bmvr Nov 09 '17 at 17:12
  • Thank you so much! – UnionP Jun 07 '18 at 04:58
  • This cured my problem too. I was getting Win32Exception (0x8009030D): The credentials supplied to the package were not recognised. This is called from within PushSharp's AuthenticateClient on an SSL Stream when connecting to APNS. Thank you kindly. – David C Aug 20 '18 at 10:56
  • Are there security implications to using a machine-wide store for an azure web app? – BenjiFB Oct 25 '18 at 22:13
  • 4
    BTW: You can pass in a null for securePasswordString. I was pulling a full PPK Cert from Azure KeyVault which gives me a cer=byte[], but no password. This worked for me. – Herb Stahl Nov 28 '18 at 14:12
  • This is the proper answer – cuasiJoe May 25 '19 at 22:49
  • I could pass an empty string for the password. Passing null would not work because that leads to an ambiguous overload error. – Shawn de Wet Jan 21 '21 at 19:03
6

Azure Websites now has native support for installing certificates to the certificate store. Have you given that a shot?

Details here: http://azure.microsoft.com/blog/2014/10/27/using-certificates-in-azure-websites-applications/

Zain Rizvi
  • 23,586
  • 22
  • 91
  • 133
0

Azure Websites run in a shared environment. I am assuming that the constructor for the certificate is attempting to create some temporary information on the instance and it does not have permission to.

You may have to upgrade to a hosted service in order to run in an elevated context and perform this work.

Also, have you validated that the password is correct? If it doesn't require a password, you at least have to pass string.Empty to the constructor. Passing in a NULL value would also cause this exception.

Justin Patten
  • 1,059
  • 6
  • 16
0

I had exactly the same problem, and struggled many hours for fixing it. In the article that you mention the last stack call is to the function LoadCertFromFile, but in your (and my) case it is LoadCertFromBlob.

So I looked for LoadCertFromBlob and found this:

Why does X509Certificate2 sometimes fail to create from a blob?

The solution was to go in IIS and change the application pool identity from "ApplicationPoolIdentity" to "LocalService", so that the certificate is loaded in right local folder.

Community
  • 1
  • 1
Sean
  • 923
  • 3
  • 14
  • 36
  • No, I do not load cert from blobs, I load it from byte array. Yes, you are right, the problem is in the access permissions of the application pool identity. But I deploy my application in Azure Websites. I cannot change IIS the application pool identity there. So, I moved it to Hosted Services. – Roman Oleynik Oct 24 '13 at 11:08
  • I was talking about the "_LoadCertFromBlob" call in the exception that you report. – Sean Dec 21 '13 at 13:27
  • Or you can stick with application identity, but turn `Load User Profile` to true. – deerchao Mar 19 '16 at 07:39
0

In Azure Websites / Web App / Mobile App - you have to use App Service Plan that allwos you to import SSL certificate - so it shoud not be a Free or Shared. You can import not only SSL certificate, but also for an example code signing cerificate and use it in signtool or from PowerShell.

I used this method in https://vmplace.eu/

If you try to use a Free or Shared plan you receive error - so in Azure in these Plans there is other version of .NET framework.

You can refer to this project also: https://github.com/onovotny/SignService

mvpbuzz

Community
  • 1
  • 1
0

I solved this by following the instructions from the official documentation. Not sure if this was an option before, but now it is, and it was easy to use / implement.

  1. First I uploaded the .pfx certificate to my Azure App Service and entered the password when asked to enter it. Copied the thumbprint.

  2. Then I ran the following command from inside Azure Console (I only added a single thumbprint). This is important as it enables your app to access the cert:

az webapp config appsettings set --name <app-name> --resource-group <resource-group-name> --settings WEBSITE_LOAD_CERTIFICATES=<comma-separated-certificate-thumbprints>
  1. I used the following method to load the previously uploaded .pfx certificate into my app:
public X509Certificate2 GetAzureCertificate(string thumbprint, bool validOnly)
{
    using (var certStore = new X509Store(StoreName.My, StoreLocation.CurrentUser))
    {
        certStore.Open(OpenFlags.ReadOnly);

        var certCollection = certStore.Certificates.Find(X509FindType.FindByThumbprint, thumbprint, validOnly);
        var cert = certCollection.OfType<X509Certificate2>().FirstOrDefault();

        if (cert == null)
        {
            throw new Exception($"Cert not found. Total cert count : {certStore.Certificates.Count}.");
        }

        return cert;
    }
}
tkit
  • 8,082
  • 6
  • 40
  • 71