Runtime: WindowsCryptographicException trying to read certificate from file

Created on 4 Sep 2017  ยท  15Comments  ยท  Source: dotnet/runtime

I have a problem adding the client certficate (that is included in the project) on my production environment. It works locally so I guess it is somehow machine related and also it works on our test server. It is trying to import the certificate file as seen below and I have no real help from the error message. I have tried adding more permission to the application pool user as well as file level permissions.

Any ideas on what could be the cause? Or should I rewrite something?

Production server is Windows 2012 R2.

The error I get is:

WindowsCryptographicException: An internal error occurred
Internal.Cryptography.Pal.CertificatePal.FilterPFXStore(Byte[] rawData, SafePasswordHandle password, PfxCertStoreFlags pfxCertStoreFlags)
Internal.Cryptography.Pal.CertificatePal.FromBlobOrFile(Byte[] rawData, string fileName, SafePasswordHandle password, X509KeyStorageFlags keyStorageFlags)
System.Security.Cryptography.X509Certificates.X509Certificate..ctor(string fileName, string password, X509KeyStorageFlags keyStorageFlags)
ChannelFactory<BolagsverketServiceReference.XMLProdukt> factory = null;
            BolagsverketServiceReference.XMLProdukt serviceProxy = null;
            BasicHttpsBinding binding = null;

            binding = new BasicHttpsBinding(BasicHttpsSecurityMode.Transport);
            binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Certificate;

            factory = new ChannelFactory<BolagsverketServiceReference.XMLProdukt>(binding, new EndpointAddress("https://eservice.bolagsverket.se/services/XMLProdukt"));

            var pathToFile = hostingEnvironment.ContentRootPath
            + Path.DirectorySeparatorChar.ToString()
            + "certificates"
            + Path.DirectorySeparatorChar.ToString()
            + "something.p12";



            factory.Credentials.ClientCertificate.Certificate = new System.Security.Cryptography.X509Certificates.X509Certificate2(pathToFile, "hiddenpassword", X509KeyStorageFlags.UserKeySet | X509KeyStorageFlags.PersistKeySet);
            serviceProxy = factory.CreateChannel();
            return serviceProxy;

.Net/EF Core 2.0.0
VS 2017 15.3-latest
Win 10

area-System.Security

Most helpful comment

Changing from X509KeyStorageFlags.DefaultKeySet to X509KeyStorageFlags.MachineKeySetfixed it for me.

All 15 comments

WindowsCryptographicException: An internal error occurred probably means that PFXImportCertStore failed, returning null and calling SetLastError with NTE_FAIL.

Things that I'd speculate can cause it:

  • The PFX was created with a different encryption password and integrity password, and the integrity password is what was provided to the constructor.

    • But that wouldn't be the case if the same PFX file works on any Windows machine.

  • The PFX was created using the PKCS12_PROTECT_TO_DOMAIN_SIDS and there's some sort of problem talking to AD on the other side.

    • I don't think we support this kind of PFX.

  • The PFX was generated using the guidance from https://tools.ietf.org/html/rfc7292#appendix-B, namely, to use PBES2+PBKDF2 for encrypting the private keys instead of pbeWithSHAAnd3-KeyTripleDES-CBC

    • In this case, the PFX will open on Windows 10, but not prior versions. (At least, I think that's the behavior I recall hitting with this case).

    • It also, IIRC, won't open on macOS.

  • PKCS#12 v1.1 also talks about encrypting and/or signing a PFX using asymmetric cryptography rather than a password-based scheme. But I don't know if even Win10 supports that, since I don't see any controls for it in PFXExportCertStoreEx.

So the only thing that I can quickly think of that makes sense is you have a "new" PFX and your test environment is Windows 10, but your production environment is Win2012R2.

This works locally on my Windows 10 machine and fails with an error that resembles issue 9543 on a Windows 2016 Standard server.

The certificate was created by a Swedish state owned company so I cannot alter how the certificate is made. I can however pm you the certificate if you want to look at without the passcode of course.

The error I get on the Windows 2016 server (error above is from a Windows 2012 R2 - same code) is:

System.AggregateException: One or more errors occurred. (An error occurred while sending the request.) ---> System.ServiceModel.CommunicationException: An error occurred while sending the request. ---> System.Net.Http.HttpRequestException: An error occurred while sending the request. ---> System.Net.Http.WinHttpException: The client certificate credentials were not recognized
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Threading.Tasks.RendezvousAwaitable1.GetResult() at System.Net.Http.WinHttpHandler.<StartRequest>d__105.MoveNext() --- End of inner exception stack trace --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.ServiceModel.Channels.ServiceModelHttpMessageHandler.<SendAsync>d__41.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.ConfiguredTaskAwaitable1.ConfiguredTaskAwaiter.GetResult()
at System.Net.Http.HttpClient.d__59.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.ServiceModel.Channels.HttpChannelFactory1.HttpClientRequestChannel.HttpClientChannelAsyncRequest.<SendRequestAsync>d__13.MoveNext() --- End of inner exception stack trace --- at System.Runtime.AsyncResult.End[TAsyncResult](IAsyncResult result) at System.ServiceModel.Channels.ServiceChannel.SendAsyncResult.End(SendAsyncResult result) at System.ServiceModel.Channels.ServiceChannel.EndCall(String action, Object[] outs, IAsyncResult result) at System.ServiceModel.Channels.ServiceChannelProxy.TaskCreator.<>c__DisplayClass1_0.<CreateGenericTask>b__0(IAsyncResult asyncResult) --- End of inner exception stack trace --- at System.Threading.Tasks.Task1.GetResultCore(Boolean waitCompletionNotification)

@bartonjs
We actually got this working, sort of.

First we created a powershell script to replicate the call to the webservice that used X509Certificate2 just to make sure the communication was up and no other interference.

Then we created a consoleapp using the exact same code as from IIS. Worked liked a charm.

Then we skipped IIS and ran the web using the dotnet command. Everything worked.

So all that was left was that it had something to do with the integration of IIS. When we changed the Identity of the Application Pool from ApplicationPoolIdentity to a user that was local admin everything worked. So it has something to do with security rights. Exactly what rights are needed we don't know.

Ideas?

@johanskoldekrans Sounds like a variant of an access denied problem.

I see that you are asserting PersistKeySet, are you planning on adding the cert to a cert store? If not, you're creating an additional file on disk every time you read the PFX.

If you aren't running on macOS you can assert EphemeralKeySet instead. That means that the private key is never written to disk, which will probably work around your AppPoolIdentity permissions limitation.

X509KeyStorageFlags keyStorageFlags = RuntimeInformation.IsPlatform(OSPlatform.Windows) ? X509KeyStorageFlags.EphemeralKeySet : X509KeyStorageFlags.DefaultKeySet;

I am running Win2016Std so that might do the trick.

Will try and let you know the results.

ons 13 sep. 2017 kl. 17:39 skrev Jeremy Barton notifications@github.com:

@johanskoldekrans https://github.com/johanskoldekrans Sounds like a
variant of an access denied problem.

I see that you are asserting PersistKeySet, are you planning on adding
the cert to a cert store? If not, you're creating an additional file on
disk every time you read the PFX.

If you aren't running on macOS you can assert EphemeralKeySet instead.
That means that the private key is never written to disk, which will
probably work around your AppPoolIdentity permissions limitation.

X509KeyStorageFlags keyStorageFlags =
RuntimeInformation.IsPlatform(OSPlatform.Windows) ?
X509KeyStorageFlags.EphemeralKeySet : X509KeyStorageFlags.DefaultKeySet;

โ€”
You are receiving this because you were mentioned.

Reply to this email directly, view it on GitHub
https://github.com/dotnet/corefx/issues/23780#issuecomment-329208054,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AR1TiwU1548w2JLotWKN1F0TU1OdeJkHks5sh_c8gaJpZM4PLtaA
.

--

www.penser.se
Disclaimer: www.penser.se/disclaimer

I got another exception when using EphemeralKeySet and the AppPoolIdentity.

Internal.Cryptography.CryptoThrowHelper+WindowsCryptographicException: Bad Data
   at Internal.Cryptography.Pal.CertificatePal.FilterPFXStore(Byte[] rawData, SafePasswordHandle password, PfxCertStoreFlags pfxCertStoreFlags)
   at Internal.Cryptography.Pal.CertificatePal.FromBlobOrFile(Byte[] rawData, String fileName, SafePasswordHandle password, X509KeyStorageFlags keyStorageFlags)
   at System.Security.Cryptography.X509Certificates.X509Certificate..ctor(String fileName, String password, X509KeyStorageFlags keyStorageFlags)

Just to clarify, we have been able to check thumbprint etc all along but the request fails.

@johanskoldekrans Sorry to have missed the reply a while ago. If you haven't managed to get this to work yet you might instead/also want to try specifying the UserKeySet flag. It's possible that the PFX contains the "use the machine store" marker internally and that even with the EphemeralKeySet flag something is not adding up inside the importer.

And if that doesn't work, try MachineKeySet | EphemeralKeySet. (MachineKeySet without EphemeralKeySet should definitely be a permissions problem for ApplicationPoolIdentity)

Thanks! I will try. Setting a user as Applicationpoolidentity with lots of
permissions locally on the server (internal web) solved it for now. But I
will try changing flags, any ideas of exactly what permissions has to be
set so we dont give too much on the apppool?

ons 10 jan. 2018 kl. 21:38 skrev Jeremy Barton notifications@github.com:

@johanskoldekrans https://github.com/johanskoldekrans Sorry to have
missed the reply a while ago. If you haven't managed to get this to work
yet you might instead/also want to try specifying the UserKeySet flag.
It's possible that the PFX contains the "use the machine store" marker
internally and that even with the EphemeralKeySet flag something is not
adding up inside the importer.

And if that doesn't work, try MachineKeySet | EphemeralKeySet.
(MachineKeySet without EphemeralKeySet should definitely be a permissions
problem for ApplicationPoolIdentity)

โ€”
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/dotnet/corefx/issues/23780#issuecomment-356728791,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AR1Ti68ZDzzOn7egwZWR3sGVL2kSvKriks5tJR-ogaJpZM4PLtaA
.

--

www.penser.se
Disclaimer: www.penser.se/disclaimer

@johanskoldekrans Nope, sorry. The guts of where things go wrong is pretty much black box to me, too :smile:.

๐Ÿ˜†๐Ÿ‘๐Ÿป

10 jan. 2018 kl. 23:29 skrev Jeremy Barton notifications@github.com:

@johanskoldekrans Nope, sorry. The guts of where things go wrong is pretty much black box to me, too ๐Ÿ˜„.

โ€”
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub, or mute the thread.

--

www.penser.se
Disclaimer: www.penser.se/disclaimer

Setting up X509Store may help.

   private X509Certificate2 GetMyX509Certificate(string path, string password)
    {
        try
        {
            X509Store store = new X509Store("My", StoreLocation.CurrentUser);
            X509Certificate2 cert;
                cert = new X509Certificate2(File.ReadAllBytes(path), password, X509KeyStorageFlags.MachineKeySet);
                bool result = cert.Verify();
                var r2 = result;
            return cert;
        }
        catch (Exception ex)
        {
            Console.Out.WriteLine("ErrorGettingCertificate in base:" + ex.Message);
            return null;
        }
    }

@wparzych Thanks, that did it for me.... this is really odd shouldn't this be open?

to clarify I am running a docker image based on microsoft/dotnet:2.2-sdk-nanoserver-1803 and just trying to import the cert

@wparzych Why does the X509Store initialization matter? You're not referencing it.

Changing from X509KeyStorageFlags.DefaultKeySet to X509KeyStorageFlags.MachineKeySetfixed it for me.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

iCodeWebApps picture iCodeWebApps  ยท  3Comments

sahithreddyk picture sahithreddyk  ยท  3Comments

jchannon picture jchannon  ยท  3Comments

bencz picture bencz  ยท  3Comments

Timovzl picture Timovzl  ยท  3Comments