Client Certificate Authentication (Part 2)

This is a continuation of my earlier post on Client Certificate Authentication (Part 1) aka TLS Mutual Authentication. Earlier, I had discussed on what Client Certificates are and how they work in SSL/TLS Handshake.

In this post, I will explain how to review SSL/TLS handshake with help of tools like WireShark & Curl.

Before proceeding further, lets review the SERVER HELLO definition in RFC 5246.  As per the RFC, the server will request the certificate from the client in the Server hello by including certificate_request message, which has the decimal code of 13 (0d in Hex). This will help us in determining the Certificate Request in Server Hello.

enum {
    hello_request(0), client_hello(1), server_hello(2),
    certificate(11), server_key_exchange (12),
    certificate_request(13), server_hello_done(14),
    certificate_verify(15), client_key_exchange(16),
    finished(20), (255)
} HandshakeType;
struct {
    HandshakeType msg_type;    /* handshake type */
    uint24 length;             /* bytes in message */
    select (HandshakeType) {
        case hello_request:       HelloRequest;
        case client_hello:        ClientHello;
        case server_hello:        ServerHello;
        case certificate:         Certificate;
        case server_key_exchange: ServerKeyExchange;
        case certificate_request: CertificateRequest;
        case server_hello_done:   ServerHelloDone;
        case certificate_verify:  CertificateVerify;
        case client_key_exchange: ClientKeyExchange;
        case finished:            Finished;
    } body;
} Handshake;

Once the client sees the certificate_request message it will provide the certificate to the server.

Real world example:

Setup: Hosted a site on IIS inside an Azure VM. This requires a client certificate for authentication. Here is the endpoint

I have added the SSL binding via netsh using the following command:

netsh http add sslcert ipport= certhash=<certificate thumbprint goes here> appid={4dc3e181-e14b-4a21-b022-59fc669b0914} certstorename=My clientcertnegotiation=enable


Analyzing the TLS/SSL handshake in WireShark

As shown below, the server has sent a certificate request message to the client and the client has then responded with the certificate in the next communication.


In WireShark you can set the filter to “ssl.handshake.type == 13” to specifically look for certificate_request message in Server Hello.


NOTE: Some SSL/TLS servers support only Secure negotiation. Therefore you may not see the Certificate Request message explicitly in the Server Hello. In this case, the server will send the Certificate Request message as a part of the Encrypted Handshake Message.

Analyzing the TLS/SSL handshake in cURL

By far, cURL has made it easier to determine if the server is sending the Certificate Request in Server Hello. You will have to download the version of curl that includes support SSL Protocol. This works in scenarios where the server supports only Secure Negotiation. I

You can find various builds of cURL available for download here:*&cpu=x86_64 

I am using the following

curl version: 7.54.0 – SSL enabled SSH enabled

Execute the following from command prompt

curl –v https://<hostname> –k –cert clientcert.pem:<password>

     -v, –verbose       Make the operation more talkative
     -k, –insecure      Allow connections to SSL sites without certs (H)
     -E, –cert CERT[:PASSWD] Client certificate file and password (SSL)

Once the request has been issued via cURL. In the verbose output, search for TLS handshake message “Request CERT (13)” as highlighted in the below example.

C:\>curl -v -k –cert ClientCert.pem:Password

* Rebuilt URL to:
* Adding handle: conn: 0x34a6540
* Adding handle: send: 0
* Adding handle: recv: 0
* Curl_addHandleToPipeline: length: 1
* – Conn 0 (0x34a6540) send_pipe: 1, recv_pipe: 0
* About to connect() to port 443 (#0)
*   Trying…
* Connected to ( port 443 (#0)
* SSLv3, TLS handshake, Client hello (1):
* SSLv3, TLS handshake, Server hello (2):
* SSLv3, TLS handshake, CERT (11):
* SSLv3, TLS handshake, Request CERT (13):
* SSLv3, TLS handshake, Server finished (14):
* SSLv3, TLS handshake, CERT (11):
* SSLv3, TLS handshake, Client key exchange (16):
* SSLv3, TLS handshake, CERT verify (15):
* SSLv3, TLS change cipher, Client hello (1):
* SSLv3, TLS handshake, Finished (20):
* SSLv3, TLS change cipher, Client hello (1):
* SSLv3, TLS handshake, Finished (20):
* SSL connection using AES256-SHA
* Server certificate:
*        subject: OU=Domain Control Validated; CN=*
*        start date: 2017-05-10 09:05:00 GMT
*        expire date: 2018-05-10 09:05:00 GMT
*        issuer: C=US; ST=Arizona; L=Scottsdale;, Inc.; OU=; CN=Go Daddy Secure Certificate Authority – G2
*        SSL certificate verify result: unable to get local issuer certificate (20), continuing anyway.
> GET / HTTP/1.1
> User-Agent: curl/7.33.0
> Host:
> Accept: */*
< HTTP/1.1 200 OK
< Cache-Control: private
< Content-Type: text/html; charset=utf-8
* Server Microsoft-IIS/8.5 is not blacklisted
< Server: Microsoft-IIS/8.5
< X-AspNetMvc-Version: 5.2
< X-AspNet-Version: 4.0.30319
< X-Powered-By: ASP.NET
< Date: Fri, 09 Jun 2017 16:51:13 GMT
< Content-Length: 5767

. . .


On Windows, in order to pass the client certificate via cURL, you will have to extract the .pem file out of the .pfx. Use the following command:

openssl pkcs12 -in clientcertificate.pfx -out clientcertificate.pem –clcerts
Enter Import Password:
MAC verified OK
Enter PEM pass phrase:
Verifying – Enter PEM pass phrase:




Comments (0)

Skip to main content