I am attempting to use the .Net System.Security.SslStream class to process the server side of a SSL/TLS stream with client authentication.
To perform the handshake, I am using this code:
SslStream sslStream = new SslStream(innerStream, false, RemoteCertificateValidation, LocalCertificateSelectionCallback);
sslStream.AuthenticateAsServer(serverCertificate, true, SslProtocols.Default, false);
Unfortunately, this results in the SslStream transmitting a CertificateRequest containing the subjectnames of all certificates in my CryptoAPI Trusted Root Store.
I would like to be able to override this. It is not an option for me to require the user to install or remove certificates from the Trusted Root Store.
It looks like the SslStream uses SSPI/SecureChannel underneath, so if anyone knows how to do the equivalent with that API, that would be helpful, too.
Any ideas?
It does not look like this is currently possible using the .NET libraries.
I solved it by using the Mono class library implementation of System.Security.SslStream, which gives better access to overriding the servers behavior during the handshake.
What the certificate validation is doing is validating all certificates in the chain. In order to truely do that it just contact the root store of each of those cerficates.
If that's not something you want to happen you can deploy your own root store locally.
It is not the validation part I want to change. The problem is in the initial handshake, the server transmits the message informing the client that client authentication is required (that is the CertificateRequest message). As part of this message, the server sends the names of CAs that it will accept as issuers of the client certificate. It is that list which per default contains all the Trusted Roots in the store.
But if is possible to override the certificate root store for a single application, that would probably fix the problem. Is that what you mean? And if so, how do I do that?
Related
I'm trying to set up a TCP stream (non-HTTP) on a server that will be exposed to the public internet, but only "chosen" clients should be able to connect to. As I understand it, this is generally handled by certificate pinning, but I'm not familiar with all the details of how this is done.
Generate a SSL cert for the server. That's easy enough.
Set up the server. Have it call something like the following when a client connects:
private SslStream GetSslStream(TcpClient client, string certificateFile)
{
var c = X509Certificate.CreateFromCertFile(certificateFile);
var cert = new X509Certificate2(c);
if (!cert.Verify()) {
throw new Exception("Certificate failed verification");
}
var stream = new SslStream(client.GetStream(), false, VerifyClientCert);
stream.AuthenticateAsServer(cert, true, true);
return stream;
}
Where does the client certificate come from? Is it a copy of the server's SSL certificate? Is it a separate cert that has to be derived from the server's cert in some way? Is it a completely different cert?
What goes in the VerifyClientCert method to make sure it's the right certificate? Just Verify() and check the Thumbprint against an expected value, or is there more that needs to happen?
Should every client get a copy of the same client cert?
Certificate pinning is a different concept. When server uses SSL certificate, it's usually issued by some Certificate Authority. Certificate of that authority can in turn be issued by yet another Certificate Authority, forming a chain. When client validates server certificate, it checks if that Certificate Authority is "trusted" by this particular client. "Trusted" in turn means that some certificate in the chain described above is trusted by this client. For example, Windows and Linux OS both come with certain set of Certificate Auhorities trusted by default. If server certificate has some of those authorities in their chain - then it's also trusted.
Certificate pinning means you, as a client, impose more restrictions on server certificate than what is described above. For example, you might say that for this domain \ endpoint, I only trust this specific certificate (with this specific digest), and I do not care about trust chain at all. Or that for this endpoint I only trust this specific Certificate Authority. This is certificate pinning. You can use it for your server, but it has its drawbacks which I won't describe here.
Now back to your problem. What you describe is called Client Certificate Authentication. In SSL handshake, not only server can present certificate for client to verify. Server might also request client to send his own certificate, to authenticate that client. This certificate is of course completely different from server's certificate.
Every client must have different certificate. Usually one of the two options are used:
You (server owner) create your own Certificate Authority (for example with couple of simple openssl commands), and then you issue separate certificate for each client from this authority. Then you send those certificates (one for each client), including private keys, to their respective clients. After that you can throw away (delete) those certificates, because you will no longer need them. When validating client certificate - just check if it was issued by your Certificate Authority.
OR separate certificate is issued for each client and you store thumbprints (digests\hashes) of those certificates, one for each client. Then during validation you ensure that Thumbprint has the expected value for given client. The benefit is that you can ask client to generate such certificate and send you (the server owner) the thumbprint. Then you never had private key of that certificate in your possession (unlike method with your own Certificate Authority where you generated certificate yourself and at one point posessed its private key).
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.
I am trying to connect to Bloomberg FIX (EMSX) through SSL using QuickFIX/n.
I have got 3 .pem files from Bloomberg using which I have to configure the SSL connectivity. I have gone through all the available reference material on the internet but in vain.
Can anybody help in doing this configuration??
Thanks in advance.
Have a look at using Stunnel and check out this question...
QuickFIX/n expects .pfx extension files. See http://quickfixn.org/tutorial/configuration.html#ssl
This extension is normally used for PKCS #12 encoded files which contain the certificate and the private key, protected by a password.
You may need to convert your .pem files so that you have one .pfx file which contains your private key and the certificate and another file which contains your CA Certificate. The SSLCertificate and SSLCACertificate configuration parameters of the QuickFIX/n session should then be set to the path of these two files. SSLCertificatePassword should contain the password.
QuickFIX/n is strict and requires the FIX Acceptor (Bloomberg EMSX in this case) to have the x509v3 extended key attribute "TLS Web Server Authentication" (1.3.6.1.5.5.7.3.1) to be explicitly present in the server certificate that is presented during the SSL handshake.
If not present, you will get an error message in the QuickFIX/n session event log:
Remote certificate is not intended for server authentication: It is
missing enhanced key usage 1.3.6.1.5.5.7.3.1
Some FIX Acceptors still don't define this in their server certificate, so even if you go to the effort of converting your PEM files to PFX, it still won't work.
You can use Stunnel which doesn't seem to mind what purpose was intended for the server certificate that is presented. As an added bonus Stunnel understands PEM files so no need to convert. However, you should be aware that if your end goal is end-to-end encryption, the hop between your QuickFIX/n application and Stunnel will be in clear text.
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.
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.