diff -r 6657142678fa -r 04c4e7da49ff bin/ca --- a/bin/ca Wed Dec 15 23:43:30 2010 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,226 +0,0 @@ -#! /usr/bin/perl -use strict; -use warnings; -use Template; -use IO::File; -use File::Path; -use File::Temp qw(tempdir); -use File::Basename; -use Getopt::Long qw(GetOptionsFromArray); -use Pod::Usage; -use feature qw(switch); - -my $CA_CRT = "CA/ca-crt.pem"; -my $CA_KEY = "CA/private/ca-key.pem"; -my $CA_DIR = "./var"; - -my %TEMPLATE = ( - ca => "lib/templates/ca", - req => "lib/templates/req", -); - -my $TMP = tempdir("/tmp/$ENV{USER}.ca.XXXXXX", CLEANUP => 1); - -my $opt_days = undef; # see the templates/ca for a default -my $opt_type = undef; # see the templates/ca for a default -my $opt_policy = "de"; # see the templates/ca for a default -my $opt_outfile = undef; -my $opt_force = undef; - -sub init_ca(); -sub ask_pass($); - -MAIN: { - my $csrfile; - - GetOptions( - "d|days=i" => \$opt_days, - "t|type=s" => \$opt_type, - "p|policy=s" => \$opt_policy, - "o|outfile=s" => \$opt_outfile, - "f|force" => \$opt_force, - "i|init" => sub { eval { init_ca() }; if ($@) { warn $@; exit 1 }; exit 0 }, - "h|help" => sub { pod2usage(-verbose => 1, -exit => 0) }, - "m|man" => sub { pod2usage(-verbose => 2, -exit => 0) }, - ) or pod2usage; - - pod2usage if @ARGV > 1; - $csrfile = $ARGV[0]; # don't shift, we'll need it later! - - my $cnf = new IO::File ">$TMP/cnf" or die "Can't open >$TMP/cnf: $!\n"; - my $csr = new IO::File "+>$TMP/csr" or die "Can't open +>$TMP/csr: $!\n"; - my $crt = new IO::File "+>$TMP/crt" or die "Can't open +>$TMP/crt: $!\n"; - my $tt2 = new Template or die $Template::ERROR; - - # get a private copy of the request - print { IO::File->new("|openssl req -out $TMP/csr") } <>; - open(STDIN, "process( - $TEMPLATE{ca}, - { - type => $opt_type, - days => $opt_days, - policy => "policy_$opt_policy", - cacrt => $CA_CRT, - cakey => $CA_KEY, - cadir => $CA_DIR, - } => "$TMP/cnf" - ) or die $tt2->error, "\n"; - - system( "openssl ca -config $TMP/cnf -in $TMP/csr -out $TMP/crt" - . " -utf8 \${CA_PASS:+-passin env:CA_PASS}"); - - die "ERR: Cert is zero size\n" if not -s $crt; - - # get the name of the output crt file - my $outfile = $opt_outfile; - if (not defined $outfile and defined($_ = $csrfile)) { - if (/(.*[\W_])(?:req|csr).pem$/) { $outfile = "$1crt.pem" } - elsif (/(.*[\W_])req$/) { $outfile = "$1crt" } - else { $outfile .= ".crt.pem" } - } - - # to be sure not to have an invalid/dangerous file name - if (fork() == 0) { - if (defined $outfile) { - open(STDOUT, ">$outfile") - or die "Can't open >$outfile: $!\n"; - } - exec "openssl x509 -in $TMP/crt"; - die "Can't exec openssl x509: $!\n"; - } - else { wait } - - # and now, since it's finally done, we'll copy the request - # away (for later use (thing about re-issuing a certificate)) - my $subject = `openssl x509 -in $TMP/crt -noout -subject`; - if (my ($cn) = $subject =~ /CN=(\S+?)[,\/\s\$]/) { - if (fork() == 0) { - open(STDOUT, ">$CA_DIR/requests/$cn-csr.pem") - or die "Can't open >$CA_DIR/requests/$cn-csr.pem: $!\n"; - exec "openssl req -in $TMP/csr"; - die "Can't exec openssl req: $!\n"; - } - else { wait } - } - else { - die "Can't determine the CN from $subject, not saving the request\n"; - } - - exit; -} - -sub ask_pass($) { - my $prompt = shift; - my @keys = ("x", "y"); - - while (1) { - print $prompt; - my $stty = `stty -g`; - system("stty -echo"); - chomp($keys[0] = IO::File->new("/dev/tty")->getline()); - print "\n"; - system("stty $stty"); - print "please again for verification: "; - system("stty -echo"); - chomp($keys[1] = IO::File->new("/dev/tty")->getline()); - print "\n"; - system("stty $stty"); - return $keys[0] if $keys[0] eq $keys[1]; - print "keys mismatch, again\n"; - } -} - -sub init_ca() { - - # initialize the CA directory structure. This should - # correspond to the values found in templates/ca - die "$CA_DIR already exists" if -d $CA_DIR and not $opt_force; - mkpath(map { "$CA_DIR/$_" } qw(newcerts requests)); - mkpath(map { dirname $_ } $CA_CRT, $CA_KEY); - (new IO::File ">$CA_DIR/index"); - (new IO::File ">$CA_DIR/serial")->print("01\n"); - - # now - my $tt2 = new Template or die $Template::ERROR; - $tt2->process( - $TEMPLATE{req}, - { - - # not used yet - } => "$TMP/cnf" - ) or die $tt2->error; - - $ENV{CA_PASS} = ask_pass("passphrase for CA key: "); - system( -"openssl req -config $TMP/cnf -x509 -days 3650 -new -passout env:CA_PASS -keyout $TMP/ca-key.pem -out $TMP/ca-crt.pem" - ) and exit; - - system("openssl x509 -in $TMP/ca-crt.pem -out $CA_CRT") and exit; - $_ = umask(077); - system( -"openssl rsa -in $TMP/ca-key.pem -des3 -passin env:CA_PASS -passout env:CA_PASS -out $CA_KEY" - ) and exit; - umask($_); - - return 0; - -} - -__END__ - -=head1 NAME - - ca - the ultimative CA tool - -=head1 SYNOPSIS - - ca [--force] --init - ca --type=TYPE --days=DAYS [request.pem] - - (not yet: request c=COUNTRY ST=STATE l=LOCATION o=ORGANIZATION OU=ORG-UNIT cn=COMMON-NAME) - -=head1 DESCRIPTION - -This B tool signs the request file. If no file is given, it -expects the request on STDIN - -=head1 OPTIONS - -=over 4 - -=item B<-d>|B<--days> I - -The number of days the certificate should be valid. (default: 365) - -=item B<-h>|B<--help> - -Print the reference help and exit. (default: off) - -=item B<-i>|B<--init> - -Initialize the CA (keys, directories). This may be enforce with -B<--force>. (default: off) - -=item B<-m>|B<--man> - -Open the reference manual and exit. (default: off) - -=item B<-o>|B<--out> I - -The name of the output file. If not set (the default), the output goes -to I if the CSR came from stdin and it goes to a file named -similar to the CSR, if the request came from a file. - -=item B<-t>|B<--type> I - -The (NSCertType) type of the certificate. Should be client or server. -(default: none) - -=back - -=cut -## Please see file perltidy.ERR