I'm trying to connect to Java web service with C# client. I don't have physical access to that service, but I was told by it's developers that I need to provide client certificate AND intermediate CA in my requests.
I tried using Service Reference and WebClient but I only managed to send just client certificate.
protected override WebRequest GetWebRequest(Uri address)
{
HttpWebRequest request = (HttpWebRequest)base.GetWebRequest(address);
request.ClientCertificates.Add(m_TransportCertificate);
//m_TransportCertificate holds client certificate with key and rest of the cert chain (intermediate and root)
return request;
}
How to send two client certificates in C#? As far as i know it isn't normal behaviour - client should only send one certificate.
I can't make my app to do it - it always sends one certificate.
Wireshark screenshot
However it works in SoapUI (the second one is intermediate CA)
Wireshark screenshot
I found a solution. Intermediate CA was automatically installed in Intermediate Certification Authorities but it needs to be in Trusted Root Certification Authorities.
Just moved it there and it worked.
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'm trying to get mutual SSL authentication working with ServiceStack, which under the hood uses HttpListener.
I use this command on the server to bind the server certificate to the required port, and enable client certificates:
netsh http add sslcert ipport=0.0.0.0:1234 certhash=5d51087438cbea33f2a4d86214b11a866876b9c5 appid={00000000-1111-2222-3333-444444444444} clientcertnegotiation=enable
If I run this command I can confirm that it says Negotiate Client Certificate : Enabled:
netsh http show sslcert
Then I add a reservation for the namespace using:
netsh http add urlacl url=https://+:1234/ user="NT AUTHORITY\NETWORK SERVICE"
'Normal' server authentication seems to work fine; the client is receiving the server's certificate and I can use ServicePointManager.ServerCertificateValidationCallback to override what is trusted.
But if the client sends no client certificate, it works as normal. If the client sends a client certificate (whether trusted or untrusted by the server), it still works as normal. This is of course not right!
I'm adding the client certificate using HttpWebRequest.ClientCertificates.Add(X509Certificate).
I've taken a look at the traffic on the wire, and AFAICS the server is sending a list of trusted CAs and requesting a client certificate.
Do I need to override something at the server to perform verification of the certificate sent by the client, similar to how clients can use ServicePointManager.ServerCertificateValidationCallback to verify the server certificate?
ServiceStack allows you to configure 'global filters', which can intercept all requests.
I solved my problem using a global filter:
public static void Validate(IRequest request, IResponse response, object message)
{
var httpRequest = ((ListenerRequest)request).HttpRequest;
var consoleCert = httpRequest.GetClientCertificate();
...
I can then send 403 if the client certificate is not present or is invalid:
response.StatusCode = 403;
response.EndRequest();
return;
Not sure if this is the recommended approach (or indeed if there is a recommended approach, but it works for me.
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've written a .NET application in C# that contacts a few web services. Everything works fine when I reference the web services on a non-secure URL; but when I reference the same web services on a secure HTTPS URL, I get an exception with the message "Could not establish trust relationship for the SSL/TLS secure channel".
I was pointed to at this web page http://www.codemeit.com/wcf/wcf-could-not-establish-trust-relationship-for-the-ssltls-secure-channel-with-authority.html. And added the code to my application. This resolves the issue and I can now use the secure web services as expected.
I notice the code snippet above says "not intended to be used in a production environment" and it currently trusts any certificate. In order to make it more secure for a production environment, should I be checking it for our own certificate? How should I check this, the 'cert' parameter in the method RemoteCertificateValidate() has a lot of information in it so I'm not sure how or what I should be checking.
TIA
It is highly likely there is a problem with the certificate used by the server to identity itself when your client is establishing the secure SSL connection. In my experience this is primarily caused by the certificate that the server returns is either not signed by a trusted root authority or one or more certificates in the chain is not valid (e.g. they have expired or are for a domain other than the one you are connecting to).
To help identify the problem you could try browsing to the service endpoint URL and seeing whether the browser reports any issues. Modern browsers will warn you if a server certificate is not valid and will sometimes tell you why. If you are using Chrome and there is a problem with the certificate you will see a red cross through the padlock icon at the left end of the address bar (or omnibox). If you click on the padlock Chrome will tell you why it doesn't like the certificate.
As for the code snippet you found, this is an example of how to implement validation of server certificates as a client in .Net. However, the example itself will never reject a connection to the server even if there is a problem with the servers certificate, hence the comment about not using the code in production. Outside of a test scenario you never want to be communicating with a server who's identity you cannot trust due to a bad certificate. To provide a "production" implementation you must check the value of the "error" parameter to see if there are any problems with the certificate and only return true if there are none.
using System;
using System.Net;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
// Attach my certificate validation delegate.
ServicePointManager.ServerCertificateValidationCallback += ValidateRemoteCertificate;
System.Net.HttpWebRequest request = WebRequest.CreateHttp("https://www.google.com");
HttpWebResponse response = request.GetResponse() as HttpWebResponse;
}
private static bool ValidateRemoteCertificate(object sender, X509Certificate cert, X509Chain chain, SslPolicyErrors error)
{
if (error == SslPolicyErrors.None)
{
return true;
}
return false;
}
}
}
In this example I am only returning true from my ServerCertificateValidationCallback delegate if the value of error is equal to SslPolicyErrors.None, otherwise I return false which generates an exception. To make this example work I used Fiddler to proxy the request and return an untrusted certificate to my client code. If you navigate to https://www.google.com under normal conditions you should not see any certificate errors as Google plays nice!!
PKI with certificates can be a gnarly domain to work in. In terms of documentation Wikipedia has a good article on PKI that covers how trusted authorities are used to sign certificates used in SSL so that clients can trust the identity of a server they are communicating with.
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?