SSL Authentication in HTTP : Using cURL – Part 3

Open source curl is one of best & stable http client tool as well as library. In this article, we’ll see how can we use libcurl library as well as curl command line utility for two way client authentication.

Using libcurl library for HTTPS client authentication:

Here are the basics steps for the client setup before going into actual libcurl code.

 1. Extract client certificate and client private key files in PEM format from the client keystore.

We have client.p12 keystore file in pkcs#12 format and it’s pass phrase. Following commands can be used further.

 1.1 Extract client certificate without key.

openssl pkcs12 -in client.p12 -nokeys -out clientCert.pem

 1.2 Extract client private key without cert.

openssl pkcs12 -in client.p12 -nocerts -out privateKey.pem

with PEM password. For both above commands ask for keystore pass phrase and only while generating key without cert ask for PEM password (new password will be setup for that key).

 1.3 Extract both client certificate & key in single file.

openssl pkcs12 -in client.p12 -out combinedClient.pem -clcerts

This prompts for both keystore & PEM pass phrase. Generated file contains both cert & key.

 2. Option step to verify generated files to crosscheck.

openssl x509 -noout -modulus -in clientCert.pem | openssl md5

d7207cf82b771251471672dd54c59927

openssl rsa -noout -modulus -in privateKey.pem | openssl md5

Enter pass phrase for privateKey.pem:

d7207cf82b771251471672dd54c59927

Both these md5 outputs are same and that confirms both are good to go.

 3. Libcurl client code for client authentication.

Using following snippet code for curl easy handle. Actually, for client certificate we don’t need to set pass phrase, still setting the same PEM pass phrase of client key.

curl_easy_setopt(curl,CURLOPT_SSLCERT,"clientCert.pem");

curl_easy_setopt(curl,CURLOPT_SSLCERTPASSWD,"changeit");

curl_easy_setopt(curl,CURLOPT_SSLCERTTYPE,"PEM");

curl_easy_setopt(curl,CURLOPT_SSLKEY,"privateKey.pem");

curl_easy_setopt(curl,CURLOPT_SSLKEYPASSWD,"changeit");

curl_easy_setopt(curl,CURLOPT_SSLKEYTYPE,"PEM");

Some times, setting client certificate & client key like this by extracting separately will not work with libcurl. It ends with following error sometimes.

*err unable to set private key file: 'C:privateKey.pem' type PEM*

Reasons are unknown atleast for me. In that case, solution I found is that use your combinedClient.pem file we generated above in place of client key and client pass phrase in above code snippet (client certificate is optional if you are using combinedClient.pem file). I tried manually appending both client cert & key, but that’s didn’t help, so better generate with command only.

 4. Curl command line tool for client authentication:

Following curl command sends C:myrequest.xml file content as binary HTTP request content with headers SOAPAction & Contenty-Type fields and client cert & client key set to the final url with verbose mode.

$ curl --data-binary @"C:myrequest.xml" --header "SOAPAction: " --header
"Content-Type: text/xml" --cert c:clientCert.pem --cert-type PEM --key
c:privkey.pem --key-type PEM --cacert c:ca-bundle.crt https://mydomain.myco.com:443/soap -v

It prompts for PEM passwd and then following error.

* About to connect() to mydomain.myco.com port 443 (#0)
*   Trying 69.181.219.20... connected
* Connected to mydomain.myco.com (69.181.219.20) port 443 (#0)
Enter PEM pass phrase:
* unable to set private key file: 'privateKey.pem' type PEM
* Closing connection #0
*curl: (58) unable to set private key file: 'privateKey.pem' type PEM*

And then I tried appending both private key along with cert in a single file with format —–RSA CERTIFICATE START —– & —-RSA CERT END —– then immediately with ——CERTIFICATE START —– & —–CERTIFICATE END —– and tried following.

$ curl --cert testCert.pem --Verbose -H "Content-Type: text/xml"
https://mydomain.myco.com:443/soap
* About to connect() to mydomain.myco.com port 443 (#0)
*   Trying 69.181.219.20... connected
* Connected to mydomain.myco.com (69.181.219.20) port 443 (#0)
Enter PEM pass phrase:
* unable to set private key file: 'testCert.pem' type PEM
* Closing connection #0
*curl: (58) unable to set private key file: 'testCert.pem' type PEM*

Finally, I used the above mentioned command for combined file and then I got the response properly back as below. Following command is used to send both key & cert in a single file with option –cert and –cacert option to set cacert bundle file.

$ curl --cert combinedClient.pem --data-binary @"request.xml" --Verbose -H "Content-Type: text/xml"
--cacert "ca-bundle.crt"  https://mydomain.myco.com:443/soap

* About to connect() to mydomain.myco.com port 443 (#0)
*   Trying 69.181.219.20... connected
* Connected to mydomain.myco.com (69.181.219.20) port 443 (#0)
Enter PEM pass phrase:
* successfully set certificate verify locations:
*   CAfile: ca-bundle.crt
  CApath: /usr/ssl/certs
* SSLv3, TLS handshake, Client hello (1):
* SSLv3, TLS handshake, Server hello (2):
* SSLv3, TLS handshake, CERT (11):
* SSLv3, TLS handshake, Server key exchange (12):
* 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 DHE-RSA-AES256-SHA
* Server certificate:
*        subject: C=US; ST=New York; L=New York; O=myco; OU=NDIS; CN=mydomain.myco.com
*        start date: 2011-06-23 00:00:00 GMT
*        expire date: 2012-07-29 23:59:59 GMT
*        common name: mydomain.myco.com (matched)
*        issuer: C=US; O=VeriSign, Inc.; OU=VeriSign Trust Network; OU=Terms of
use at https://www.verisign.com/rpa (c)10; CN=VeriSign Class 3 International Ser
ver CA - G3
*        SSL certificate verify ok.
> POST /soap HTTP/1.1
> User-Agent: curl/7.19.6 (i686-pc-cygwin) libcurl/7.19.6 OpenSSL/0.9.8n zlib/1.
2.3 libidn/1.18 libssh2/1.2
> Host: mydomain.myco.com
> Accept: */*
> Content-Type: text/xml
> Content-Length: 586
>
< HTTP/1.1 200 OK
< Date: Tue, 27 Sep 2011 09:16:19 GMT
< Server: ACE XML Gateway
< Content-Type: text/xml
< Content-Length: 498
<
* Connection #0 to host mydomain.myco.com left intact
* Closing connection #0
* SSLv3, TLS alert, Client hello (1):
<?xml version="1.0" encoding="UTF-8"?><env:Envelope xmlns:env="http://schemas.xm
lsoap.org/soap/envelope/" xmlns:dlws="http://mydomain.myco.com">
<env:Body><dlws:MyResponse><dlws:statusCode><dlws:c
ode>0</dlws:code><dlws:description>Success</dlws:description></dlws:statusCode><
dlws:requestId>f828131e-bab0-4f50-96d0-a2512c7926d4</dlws:requestId><dlws:respon
seId>1317114979-190385186</dlws:responseId>&lt
;/dlws:MyResponse></env:Bo
dy></env:Envelope>

From all this, the learning I guess is curl behavior is not very sure with respect to client authentication. Some times it works perfectly if we send client cert & key separately, but some times it works only with combined file.

2 thoughts on “SSL Authentication in HTTP : Using cURL – Part 3

  1. Hi,

    I’m working on a job vacancy for a company called Smiley Media. Please send me an email if you are open to discussing the details.

    Thanks,

    Marco

  2. Pingback:

Leave a Reply

Your email address will not be published. Required fields are marked *