Currently [RO]' Http channels don't support client certificates (i.e. client certificates are required in IIS when the option "Require Client Certificates" is selected). Why would you need them anyway? Client certificates are an excellent way of authenticating user and thus restricting the access to your website.
According to Marc, Chief Architect/[RO], client certificates support is planned for RemObjects SDK version 5 – next version.
But what if you need them now? Required change is really not that big but unfortunately isn't easily applicable. So [RO] guys suggested modifying WinInetHttpClientChannel.IntDispatch method where this feature belongs. It is certainly feasible and easy to apply, however I am not that keen to recompiling source codes. Not that it is difficult but when new version arrives you need to re-apply and re-compile.
That's why I went with deriving a new channel out of WinInetHttpClientChannel (note: you'll still need source code). Since IntDispatch method is declared as virtual so it can be overriden. The first pass is to create a derived channel named RhHttpClientChannel, add a X509Certificate2 field to it – we'll keep our client certificate in that field and a constructor that takes a X509Certificate2 as an argument. One more thing is to do done at this point. Since we'll be addressing a private field WinInetHttpClientChannel.fHttpHeader we need some reflection. The reference to this field will be stored in fHttpHeaderPI field and initialized in constructor. Here is the code so far:
public class RhHttpClientChannel: WinInetHttpClientChannel { private X509Certificate2 cert; private FieldInfo fHttpHeaderPI; public RhHttpClientChannel(X509Certificate2 cert) { this.cert = cert; fHttpHeaderPI = typeof(WinInetHttpClientChannel).GetField("fHttpHeader", BindingFlags.NonPublic | BindingFlags.Instance); }
Next, property HttpHeader, which is a wrapper to fHttpHeader, is implemented. Again, with a help of reflection (note that FullTrust will be required since we use reflection). fHttpHeader is a HttpHeader class. The problem is that HttpHeader class is declared as an internal in [RO] SDK assembly and thus can't be directly used. Lucky for us, it derives from Hashtable and we need only its Hashtable properties. So it is gonna be treated as a Hashtable.
private Hashtable HttpHeader { get { Hashtable value = (Hashtable)fHttpHeaderPI.GetValue(this); return value; } set { fHttpHeaderPI.SetValue(this, value); } }
Now you have to open WinInetHttpClientChannel source code and copy the entire method above (I won't show the implementation since I can't for legal issues)
private void CopyFromStreamToStream(Stream iSourceStream, Stream iDestinationStream, TransferDirection aDirection) { ... }
method (it is used by IntDispatch and private and I didn't want to bother with reflection).
Next step is to copy and paste IntDispatch method:
protected override void IntDispatch(Stream aRequestStream, IMessage aResponseMessage) { ... }
Inside the method fUserAgent with UserAgent and fKeepAlive with KeepAlive have to be replaced.
And the final step is to use the certificate that is passed to the constructor. Add this piece of code right after HttpWebRequest instance creation (first line below):
// original code lWebRequest = (HttpWebRequest)WebRequest.Create(TargetUrl); // add this below if (cert != null) lWebRequest.ClientCertificates.Add(cert);
There you go. You have a WinInetHttpClientChannel capable of using client certificates.
Hi Miha
great article 😉
Thank you very much
Best Regards
Claudio Piffer
CSoft