Thursday, 22 December 2011

Negotiate client certificate in IIS 6.0

The ongoing saga of our SSL issues continues. After some help from a kind soul, we got some java code to do mutual authentication against IIS but this wasn’t working either. If you are wondering why we are after some java code, well that is what they are throwing against IIS.

In my opinion, OpenSSL is the ultimate arbiter of all things SSL and OpenSSL was working, so I was a bit miffed to be asked to make some changes, but I do like playing about with computers so I thought I’d give it a go.

The feeling was that IIS implementation of the SSL handshake would not work with Apache’s HTTPClient and thus we needed to change IIS's configuration. It is true that IIS will only ask for the client certificate when the client makes a request, at which point there will be a renegotiation, rather than within the original handshake, but my understanding is that this is a perfectly valid approach, I’ll need to investigate whether this is indeed the case. In other words, IIS does a simple TLS handshake upon connection and then a renegotiation attempt, when an actual request is made to the server. This is simply a client authenticated handshake, that is initiated by the server via making a HelloRequest.

At any rate, I ran a couple of tests against our IIS dev box using OpenSSL, see this post for more details. Edited output from OpenSSL:
CONNECTED(00000003)
>>> TLS 1.0 Handshake [length 006c], ClientHello
    01 ... 00
<<< TLS 1.0 Handshake [length 0051], ServerHello
    02 ..   00
<<< TLS 1.0 Handshake [length 058e], Certificate
    0b ... 41
<<< TLS 1.0 Handshake [length 0004], ServerHelloDone
    0e 00 00 00
>>> TLS 1.0 Handshake [length 0086], ClientKeyExchange
    10 .. d1
>>> TLS 1.0 ChangeCipherSpec [length 0001]
    01
>>> TLS 1.0 Handshake [length 0010], Finished
    14 00 00 0c ae 1d da b8 d4 14 ab ed 03 50 77 a0
<<< TLS 1.0 ChangeCipherSpec [length 0001]
    01
<<< TLS 1.0 Handshake [length 0010], Finished
    14 00 00 0c d6 9b 9b 66 04 5d c6 5d d2 e1 25 7e
---
Certificate chain
 0 s:/C=US/ST=York/L=York/O=/OU=YORK/CN=server.dev.com
   i:/DC=com/DC=dev/CN=TESTAuthority
---
Server certificate
-----BEGIN CERTIFICATE-----
-----END CERTIFICATE-----
subject=/C=US/ST=York/L=York/O=YORK/OU=YORK/CN=server.dev.com
issuer=/DC=com/DC=dev/CN=TESTAuthority
---
No client certificate CA names sent
---
SSL handshake has read 1555 bytes and written 295 bytes
---
New, TLSv1/SSLv3, Cipher is RC4-MD5
Server public key is 1024 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
SSL-Session:
    Protocol  : TLSv1
    Cipher    : RC4-MD5
    Session-ID: 7C130000B6F9142AD5E179ECCEC696C6F3C3CE4E100B503ABF3477FD3F7AD8B3
    Session-ID-ctx:
    Master-Key: 44EA6B1B1AACCE188E097F3E904838F33361FAC67207B6311CC0477A2CEEFE6D8F2AE7AF338B9537713381D34460FE05
    Key-Arg   : None
    Krb5 Principal: None
    PSK identity: None
    PSK identity hint: None
    Start Time: 1324388318
    Timeout   : 7200 (sec)
    Verify return code: 0 (ok)
---
At this point a request is made to IIS:
GET /home.htm

<<< TLS 1.0 Handshake [length 0004], HelloRequest
    00 00 00 00
>>> TLS 1.0 Handshake [length 007b], ClientHello
    01 ... 00
<<< TLS 1.0 Handshake [length 0069], ServerHello
    02 ...7e
<<< TLS 1.0 Handshake [length 058e], Certificate
    0b ... 41
<<< TLS 1.0 Handshake [length 0c0f], CertificateRequest
    0d ... 79
<<< TLS 1.0 Handshake [length 0004], ServerHelloDone
    0e 00 00 00
>>> TLS 1.0 Handshake [length 09a4], Certificate
    0b ..    d4
>>> TLS 1.0 Handshake [length 0086], ClientKeyExchange
    10 ... 05
>>> TLS 1.0 Handshake [length 0086], CertificateVerify
    0f ... 9b
>>> TLS 1.0 ChangeCipherSpec [length 0001]
    01
>>> TLS 1.0 Handshake [length 0010], Finished
    14 00 00 0c 58 50 80 3d d0 74 f4 52 cb ad ad 50
<<< TLS 1.0 ChangeCipherSpec [length 0001]
    01
<<< TLS 1.0 Handshake [length 0010], Finished
    14 00 00 0c 3e d0 e2 69 96 29 7b 83 16 68 f7 c8

As I mentioned above the renegotiation process is started by the server and then all that follows is a simple client authenticated handshake. Note that the arrows indicate the direction from the server's perspective, so that the bold line means that the server is requesting a Certificate from the client.

This appears to be the problem, but how to solve it? Well, after a little bit of investigation it turns out that it is fairly easy to enable client certificate negotiation on, remembering that I’m using windows 2003. The httpcfg tool is all you need (maybe, see what happens after an iisreset below). This tool is available with the windows support tools package. There are a myriad of options for this tool but in this case we are only interested in a few commands.

List current SSL configuration with:
httpcfg query ssl

IP                      : 0.0.0.0:443
Hash                    : 76c659afffcb906038682de115c2 ddb2af7c113
Guid                    : {4dc3e181-e14b-4a21-b022-59fc669b0914}
CertStoreName           : MY
CertCheckMode           : 0
RevocationFreshnessTime : 0
UrlRetrievalTimeout     : 0
SslCtlIdentifier        :
SslCtlStoreName         :
Flags                   : 0

Note the typo in the hash field, i.e. the certificate thumbprint. For some reason it does not display zeros, so be careful if copying from the console.

Delete the binding, if the return code is not 0 there is a problem:
httpcfg –delete –i 0.0.0.0:443
HttpDeleteServiceConfiguration completed with 0.
Now you can add it again (there must be a better way of doing this?)
httpcfg set ssl -i 0.0.0.0:443 -h 76c659afffcb906038682de115c20ddb2af7c113 -g {4dc3e181-e14b-4a21-b022-59fc669b0914} -c MY -f 2
HttpDeleteServiceConfiguration completed with 0.
You can list current SSL configuration again and see the changes:
httpcfg query ssl
IP                      : 0.0.0.0:443
Hash                    : 76c659afffcb906038682de115c2 ddb2af7c113
Guid                    : {4dc3e181-e14b-4a21-b022-59fc669b0914}
CertStoreName           : MY
CertCheckMode           : 0
RevocationFreshnessTime : 0
UrlRetrievalTimeout     : 0
SslCtlIdentifier        : (null)
SslCtlStoreName         : (null)
Flags                   : 2
Now you can run the same OpenSSL command as above:
CONNECTED(00000003)
>>> TLS 1.0 Handshake [length 006c], ClientHello
    01 .. 00
<<< TLS 1.0 Handshake [length 0051], ServerHello
    02 ..    00
<<< TLS 1.0 Handshake [length 058e], Certificate
    0b ... 41
<<< TLS 1.0 Handshake [length 0c0f], CertificateRequest
    0d ... 79
<<< TLS 1.0 Handshake [length 0004], ServerHelloDone
    0e 00 00 00
>>> TLS 1.0 Handshake [length 09a4], Certificate
    0b .. d4
>>> TLS 1.0 Handshake [length 0086], ClientKeyExchange
    10 ... a4
>>> TLS 1.0 Handshake [length 0086], CertificateVerify
    0f .. 24
>>> TLS 1.0 ChangeCipherSpec [length 0001]
    01
>>> TLS 1.0 Handshake [length 0010], Finished
    14 00 00 0c 66 1d 74 73 26 4c 93 5a 24 e7 97 4d
<<< TLS 1.0 ChangeCipherSpec [length 0001]
    01
<<< TLS 1.0 Handshake [length 0010], Finished
    14 00 00 0c 76 d4 32 92 0f d8 b9 f8 f9 a8 72 b4
---
Certificate chain
 0 s:/C=US/ST=YORK/L=YORK/O=YORK/OU=YORK/CN=server.dev.com
   i:/DC=com/DC=dev/CN=TESTAuthority
---
Server certificate
-----BEGIN CERTIFICATE-----
-----END CERTIFICATE-----
subject=/C=US/ST=YORK/L=YORK/O=YORK/OU=YORK/CN=server.dev.com
issuer=/DC=com/DC=dev/CN=TESTAuthority
---
Acceptable client certificate CA names

/DC=com/DC=dev/CN=TESTAuthority
---
SSL handshake has read 4642 bytes and written 2907 bytes
---
New, TLSv1/SSLv3, Cipher is RC4-MD5
Server public key is 1024 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
SSL-Session:
    Protocol  : TLSv1
    Cipher    : RC4-MD5
    Session-ID: 4D12000059032A66B9D456537EDB9B0AAFA0D5762F94CED74DC8EC4E8F175B20
    Session-ID-ctx:
    Master-Key: 9C8A4CF2078FCE4B77DC9CD902DEA7B36AA64A7E110F0634F90FEA2D6AD5FA00BFA761C813387A2CBB2CA5683850724A
    Key-Arg   : None
    Krb5 Principal: None
    PSK identity: None
    PSK identity hint: None
    Start Time: 1324388273
    Timeout   : 7200 (sec)
    Verify return code: 0 (ok)
---
The key is the bolded line, the certificate request, which the server now makes during the initial handshake, or the handshake, as there will only be one handshake now. If no certificate is presented by the client, then the server concludes that no client certificate is being used and the handshake finishes successfully. It is worth noting that once a request is made to the server, this request will fail with 403.7.

If IIS is configured to accept, rather than require, client certificates, then CertificateRequest is only made when a certificate is presented to the server.

One little problem though, changes with the httpcfg tool do not appear to be permanent, which means that it is necessary to edit the IIS metabase, which you can find in <WindowsFolder>\system32\inetsrv\metabase.xml, to make them permanent.

Stop IIS and edit the file. I’m editing a website with id 2100412512, so I changed this:
<IIsWebServer  Location ="/LM/W3SVC/2100412512"
                                CertCheckMode="0"
                                SSLCertHash="76c659afffcb906038682de115c20ddb2af7c113"
                                SSLStoreName="MY"
                                SecureBindings=":443:"
                                ServerAutoStart="TRUE"
                                ServerBindings=":8080:"
                                ServerComment="test SSL website"
                >
To:
<IIsWebServer  Location ="/LM/W3SVC/2100412512"
                                CertCheckMode="0"
                                SSLAlwaysNegoClientCert="TRUE"
                                SSLCertHash="76c659afffcb906038682de115c20ddb2af7c113"
                                SSLStoreName="MY"
                                SecureBindings=":443:"
                                ServerAutoStart="TRUE"
                                ServerBindings=":8080:"
                                ServerComment="test SSL website"
                >
Start IIS and run:
httpcfg query ssl

IP                      : 0.0.0.0:443
Hash                    : 76c659afffcb906038682de115c2 ddb2af7c113
Guid                    : {4dc3e181-e14b-4a21-b022-59fc669b0914}
CertStoreName           : MY
CertCheckMode           : 0
RevocationFreshnessTime : 0
UrlRetrievalTimeout     : 0
SslCtlIdentifier        :
SslCtlStoreName         :
Flags                   : 2
SUCCESS

No comments:

Post a Comment