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;
|