I’m developing simple Windows Service sending and receiving data from remote web service.
I decided to use WebClient class for it’s simplicity and enhanced it to include certificate in request, like this:
class MyWebClient : WebClient
{
public X509Certificate cert { set; get; }
protected override WebRequest GetWebRequest(Uri address)
{
HttpWebRequest req = (HttpWebRequest)base.GetWebRequest(address);
req.ClientCertificates.Clear();
req.ClientCertificates.Add(cert);
return req;
}
}
This is how i prepare and make requests:
try {
//...
string qry = "Some xml query...";
System.Net.ServicePointManager.ServerCertificateValidationCallback = delegate(object sender,
X509Certificate certificate,
X509Chain chain,
SslPolicyErrors sslPolicyErrors) { return true; };
X509Certificate cert = X509Certificate.CreateFromCertFile(appPath + @"\data\cert.der");
MyWebClient cl = new MyWebClient();
cl.cert = cert;
string xmlReq = qry;
cl.Headers[HttpRequestHeader.ContentType] = "text/xml";
var data = Encoding.UTF8.GetBytes(xmlReq);
byte[] res = cl.UploadData(apiUrl, data);
cl.Dispose();
string result = Encoding.UTF8.GetString(res);
} catch (Exception ex) {
//...
}
//...
Now, i know that loading certificate from file is not the best idea, but that’s not the point. The point is that above code works perfectly in desktop application, but throws “Could not establish secure channel for SSL/TLS” exception in Windows Service.
I tried to install the service under “localService” and “networkService” account and it makes no diffirence.
UPDATE: installing under “user” didn’t help also.
Am I missing something?
When you load a certificate with this:
You’re only loading the certificate. However, for client-certificate authentication to work, you need to provide the private key too (otherwise, anyone could use any certificate).
Following the discussion in the chat room, you’ve indicated you also have a
.p12(PKCS#12), a.keyand.pemfile, as will as the.derfile you were trying to load.Usually, these extensions are used in this way:
.derfor the certificate itself in DER encoding (binary)..pemfor the certificate itself in PEM encoding (base64-encoding of DER, within---BEGIN....--- ... --- END ---delimiters)..keyfor the private key..p12(or.pfx) for the PKCS#12 file, which will contain both the certificate (and possibly the CA chain) and the private key.In .Net, the base
X509Certificatewill not allow you to load a private key along with the certificate. You should look into loading the p12 file into aX509Certificate2instance: it’s essentially a convenience class that not only models certificates, but can associate a private key to the object too.I’m not sure why this worked as a desktop application and not as a service. My guess is that it would have picked up a cert+private key from the user or machine store automatically in one of the situations. Either way, your desktop application was not authenticating simply with the DER file anyway.