Actually, this sounds a little weird for us. The TLS/SSL layer is absolutely independent from HTTP protocol. You can use it for securing other protocols such as FTP. So interlink certificate validation with a WebRequest
is little weird.
Also, the TLS/SSL session is created on server name (domain) basis, not URL basis. This means that subsequent requests for same server name (domain) doesn't require creating new TLS/SSL session (and certificate validation). Please see following sample code:
var creator = new HttpRequestCreator();
creator.ValidatingCertificate += (s, e) =>
{
Console.WriteLine("Validating certificate for server: '{0}', IP: '{1}'.", e.ServerName, e.Socket.RemoteEndPoint);
e.Accept();
};
string[] urls =
{
"https://rebex.net/sftp.net", // rebex.net
"https://rebex.net/zip.net", // rebex.net
"https://www.rebex.net/sftp.net", // www.rebex.net
"https://www.rebex.net/zip.net", // www.rebex.net
"https://rebex.net/sftp.net" // rebex.net
};
foreach (var url in urls)
{
Console.WriteLine("Requesting: {0}", url);
var request = creator.Create(url);
var response = request.GetResponse();
response.Close();
}
And this is the output:
Requesting: https://rebex.net/sftp.net
Validating certificate for server: 'rebex.net', IP: '195.144.107.196:443'.
Requesting: https://rebex.net/zip.net
Requesting: https://www.rebex.net/sftp.net
Validating certificate for server: 'www.rebex.net', IP: '195.144.107.196:443'.
Requesting: https://www.rebex.net/zip.net
Requesting: https://rebex.net/sftp.net
As you can see, the certificate validation was required only two times. Once for 'rebex.net' server name and once for 'www.rebex.net' server name. All subsequent requests were associated with already established TLS/SSL sessions.
If you want to disable this behavior, you can set creator.Settings.SslSessionCacheEnabled = false
. This will cause, the TLS/SSL session will be created and validated for each request again.
Now to your problem. If you really need WebRequest
instance at time you are validating a certificate, I suggest you to associate created requests with HttpRequestCreator
instance like this:
public class MyCreator : HttpRequestCreator
{
// holds request based on server name
public Dictionary<string, WebRequest> Requests = new Dictionary<string, WebRequest>();
public new HttpRequest Create(string uriString)
{
var request = base.Create(uriString);
if (request.RequestUri.Scheme.Equals("https", StringComparison.OrdinalIgnoreCase))
{
lock (Requests)
{
// save request for current server name
Requests[request.RequestUri.Host] = request;
}
}
return request;
}
}
Then you can get instance of the last created WebRequest
in ValidatingCertificate
like this:
var creator = new MyCreator();
creator.ValidatingCertificate += (s, e) =>
{
WebRequest request = creator.Requests[e.ServerName];
Console.WriteLine("Validating certificate for server: '{0}', IP: '{1}'.", e.ServerName, e.Socket.RemoteEndPoint);
Console.WriteLine("Validating URI: {0}", request.RequestUri);
e.Accept();
};
Please note, that this code will work for sequential requests, but can fail for parallel requests.
If parallel requests are necessary, you can either add synchronization or simply create own HttpRequestCreator
instance for each WebRequest
. In second case, association between creator and request is unique, but TLS/SSL sessions cannot be shared.