Using the "Service Account" certificate store with .NET 2.0 HttpWebRequest - c#

I have a .net 2.0 windows service application written in C# that access a non-soap web service via SSL with HttpWebRequest. My windows service runs under the NETWORK SERVICE account.
I am able to make SSL work by issuing a cert to the web server that my windows service talks to from a CA, then installing the CA's cert to the Local Machine\trusted root store.
What I would like to be able to do is to install the CA's cert to the "service account" trusted root cert store instead of the "local machine" store, and have this "just work."
In other words, it seems that the SSL authentication used by the .net framework is hard coded to check the current user store, and the local machine store for trusted roots when validating an SSL certificate, but is there any clean way to get it to check the "service account" store instead of (or in addition to) the other stores?
The only solution I can come up with is to override the ServicePointManager.ServerCertificateValidationCallback and then P/Invoke to the Crypto API to open the "service account" cert store in Win32, and manually search for the root in there. I'd rather not do that, since it would seem to be a pretty large performance penalty, having to wait for failure from 2 cert stores I don't intend to use anyway.
Possibly, i'm taking entirely the wrong approach here, but it really seems like I'm missing a simply way to solve my problem.

If your code controls the server it is talking to, then why dont you just return "true" from the CertValidationCallback? It is not as if the server cert will change every time you make a request?
Unless you really need the system to also verify other things related to the cert, for eg CRL, expiry etc, you might just make it work by implementing a custom cert validation callback.
If you really want to install the cert into the NETWORK_SERVICE a/c context, then there are tools from Microsoft to help you do that.
http://support.microsoft.com/kb/901183
http://web.asu.edu/community/installing-client-certificate-windows-machine
http://www.derkeiler.com/Newsgroups/microsoft.public.dotnet.security/2008-03/msg00011.html
good luck!

Related

Read certificates from a PKI card

How can I read certificates from a PKI card?
I tried finding answer on the Internet but I didn't get any good results.
Any ideas how to get the certs from a PKI card?
I need to sign some forms with a certificate key. All this will happen in a web app.
Later...
I didn't tried much because I don't have a point to start. I've just learned that all of the certs are read by Windows when you insert the card. This way I think I can get them using X509Store. I'll try it and I'll be back but still I'm in the need of some help.
As soon as you plugin in your SmartCard the certificates are copied to your local, personal certificate store. You can use "certmgr.msc" (run -> enter) to have a look at these certs.
You can access the certificates, as well as the associated private keys, with the X509Store. But of course you can only do it locally on your machine due to security reasons. Imagine every website would have access to your private keys...
How to Sign and Verify the signature with .NET and a certificate (C#)
If you are using CAPICOM, you will still need to execute code on the local machine (JavaScript).
You find the following statement here :
[CAPICOM is a 32-bit only component that is available for use in the following operating systems: Windows Server 2008, Windows Vista, Windows XP. Instead, use the .NET Framework to implement security features. For more information, see the alternatives listed below.]
Important None of the alternatives to CAPICOM offer a solution for scripts; therefore, you must write your own ActiveX control. For more information, see ActiveX Controls.
Which indicates that the .Net classes are not a "full" replacement to CAPICOM. So you can't use the "X509" classes in JavaScript.
If you want to use a client side private certificate to sign some data (assume a hash), you need to run code on the client. Here are some ideas what you could do:
Write an ActiveX control
Write browser Plugin(s)
Write an application which can be called by using a custom URI schema (can't post another Link, google it and you will find it).
Of course you need to retrieve the data on the server side and for the last solution you may need a kind of a webservice.
Conclusion
Don't be confused about private and public keys from a certificate.
There are scenarios where you send a certificate to the server for e.g. authentication.
But then its your public key. You should never send your private key around (of course technically its possible).

How to programmatically add x509 certificate to local machine store using c#

I understand the question title may be a duplicate but I have not found an answer for my situation yet so here goes;
I have this simple peice of code
// Convert the Filename to an X509 Certificate
X509Certificate2 cert = new X509Certificate2(certificateFilePath);
// Get the server certificate store
X509Store store = new X509Store(StoreName.TrustedPeople, StoreLocation.LocalMachine);
store.Open(OpenFlags.MaxAllowed);
store.Add(cert); // x509 certificate created from a user supplied filename
But keep being presented with an "Access Denied" exception.
I have read some information that suggests using StorePermissions would solve my issue but I don't think this is relevant in my code.
Having said that, I did test it to to be sure and I couldn't get it to work.
I also found suggestions that changing folder permissions within Windows was the way to go and while this may work(not tested), it doesn't seem practical for what will become distributed code.
I also have to add that as the code will be running as a service on a server, adding the certificates to the current user store also seems wrong.
Is there anyway to programmatically add a certificate into the local machine store?
Thank you to Oscar and Bob for asking the questions and leading me in the right direction +10 to you both :)
My issue, as I think we all knew (even me) was the user running the application had insufficient privilages to add a certificate to the local machine store.
But various attempts to elevate the user permissions were failing for me, let me explain why.
I had 3 seperate projects in my solution, the wcf service which requires the X509certificates, the windows form client and the cryptography class library which, amongst other things, installs the certificates provided via the windows form client.
As most of the code within all 3 projects could run without elevated permissions, I really wanted to only elevate them at the certificate install stage within the class library but I tried to use Process and Verb= "runas" in code and this didn't work.
Then I tried to add a custom manifest but if you try to alter the properties of a class library to use a custom manifest, you'll find the option is disabled.
So I changed things.
My cryptography class is now within my windows form client and I've added the custom manifest to the client. This now means the whole client opens with elevated privilages but I'd rather that than the alternative.
Thank you again

How to detect fake trusted personal/root SSL certificates of target domain

Someone can add a fake SSL cert. into trusted certificates collection. How can I detect these fakes? How can I verify a cert is official, is there any list to compare?
I've added a screenshot of a legal & a fake one (created by Fiddler):
ADDITION:
To ensure your sensitive SSL communication is secure, you have to use certificates of common trusted authorities. If someone installed Fiddler -or a malicious software installed its own cert.- then I need to cancel any communication attempts and alert in my app at client's pc.
ADDITION 2
I only care about communication between end user's pc & Google Docs. We know Google Docs web site's public certificate is given by "Google Internet Authority". I think I have to compare it & installed certificate for Google Docs on user's pc.
Last word:
I need to simply compare the certificate in use for target site vs the target site's original SSL certificate just before any SSL comunication.
More info: This link
There is no single "official list". You must compare your list to someone else's list.
Windows has its own list which is used by Internet Explorer.
Firefox maintains a separate list of its own.
I don't know about Chrome, Safari or Opera.
But the long and short is you need to compare your list to other lists which you know are correct, for example from a colleague's computer.
You can't. If user added it, that means she trusts it. And also, a certificate can be valid without being part of the "official" lists.
Each application maintains (or relies on other applications) the list of trusted root certificate authorities. Windows has its own list, OpenSSL has its own list, all major browsers have their own lists (Chrome uses or can use Windows one, if memory serves).
If you create a Windows application, your best bet is to rely on system list, as it is updated on a regular basis (if you carry your own list, you have to maintain it as well).
One thing to pay attention to is that the certificate issued by trusted authority doesn't mean trusted certificate. Some certificates are issued by hacking (this happened at least with two intermediate CAs during last years), private keys for others are leaked, and this causes the need to revoke such certificates. Revocation status can be checked by inspecting CRLs (revocation lists published by CAs) or using OCSP (online certificate status protocol).
You need to use them no matter where you get the list of trusted CAs.

WebException Could not establish trust relationship for the SSL/TLS secure channel

My company has developed a .NET web service and a client dll that uses that web service. The webservice is hosted on our server over ssl and the cert is provided and signed by GoDaddy. We have some clients in a hosted environment that are getting the following error message from the client dll when it tries to access our web service.
System.Net.WebException The underlying connection was closed: Could not establish trust relationship for the SSL/TLS secure channel.
Our fix has been to have them open IE on the server, which is a challenge in and of itself for a lot of the hosted services, and go to the WSDL url. IE then prompts them with a security alert dialog. It says the cert date is valid and a valid name matching the name of the page, but was issued by a company you have not chosen to trust. When they click Yes to proceed, the client dll can then succesfully connect to the web service and operate as normal.
Does anyone have any idea why GoDaddy would not have been in there valid publishers list? All of the servers we have running has GoDaddy as a valid authority. I'm guessing, for security reasons, they've uninstalled the authority for GoDaddy, but not totally convinced that there's not some other underlying issue.
Unfortunately, I haven't had much luck trying to recreate this locally. If I go into Internet Options and remove the GoDaddy authorities and hit our service, ssl works just fine. I go back into the list of publishers and GoDaddy gets put right back in. So my second question is, How the heck do you get rid of GoDaddy so I can get an invalid cert warning?
Okay, last question. Is there a way in code I can tell the web service to ignore invalid certs. I've seen some posts on doing this programatically with WCF but not old web services.
I fixed this error by adding this line before calling the web method:
System.Net.ServicePointManager.ServerCertificateValidationCallback = (senderX, certificate, chain, sslPolicyErrors) => { return true; };
You may need to install on your servers the intermediate certificates used to sign your SSL certs.
Browsers will attempt to validate the SSL certificate by checking the validation of the chain of certs that signed the SSL cert. If the server doesn't supply the certificate chain with the SSL certificate, the browser may reject the SSL cert. (More of an issue for Firefox than IE). The root certificate must still be installed on the client machine for any of this to work.
VB.NET equivalent is
System.Net.ServicePointManager.ServerCertificateValidationCallback = Function(senderX, certificate, chain, sslPolicyErrors)
Return True
End Function
This is really more of a serverfault question, but I'll add what I can here.
The root cert authority list that windows machines normally trust is updated fairly regularly. This comes down as a windows update to IE. You can see MSDN for more information.
If your clients do not have windows update turned on or are actively ignoring windows updates, which is unfortunately very common for a lot of IT departments, then there's not much you can do other than switch SSL providers.
Basically, they need to get the certificate updates or you need to switch to a cert provider that has a high probability of already being trusted by the machines in question. Typically this means Verisign or Thawte. The third alternative is the route you've been going down: have them manually trust the root CA.
At the end of the day I hate the idea of changing an app in this way simply because the IT departments in question are morons but the real question boils down to how your company wants to handle this.
When you look at the Go Daddy Certification Path of that certificate on the web server, do you see Go Daddy Class xxx or Starfield Class xxx ?
And from your non-legacy client i.e Windows Vista upwards, what does the Go Daddy Certification Path display? Go Daddy Class xxx or Starfield Class xxx ?
And these clients that get the warning, are they legacy clients? i.e WinXP and older?
Root Certificate updates works differently as of Windows Vista.
http://support.microsoft.com/kb/931125
Root certificates on Windows Vista and later are distributed via the automatic root update mechanism – that is, per root certificate. When a user visits a secure Web site (by using HTTPS SSL), reads a secure email (S/MIME), or downloads an ActiveX control that is signed (code signing) and encounters a new root certificate, the Windows certificate chain verification software checks Microsoft Update for the root certificate. If it finds it, it downloads the current Certificate Trust List (CTL) containing the list of all trusted root certificates in the Program, and verifies that the root certificate is listed there; it then downloads the specified root certificate to the system and installs it in the Windows Trusted Root Certification Authorities Store.
You'll probably find that your Go Daddy Certification Path on the web server thinks it's Starfield Class 2 instead of Go Daddy Class 2 so you installed the wrong root certificate. It caught me out as when you view in on the web server it doesn't display a root certificate warning, download and install the Do Daddy class 2 root cert and remove the Starfield one and your problem should dissapear.

Programmatically import cert into IIS?

I have a .pem certificate for SSL, I want to distribute it with my web application in an MSI (has to run on clients' computers). I then need to import it (into some credentials store?) and tell my site bindings to use it. But how can I do this in code? I've discovered Microsoft.Web.Administration, but not sure where to go from there …
This is in IIS7 btw.
EDIT: The goal here is to have a web application that customers can run on their intranets. It mainly acts as an API for an iPhone app. (Maybe this isn't the best design but we're locked in now.) So the customer installs the MSI, and voila, they have a web service. Now there needs to be password authentication between the iPhone and the web service; the simplest way seemed to be to do it in https. So I made a self-signed cert.
I'm aware that redistributing a single cert is generally a bad idea, but we're just trying to defeat casual hackers here … this is going to be intranet only and for businesses only, it seems unlikely that anyone is going to be doing anything too crazy, and the API severely restricts the amount of Bad Things you are able to do to the database anyways.
So there we go, the goal is to have password authentication on an intranet web app, with one-click(ish) installation. :-D
The answer, dear readers, is this:
// Assume 'site' is already set to your site via something like
// Site site = mgr.Sites.Add(siteName, directory, 443);
X509Store store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
store.Open(OpenFlags.OpenExistingOnly | OpenFlags.ReadWrite);
// Here, directory is my install dir, and (directory)\bin\certificate.pfx is where the cert file is.
// 1234 is the password to the certfile (exported from IIS)
X509Certificate2 certificate = new X509Certificate2(directory + #"\bin\certificate.pfx", "1234");
store.Add(certificate);
var binding = site.Bindings.Add("*:443:", certificate.GetCertHash(), store.Name);
binding.Protocol = "https";
store.Close();
Thanks to this random thread: http://forums.iis.net/t/1163325.aspx
You need to narrow down your question. What is the certificate used for (exactly)?
If your certificate is used for client-side authentication (to authenticate the client on the server), then distributing it with your application makes such authentication plain useless, as you would be disclosing the secret key.
If you need to validate the server's certificate (and you've been given server's certificate or certificate chain in your PEM file), then this could work, but why would you need to install the certificate to the certificate store?
You should note, that PEM format is not natively supported by Windows or .NET libraries so you need to convert it to PFX before deployment, and then import a PFX or just create a store in memory based on PFX (you will find plenty of information by looking for PFX or PKCS#12 on StackOVerflow).
Upd: it would be a better approach to generate the certificate each time you install the application and let the user have their own certificate (eg. it's possible that they already have a valid certificate for their web site).

Categories