New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WindowsCryptographicException trying to read certificate from file #23780

Closed
johanskoldekrans opened this Issue Sep 4, 2017 · 13 comments

Comments

Projects
None yet
6 participants
@johanskoldekrans
Copy link

johanskoldekrans commented Sep 4, 2017

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

@bartonjs

This comment has been minimized.

Copy link
Member

bartonjs commented Sep 11, 2017

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.

@johanskoldekrans

This comment has been minimized.

Copy link

johanskoldekrans commented Sep 12, 2017

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)

@johanskoldekrans

This comment has been minimized.

Copy link

johanskoldekrans commented Sep 12, 2017

@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?

@bartonjs

This comment has been minimized.

Copy link
Member

bartonjs commented Sep 13, 2017

@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;

@johanskoldekrans

This comment has been minimized.

Copy link

johanskoldekrans commented Sep 13, 2017

@johanskoldekrans

This comment has been minimized.

Copy link

johanskoldekrans commented Sep 15, 2017

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)
@johanskoldekrans

This comment has been minimized.

Copy link

johanskoldekrans commented Sep 15, 2017

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

@bartonjs

This comment has been minimized.

Copy link
Member

bartonjs commented Jan 10, 2018

@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)

@bartonjs bartonjs added this to the Future milestone Jan 10, 2018

@johanskoldekrans

This comment has been minimized.

Copy link

johanskoldekrans commented Jan 10, 2018

@bartonjs

This comment has been minimized.

Copy link
Member

bartonjs commented Jan 10, 2018

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

@johanskoldekrans

This comment has been minimized.

Copy link

johanskoldekrans commented Jan 10, 2018

@wparzych

This comment has been minimized.

Copy link

wparzych commented Aug 1, 2018

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;
        }
    }

@bartonjs bartonjs closed this Sep 13, 2018

@karelz karelz modified the milestones: Future, 3.0 Nov 15, 2018

@Tarig0

This comment has been minimized.

Copy link

Tarig0 commented Dec 26, 2018

@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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment