xref: /freebsd/crypto/openssl/apps/CA.pl (revision 4757b351ea9d59d71d4a38b82506d2d16fcd560d)
1*4757b351SPierre Pronchery#!/usr/local/bin/perl
2*4757b351SPierre Pronchery# Copyright 2000-2025 The OpenSSL Project Authors. All Rights Reserved.
3*4757b351SPierre Pronchery#
4*4757b351SPierre Pronchery# Licensed under the Apache License 2.0 (the "License").  You may not use
5*4757b351SPierre Pronchery# this file except in compliance with the License.  You can obtain a copy
6*4757b351SPierre Pronchery# in the file LICENSE in the source distribution or at
7*4757b351SPierre Pronchery# https://www.openssl.org/source/license.html
8*4757b351SPierre Pronchery
9*4757b351SPierre Pronchery#
10*4757b351SPierre Pronchery# Wrapper around the ca to make it easier to use
11*4757b351SPierre Pronchery#
12*4757b351SPierre Pronchery# WARNING: do not edit!
13*4757b351SPierre Pronchery# Generated by Makefile from apps/CA.pl.in
14*4757b351SPierre Pronchery
15*4757b351SPierre Proncheryuse strict;
16*4757b351SPierre Proncheryuse warnings;
17*4757b351SPierre Pronchery
18*4757b351SPierre Proncherymy $verbose = 1;
19*4757b351SPierre Proncherymy @OPENSSL_CMDS = ("req", "ca", "pkcs12", "x509", "verify");
20*4757b351SPierre Pronchery
21*4757b351SPierre Proncherymy $openssl = $ENV{'OPENSSL'} // "openssl";
22*4757b351SPierre Pronchery$ENV{'OPENSSL'} = $openssl;
23*4757b351SPierre Proncherymy @openssl = split_val($openssl);
24*4757b351SPierre Pronchery
25*4757b351SPierre Proncherymy $OPENSSL_CONFIG = $ENV{"OPENSSL_CONFIG"} // "";
26*4757b351SPierre Proncherymy @OPENSSL_CONFIG = split_val($OPENSSL_CONFIG);
27*4757b351SPierre Pronchery
28*4757b351SPierre Pronchery# Command invocations.
29*4757b351SPierre Proncherymy @REQ = (@openssl, "req", @OPENSSL_CONFIG);
30*4757b351SPierre Proncherymy @CA = (@openssl, "ca", @OPENSSL_CONFIG);
31*4757b351SPierre Proncherymy @VERIFY = (@openssl, "verify");
32*4757b351SPierre Proncherymy @X509 = (@openssl, "x509");
33*4757b351SPierre Proncherymy @PKCS12 = (@openssl, "pkcs12");
34*4757b351SPierre Pronchery
35*4757b351SPierre Pronchery# Default values for various configuration settings.
36*4757b351SPierre Proncherymy $CATOP = "./demoCA";
37*4757b351SPierre Proncherymy $CAKEY = "cakey.pem";
38*4757b351SPierre Proncherymy $CAREQ = "careq.pem";
39*4757b351SPierre Proncherymy $CACERT = "cacert.pem";
40*4757b351SPierre Proncherymy $CACRL = "crl.pem";
41*4757b351SPierre Proncherymy @DAYS = qw(-days 365);
42*4757b351SPierre Proncherymy @CADAYS = qw(-days 1095);	# 3 years
43*4757b351SPierre Proncherymy @EXTENSIONS = qw(-extensions v3_ca);
44*4757b351SPierre Proncherymy @POLICY = qw(-policy policy_anything);
45*4757b351SPierre Proncherymy $NEWKEY = "newkey.pem";
46*4757b351SPierre Proncherymy $NEWREQ = "newreq.pem";
47*4757b351SPierre Proncherymy $NEWCERT = "newcert.pem";
48*4757b351SPierre Proncherymy $NEWP12 = "newcert.p12";
49*4757b351SPierre Pronchery
50*4757b351SPierre Pronchery# Commandline parsing
51*4757b351SPierre Proncherymy %EXTRA;
52*4757b351SPierre Proncherymy $WHAT = shift @ARGV // "";
53*4757b351SPierre Pronchery@ARGV = parse_extra(@ARGV);
54*4757b351SPierre Proncherymy $RET = 0;
55*4757b351SPierre Pronchery
56*4757b351SPierre Proncherysub split_val {
57*4757b351SPierre Pronchery    return split_val_win32(@_) if ($^O eq 'MSWin32');
58*4757b351SPierre Pronchery    my ($val) = @_;
59*4757b351SPierre Pronchery    my (@ret, @frag);
60*4757b351SPierre Pronchery
61*4757b351SPierre Pronchery    # Skip leading whitespace
62*4757b351SPierre Pronchery    $val =~ m{\A[ \t]*}ogc;
63*4757b351SPierre Pronchery
64*4757b351SPierre Pronchery    # Unix shell-compatible split
65*4757b351SPierre Pronchery    #
66*4757b351SPierre Pronchery    # Handles backslash escapes outside quotes and
67*4757b351SPierre Pronchery    # in double-quoted strings.  Parameter and
68*4757b351SPierre Pronchery    # command-substitution is silently ignored.
69*4757b351SPierre Pronchery    # Bare newlines outside quotes and (trailing) backslashes are disallowed.
70*4757b351SPierre Pronchery
71*4757b351SPierre Pronchery    while (1) {
72*4757b351SPierre Pronchery        last if (pos($val) == length($val));
73*4757b351SPierre Pronchery
74*4757b351SPierre Pronchery        # The first char is never a SPACE or TAB.  Possible matches are:
75*4757b351SPierre Pronchery        # 1. Ordinary string fragment
76*4757b351SPierre Pronchery        # 2. Single-quoted string
77*4757b351SPierre Pronchery        # 3. Double-quoted string
78*4757b351SPierre Pronchery        # 4. Backslash escape
79*4757b351SPierre Pronchery        # 5. Bare backlash or newline (rejected)
80*4757b351SPierre Pronchery        #
81*4757b351SPierre Pronchery        if ($val =~ m{\G([^'" \t\n\\]+)}ogc) {
82*4757b351SPierre Pronchery            # Ordinary string
83*4757b351SPierre Pronchery            push @frag, $1;
84*4757b351SPierre Pronchery        } elsif ($val =~ m{\G'([^']*)'}ogc) {
85*4757b351SPierre Pronchery            # Single-quoted string
86*4757b351SPierre Pronchery            push @frag, $1;
87*4757b351SPierre Pronchery        } elsif ($val =~ m{\G"}ogc) {
88*4757b351SPierre Pronchery            # Double-quoted string
89*4757b351SPierre Pronchery            push @frag, "";
90*4757b351SPierre Pronchery            while (1) {
91*4757b351SPierre Pronchery                last if ($val =~ m{\G"}ogc);
92*4757b351SPierre Pronchery                if ($val =~ m{\G([^"\\]+)}ogcs) {
93*4757b351SPierre Pronchery                    # literals
94*4757b351SPierre Pronchery                    push @frag, $1;
95*4757b351SPierre Pronchery                } elsif ($val =~ m{\G.(["\`\$\\])}ogc) {
96*4757b351SPierre Pronchery                    # backslash-escaped special
97*4757b351SPierre Pronchery                    push @frag, $1;
98*4757b351SPierre Pronchery                } elsif ($val =~ m{\G.(.)}ogcs) {
99*4757b351SPierre Pronchery                    # backslashed non-special
100*4757b351SPierre Pronchery                    push @frag, "\\$1" unless $1 eq "\n";
101*4757b351SPierre Pronchery                } else {
102*4757b351SPierre Pronchery                    die sprintf("Malformed quoted string: %s\n", $val);
103*4757b351SPierre Pronchery                }
104*4757b351SPierre Pronchery            }
105*4757b351SPierre Pronchery        } elsif ($val =~ m{\G\\(.)}ogc) {
106*4757b351SPierre Pronchery            # Backslash is unconditional escape outside quoted strings
107*4757b351SPierre Pronchery            push @frag, $1 unless $1 eq "\n";
108*4757b351SPierre Pronchery        } else {
109*4757b351SPierre Pronchery            die sprintf("Bare backslash or newline in: '%s'\n", $val);
110*4757b351SPierre Pronchery        }
111*4757b351SPierre Pronchery        # Done if at SPACE, TAB or end, otherwise continue current fragment
112*4757b351SPierre Pronchery        #
113*4757b351SPierre Pronchery        next unless ($val =~ m{\G(?:[ \t]+|\z)}ogcs);
114*4757b351SPierre Pronchery        push @ret, join("", splice(@frag)) if (@frag > 0);
115*4757b351SPierre Pronchery    }
116*4757b351SPierre Pronchery    # Handle final fragment
117*4757b351SPierre Pronchery    push @ret, join("", splice(@frag)) if (@frag > 0);
118*4757b351SPierre Pronchery    return @ret;
119*4757b351SPierre Pronchery}
120*4757b351SPierre Pronchery
121*4757b351SPierre Proncherysub split_val_win32 {
122*4757b351SPierre Pronchery    my ($val) = @_;
123*4757b351SPierre Pronchery    my (@ret, @frag);
124*4757b351SPierre Pronchery
125*4757b351SPierre Pronchery    # Skip leading whitespace
126*4757b351SPierre Pronchery    $val =~ m{\A[ \t]*}ogc;
127*4757b351SPierre Pronchery
128*4757b351SPierre Pronchery    # Windows-compatible split
129*4757b351SPierre Pronchery    # See: "Parsing C++ command-line arguments" in:
130*4757b351SPierre Pronchery    # https://learn.microsoft.com/en-us/cpp/cpp/main-function-command-line-args?view=msvc-170
131*4757b351SPierre Pronchery    #
132*4757b351SPierre Pronchery    # Backslashes are special only when followed by a double-quote
133*4757b351SPierre Pronchery    # Pairs of double-quotes make a single double-quote.
134*4757b351SPierre Pronchery    # Closing double-quotes may be omitted.
135*4757b351SPierre Pronchery
136*4757b351SPierre Pronchery    while (1) {
137*4757b351SPierre Pronchery        last if (pos($val) == length($val));
138*4757b351SPierre Pronchery
139*4757b351SPierre Pronchery        # The first char is never a SPACE or TAB.
140*4757b351SPierre Pronchery        # 1. Ordinary string fragment
141*4757b351SPierre Pronchery        # 2. Double-quoted string
142*4757b351SPierre Pronchery        # 3. Backslashes preceding a double-quote
143*4757b351SPierre Pronchery        # 4. Literal backslashes
144*4757b351SPierre Pronchery        # 5. Bare newline (rejected)
145*4757b351SPierre Pronchery        #
146*4757b351SPierre Pronchery        if ($val =~ m{\G([^" \t\n\\]+)}ogc) {
147*4757b351SPierre Pronchery            # Ordinary string
148*4757b351SPierre Pronchery            push @frag, $1;
149*4757b351SPierre Pronchery        } elsif ($val =~ m{\G"}ogc) {
150*4757b351SPierre Pronchery            # Double-quoted string
151*4757b351SPierre Pronchery            push @frag, "";
152*4757b351SPierre Pronchery            while (1) {
153*4757b351SPierre Pronchery                if ($val =~ m{\G("+)}ogc) {
154*4757b351SPierre Pronchery                    # Two double-quotes make one literal double-quote
155*4757b351SPierre Pronchery                    my $l = length($1);
156*4757b351SPierre Pronchery                    push @frag, q{"} x int($l/2) if ($l > 1);
157*4757b351SPierre Pronchery                    next if ($l % 2 == 0);
158*4757b351SPierre Pronchery                    last;
159*4757b351SPierre Pronchery                }
160*4757b351SPierre Pronchery                if ($val =~ m{\G([^"\\]+)}ogc) {
161*4757b351SPierre Pronchery                    push @frag, $1;
162*4757b351SPierre Pronchery                } elsif ($val =~ m{\G((?>[\\]+))(?=")}ogc) {
163*4757b351SPierre Pronchery                    # Backslashes before a double-quote are escapes
164*4757b351SPierre Pronchery                    my $l = length($1);
165*4757b351SPierre Pronchery                    push @frag, q{\\} x int($l / 2);
166*4757b351SPierre Pronchery                    if ($l % 2 == 1) {
167*4757b351SPierre Pronchery                        ++pos($val);
168*4757b351SPierre Pronchery                        push @frag, q{"};
169*4757b351SPierre Pronchery                    }
170*4757b351SPierre Pronchery                } elsif ($val =~ m{\G((?:(?>[\\]+)[^"\\]+)+)}ogc) {
171*4757b351SPierre Pronchery                    # Backslashes not before a double-quote are not special
172*4757b351SPierre Pronchery                    push @frag, $1;
173*4757b351SPierre Pronchery                } else {
174*4757b351SPierre Pronchery                    # Tolerate missing closing double-quote
175*4757b351SPierre Pronchery                    last;
176*4757b351SPierre Pronchery                }
177*4757b351SPierre Pronchery            }
178*4757b351SPierre Pronchery        } elsif ($val =~ m{\G((?>[\\]+))(?=")}ogc) {
179*4757b351SPierre Pronchery            my $l = length($1);
180*4757b351SPierre Pronchery            push @frag, q{\\} x int($l / 2);
181*4757b351SPierre Pronchery            if ($l % 2 == 1) {
182*4757b351SPierre Pronchery                ++pos($val);
183*4757b351SPierre Pronchery                push @frag, q{"};
184*4757b351SPierre Pronchery            }
185*4757b351SPierre Pronchery        } elsif ($val =~ m{\G([\\]+)}ogc) {
186*4757b351SPierre Pronchery            # Backslashes not before a double-quote are not special
187*4757b351SPierre Pronchery            push @frag, $1;
188*4757b351SPierre Pronchery        } else {
189*4757b351SPierre Pronchery            die sprintf("Bare newline in: '%s'\n", $val);
190*4757b351SPierre Pronchery        }
191*4757b351SPierre Pronchery        # Done if at SPACE, TAB or end, otherwise continue current fragment
192*4757b351SPierre Pronchery        #
193*4757b351SPierre Pronchery        next unless ($val =~ m{\G(?:[ \t]+|\z)}ogcs);
194*4757b351SPierre Pronchery        push @ret, join("", splice(@frag)) if (@frag > 0);
195*4757b351SPierre Pronchery    }
196*4757b351SPierre Pronchery    # Handle final fragment
197*4757b351SPierre Pronchery    push @ret, join("", splice(@frag)) if (@frag);
198*4757b351SPierre Pronchery    return @ret;
199*4757b351SPierre Pronchery}
200*4757b351SPierre Pronchery
201*4757b351SPierre Pronchery# Split out "-extra-CMD value", and return new |@ARGV|. Fill in
202*4757b351SPierre Pronchery# |EXTRA{CMD}| with list of values.
203*4757b351SPierre Proncherysub parse_extra
204*4757b351SPierre Pronchery{
205*4757b351SPierre Pronchery    my @args;
206*4757b351SPierre Pronchery    foreach ( @OPENSSL_CMDS ) {
207*4757b351SPierre Pronchery        $EXTRA{$_} = [];
208*4757b351SPierre Pronchery    }
209*4757b351SPierre Pronchery    while (@_) {
210*4757b351SPierre Pronchery        my $arg = shift(@_);
211*4757b351SPierre Pronchery        if ( $arg !~ m{^-extra-(\w+)$} ) {
212*4757b351SPierre Pronchery            push @args, split_val($arg);
213*4757b351SPierre Pronchery            next;
214*4757b351SPierre Pronchery        }
215*4757b351SPierre Pronchery        $arg = $1;
216*4757b351SPierre Pronchery        die "Unknown \"-extra-${arg}\" option, exiting\n"
217*4757b351SPierre Pronchery            unless grep { $arg eq $_ } @OPENSSL_CMDS;
218*4757b351SPierre Pronchery        die "Missing \"-extra-${arg}\" option value, exiting\n"
219*4757b351SPierre Pronchery            unless (@_ > 0);
220*4757b351SPierre Pronchery        push @{$EXTRA{$arg}}, split_val(shift(@_));
221*4757b351SPierre Pronchery    }
222*4757b351SPierre Pronchery    return @args;
223*4757b351SPierre Pronchery}
224*4757b351SPierre Pronchery
225*4757b351SPierre Pronchery
226*4757b351SPierre Pronchery# See if reason for a CRL entry is valid; exit if not.
227*4757b351SPierre Proncherysub crl_reason_ok
228*4757b351SPierre Pronchery{
229*4757b351SPierre Pronchery    my $r = shift;
230*4757b351SPierre Pronchery
231*4757b351SPierre Pronchery    if ($r eq 'unspecified' || $r eq 'keyCompromise'
232*4757b351SPierre Pronchery        || $r eq 'CACompromise' || $r eq 'affiliationChanged'
233*4757b351SPierre Pronchery        || $r eq 'superseded' || $r eq 'cessationOfOperation'
234*4757b351SPierre Pronchery        || $r eq 'certificateHold' || $r eq 'removeFromCRL') {
235*4757b351SPierre Pronchery        return 1;
236*4757b351SPierre Pronchery    }
237*4757b351SPierre Pronchery    print STDERR "Invalid CRL reason; must be one of:\n";
238*4757b351SPierre Pronchery    print STDERR "    unspecified, keyCompromise, CACompromise,\n";
239*4757b351SPierre Pronchery    print STDERR "    affiliationChanged, superseded, cessationOfOperation\n";
240*4757b351SPierre Pronchery    print STDERR "    certificateHold, removeFromCRL";
241*4757b351SPierre Pronchery    exit 1;
242*4757b351SPierre Pronchery}
243*4757b351SPierre Pronchery
244*4757b351SPierre Pronchery# Copy a PEM-format file; return like exit status (zero means ok)
245*4757b351SPierre Proncherysub copy_pemfile
246*4757b351SPierre Pronchery{
247*4757b351SPierre Pronchery    my ($infile, $outfile, $bound) = @_;
248*4757b351SPierre Pronchery    my $found = 0;
249*4757b351SPierre Pronchery
250*4757b351SPierre Pronchery    open IN, $infile || die "Cannot open $infile, $!";
251*4757b351SPierre Pronchery    open OUT, ">$outfile" || die "Cannot write to $outfile, $!";
252*4757b351SPierre Pronchery    while (<IN>) {
253*4757b351SPierre Pronchery        $found = 1 if /^-----BEGIN.*$bound/;
254*4757b351SPierre Pronchery        print OUT $_ if $found;
255*4757b351SPierre Pronchery        $found = 2, last if /^-----END.*$bound/;
256*4757b351SPierre Pronchery    }
257*4757b351SPierre Pronchery    close IN;
258*4757b351SPierre Pronchery    close OUT;
259*4757b351SPierre Pronchery    return $found == 2 ? 0 : 1;
260*4757b351SPierre Pronchery}
261*4757b351SPierre Pronchery
262*4757b351SPierre Pronchery# Wrapper around system; useful for debugging.  Returns just the exit status
263*4757b351SPierre Proncherysub run
264*4757b351SPierre Pronchery{
265*4757b351SPierre Pronchery    my ($cmd, @args) = @_;
266*4757b351SPierre Pronchery    print "====\n$cmd @args\n" if $verbose;
267*4757b351SPierre Pronchery    my $status = system {$cmd} $cmd, @args;
268*4757b351SPierre Pronchery    print "==> $status\n====\n" if $verbose;
269*4757b351SPierre Pronchery    return $status >> 8;
270*4757b351SPierre Pronchery}
271*4757b351SPierre Pronchery
272*4757b351SPierre Pronchery
273*4757b351SPierre Proncheryif ( $WHAT =~ /^(-\?|-h|-help)$/ ) {
274*4757b351SPierre Pronchery    print STDERR <<EOF;
275*4757b351SPierre ProncheryUsage:
276*4757b351SPierre Pronchery    CA.pl -newcert | -newreq | -newreq-nodes | -xsign | -sign | -signCA | -signcert | -crl | -newca [-extra-cmd parameter]
277*4757b351SPierre Pronchery    CA.pl -pkcs12 [certname]
278*4757b351SPierre Pronchery    CA.pl -verify certfile ...
279*4757b351SPierre Pronchery    CA.pl -revoke certfile [reason]
280*4757b351SPierre ProncheryEOF
281*4757b351SPierre Pronchery    exit 0;
282*4757b351SPierre Pronchery}
283*4757b351SPierre Pronchery
284*4757b351SPierre Proncheryif ($WHAT eq '-newcert' ) {
285*4757b351SPierre Pronchery    # create a certificate
286*4757b351SPierre Pronchery    $RET = run(@REQ, qw(-new -x509 -keyout), $NEWKEY, "-out", $NEWCERT, @DAYS, @{$EXTRA{req}});
287*4757b351SPierre Pronchery    print "Cert is in $NEWCERT, private key is in $NEWKEY\n" if $RET == 0;
288*4757b351SPierre Pronchery} elsif ($WHAT eq '-precert' ) {
289*4757b351SPierre Pronchery    # create a pre-certificate
290*4757b351SPierre Pronchery    $RET = run(@REQ, qw(-x509 -precert -keyout), $NEWKEY, "-out", $NEWCERT, @DAYS, @{$EXTRA{req}});
291*4757b351SPierre Pronchery    print "Pre-cert is in $NEWCERT, private key is in $NEWKEY\n" if $RET == 0;
292*4757b351SPierre Pronchery} elsif ($WHAT =~ /^\-newreq(\-nodes)?$/ ) {
293*4757b351SPierre Pronchery    # create a certificate request
294*4757b351SPierre Pronchery    $RET = run(@REQ, "-new", (defined $1 ? ($1,) : ()), "-keyout", $NEWKEY, "-out", $NEWREQ, @{$EXTRA{req}});
295*4757b351SPierre Pronchery    print "Request is in $NEWREQ, private key is in $NEWKEY\n" if $RET == 0;
296*4757b351SPierre Pronchery} elsif ($WHAT eq '-newca' ) {
297*4757b351SPierre Pronchery    # create the directory hierarchy
298*4757b351SPierre Pronchery    my @dirs = ( "${CATOP}", "${CATOP}/certs", "${CATOP}/crl",
299*4757b351SPierre Pronchery                "${CATOP}/newcerts", "${CATOP}/private" );
300*4757b351SPierre Pronchery    die "${CATOP}/index.txt exists.\nRemove old sub-tree to proceed,"
301*4757b351SPierre Pronchery        if -f "${CATOP}/index.txt";
302*4757b351SPierre Pronchery    die "${CATOP}/serial exists.\nRemove old sub-tree to proceed,"
303*4757b351SPierre Pronchery        if -f "${CATOP}/serial";
304*4757b351SPierre Pronchery    foreach my $d ( @dirs ) {
305*4757b351SPierre Pronchery        if ( -d $d ) {
306*4757b351SPierre Pronchery            warn "Directory $d exists" if -d $d;
307*4757b351SPierre Pronchery        } else {
308*4757b351SPierre Pronchery            mkdir $d or die "Can't mkdir $d, $!";
309*4757b351SPierre Pronchery        }
310*4757b351SPierre Pronchery    }
311*4757b351SPierre Pronchery
312*4757b351SPierre Pronchery    open OUT, ">${CATOP}/index.txt";
313*4757b351SPierre Pronchery    close OUT;
314*4757b351SPierre Pronchery    open OUT, ">${CATOP}/crlnumber";
315*4757b351SPierre Pronchery    print OUT "01\n";
316*4757b351SPierre Pronchery    close OUT;
317*4757b351SPierre Pronchery    # ask user for existing CA certificate
318*4757b351SPierre Pronchery    print "CA certificate filename (or enter to create)\n";
319*4757b351SPierre Pronchery    my $FILE;
320*4757b351SPierre Pronchery    $FILE = "" unless defined($FILE = <STDIN>);
321*4757b351SPierre Pronchery    $FILE =~ s{\R$}{};
322*4757b351SPierre Pronchery    if ($FILE ne "") {
323*4757b351SPierre Pronchery        copy_pemfile($FILE,"${CATOP}/private/$CAKEY", "PRIVATE");
324*4757b351SPierre Pronchery        copy_pemfile($FILE,"${CATOP}/$CACERT", "CERTIFICATE");
325*4757b351SPierre Pronchery    } else {
326*4757b351SPierre Pronchery        print "Making CA certificate ...\n";
327*4757b351SPierre Pronchery        $RET = run(@REQ, qw(-new -keyout), "${CATOP}/private/$CAKEY",
328*4757b351SPierre Pronchery                   "-out", "${CATOP}/$CAREQ", @{$EXTRA{req}});
329*4757b351SPierre Pronchery        $RET = run(@CA, qw(-create_serial -out), "${CATOP}/$CACERT", @CADAYS,
330*4757b351SPierre Pronchery                   qw(-batch -keyfile), "${CATOP}/private/$CAKEY", "-selfsign",
331*4757b351SPierre Pronchery                   @EXTENSIONS, "-infiles", "${CATOP}/$CAREQ", @{$EXTRA{ca}})
332*4757b351SPierre Pronchery            if $RET == 0;
333*4757b351SPierre Pronchery        print "CA certificate is in ${CATOP}/$CACERT\n" if $RET == 0;
334*4757b351SPierre Pronchery    }
335*4757b351SPierre Pronchery} elsif ($WHAT eq '-pkcs12' ) {
336*4757b351SPierre Pronchery    my $cname = $ARGV[0];
337*4757b351SPierre Pronchery    $cname = "My Certificate" unless defined $cname;
338*4757b351SPierre Pronchery    $RET = run(@PKCS12, "-in", $NEWCERT, "-inkey", $NEWKEY,
339*4757b351SPierre Pronchery               "-certfile", "${CATOP}/$CACERT", "-out", $NEWP12,
340*4757b351SPierre Pronchery               qw(-export -name), $cname, @{$EXTRA{pkcs12}});
341*4757b351SPierre Pronchery    print "PKCS#12 file is in $NEWP12\n" if $RET == 0;
342*4757b351SPierre Pronchery} elsif ($WHAT eq '-xsign' ) {
343*4757b351SPierre Pronchery    $RET = run(@CA, @POLICY, "-infiles", $NEWREQ, @{$EXTRA{ca}});
344*4757b351SPierre Pronchery} elsif ($WHAT eq '-sign' ) {
345*4757b351SPierre Pronchery    $RET = run(@CA, @POLICY, "-out", $NEWCERT,
346*4757b351SPierre Pronchery               "-infiles", $NEWREQ, @{$EXTRA{ca}});
347*4757b351SPierre Pronchery    print "Signed certificate is in $NEWCERT\n" if $RET == 0;
348*4757b351SPierre Pronchery} elsif ($WHAT eq '-signCA' ) {
349*4757b351SPierre Pronchery    $RET = run(@CA, @POLICY, "-out", $NEWCERT, @EXTENSIONS,
350*4757b351SPierre Pronchery               "-infiles", $NEWREQ, @{$EXTRA{ca}});
351*4757b351SPierre Pronchery    print "Signed CA certificate is in $NEWCERT\n" if $RET == 0;
352*4757b351SPierre Pronchery} elsif ($WHAT eq '-signcert' ) {
353*4757b351SPierre Pronchery    $RET = run(@X509, qw(-x509toreq -in), $NEWREQ, "-signkey", $NEWREQ,
354*4757b351SPierre Pronchery               qw(-out tmp.pem), @{$EXTRA{x509}});
355*4757b351SPierre Pronchery    $RET = run(@CA, @POLICY, "-out", $NEWCERT,
356*4757b351SPierre Pronchery               qw(-infiles tmp.pem), @{$EXTRA{ca}}) if $RET == 0;
357*4757b351SPierre Pronchery    print "Signed certificate is in $NEWCERT\n" if $RET == 0;
358*4757b351SPierre Pronchery} elsif ($WHAT eq '-verify' ) {
359*4757b351SPierre Pronchery    my @files = @ARGV ? @ARGV : ( $NEWCERT );
360*4757b351SPierre Pronchery    foreach my $file (@files) {
361*4757b351SPierre Pronchery        my $status = run(@VERIFY, "-CAfile", "${CATOP}/$CACERT", $file, @{$EXTRA{verify}});
362*4757b351SPierre Pronchery        $RET = $status if $status != 0;
363*4757b351SPierre Pronchery    }
364*4757b351SPierre Pronchery} elsif ($WHAT eq '-crl' ) {
365*4757b351SPierre Pronchery    $RET = run(@CA, qw(-gencrl -out), "${CATOP}/crl/$CACRL", @{$EXTRA{ca}});
366*4757b351SPierre Pronchery    print "Generated CRL is in ${CATOP}/crl/$CACRL\n" if $RET == 0;
367*4757b351SPierre Pronchery} elsif ($WHAT eq '-revoke' ) {
368*4757b351SPierre Pronchery    my $cname = $ARGV[0];
369*4757b351SPierre Pronchery    if (!defined $cname) {
370*4757b351SPierre Pronchery        print "Certificate filename is required; reason optional.\n";
371*4757b351SPierre Pronchery        exit 1;
372*4757b351SPierre Pronchery    }
373*4757b351SPierre Pronchery    my @reason;
374*4757b351SPierre Pronchery    @reason = ("-crl_reason", $ARGV[1])
375*4757b351SPierre Pronchery        if defined $ARGV[1] && crl_reason_ok($ARGV[1]);
376*4757b351SPierre Pronchery    $RET = run(@CA, "-revoke", $cname, @reason, @{$EXTRA{ca}});
377*4757b351SPierre Pronchery} else {
378*4757b351SPierre Pronchery    print STDERR "Unknown arg \"$WHAT\"\n";
379*4757b351SPierre Pronchery    print STDERR "Use -help for help.\n";
380*4757b351SPierre Pronchery    exit 1;
381*4757b351SPierre Pronchery}
382*4757b351SPierre Pronchery
383*4757b351SPierre Proncheryexit $RET;
384