The HTML source for this form is as follows:
<HTML><HEAD><TITLE>Create Client Certificate</TITLE></HEAD><BODY> <CENTER><H1>Create Client Certificate</H1></CENTER> <FORM NAME="GenerateForm" ACTION="http://example.opengroup.org/cgi-bin/ns_key.pl"> <TABLE> <TR><TD>Common Name:</TD><TD> <INPUT TYPE="TEXT" NAME="commonName" VALUE="Client Certificate" SIZE=64> </TD></TR> <TR><TD>email:</TD><TD> <INPUT TYPE="TEXT" NAME="emailAddress" VALUE="ssl_admin@opengroup.org" SIZE=40> </TD></TR> <TR><TD>Organization:</TD><TD> <INPUT TYPE="TEXT" NAME="organizationName" VALUE="The Open Group"> </TD></TR> <TR><TD>Organizational Unit:</TD><TD> <INPUT TYPE="TEXT" NAME="organizationalUnitName" VALUE="Research Institute"> </TD></TR> <TR><TD>Locality (City):</TD><TD> <INPUT TYPE="TEXT" NAME="localityName" VALUE="Cambridge"> </TD></TR> <TR><TD>State:</TD><TD> <INPUT TYPE="TEXT" NAME="stateOrProvinceName" VALUE="MA"> </TD></TR> <TR><TD>Country:</TD><TD> <INPUT TYPE="TEXT" NAME="countryName" VALUE="US" SIZE="2"> </TD></TR> </TABLE> <!-- ' keygen is Netscape specific and will be ignored in ' internet explorer --> <KEYGEN NAME="SPKAC" CHALLENGE="challengePassword"> <INPUT TYPE="SUBMIT" NAME="SUBMIT"> </FORM> <P><HR></BODY></HTML> |
The CGI script for Netscape Navigator creates a file containing the distinguished name values returned by the form, and a special SPKAC value for the "Signed Public Key And Challenge (SPKAC)" generated by Navigator. This file contains the information which would be in a certificate request, and is used to generate the certificate.
The SSLeay "ca" command is called with this file as an argument to generate a client certificate, as follows (Also see ns-ca.doc from the SSLeay documentation) :
$SSLDIR/bin/ca -spkac $req_file -out $result_file -days 360 -key $CAPASS \ -config /opt/www/lib/ssleay.cnf 2>errs |
If the "ca" command is successful, then the certificate is returned by the CGI script as an application/x-x509-user-cert HTTP Content-Type. Navigator recognizes this type, and prompts the user for the remainder of the steps required to install the user certificate in the browser.
The sample script source is as follows:
#!/usr/local/bin/perl require 5.003; use strict; use CGI; use File::CounterFile; # module to maintain certificate request counter my $doc_dir = $ENV{'DOCUMENT_ROOT'}; # apache specific location for storage unless($doc_dir) { print "<HTML><HEAD><TITLE>Failure</TITLE></HEAD>"; print "<BODY>DOCUMENT_ROOT not defined</BODY></HTML>"; exit(0); } my $base_dir = $doc_dir; $base_dir =~ s/\/htdocs//; my $SSLDIR = '/opt/dev/ssl'; # define where SSLeay files are located my $CA = "$SSLDIR/bin/ca"; my $CONFIG = "/opt/www/lib/ssleay.cnf"; my $CAPASS = "caKEY"; my $query = new CGI; # get a handle on the form data my $key = $query->param('SPKAC'); # this will fail if not Netscape browser unless($key) { fail("No Key provided $key. Netscape required"); } my $counter = new File::CounterFile("$base_dir/.counter", 1); unless($counter) { fail("Could not create counter: $!"); } my $count = $counter->inc(); my $certs_dir = "$base_dir/certs"; my $req_file = "$certs_dir/cert$count.req"; # certificate request filename my $result_file = "$certs_dir/cert$count.result"; # certificate filename # Explicitly list form fields we must have for certificate creation to work. my @req_names = ('commonName', 'emailAddress', 'organizationName', 'organizationalUnitName', 'localityName', 'stateOrProvinceName', 'countryName', 'SPKAC'); # build the request file open(REQ, ">$req_file") or fail("Could not create request $req_file: $!"); my $name; foreach $name (@req_names) { my $value = $query->param("$name"); $value =~ tr/\n//d; print REQ "$name = $value\n"; } close(REQ); # make sure we actually created a request file unless(-f $req_file) { fail("request missing: $req_file"); } unless(-e $CA) { fail("command missing"); } # ensure that ca command will run # command for processing certificate request, without password my $cmd = "$CA -config $CONFIG -spkac $req_file -out $result_file -days 360"; my $rc = system("$cmd -key $CAPASS 2>errs"); if($rc != 0) { fail("$cmd<P>rc = $rc", "errs"); } open(CERT, "<$result_file") or fail("Could not open $result_file<P>$!"); # send the client certificate to the browser print "Content-Type: application/x-x509-user-cert\n"; my $result = join '', <CERT>; close CERT; my $len = length($result); print "Content-Length: $len\n\n"; print $result; exit(0); sub fail { my($msg, $errs) = @_; print $query->header; print $query->start_html(-title => "Certificate Request Failure"); print "<H2>Certificate request failed</H2>$msg<P>"; if($errs) { if(open(ERR, "<errs")) { while(<ERR>) { print "$_<BR>"; } close ERR; } } print $query->dump(); print $query->end_html(); exit(0); } 1; |