1#!{- $config{HASHBANGPERL} -} 2# Copyright 2000-2018 The OpenSSL Project Authors. All Rights Reserved. 3# 4# Licensed under the OpenSSL license (the "License"). You may not use 5# this file except in compliance with the License. You can obtain a copy 6# in the file LICENSE in the source distribution or at 7# https://www.openssl.org/source/license.html 8 9# 10# Wrapper around the ca to make it easier to use 11# 12# {- join("\n# ", @autowarntext) -} 13 14use strict; 15use warnings; 16 17my $openssl = "openssl"; 18if(defined $ENV{'OPENSSL'}) { 19 $openssl = $ENV{'OPENSSL'}; 20} else { 21 $ENV{'OPENSSL'} = $openssl; 22} 23 24my $verbose = 1; 25 26my $OPENSSL_CONFIG = $ENV{"OPENSSL_CONFIG"} || ""; 27my $DAYS = "-days 365"; 28my $CADAYS = "-days 1095"; # 3 years 29my $REQ = "$openssl req $OPENSSL_CONFIG"; 30my $CA = "$openssl ca $OPENSSL_CONFIG"; 31my $VERIFY = "$openssl verify"; 32my $X509 = "$openssl x509"; 33my $PKCS12 = "$openssl pkcs12"; 34 35# default openssl.cnf file has setup as per the following 36my $CATOP = "./demoCA"; 37my $CAKEY = "cakey.pem"; 38my $CAREQ = "careq.pem"; 39my $CACERT = "cacert.pem"; 40my $CACRL = "crl.pem"; 41my $DIRMODE = 0777; 42 43my $NEWKEY = "newkey.pem"; 44my $NEWREQ = "newreq.pem"; 45my $NEWCERT = "newcert.pem"; 46my $NEWP12 = "newcert.p12"; 47my $RET = 0; 48my $WHAT = shift @ARGV || ""; 49my @OPENSSL_CMDS = ("req", "ca", "pkcs12", "x509", "verify"); 50my %EXTRA = extra_args(\@ARGV, "-extra-"); 51my $FILE; 52 53sub extra_args { 54 my ($args_ref, $arg_prefix) = @_; 55 my %eargs = map { 56 if ($_ < $#$args_ref) { 57 my ($arg, $value) = splice(@$args_ref, $_, 2); 58 $arg =~ s/$arg_prefix//; 59 ($arg, $value); 60 } else { 61 (); 62 } 63 } reverse grep($$args_ref[$_] =~ /$arg_prefix/, 0..$#$args_ref); 64 my %empty = map { ($_, "") } @OPENSSL_CMDS; 65 return (%empty, %eargs); 66} 67 68# See if reason for a CRL entry is valid; exit if not. 69sub crl_reason_ok 70{ 71 my $r = shift; 72 73 if ($r eq 'unspecified' || $r eq 'keyCompromise' 74 || $r eq 'CACompromise' || $r eq 'affiliationChanged' 75 || $r eq 'superseded' || $r eq 'cessationOfOperation' 76 || $r eq 'certificateHold' || $r eq 'removeFromCRL') { 77 return 1; 78 } 79 print STDERR "Invalid CRL reason; must be one of:\n"; 80 print STDERR " unspecified, keyCompromise, CACompromise,\n"; 81 print STDERR " affiliationChanged, superseded, cessationOfOperation\n"; 82 print STDERR " certificateHold, removeFromCRL"; 83 exit 1; 84} 85 86# Copy a PEM-format file; return like exit status (zero means ok) 87sub copy_pemfile 88{ 89 my ($infile, $outfile, $bound) = @_; 90 my $found = 0; 91 92 open IN, $infile || die "Cannot open $infile, $!"; 93 open OUT, ">$outfile" || die "Cannot write to $outfile, $!"; 94 while (<IN>) { 95 $found = 1 if /^-----BEGIN.*$bound/; 96 print OUT $_ if $found; 97 $found = 2, last if /^-----END.*$bound/; 98 } 99 close IN; 100 close OUT; 101 return $found == 2 ? 0 : 1; 102} 103 104# Wrapper around system; useful for debugging. Returns just the exit status 105sub run 106{ 107 my $cmd = shift; 108 print "====\n$cmd\n" if $verbose; 109 my $status = system($cmd); 110 print "==> $status\n====\n" if $verbose; 111 return $status >> 8; 112} 113 114 115if ( $WHAT =~ /^(-\?|-h|-help)$/ ) { 116 print STDERR "usage: CA.pl -newcert | -newreq | -newreq-nodes | -xsign | -sign | -signCA | -signcert | -crl | -newca [-extra-cmd extra-params]\n"; 117 print STDERR " CA.pl -pkcs12 [-extra-pkcs12 extra-params] [certname]\n"; 118 print STDERR " CA.pl -verify [-extra-verify extra-params] certfile ...\n"; 119 print STDERR " CA.pl -revoke [-extra-ca extra-params] certfile [reason]\n"; 120 exit 0; 121} 122if ($WHAT eq '-newcert' ) { 123 # create a certificate 124 $RET = run("$REQ -new -x509 -keyout $NEWKEY -out $NEWCERT $DAYS $EXTRA{req}"); 125 print "Cert is in $NEWCERT, private key is in $NEWKEY\n" if $RET == 0; 126} elsif ($WHAT eq '-precert' ) { 127 # create a pre-certificate 128 $RET = run("$REQ -x509 -precert -keyout $NEWKEY -out $NEWCERT $DAYS"); 129 print "Pre-cert is in $NEWCERT, private key is in $NEWKEY\n" if $RET == 0; 130} elsif ($WHAT =~ /^\-newreq(\-nodes)?$/ ) { 131 # create a certificate request 132 $RET = run("$REQ -new $1 -keyout $NEWKEY -out $NEWREQ $DAYS $EXTRA{req}"); 133 print "Request is in $NEWREQ, private key is in $NEWKEY\n" if $RET == 0; 134} elsif ($WHAT eq '-newca' ) { 135 # create the directory hierarchy 136 mkdir ${CATOP}, $DIRMODE; 137 mkdir "${CATOP}/certs", $DIRMODE; 138 mkdir "${CATOP}/crl", $DIRMODE ; 139 mkdir "${CATOP}/newcerts", $DIRMODE; 140 mkdir "${CATOP}/private", $DIRMODE; 141 open OUT, ">${CATOP}/index.txt"; 142 close OUT; 143 open OUT, ">${CATOP}/crlnumber"; 144 print OUT "01\n"; 145 close OUT; 146 # ask user for existing CA certificate 147 print "CA certificate filename (or enter to create)\n"; 148 $FILE = "" unless defined($FILE = <STDIN>); 149 $FILE =~ s{\R$}{}; 150 if ($FILE ne "") { 151 copy_pemfile($FILE,"${CATOP}/private/$CAKEY", "PRIVATE"); 152 copy_pemfile($FILE,"${CATOP}/$CACERT", "CERTIFICATE"); 153 } else { 154 print "Making CA certificate ...\n"; 155 $RET = run("$REQ -new -keyout" 156 . " ${CATOP}/private/$CAKEY" 157 . " -out ${CATOP}/$CAREQ $EXTRA{req}"); 158 $RET = run("$CA -create_serial" 159 . " -out ${CATOP}/$CACERT $CADAYS -batch" 160 . " -keyfile ${CATOP}/private/$CAKEY -selfsign" 161 . " -extensions v3_ca $EXTRA{ca}" 162 . " -infiles ${CATOP}/$CAREQ") if $RET == 0; 163 print "CA certificate is in ${CATOP}/$CACERT\n" if $RET == 0; 164 } 165} elsif ($WHAT eq '-pkcs12' ) { 166 my $cname = $ARGV[0]; 167 $cname = "My Certificate" unless defined $cname; 168 $RET = run("$PKCS12 -in $NEWCERT -inkey $NEWKEY" 169 . " -certfile ${CATOP}/$CACERT" 170 . " -out $NEWP12" 171 . " -export -name \"$cname\" $EXTRA{pkcs12}"); 172 print "PKCS #12 file is in $NEWP12\n" if $RET == 0; 173} elsif ($WHAT eq '-xsign' ) { 174 $RET = run("$CA -policy policy_anything $EXTRA{ca} -infiles $NEWREQ"); 175} elsif ($WHAT eq '-sign' ) { 176 $RET = run("$CA -policy policy_anything -out $NEWCERT $EXTRA{ca} -infiles $NEWREQ"); 177 print "Signed certificate is in $NEWCERT\n" if $RET == 0; 178} elsif ($WHAT eq '-signCA' ) { 179 $RET = run("$CA -policy policy_anything -out $NEWCERT" 180 . " -extensions v3_ca $EXTRA{ca} -infiles $NEWREQ"); 181 print "Signed CA certificate is in $NEWCERT\n" if $RET == 0; 182} elsif ($WHAT eq '-signcert' ) { 183 $RET = run("$X509 -x509toreq -in $NEWREQ -signkey $NEWREQ" 184 . " -out tmp.pem $EXTRA{x509}"); 185 $RET = run("$CA -policy policy_anything -out $NEWCERT" 186 . "$EXTRA{ca} -infiles tmp.pem") if $RET == 0; 187 print "Signed certificate is in $NEWCERT\n" if $RET == 0; 188} elsif ($WHAT eq '-verify' ) { 189 my @files = @ARGV ? @ARGV : ( $NEWCERT ); 190 my $file; 191 foreach $file (@files) { 192 my $status = run("$VERIFY \"-CAfile\" ${CATOP}/$CACERT $file $EXTRA{verify}"); 193 $RET = $status if $status != 0; 194 } 195} elsif ($WHAT eq '-crl' ) { 196 $RET = run("$CA -gencrl -out ${CATOP}/crl/$CACRL $EXTRA{ca}"); 197 print "Generated CRL is in ${CATOP}/crl/$CACRL\n" if $RET == 0; 198} elsif ($WHAT eq '-revoke' ) { 199 my $cname = $ARGV[0]; 200 if (!defined $cname) { 201 print "Certificate filename is required; reason optional.\n"; 202 exit 1; 203 } 204 my $reason = $ARGV[1]; 205 $reason = " -crl_reason $reason" 206 if defined $reason && crl_reason_ok($reason); 207 $RET = run("$CA -revoke \"$cname\"" . $reason . $EXTRA{ca}); 208} else { 209 print STDERR "Unknown arg \"$WHAT\"\n"; 210 print STDERR "Use -help for help.\n"; 211 exit 1; 212} 213 214exit $RET; 215