Subversion client authentication using PKCS#12 based certificates - howto

29 May 2008
Posted by stylesen
This document explains the step by step procedure in configuring SSL based apache server (https) which servers subversion repositories and authenticates the clients using a PKCS#12 (http://www.rsa.com/rsalabs/node.asp?id=2138) based certificate provided by the client, when challenged by the server.

Components required

- apache 2.0 or greater - openssl 0.9 or greater

Compiling apache

 ./configure --prefix=/usr/local/httpd-2.2.9 --enable-ssl
 --enable-dav=shared --enable-dav-fs=shared --enable-dav-lock=shared
 --enable-rewrite=shared

Create certificate authority

Edit the following parameters in openssl.cnf file, this will help in adding some default values, when you are prompted.
####################################################################
[ ca ]
default_ca	= ServerCA		# The default ca section

####################################################################
[ ServerCA ]

dir		= /opt/ssl/ServerCA # Where everything is kept
certs		= $dir/certs		# Where the issued certs are kept
crl_dir		= $dir/crl		# Where the issued crl are kept
database	= $dir/index.txt	# database index file.
#unique_subject	= no			# Set to 'no' to allow creation of
					# several ctificates with same subject.
new_certs_dir	= $dir/newcerts		# default place for new certs.

certificate	= $dir/CA/ServerCA.crt 	# The CA certificate
serial		= $dir/serial 		# The current serial number
crlnumber	= $dir/crlnumber	# the current crl number
					# must be commented out to leave a V1 CRL
crl		= $dir/CA/ServerCA.crl	# The current CRL
private_key	= $dir/CA/ServerCA.key  # The private key
RANDFILE	= /var/run/prngd-socket	# private random number file

x509_extensions	= usr_cert		# The extentions to add to the cert

# Comment out the following two lines for the "traditional"
# (and highly broken) format.
name_opt 	= ca_default		# Subject Name options
cert_opt 	= ca_default		# Certificate field options

...
...

[ req_distinguished_name ]
countryName			= Country Name (2 letter code)
countryName_default		= IN
countryName_min			= 2
countryName_max			= 2

stateOrProvinceName		= State or Province Name (full name)
stateOrProvinceName_default	= TamilNadu

localityName			= Locality Name (eg, city)
localityName_default            = Chennai

0.organizationName		= Organization Name (eg, company)
0.organizationName_default	= My organization

# we can do this but it is not needed normally :-)
#1.organizationName		= Second Organization Name (eg, company)
#1.organizationName_default	= World Wide Web Pty Ltd

organizationalUnitName		= Organizational Unit Name (eg, section)
organizationalUnitName_default	= Subversion

commonName			= Common Name (eg, YOUR name)
commonName_max			= 64

emailAddress			= Email Address
emailAddress_default		= mymail@example.com
emailAddress_max		= 64

# SET-ex3			= SET extension number 3

...

Create directories for certificate management

These are the directories which we ll use as sandbox to hold our certificates, keys, requests, etc.
$ mkdir -p /opt/ssl/ServerCA/CA
$ mkdir -p /opt/ssl/ServerCA/newcerts
$ mkdir -p /opt/ssl/ServerCA/server/certs
$ mkdir -p /opt/ssl/ServerCA/server/requests
$ mkdir -p /opt/ssl/ServerCA/server/keys
$ mkdir -p /opt/ssl/ServerCA/user/certs
$ mkdir -p /opt/ssl/ServerCA/user/requests
$ mkdir -p /opt/ssl/ServerCA/user/keys
$ echo "01" > /opt/ssl/ServerCA/serial
$ touch /opt/ssl/ServerCA/index.txt

Create a root certificate

Create the CA key for the root certificate,
$ openssl genrsa -out /opt/ssl/ServerCA/CA/ServerCA.key 1024
Generating RSA private key, 1024 bit long modulus
......++++++
...............++++++
e is 65537 (0x10001)
Create CA cert request for root certificate,
$ openssl req -new -key /opt/ssl/ServerCA/CA/ServerCA.key -out \
/opt/ssl/ServerCA/CA/ServerCA.csr
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) [IN]:
State or Province Name (full name) [TamilNadu]:
Locality Name (eg, city) [Chennai]:
Organization Name (eg, company) [My organization]:
Organizational Unit Name (eg, section) [Subversion]:
Common Name (eg, YOUR name) []:Senthil Kumaran S
Email Address [mymail@example.com]:

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:simple
An optional company name []:
Self sign the CA certificate,
$ openssl x509 -req -days 365 -in /opt/ssl/ServerCA/CA/ServerCA.csr -out \
/opt/ssl/ServerCA/CA/ServerCA.crt -signkey /opt/ssl/ServerCA/CA/ServerCA.key
Signature ok
subject=/C=IN/ST=TamilNadu/L=Chennai/O=My organization/OU=Subversion/CN=Senthil Kumaran S/emailAddress=mymail@example.com
Getting Private key

Creating web server certificate

Create a key for the web server certificate,
 openssl genrsa -des3 -out /opt/ssl/ServerCA/server/keys/serverWEB.key 1024
Generating RSA private key, 1024 bit long modulus
..............++++++
................................................++++++
e is 65537 (0x10001)
Enter pass phrase for /opt/ssl/ServerCA/server/keys/serverWEB.key:
Verifying - Enter pass phrase for /opt/ssl/ServerCA/server/keys/serverWEB.key:
Create web server certificate request,
$ openssl req -new -key /opt/ssl/ServerCA/server/keys/serverWEB.key -out \
/opt/ssl/ServerCA/server/requests/serverWEB.csr
Enter pass phrase for /opt/ssl/ServerCA/server/keys/serverWEB.key:
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) [IN]:
State or Province Name (full name) [TamilNadu]:
Locality Name (eg, city) [Chennai]:
Organization Name (eg, company) [My organization]:
Organizational Unit Name (eg, section) [Subversion]:
Common Name (eg, YOUR name) []:Senthil Kumaran S
Email Address [mymail@example.com]:

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:
Sign web server cert with CA key,
$ openssl ca -days 365 -in /opt/ssl/ServerCA/server/requests/serverWEB.csr \
-cert /opt/ssl/ServerCA/CA/ServerCA.crt -keyfile /opt/ssl/ServerCA \
/CA/ServerCA.key -out /opt/ssl/ServerCA/server/certs/serverWEB.crt
Using configuration from /usr/lib/ssl/openssl.cnf
Check that the request matches the signature
Signature ok
Certificate Details:
        Serial Number: 1 (0x1)
        Validity
            Not Before: Jul 10 11:36:24 2008 GMT
            Not After : Jul 10 11:36:24 2009 GMT
        Subject:
            countryName               = IN
            stateOrProvinceName       = TamilNadu
            organizationName          = My organization
            organizationalUnitName    = Subversion
            commonName                = Senthil Kumaran S
            emailAddress              = mymail@example.com
        X509v3 extensions:
            X509v3 Basic Constraints:
                CA:FALSE
            Netscape Comment:
                OpenSSL Generated Certificate
            X509v3 Subject Key Identifier:
                C3:F7:B3:1F:54:7B:EC:B0:A4:49:A7:65:06:87:73:31:87:95:E5:6E
            X509v3 Authority Key Identifier:
                DirName:/C=IN/ST=TamilNadu/L=Chennai/O=My organization/OU=Subversion/CN=Senthil Kumaran S/emailAddress=mymail@example.com
                serial:BF:73:AC:03:5A:18:E2:E2

Certificate is to be certified until Jul 10 11:36:24 2009 GMT (365 days)
Sign the certificate? [y/n]:y


1 out of 1 certificate requests certified, commit? [y/n]y
Write out database with 1 new entries
Data Base Updated

Configuring Apache

Put the following in your apache virtual host configuration:
NameVirtualHost *:443
<VirtualHost *:443>
        ServerAdmin webmaster@localhost
        ServerName localhost
        DocumentRoot /var/www/
        <Directory />
                Options FollowSymLinks
                AllowOverride None
        </Directory>

      <Location /svn/repos>
          DAV svn
          SVNPath /path/to/repos
	   <IfDefine SSL>
              SSLRequireSSL
              SSLRequire    %{SSL_CLIENT_S_DN_O}  eq "My Organization"
          </IfDefine>
      </Location>

        ServerSignature On
        SSLEngine on
        SSLCertificateFile /opt/ssl/ServerCA/server/certs/serverWEB.crt
        SSLCertificateKeyFile /opt/ssl/ServerCA/server/keys/serverWEB.key
        SSLCACertificateFile /opt/ssl/ServerCA/CA/ServerCA.crt
        SSLVerifyClient optional
        SSLVerifyDepth 2

</VirtualHost>
Restart apache web server.
Apache/2.2.9 mod_ssl/2.2.9 (Pass Phrase Dialog)
Some of your private key files are encrypted for security reasons.
In order to read them you have to provide the pass phrases.

Server localhost:443 (RSA)
Enter pass phrase:

OK: Pass Phrase Dialog successful.

Create client certificates

Create user key,
$ openssl genrsa -des3 -out /opt/ssl/ServerCA/user/keys/user.key 1024
Generating RSA private key, 1024 bit long modulus
.....................++++++
................................................++++++
e is 65537 (0x10001)
Enter pass phrase for /opt/ssl/ServerCA/user/keys/user.key:
Verifying - Enter pass phrase for /opt/ssl/ServerCA/user/keys/user.key:
Create user cert request,
$  openssl req -new -key /opt/ssl/ServerCA/user/keys/user.key -out \
/opt/ssl/ServerCA/user/requests/user.csr
Enter pass phrase for /opt/ssl/ServerCA/user/keys/user.key:
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) [IN]:
State or Province Name (full name) [TamilNadu]:
Locality Name (eg, city) [Chennai]:
Organization Name (eg, company) [My organization]:
Organizational Unit Name (eg, section) [Subversion]:
Common Name (eg, YOUR name) []:Senthil Kumaran S
Email Address [mymail@example.com]:

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:
Sign the user certificate request,
$ openssl ca -in /opt/ssl/ServerCA/user/requests/user.csr -cert \
/opt/ssl/ServerCA/CA/ServerCA.crt -keyfile /opt/ssl/ServerCA/CA/ServerCA.key \
-out /opt/ssl/ServerCA/user/certs/user.crt
Using configuration from /usr/lib/ssl/openssl.cnf
Check that the request matches the signature
Signature ok
Certificate Details:
        Serial Number: 2 (0x2)
        Validity
            Not Before: Jul 10 11:46:27 2008 GMT
            Not After : Jul 10 11:46:27 2009 GMT
        Subject:
            countryName               = IN
            stateOrProvinceName       = TamilNadu
            organizationName          = My organization
            organizationalUnitName    = Subversion
            commonName                = Senthil Kumaran S
            emailAddress              = mymail@example.com
        X509v3 extensions:
            X509v3 Basic Constraints:
                CA:FALSE
            Netscape Comment:
                OpenSSL Generated Certificate
            X509v3 Subject Key Identifier:
                BA:DA:1D:41:35:CF:45:86:56:26:7C:F3:5B:5B:7E:CA:72:C8:FB:D2
            X509v3 Authority Key Identifier:
                DirName:/C=IN/ST=TamilNadu/L=Chennai/O=My organization/OU=Subversion/CN=Senthil Kumaran S/emailAddress=mymail@example.com
                serial:BF:73:AC:03:5A:18:E2:E2

Certificate is to be certified until Jul 10 11:46:27 2009 GMT (365 days)
Sign the certificate? [y/n]:y


1 out of 1 certificate requests certified, commit? [y/n]y
Write out database with 1 new entries
Data Base Updated

Import client certificate in PKCS#12 format

$ openssl pkcs12 -export -clcerts -in /opt/ssl/ServerCA/user/certs/user.crt \
-inkey /opt/ssl/ServerCA/user/keys/user.key -out /home/stylesen/user.p12
Enter pass phrase for /opt/ssl/ServerCA/user/keys/user.key:
Enter Export Password:
Verifying - Enter Export Password:

Sample run

A sample run of accessing your subversion repository configured to accept client certificate for authentication would look like the following with current subversion trunk (r31507) code:
$ svn co https://localhost/svn/repos wc
Authentication realm: https://localhost:443
Client certificate filename: /home/stylesen/user.p12
Passphrase for '/home/stylesen/user.p12':
Checked out revision 0.

svn always asks for client certificate file

Ok, great, thank you!
How do I configure svn client not to ask for a client certificate every time?
 


Thanks, that was helpful.

Thanks, that was helpful.


Thanks and "no author"

Thanks for this very helpful page. It got me going with client side certificates. But now all commits have a "no author". I'm assuming I missed something in the Apache config.


Something wrong with your config

There is something wrong with your config I guess. You can reach me via email or catch me in #svn IRC channel, so that I can help you out.


"no author" here, too

Hi, I use Client Certs as well and have the same problem. I would like to use Clinet Certs PLUS username/password to control access in a fine-grained way.


Send a personal email

Hi,

You can send a personal email so that I can try to help you.


How to deal with a common error when signing the web server cert

When you run the command given above to sign the web server cert with the CA key...

openssl ca -days 365 -in \
/usr/local/ssl/CollabCA/server/requests/serverWEB.csr -cert \
/usr/local/ssl/CollabCA/CollabCA.crt -keyfile \
/usr/local/ssl/CollabCA/CollabCA.key -out \
/usr/local/ssl/CollabCA/server/certs/serverWEB.crt -config /etc/ssl/openssl.cnf

...you might encounter an error like this:

Using configuration from /etc/ssl/openssl.cnf
variable lookup failed for CollabCA::default_md
28082:error:02001002:system library:fopen:No such file or directory:\
bss_file.c:122:fopen('/usr/local/ssl/CollabCA/index.txt.attr','rb')
28082:error:2006D080:BIO routines:BIO_new_file:no such file:\
bss_file.c:125:
28082:error:0E078072:configuration file routines:DEF_LOAD:no such file:\
conf_def.c:197:
28082:error:0E06D06C:configuration file routines:NCONF_get_string:\
no value:conf_lib.c:329:group=CollabCA name=default_md

Note that the error might be about some other variable besides "default_md" (for example, once I solved the "default_md" problem, I got the same error about the "policy" variable).

The solution is to add new variables to /etc/ssl/openssl.cnf in the [CollabCA] section (substitute whatever name you used for your cert, of course):

[CollabCA]
...
default_md = sha1 # You could also use "md5" instead of "sha1".
policy = policy_match # I don't know what other policies are available.
...

The reason these errors didn't happen for stylesen is probably that he already had those variables set in the relevant section (they're part of the "..." material in his example).

I found this solution by doing...

search://"No such file or directory" bss_file.c index.txt.attr/

...which turned up this web page:

http://www.onlamp.com/pub/a/security/2004/10/21/vpns_and_pki.html?page=last


Thanks for mentioning it Karl

I have the following in my openssl.cnf
# Extensions to add to a CRL. Note: Netscape communicator chokes on V2 CRLs
# so this is commented out by default to leave a V1 CRL.
# crlnumber must also be commented out to leave a V1 CRL.
# crl_extensions        = crl_ext

default_days    = 365                   # how long to certify for
default_crl_days= 30                    # how long before next CRL
default_md      = sha1                  # which md to use.
preserve        = no                    # keep passed DN ordering

# A few difference way of specifying how similar the request should look
# For type CA, the listed attributes must be the same, and the optional
# and supplied fields are just that :-)
policy          = policy_match

# For the CA policy
[ policy_match ]
countryName             = match
stateOrProvinceName     = match
organizationName        = match
organizationalUnitName  = optional
commonName              = supplied
emailAddress            = optional
The above was a part of default openssl.cnf provided in Debian etch, hence I didn't made a note of it here and as Karl pointed out it went as a part of the "..." material. I checked the openssl.cnf in Debian lenny package (http://packages.debian.org/lenny/openssl) which also had the same contents. Karl, Thanks for noting it here which will help someone who gets the same error :)

that

Thanks a lot...helped alot