Update CA.pl podpage, and script
authorRich Salz <rsalz@akamai.com>
Mon, 16 Mar 2020 19:53:00 +0000 (15:53 -0400)
committerDmitry Belyavskiy <beldmit@gmail.com>
Mon, 23 Mar 2020 11:20:21 +0000 (14:20 +0300)
Merge the NOTES section into the relevant parts of the manpage.
Add the $EXTRA parameter in consistent places (the end) to call
commands.  Document that multiple -extra-XXX might be needed.

Reviewed-by: Tomas Mraz <tmraz@fedoraproject.org>
Reviewed-by: Dmitry Belyavskiy <beldmit@gmail.com>
(Merged from https://github.com/openssl/openssl/pull/11338)

apps/CA.pl.in
doc/man1/CA.pl.pod

index 12c9452..918534d 100644 (file)
 use strict;
 use warnings;
 
 use strict;
 use warnings;
 
-my $openssl = "openssl";
-if(defined $ENV{'OPENSSL'}) {
-    $openssl = $ENV{'OPENSSL'};
-} else {
-    $ENV{'OPENSSL'} = $openssl;
-}
-
 my $verbose = 1;
 my $verbose = 1;
+my @OPENSSL_CMDS = ("req", "ca", "pkcs12", "x509", "verify");
 
 
-my $OPENSSL_CONFIG = $ENV{"OPENSSL_CONFIG"} || "";
-my $DAYS = "-days 365";
-my $CADAYS = "-days 1095";     # 3 years
+my $openssl = $ENV{'OPENSSL'} // "openssl";
+$ENV{'OPENSSL'} = $openssl;
+my $OPENSSL_CONFIG = $ENV{"OPENSSL_CONFIG"} // "";
+
+# Command invocations.
 my $REQ = "$openssl req $OPENSSL_CONFIG";
 my $CA = "$openssl ca $OPENSSL_CONFIG";
 my $VERIFY = "$openssl verify";
 my $X509 = "$openssl x509";
 my $PKCS12 = "$openssl pkcs12";
 
 my $REQ = "$openssl req $OPENSSL_CONFIG";
 my $CA = "$openssl ca $OPENSSL_CONFIG";
 my $VERIFY = "$openssl verify";
 my $X509 = "$openssl x509";
 my $PKCS12 = "$openssl pkcs12";
 
-# default openssl.cnf file has setup as per the following
+# Default values for various configuration settings.
 my $CATOP = "./demoCA";
 my $CAKEY = "cakey.pem";
 my $CAREQ = "careq.pem";
 my $CACERT = "cacert.pem";
 my $CACRL = "crl.pem";
 my $CATOP = "./demoCA";
 my $CAKEY = "cakey.pem";
 my $CAREQ = "careq.pem";
 my $CACERT = "cacert.pem";
 my $CACRL = "crl.pem";
-my $DIRMODE = 0777;
-
+my $DAYS = "-days 365";
+my $CADAYS = "-days 1095";     # 3 years
 my $NEWKEY = "newkey.pem";
 my $NEWREQ = "newreq.pem";
 my $NEWCERT = "newcert.pem";
 my $NEWP12 = "newcert.p12";
 my $NEWKEY = "newkey.pem";
 my $NEWREQ = "newreq.pem";
 my $NEWCERT = "newcert.pem";
 my $NEWP12 = "newcert.p12";
-my $RET = 0;
+
+# Commandline parsing
+my %EXTRA;
 my $WHAT = shift @ARGV || "";
 my $WHAT = shift @ARGV || "";
-my @OPENSSL_CMDS = ("req", "ca", "pkcs12", "x509", "verify");
-my %EXTRA = extra_args(\@ARGV, "-extra-");
-my $FILE;
-
-sub extra_args {
-    my ($args_ref, $arg_prefix) = @_;
-    my %eargs = map {
-       if ($_ < $#$args_ref) {
-           my ($arg, $value) = splice(@$args_ref, $_, 2);
-           $arg =~ s/$arg_prefix//;
-           ($arg, $value);
-       } else {
-           ();
-       }
-    } reverse grep($$args_ref[$_] =~ /$arg_prefix/, 0..$#$args_ref);
-    my %empty = map { ($_, "") } @OPENSSL_CMDS;
-    return (%empty, %eargs);
+@ARGV = parse_extra(@ARGV);
+my $RET = 0;
+
+# Split out "-extra-CMD value", and return new |@ARGV|. Fill in
+# |EXTRA{CMD}| with list of values.
+sub parse_extra
+{
+    foreach ( @OPENSSL_CMDS ) {
+        $EXTRA{$_} = '';
+    }
+
+    my @result;
+    while ( scalar(@_) > 0 ) {
+        my $arg = shift;
+        if ( $arg !~ m/-extra-([a-z0-9]+)/ ) {
+            push @result, $arg;
+            next;
+        }
+        $arg =~ s/-extra-//;
+        die("Unknown \"-${arg}-extra\" option, exiting")
+            unless scalar grep { $arg eq $_ } @OPENSSL_CMDS;
+        $EXTRA{$arg} .= " " . shift;
+    }
+    return @result;
 }
 
 }
 
+
 # See if reason for a CRL entry is valid; exit if not.
 sub crl_reason_ok
 {
 # See if reason for a CRL entry is valid; exit if not.
 sub crl_reason_ok
 {
@@ -113,19 +119,25 @@ sub run
 
 
 if ( $WHAT =~ /^(-\?|-h|-help)$/ ) {
 
 
 if ( $WHAT =~ /^(-\?|-h|-help)$/ ) {
-    print STDERR "usage: CA.pl -newcert | -newreq | -newreq-nodes | -xsign | -sign | -signCA | -signcert | -crl | -newca [-extra-cmd extra-params]\n";
-    print STDERR "       CA.pl -pkcs12 [-extra-pkcs12 extra-params] [certname]\n";
-    print STDERR "       CA.pl -verify [-extra-verify extra-params] certfile ...\n";
-    print STDERR "       CA.pl -revoke [-extra-ca extra-params] certfile [reason]\n";
+    print STDERR <<EOF;
+Usage:
+    CA.pl -newcert | -newreq | -newreq-nodes | -xsign | -sign | -signCA | -signcert | -crl | -newca [-extra-cmd parameter]
+    CA.pl -pkcs12 [-extra-pkcs12 parameter] [certname]
+    CA.pl -verify [-extra-verify parameter] certfile ...
+    CA.pl -revoke [-extra-ca parameter] certfile [reason]
+EOF
     exit 0;
 }
     exit 0;
 }
+
 if ($WHAT eq '-newcert' ) {
     # create a certificate
 if ($WHAT eq '-newcert' ) {
     # create a certificate
-    $RET = run("$REQ -new -x509 -keyout $NEWKEY -out $NEWCERT $DAYS $EXTRA{req}");
+    $RET = run("$REQ -new -x509 -keyout $NEWKEY -out $NEWCERT $DAYS"
+            . " $EXTRA{req}");
     print "Cert is in $NEWCERT, private key is in $NEWKEY\n" if $RET == 0;
 } elsif ($WHAT eq '-precert' ) {
     # create a pre-certificate
     print "Cert is in $NEWCERT, private key is in $NEWKEY\n" if $RET == 0;
 } elsif ($WHAT eq '-precert' ) {
     # create a pre-certificate
-    $RET = run("$REQ -x509 -precert -keyout $NEWKEY -out $NEWCERT $DAYS");
+    $RET = run("$REQ -x509 -precert -keyout $NEWKEY -out $NEWCERT $DAYS"
+            . " $EXTRA{req}");
     print "Pre-cert is in $NEWCERT, private key is in $NEWKEY\n" if $RET == 0;
 } elsif ($WHAT =~ /^\-newreq(\-nodes)?$/ ) {
     # create a certificate request
     print "Pre-cert is in $NEWCERT, private key is in $NEWKEY\n" if $RET == 0;
 } elsif ($WHAT =~ /^\-newreq(\-nodes)?$/ ) {
     # create a certificate request
@@ -133,11 +145,20 @@ if ($WHAT eq '-newcert' ) {
     print "Request is in $NEWREQ, private key is in $NEWKEY\n" if $RET == 0;
 } elsif ($WHAT eq '-newca' ) {
     # create the directory hierarchy
     print "Request is in $NEWREQ, private key is in $NEWKEY\n" if $RET == 0;
 } elsif ($WHAT eq '-newca' ) {
     # create the directory hierarchy
-    mkdir ${CATOP}, $DIRMODE;
-    mkdir "${CATOP}/certs", $DIRMODE;
-    mkdir "${CATOP}/crl", $DIRMODE ;
-    mkdir "${CATOP}/newcerts", $DIRMODE;
-    mkdir "${CATOP}/private", $DIRMODE;
+    my @dirs = ( "${CATOP}", "${CATOP}/certs", "${CATOP}/crl",
+                "${CATOP}/newcerts", "${CATOP}/private" );
+    die "${CATOP}/index.txt exists.\nRemove old sub-tree to proceed,"
+        if -f "${CATOP}/index.txt";
+    die "${CATOP}/serial exists.\nRemove old sub-tree to proceed,"
+        if -f "${CATOP}/serial";
+    foreach my $d ( @dirs ) {
+        if ( -d $d ) {
+            warn "Directory $d exists" if -d $d;
+        } else {
+            mkdir $d or die "Can't mkdir $d, $!";
+        }
+    }
+
     open OUT, ">${CATOP}/index.txt";
     close OUT;
     open OUT, ">${CATOP}/crlnumber";
     open OUT, ">${CATOP}/index.txt";
     close OUT;
     open OUT, ">${CATOP}/crlnumber";
@@ -145,6 +166,7 @@ if ($WHAT eq '-newcert' ) {
     close OUT;
     # ask user for existing CA certificate
     print "CA certificate filename (or enter to create)\n";
     close OUT;
     # ask user for existing CA certificate
     print "CA certificate filename (or enter to create)\n";
+    my $FILE;
     $FILE = "" unless defined($FILE = <STDIN>);
     $FILE =~ s{\R$}{};
     if ($FILE ne "") {
     $FILE = "" unless defined($FILE = <STDIN>);
     $FILE =~ s{\R$}{};
     if ($FILE ne "") {
@@ -152,44 +174,42 @@ if ($WHAT eq '-newcert' ) {
         copy_pemfile($FILE,"${CATOP}/$CACERT", "CERTIFICATE");
     } else {
         print "Making CA certificate ...\n";
         copy_pemfile($FILE,"${CATOP}/$CACERT", "CERTIFICATE");
     } else {
         print "Making CA certificate ...\n";
-        $RET = run("$REQ -new -keyout"
-                . " ${CATOP}/private/$CAKEY"
+        $RET = run("$REQ -new -keyout ${CATOP}/private/$CAKEY"
                 . " -out ${CATOP}/$CAREQ $EXTRA{req}");
         $RET = run("$CA -create_serial"
                 . " -out ${CATOP}/$CACERT $CADAYS -batch"
                 . " -keyfile ${CATOP}/private/$CAKEY -selfsign"
                 . " -out ${CATOP}/$CAREQ $EXTRA{req}");
         $RET = run("$CA -create_serial"
                 . " -out ${CATOP}/$CACERT $CADAYS -batch"
                 . " -keyfile ${CATOP}/private/$CAKEY -selfsign"
-                . " -extensions v3_ca $EXTRA{ca}"
-                . " -infiles ${CATOP}/$CAREQ") if $RET == 0;
+                . " -extensions v3_ca"
+                . " -infiles ${CATOP}/$CAREQ $EXTRA{ca}") if $RET == 0;
         print "CA certificate is in ${CATOP}/$CACERT\n" if $RET == 0;
     }
 } elsif ($WHAT eq '-pkcs12' ) {
     my $cname = $ARGV[0];
     $cname = "My Certificate" unless defined $cname;
     $RET = run("$PKCS12 -in $NEWCERT -inkey $NEWKEY"
         print "CA certificate is in ${CATOP}/$CACERT\n" if $RET == 0;
     }
 } elsif ($WHAT eq '-pkcs12' ) {
     my $cname = $ARGV[0];
     $cname = "My Certificate" unless defined $cname;
     $RET = run("$PKCS12 -in $NEWCERT -inkey $NEWKEY"
-            . " -certfile ${CATOP}/$CACERT"
-            . " -out $NEWP12"
+            . " -certfile ${CATOP}/$CACERT -out $NEWP12"
             . " -export -name \"$cname\" $EXTRA{pkcs12}");
     print "PKCS #12 file is in $NEWP12\n" if $RET == 0;
 } elsif ($WHAT eq '-xsign' ) {
             . " -export -name \"$cname\" $EXTRA{pkcs12}");
     print "PKCS #12 file is in $NEWP12\n" if $RET == 0;
 } elsif ($WHAT eq '-xsign' ) {
-    $RET = run("$CA -policy policy_anything $EXTRA{ca} -infiles $NEWREQ");
+    $RET = run("$CA -policy policy_anything -infiles $NEWREQ $EXTRA{ca}");
 } elsif ($WHAT eq '-sign' ) {
 } elsif ($WHAT eq '-sign' ) {
-    $RET = run("$CA -policy policy_anything -out $NEWCERT $EXTRA{ca} -infiles $NEWREQ");
+    $RET = run("$CA -policy policy_anything -out $NEWCERT"
+            . " -infiles $NEWREQ $EXTRA{ca}");
     print "Signed certificate is in $NEWCERT\n" if $RET == 0;
 } elsif ($WHAT eq '-signCA' ) {
     $RET = run("$CA -policy policy_anything -out $NEWCERT"
     print "Signed certificate is in $NEWCERT\n" if $RET == 0;
 } elsif ($WHAT eq '-signCA' ) {
     $RET = run("$CA -policy policy_anything -out $NEWCERT"
-            . " -extensions v3_ca $EXTRA{ca} -infiles $NEWREQ");
+            . " -extensions v3_ca -infiles $NEWREQ $EXTRA{ca}");
     print "Signed CA certificate is in $NEWCERT\n" if $RET == 0;
 } elsif ($WHAT eq '-signcert' ) {
     $RET = run("$X509 -x509toreq -in $NEWREQ -signkey $NEWREQ"
             . " -out tmp.pem $EXTRA{x509}");
     $RET = run("$CA -policy policy_anything -out $NEWCERT"
     print "Signed CA certificate is in $NEWCERT\n" if $RET == 0;
 } elsif ($WHAT eq '-signcert' ) {
     $RET = run("$X509 -x509toreq -in $NEWREQ -signkey $NEWREQ"
             . " -out tmp.pem $EXTRA{x509}");
     $RET = run("$CA -policy policy_anything -out $NEWCERT"
-            . "$EXTRA{ca} -infiles tmp.pem") if $RET == 0;
+            .  "-infiles tmp.pem $EXTRA{ca}") if $RET == 0;
     print "Signed certificate is in $NEWCERT\n" if $RET == 0;
 } elsif ($WHAT eq '-verify' ) {
     my @files = @ARGV ? @ARGV : ( $NEWCERT );
     print "Signed certificate is in $NEWCERT\n" if $RET == 0;
 } elsif ($WHAT eq '-verify' ) {
     my @files = @ARGV ? @ARGV : ( $NEWCERT );
-    my $file;
-    foreach $file (@files) {
-        my $status = run("$VERIFY \"-CAfile\" ${CATOP}/$CACERT $file $EXTRA{verify}");
+    foreach my $file (@files) {
+        my $status = run("$VERIFY -CAfile ${CATOP}/$CACERT $file $EXTRA{verify}");
         $RET = $status if $status != 0;
     }
 } elsif ($WHAT eq '-crl' ) {
         $RET = $status if $status != 0;
     }
 } elsif ($WHAT eq '-crl' ) {
index 4c53970..fa07c89 100644 (file)
@@ -21,13 +21,13 @@ B<-signCA> |
 B<-signcert> |
 B<-crl> |
 B<-newca>
 B<-signcert> |
 B<-crl> |
 B<-newca>
-[B<-extra-cmd> I<extra-params>]
+[B<-extra-I<cmd>> I<parameter>]
 
 
-B<CA.pl> B<-pkcs12> [B<-extra-pkcs12> I<extra-params>] [I<certname>]
+B<CA.pl> B<-pkcs12> [B<-extra-pkcs12> I<parameter>] [I<certname>]
 
 
-B<CA.pl> B<-verify> [B<-extra-verify> I<extra-params>] I<certfile> ...
+B<CA.pl> B<-verify> [B<-extra-verify> I<parameter>] I<certfile> ...
 
 
-B<CA.pl> B<-revoke> [B<-extra-ca> I<extra-params>] I<certfile> [I<reason>]
+B<CA.pl> B<-revoke> [B<-extra-ca> I<parameter>] I<certfile> [I<reason>]
 
 =head1 DESCRIPTION
 
 
 =head1 DESCRIPTION
 
@@ -36,6 +36,23 @@ arguments to the L<openssl(1)> command for some common certificate operations.
 It is intended to simplify the process of certificate creation and management
 by the use of some simple options.
 
 It is intended to simplify the process of certificate creation and management
 by the use of some simple options.
 
+The script is intended as a simple front end for the L<openssl(1)> program for
+use by a beginner. Its behaviour isn't always what is wanted. For more control
+over the behaviour of the certificate commands call the L<openssl(1)> command
+directly.
+
+Most of the filenames mentioned below can be modified by editing the
+B<CA.pl> script.
+
+Under some environments it may not be possible to run the B<CA.pl> script
+directly (for example Win32) and the default configuration file location may
+be wrong. In this case the command:
+
+ perl -S CA.pl
+
+can be used and the B<OPENSSL_CONF> environment variable can be set to point to
+the correct path of the configuration file.
+
 =head1 OPTIONS
 
 =over 4
 =head1 OPTIONS
 
 =over 4
@@ -70,6 +87,11 @@ details of the CA will be prompted for. The relevant files and directories
 are created in a directory called F<demoCA> in the current directory.
 Uses L<openssl-req(1)> and L<openssl-ca(1)>.
 
 are created in a directory called F<demoCA> in the current directory.
 Uses L<openssl-req(1)> and L<openssl-ca(1)>.
 
+If the F<demoCA> directory already exists then the B<-newca> command will not
+overwrite it and will do nothing. This can happen if a previous call using
+the B<-newca> option terminated abnormally. To get the correct behaviour
+delete the directory if it already exists.
+
 =item B<-pkcs12>
 
 Create a PKCS#12 file containing the user certificate, private key and CA
 =item B<-pkcs12>
 
 Create a PKCS#12 file containing the user certificate, private key and CA
@@ -120,13 +142,15 @@ Verifies certificates against the CA certificate for F<demoCA>. If no
 certificates are specified on the command line it tries to verify the file
 F<newcert.pem>.  Invokes L<openssl-verify(1)>.
 
 certificates are specified on the command line it tries to verify the file
 F<newcert.pem>.  Invokes L<openssl-verify(1)>.
 
-=item B<-extra-req> | B<-extra-ca> | B<-extra-pkcs12> | B<-extra-x509> | B<-extra-verify> I<extra-params>
+=item B<-extra-I<cmd>> I<parameter>
 
 
-For each option B<extra-I<cmd>>, pass I<extra-params> to the L<openssl(1)>
+For each option B<extra-I<cmd>>, pass I<parameter> to the L<openssl(1)>
 sub-command with the same name as I<cmd>, if that sub-command is invoked.
 sub-command with the same name as I<cmd>, if that sub-command is invoked.
-For example, if L<openssl-req(1)> is invoked, the I<extra-params> given with
+For example, if L<openssl-req(1)> is invoked, the I<parameter> given with
 B<-extra-req> will be passed to it.
 B<-extra-req> will be passed to it.
-Users should consult L<openssl(1)> command documentation for more information.
+For multi-word parameters, either repeat the option or quote the I<parameters>
+so it looks like one word to your shell.
+See the individual command documentation for more information.
 
 =back
 
 
 =back
 
@@ -144,66 +168,16 @@ the request and finally create a PKCS#12 file containing it.
  CA.pl -signreq
  CA.pl -pkcs12 "My Test Certificate"
 
  CA.pl -signreq
  CA.pl -pkcs12 "My Test Certificate"
 
-=head1 DSA CERTIFICATES
-
-Although the B<CA.pl> creates RSA CAs and requests it is still possible to
-use it with DSA certificates and requests using the L<openssl-req(1)> command
-directly. The following example shows the steps that would typically be taken.
-
-Create some DSA parameters:
-
- openssl dsaparam -out dsap.pem 1024
-
-Create a DSA CA certificate and private key:
-
- openssl req -x509 -newkey dsa:dsap.pem -keyout cacert.pem -out cacert.pem
-
-Create the CA directories and files:
-
- CA.pl -newca
-
-enter a filename (for example, F<cacert.pem>) when prompted for the CA file
-name.
-
-Create a DSA certificate request and private key (a different set of parameters
-can optionally be created first):
-
- openssl req -out newreq.pem -newkey dsa:dsap.pem
-
-Sign the request:
-
- CA.pl -signreq
-
 =head1 ENVIRONMENT
 
 =head1 ENVIRONMENT
 
+The environment variable B<OPENSSL> may be used to specify the name of
+the OpenSSL program. It can be a full pathname, or a relative one.
+
 The environment variable B<OPENSSL_CONFIG> may be used to specify a
 configuration option and value to the B<req> and B<ca> commands invoked by
 this script. It's value should be the option and pathname, as in
 C<-config /path/to/conf-file>.
 
 The environment variable B<OPENSSL_CONFIG> may be used to specify a
 configuration option and value to the B<req> and B<ca> commands invoked by
 this script. It's value should be the option and pathname, as in
 C<-config /path/to/conf-file>.
 
-=head1 NOTES
-
-Most of the filenames mentioned can be modified by editing the B<CA.pl> script.
-
-If the F<demoCA> directory already exists then the B<-newca> command will not
-overwrite it and will do nothing. This can happen if a previous call using
-the B<-newca> option terminated abnormally. To get the correct behaviour
-delete the demoCA directory if it already exists.
-
-Under some environments it may not be possible to run the B<CA.pl> script
-directly (for example Win32) and the default configuration file location may
-be wrong. In this case the command:
-
- perl -S CA.pl
-
-can be used and the B<OPENSSL_CONF> environment variable changed to point to
-the correct path of the configuration file.
-
-The script is intended as a simple front end for the L<openssl(1)> program for
-use by a beginner. Its behaviour isn't always what is wanted. For more control
-over the behaviour of the certificate commands call the L<openssl(1)> command
-directly.
-
 =head1 SEE ALSO
 
 L<openssl(1)>,
 =head1 SEE ALSO
 
 L<openssl(1)>,