Tuesday, December 24, 2013

Nginx, two-way ssl authentication

See Mutual authentication or two-way authentication.
  1. Client has X509 cert and private key.
  2. Server has its own cert and key and client cert.
  3. At connection establishing time server checks client cert and client checks server cert. Server also knows client name after auth.
On server side compile nginx (nginx-1.1.11) --with-http_ssl_module. For server we'll create cert and key
openssl genpkey -algorithm RSA -out cert.key
openssl req -new -x509 -key cert.key -out cert.pem
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:
State or Province Name (full name) [Some-State]:
Locality Name (eg, city) []:
Organization Name (eg, company) [Internet Widgits Pty Ltd]:
Organizational Unit Name (eg, section) []:
Common Name (eg, YOUR name) []:localhost
Email Address []:
Note, Common Name must be equal to domain name.
Nginx config excerpt
server {
    listen       443;
    server_name  localhost;

    ssl                 on;
    ssl_certificate     cert.pem;
    ssl_certificate_key cert.key;

    ssl_session_timeout  5m;

    ssl_protocols  SSLv2 SSLv3 TLSv1;
    ssl_ciphers  HIGH:!aNULL:!MD5;
    ssl_prefer_server_ciphers   on;

    location / {
        root   html;
        index  index.html index.htm;
    }
}
Test
curl -k -i https://localhost/
HTTP/1.1 200 OK
Server: nginx/1.1.11
Date: Fri, 16 Dec 2011 11:56:28 GMT
Content-Type: text/html
Content-Length: 151
Last-Modified: Fri, 16 Dec 2011 10:49:22 GMT
Connection: keep-alive
Accept-Ranges: bytes

<html>
<head>
<title>Welcome to nginx!</title>
</head>
<body bgcolor="white" text="black">
<center><h1>Welcome to nginx!</h1></center>
</body>
</html>
The -k parameter must be specified because server cert is self-signed, see curl(1).
-k/--insecure
(SSL) This option explicitly allows curl to perform "insecure" SSL connections and transfers. All SSL connections are attempted to be made secure by using the CA certificate bundle installed by default. This makes all connections considered "insecure" fail unless -k/--insecure is used.
This was one-way ssl. Add following lines to config to require client cert
server {
    ...
    ssl_verify_client on;
    ssl_client_certificate client.pem;
    ...
}
Test
curl -k -i https://localhost/
HTTP/1.1 400 Bad Request
Server: nginx/1.1.11
Date: Fri, 16 Dec 2011 12:09:38 GMT
Content-Type: text/html
Content-Length: 253
Connection: close

<html>
<head><title>400 No required SSL certificate was sent</title></head>
<body bgcolor="white">
<center><h1>400 Bad Request</h1></center>
<center>No required SSL certificate was sent</center>
<hr><center>nginx/1.1.11</center>
</body>
</html>


curl -k -i --cert cert.pem --key cert.key https://localhost/
HTTP/1.1 200 OK
Server: nginx/1.1.11
Date: Fri, 16 Dec 2011 12:01:56 GMT
Content-Type: text/html
Content-Length: 151
Last-Modified: Fri, 16 Dec 2011 10:49:22 GMT
Connection: keep-alive
Accept-Ranges: bytes

<html>
<head>
<title>Welcome to nginx!</title>
</head>
<body bgcolor="white" text="black">
<center><h1>Welcome to nginx!</h1></center>
</body>
</html>
User agents (google-chrome, firefox, others) often use PKCS#12 format (files with .p12 extension). One can convert pair cert.pem/cert.key into PKCS#12 via openssl
openssl pkcs12 -export -in cert.pem -inkey cert.key -out cert.p12
Then import cert.p12 into browser and go to https://localhost/. Note, use certificate chains and one certificate as root for all client certs, nginx will authenticate clients against this certificate.

No comments:

Post a Comment