Proper Way to Validate Client-Certificates - ASP.NET MVC - c#

I am attempting to build an application that would make use of client-certificates to authenticate the user attempting to access the application. I am using a self-signed CA as described here. While, I have done quite a bit of research, I have yet to come away with the "proper" approach to authenticating a client-certificate.
First, it seems as if this can be done at both the application level (using .NET classes such as X509 store) or in IIS itself. What are the advantages or disadvantages of each approach?
If I were to choose to validate the certificate with in my application code, what is the proper field to validate? For example, this question uses the serial number. However, for my "test" certificates, the serial number is literally four characters (not counting spaces). What is the proper approach to ensuring that the client-certificate really was issued by me?
I attempted to apply this at the server level (I am using IIS Express for testing). However, I do not get a request for a certificate, just a 403.7 - Forbidden, error ( with the following web.config settings <access sslFlags="Ssl,SslNegotiateCert,SslRequireCert" />). However, if I remove SslRequireCert, I am able to at least "see" many certificates using the following code snippet:
X509Store store = new X509Store(StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadOnly);
X509Certificate cert = store.Certificates[0];
Even if this issue (3) were resolved, is there any way, using IIS, to authenticate a certificate without having to map it to a Windows account?
I have tried installing the client certificate both in Firefox and in my PCs keystore. Thanks for any guidance.

Related

How can I implement trust only one specific certificate (my own) using code, in ServerCertificateValidationCallback

Context
The client code have to connect to a remote web api. SSL is mandatory because of sensitive information exchanged on the wire. We have our self issued certificate a
In the project there is no currently what is properly configured in the remote IIS, and everything tested OK, except the certificate is not trusted.
I know I can install the certificate on the local machine and trust in it. However for some reasons (for example, there are Xamarin Android clients too) it is not a suitable solutions.
Current workaround is to simply ignore the certificate error in code, which is working both on desktop both on Android Xamarin:
ServicePointManager.ServerCertificateValidationCallback =
(s, certificate, chain, sslPolicyErrors) =>
{
// Here I would like to check against that the certificate
// is the specific one I issued, and only that case return with true
// if (what is the most suitable to write here?)
return true;
};
Question
Note: this is development/testing phase. In production there will be a certificate installed created by a trusted certificate authority.
Now I have bad feeling about accepting anything, so I would like to narrow it.
How to check against that the certificate is the specific one I issued, and only that case return with true.
Is that a working idea to run the code, place a breakpoint, and get the certificate.Thumbprint, then write an if to check against it?
(edit)
...or better... get the PublicKeyString and checking against it?
Checking against thumbprint is IMHO an OK option. With thumbprint you can narrow the trust to exactly one certificate.
Checking against public key or Subject Key Identifier is also OK but you will expand the trust to the same key pair. There can be several certificates issued on the same public key (with or without the same Distinguished Name).
As #bartonjs stated another option is to check raw data. This approach has the same power as checking against thumbprint. The benefit of this would IMO be that when you look at the code a year from now you will still know what certificate you trust :)
Anyway when checking against the exact certificate keep in mind that the certificate will expire someday (i.e. let's encrypt issues a 3 month certificate if I remember correctly) and your application will have to be redeployed with a new certificate check. You would have to keep track of expiration date of the certificate.

'Malformed reference element' when signing SAML assertion with x509 certificate

I am building a SAML IdP to handle SSO between a Student Portal, and a third party system. My code is based on on the CoverMyMeds Test Harness, and I have used some of their unaltered libraries in my project. I have also set up their sample identity provider to make it easier to debug this issue.
The page I built off of this foundation to handle authentication takes a GUID from the Student Portal, using it to retrieve student details from an internal service and package it into a SAML assertion, which is then signed with an x.509 certificate before sending it off to the 3rd party SAML service provider. It is here that I am stuck. When attempting to sign the SAML request I receive:
(source: tennysonhull.com)
The debugging and research I've done seems to indicate that the code is not the problem, but just so you understand how I got here: The ComputeSignature() call is coming from the CertificateUtility -> AppendSignatureToXMLDocument method, which is being called from SAML20Assertion -> SerializeAndSignSAMLResponse method, which is being called indirectly by the IdPLauncher.aspx page.
This is what the assertion looks like before I attempt to sign the package. I've examined this thoroughly and have run it through debuggers without finding an issue with it.
My hypothesis is that there is something wrong with the certificate, which is self signed for testing purposes. The company I am working with assures me that other people who have integrated have used test certs in the past, but I can think of no other reason why this would be failing. Though I have tried numerous variations, the command I thought most likely to produce a working test certificate based on my research is: makecert.exe -a md5 -r -pe -n "CN=iGradTest" -ss my -sr localmachine -sky signature "c:\iGradTest.cer", but the resultant certificates continue to have the same issue. The generated certificate has been given a friendly name and set up to work with IIS using winhttpcertcfg. The app is successfully obtaining the certificate, but failing when attempting to add it to the assertion.
I am at a loss for what to do next. Any advice on next steps to identify and resolve this issue would be greatly appreciated. Thanks for taking the time to read/assist.
The exception you are facing means that the URI of the reference does not resolve to another element tagged with an ID attribute.
Based off of the code provided, SAML20Assertion.cs:90 is prepending a # to the ID, then CertificateUtility.cs:69 is prepending it again. Remove it from one of the locations, and all is well.
SAML20Assertion.cs:90:
CertificateUtility.AppendSignatureToXMLDocument(ref xmlResponse, "#" + ((AssertionType)Response.Items[0]).ID, SigningCert);
Should be
CertificateUtility.AppendSignatureToXMLDocument(ref xmlResponse, ((AssertionType)Response.Items[0]).ID, SigningCert);

Use certificate when issuer is not is X509Store trusted roots for client authentication using Microsoft .NET framework

While working on this question, I identified that the problem is slightly different than initially stated, so I am changing title and description
I'm trying to authenticate myself against WebService using my client certificate. I am using WebRequest for that purpose. I need to use self-signed certificate, without registering it in Windows X509 trusted root store.
The way I know if client certificate is presented or not is by examining request object on the server
I tried to use code from this question as a guidance. It doesn't work on .NET. Here is my code:
var certificate = new X509Certificate2(Properties.Resources.MyCert);
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(host);
req.ClientCertificates.Add(certificate);
WebResponse resp = req.GetResponse();
var stream = resp.GetResponseStream();
What I observe is that even though req.ClientCertificates does contain certificate with a valid private key, that certificate is never presented to server. I get no indication from WebClient that certificate is not used during handshake.
If I put certificate into "trusted root", the code will work (even when certificate is not in "personal").
My questions are:
Since certificate is usable when it's placed in "trusted root", I assume it is likely due to policy or something of that kind. Is it possible to coerce .NET to ignore policy settings and use supplied client certificate during TLS negotiation?
If abovementioned coercion is not possible, is there a call which will tell me ahead of time, that certificate I am about to use is not usable, and will be ignored? Alternatively, if such call is not available, could I make WebClient fail indicating a certificate error, instead of silently skipping over?
NOTE: I am aware that configuring certificates as described by Microsoft will work. This is NOT what I am looking for. I don't want to register potentially insecure certificate in trusted root, because this is potentially security hazard. I want to use cert on client without registering in store, or at least to get an exception indicating that certificate cannot be used. I realize that there can be multiple reasons why certificate cannot be used for a session, but there must be an exception, or at least some sort of indication on the client side that it cannot use specified cert. Instead, client simply doesn't present one.
When you instantiate your X509Certificate2, is the PrivateKey property set? If it is null, you are missing the private key, meaning the SSL/TLS client will be unable to authenticate you.
Make sure you are loading the certificate from a PFX file (or similar) instead of a CER. These contain the private key, too. They are usually password protected for that purpose. See How to retrieve certificates from a pfx file with c#? for more info.

SmartCard security, how do you authenticate the certificate as not fake?

I'm trying to develop an ASP.net site that reads the clientCertificate to ensure a smart card was used to access the website (trying to do away with username/password login).
The process I have in my mind is:
User registers an account and C# records user's clientCertificate (public).
The user can then log in the next time with that same clientCertificate, and they are now an authenticated user if hash valid.
I will use the code below to ensure authenticity of certificate. The browser should deal with private keys and ensure the certificate was NOT faked.
Based on Subject+certificate combination, C# assigns them their role-access.
The following code can be used for authenticity of certificate right?
X509Certificate x509Cert = new X509Certificate(Request.ClientCertificate.Certificate);
SHA1 sha = new SHA1CryptoServiceProvider();
byte[] hashvalue = sha.ComputeHash(Request.ClientCertificate.Certificate);
byte[] x509Hash = x509Cert.GetCertHash();
// compare x509Hash WITH hashvalue to ensure they are a match.
// If not, possibly faked certificate, not a real smartcard???
Is this how SmartCard authentication process should work???
If you just need to authenticate users with client certificates you should do this in IIS. You do not need to add any code at all to your application:
Specify Whether to Use Client Certificates (IIS 7)
Unless you need to link client certificates with database accounts or perform an additional validation step. But still for client certificate authentication I would stick with IIS settings.
Update:
In case you need to manipulate the client certificate you can do:
X509Certificate2 x509Cert2 = new X509Certificate2(Page.Request.ClientCertificate.Certificate);
And then access its properties such as:
x509Cert2.Subject
However, leave the validation piece up to IIS. If the client presents a bad certificate your asp.net code will not even execute since IIS will reject it
See this thread, sir. You do not need to verify authenticity explicitly in your code. IIS will do it for you.
Does IIS do the SSL certificate check or do I have to verify it?
IIS even tries to check revocation lists (however, this is often disabled if the CRL is large). An OCSP responder should be used to validate in cases where the CRL is very large or latency in checking it is high http://www.axway.com/products-solutions/email-identity-security/identity-security/va-suite.
Client-certificate authentication is done during the SSL/TLS handshake.
It is usually done using a Public Key Infrastructure, whereby the server has a (fixed) list of trusted CA certificates which it uses to verify the client certificate (in the same way as clients to it for the server). Once the certificate is presented to your application after this stage, you will know that:
the client has the private key for that certificate (guaranteed by the Certificate Verify message in the TLS handhsake (the SSL/TLS stack will verify this for you, no need to implement anything);
the client has the identity described in the certificate, because you will have verified it against your trusted CA.
The verification against a trusted CA requires the user to be registered with that CA in advance. You can't just authenticate any certificate if it hasn't been issued by a CA you trust. (Mapping the certificate's subject to a local user ID is another matter: you could do this upon first connection if needed: have your own database or directory service to map the Subject DN to another kind of user ID in your application, for example.)
User registers an account and C# records user's clientCertificate
(public). The user can then log in the next time with that same
clientCertificate, and they are now an authenticated user if hash
valid.
It sounds like you want to allow any certificate to be presented and use it for the initial registration, without necessarily resorting to a commonly trusted CA.
This is possible in principle, and I've done this to explore alternatives to PKI in Java.
To do this, you need to let any certificate through as far as the SSL/TLS handshake is concerned, and verify the certificate itself later. (You do need to use some form of verification.) You are still guaranteed with this that the client has the private key for the public key certificate it has presented.
Doing this requires two steps:
You need to be able to advertise the fact that you're going to accept any certificate, by sending an empty list of certification authorities in the Certificate Request TLS message (explicitly allowed by TLS 1.1).
Configure the SSL/TLS stack to trust any certificate (once again, when you do this, do not forget to implement your own verification system within your application, otherwise anything will really get through).
In .Net, while it should be possible to address the second point using a remote certificate validation callback, I have never found a way to alter the first point (this was also asked in this question).
In Java, the JSSE's X509TrustManager allows you to address both points.

Application signing/verification

I'm relatively new to Windows development, but have just finished a small project. I want to make my application "verified" like a lot of other applications are. For example, when you launch the application and UAC pops up, it won't have the "publisher unknown" message with the yellow bar and should have a "Verified by: " section on it.
I hope I explained that correctly. Does anyone know how to do this? I am hoping it's not like SSL certificates where you have to pay money...
Please feel free to let me know if my question was unclear or if I am not explaining it the right way. Thanks!
Unfortunatley, yes, you do need to pay money.
What you need is a code signing certificate. You can get them from the below certificate authorities:
Thawte
VeriSign
Or if you are looking for a cheap one, I would buy one from here, that is where I got mine:
Tucows
Once you get your cert, you can integrate it into the build process to sign your application and it will show your name as the publisher.
This is known as code signing. If you want to generate your own certificate just for you, you can follow the instructions in this answer.
As Eaton suggests, you'll have to pay if you want one for public use. I recently purchased one for our BuildMaster software from http://www.instantssl.com/code-signing/index.html for 2 years at about $340.
Response to comment:
If I generated my own certificate, how would it work for me but not for everyone else (publicly)?
Steps 1 and 2 in the linked answer create a self-signed Certificate Authority (CA) and then add it to your Windows Certificate Store. Step 3 then generates the code signing certificate and specifies the issuing CA (the -ic switch). Since the issuer is a CA that was created in step 1 and then set to manually be trusted by you in step 2, the code signing certificate generated in step 3 appears to be signed by a trusted party on your machine.
There is no way it can be trusted by the public because you both generated the code signing certificate and the authority used to verify that its creator is actually who he says he is. Imagine if you were evil and put fake info in the code signing certificate that says the signer is Microsoft.com instead of Aaron! All certificates would useless if there was no trusted authority to verify the actual identity of signer.
When you order the certificate from a trusted CA, they'll verify your identity in some way. They'll probably ask for a scanned copy of your driver's license if it's for signing as your name, or something only the business owner would have like the articles of incorporation (we used our D&B D-U-N-S number to verify Inedo).
Once they have verified your or your company's identity, they'll send you a certificate that is trusted by typical web browsers that you can use to sign your executables and the public can be assured that it's actually you signing them because the CA would be whichever company you bought the certificate from. If you look in the Options section of your favorite web browser you can see which CAs are deemed trusted.
Beside bought certificates as #Eaton explained and selfcreated selfsigned certificates as #John Rasch suggested
you can try free comunitee-based certificates from http://cacert.org that works simmilar to pgp/gpg-s trustsystem. For codesigning you need a trust resumee of at least 100 points.
German users can also use free http://web.de certificates that also permits codesigning.
The drawback of this approach (as well as self-created certs) is that "verified" is only activ, if the operating system of the executing client has installed the root certificate of the signing agency which in 99% is not the case.
However Firefox knows cacert.org so certs from cacert can be used for https, java and silverlight but not for standard msie without installing the root certificate.
You can also buy a code signing certificate from StartCom (www.startssl.com) for roughly $49.90 for two years - a lot cheaper than Thawte, Verisign and the rest.

Categories

Resources