11# !/usr/bin/env perl
2- # Copyright 2000-2021 The OpenSSL Project Authors. All Rights Reserved.
2+ # Copyright 2000-2025 The OpenSSL Project Authors. All Rights Reserved.
33#
44# Licensed under the Apache License 2.0 (the "License"). You may not use
55# this file except in compliance with the License. You can obtain a copy
1010# Wrapper around the ca to make it easier to use
1111#
1212# WARNING: do not edit!
13- # Generated by Makefile from ../openssl-3.2.4 /apps/CA.pl.in
13+ # Generated by Makefile from ../openssl-3.5.3 /apps/CA.pl.in
1414
1515use strict;
1616use warnings;
2020
2121my $openssl = $ENV {' OPENSSL' } // " openssl" ;
2222$ENV {' OPENSSL' } = $openssl ;
23+ my @openssl = split_val($openssl );
24+
2325my $OPENSSL_CONFIG = $ENV {" OPENSSL_CONFIG" } // " " ;
26+ my @OPENSSL_CONFIG = split_val($OPENSSL_CONFIG );
2427
2528# Command invocations.
26- my $ REQ = " $ openssl req $ OPENSSL_CONFIG" ;
27- my $ CA = " $ openssl ca $ OPENSSL_CONFIG" ;
28- my $ VERIFY = " $ openssl verify" ;
29- my $ X509 = " $ openssl x509" ;
30- my $ PKCS12 = " $ openssl pkcs12" ;
29+ my @ REQ = ( @ openssl, " req" , @ OPENSSL_CONFIG) ;
30+ my @ CA = ( @ openssl, " ca " , @ OPENSSL_CONFIG) ;
31+ my @ VERIFY = ( @ openssl, " verify" ) ;
32+ my @ X509 = ( @ openssl, " x509" ) ;
33+ my @ PKCS12 = ( @ openssl, " pkcs12" ) ;
3134
3235# Default values for various configuration settings.
3336my $CATOP = " ./demoCA" ;
3437my $CAKEY = " cakey.pem" ;
3538my $CAREQ = " careq.pem" ;
3639my $CACERT = " cacert.pem" ;
3740my $CACRL = " crl.pem" ;
38- my $ DAYS = " -days 365" ;
39- my $ CADAYS = " -days 1095" ; # 3 years
40- my $ EXTENSIONS = " -extensions v3_ca" ;
41- my $ POLICY = " -policy policy_anything" ;
41+ my @ DAYS = qw( -days 365) ;
42+ my @ CADAYS = qw( -days 1095) ; # 3 years
43+ my @ EXTENSIONS = qw( -extensions v3_ca) ;
44+ my @ POLICY = qw( -policy policy_anything) ;
4245my $NEWKEY = " newkey.pem" ;
4346my $NEWREQ = " newreq.pem" ;
4447my $NEWCERT = " newcert.pem" ;
4548my $NEWP12 = " newcert.p12" ;
4649
4750# Commandline parsing
4851my %EXTRA ;
49- my $WHAT = shift @ARGV || " " ;
52+ my $WHAT = shift @ARGV // " " ;
5053@ARGV = parse_extra(@ARGV );
5154my $RET = 0;
5255
56+ sub split_val {
57+ return split_val_win32(@_ ) if ($^O eq ' MSWin32' );
58+ my ($val ) = @_ ;
59+ my (@ret , @frag );
60+
61+ # Skip leading whitespace
62+ $val =~ m {\A [ \t ]*} ogc ;
63+
64+ # Unix shell-compatible split
65+ #
66+ # Handles backslash escapes outside quotes and
67+ # in double-quoted strings. Parameter and
68+ # command-substitution is silently ignored.
69+ # Bare newlines outside quotes and (trailing) backslashes are disallowed.
70+
71+ while (1) {
72+ last if (pos ($val ) == length ($val ));
73+
74+ # The first char is never a SPACE or TAB. Possible matches are:
75+ # 1. Ordinary string fragment
76+ # 2. Single-quoted string
77+ # 3. Double-quoted string
78+ # 4. Backslash escape
79+ # 5. Bare backlash or newline (rejected)
80+ #
81+ if ($val =~ m {\G ([^'" \t\n\\ ]+)} ogc ) {
82+ # Ordinary string
83+ push @frag , $1 ;
84+ } elsif ($val =~ m {\G '([^']*)'} ogc ) {
85+ # Single-quoted string
86+ push @frag , $1 ;
87+ } elsif ($val =~ m {\G "} ogc ) {
88+ # Double-quoted string
89+ push @frag , " " ;
90+ while (1) {
91+ last if ($val =~ m {\G "} ogc );
92+ if ($val =~ m {\G ([^"\\ ]+)} ogcs ) {
93+ # literals
94+ push @frag , $1 ;
95+ } elsif ($val =~ m {\G .(["\`\$\\ ])} ogc ) {
96+ # backslash-escaped special
97+ push @frag , $1 ;
98+ } elsif ($val =~ m {\G .(.)} ogcs ) {
99+ # backslashed non-special
100+ push @frag , " \\ $1 " unless $1 eq " \n " ;
101+ } else {
102+ die sprintf (" Malformed quoted string: %s \n " , $val );
103+ }
104+ }
105+ } elsif ($val =~ m {\G\\ (.)} ogc ) {
106+ # Backslash is unconditional escape outside quoted strings
107+ push @frag , $1 unless $1 eq " \n " ;
108+ } else {
109+ die sprintf (" Bare backslash or newline in: '%s '\n " , $val );
110+ }
111+ # Done if at SPACE, TAB or end, otherwise continue current fragment
112+ #
113+ next unless ($val =~ m {\G (?:[ \t ]+|\z )} ogcs );
114+ push @ret , join (" " , splice (@frag )) if (@frag > 0);
115+ }
116+ # Handle final fragment
117+ push @ret , join (" " , splice (@frag )) if (@frag > 0);
118+ return @ret ;
119+ }
120+
121+ sub split_val_win32 {
122+ my ($val ) = @_ ;
123+ my (@ret , @frag );
124+
125+ # Skip leading whitespace
126+ $val =~ m {\A [ \t ]*} ogc ;
127+
128+ # Windows-compatible split
129+ # See: "Parsing C++ command-line arguments" in:
130+ # https://learn.microsoft.com/en-us/cpp/cpp/main-function-command-line-args?view=msvc-170
131+ #
132+ # Backslashes are special only when followed by a double-quote
133+ # Pairs of double-quotes make a single double-quote.
134+ # Closing double-quotes may be omitted.
135+
136+ while (1) {
137+ last if (pos ($val ) == length ($val ));
138+
139+ # The first char is never a SPACE or TAB.
140+ # 1. Ordinary string fragment
141+ # 2. Double-quoted string
142+ # 3. Backslashes preceding a double-quote
143+ # 4. Literal backslashes
144+ # 5. Bare newline (rejected)
145+ #
146+ if ($val =~ m {\G ([^" \t\n\\ ]+)} ogc ) {
147+ # Ordinary string
148+ push @frag , $1 ;
149+ } elsif ($val =~ m {\G "} ogc ) {
150+ # Double-quoted string
151+ push @frag , " " ;
152+ while (1) {
153+ if ($val =~ m {\G ("+)} ogc ) {
154+ # Two double-quotes make one literal double-quote
155+ my $l = length ($1 );
156+ push @frag , q{ "} x int ($l /2) if ($l > 1);
157+ next if ($l % 2 == 0);
158+ last ;
159+ }
160+ if ($val =~ m {\G ([^"\\ ]+)} ogc ) {
161+ push @frag , $1 ;
162+ } elsif ($val =~ m {\G ((?>[\\ ]+))(?=")} ogc ) {
163+ # Backslashes before a double-quote are escapes
164+ my $l = length ($1 );
165+ push @frag , q{ \\} x int ($l / 2);
166+ if ($l % 2 == 1) {
167+ ++pos ($val );
168+ push @frag , q{ "} ;
169+ }
170+ } elsif ($val =~ m {\G ((?:(?>[\\ ]+)[^"\\ ]+)+)} ogc ) {
171+ # Backslashes not before a double-quote are not special
172+ push @frag , $1 ;
173+ } else {
174+ # Tolerate missing closing double-quote
175+ last ;
176+ }
177+ }
178+ } elsif ($val =~ m {\G ((?>[\\ ]+))(?=")} ogc ) {
179+ my $l = length ($1 );
180+ push @frag , q{ \\} x int ($l / 2);
181+ if ($l % 2 == 1) {
182+ ++pos ($val );
183+ push @frag , q{ "} ;
184+ }
185+ } elsif ($val =~ m {\G ([\\ ]+)} ogc ) {
186+ # Backslashes not before a double-quote are not special
187+ push @frag , $1 ;
188+ } else {
189+ die sprintf (" Bare newline in: '%s '\n " , $val );
190+ }
191+ # Done if at SPACE, TAB or end, otherwise continue current fragment
192+ #
193+ next unless ($val =~ m {\G (?:[ \t ]+|\z )} ogcs );
194+ push @ret , join (" " , splice (@frag )) if (@frag > 0);
195+ }
196+ # Handle final fragment
197+ push @ret , join (" " , splice (@frag )) if (@frag );
198+ return @ret ;
199+ }
200+
53201# Split out "-extra-CMD value", and return new |@ARGV|. Fill in
54202# |EXTRA{CMD}| with list of values.
55203sub parse_extra
56204{
205+ my @args ;
57206 foreach ( @OPENSSL_CMDS ) {
58- $EXTRA {$_ } = ' ' ;
207+ $EXTRA {$_ } = [] ;
59208 }
60-
61- my @result ;
62- while ( scalar (@_ ) > 0 ) {
63- my $arg = shift ;
64- if ( $arg !~ m / -extra-([a-z0-9]+)/ ) {
65- push @result , $arg ;
209+ while (@_ ) {
210+ my $arg = shift (@_ );
211+ if ( $arg !~ m { ^-extra-(\w +)$} ) {
212+ push @args , split_val($arg );
66213 next ;
67214 }
68- $arg =~ s / -extra-// ;
69- die (" Unknown \" -${arg} -extra\" option, exiting" )
70- unless scalar grep { $arg eq $_ } @OPENSSL_CMDS ;
71- $EXTRA {$arg } .= " " . shift ;
215+ $arg = $1 ;
216+ die " Unknown \" -extra-${arg} \" option, exiting\n "
217+ unless grep { $arg eq $_ } @OPENSSL_CMDS ;
218+ die " Missing \" -extra-${arg} \" option value, exiting\n "
219+ unless (@_ > 0);
220+ push @{$EXTRA {$arg }}, split_val(shift (@_ ));
72221 }
73- return @result ;
222+ return @args ;
74223}
75224
76225
@@ -113,9 +262,9 @@ sub copy_pemfile
113262# Wrapper around system; useful for debugging. Returns just the exit status
114263sub run
115264{
116- my $cmd = shift ;
117- print " ====\n $cmd \n " if $verbose ;
118- my $status = system ( $cmd ) ;
265+ my ( $cmd , @args ) = @_ ;
266+ print " ====\n $cmd @args \n " if $verbose ;
267+ my $status = system { $cmd } $cmd , @args ;
119268 print " ==> $status \n ====\n " if $verbose ;
120269 return $status >> 8;
121270}
@@ -134,17 +283,15 @@ sub run
134283
135284if ($WHAT eq ' -newcert' ) {
136285 # create a certificate
137- $RET = run(" $REQ -new -x509 -keyout $NEWKEY -out $NEWCERT $DAYS "
138- . " $EXTRA {req}" );
286+ $RET = run(@REQ , qw( -new -x509 -keyout) , $NEWKEY , " -out" , $NEWCERT , @DAYS , @{$EXTRA {req }});
139287 print " Cert is in $NEWCERT , private key is in $NEWKEY \n " if $RET == 0;
140288} elsif ($WHAT eq ' -precert' ) {
141289 # create a pre-certificate
142- $RET = run(" $REQ -x509 -precert -keyout $NEWKEY -out $NEWCERT $DAYS "
143- . " $EXTRA {req}" );
290+ $RET = run(@REQ , qw( -x509 -precert -keyout) , $NEWKEY , " -out" , $NEWCERT , @DAYS , @{$EXTRA {req }});
144291 print " Pre-cert is in $NEWCERT , private key is in $NEWKEY \n " if $RET == 0;
145292} elsif ($WHAT =~ / ^\- newreq(\- nodes)?$ / ) {
146293 # create a certificate request
147- $RET = run(" $ REQ -new $1 -keyout $NEWKEY -out $NEWREQ $DAYS $ EXTRA {req}" );
294+ $RET = run(@ REQ, " -new" , ( defined $1 ? ( $1 ,) : ()), " -keyout" , $NEWKEY , " -out" , $NEWREQ , @{ $ EXTRA {req }} );
148295 print " Request is in $NEWREQ , private key is in $NEWKEY \n " if $RET == 0;
149296} elsif ($WHAT eq ' -newca' ) {
150297 # create the directory hierarchy
@@ -177,59 +324,56 @@ sub run
177324 copy_pemfile($FILE ," ${CATOP} /$CACERT " , " CERTIFICATE" );
178325 } else {
179326 print " Making CA certificate ...\n " ;
180- $RET = run(" $REQ -new -keyout ${CATOP} /private/$CAKEY "
181- . " -out ${CATOP} /$CAREQ $EXTRA {req}" );
182- $RET = run(" $CA -create_serial"
183- . " -out ${CATOP} /$CACERT $CADAYS -batch"
184- . " -keyfile ${CATOP} /private/$CAKEY -selfsign"
185- . " $EXTENSIONS "
186- . " -infiles ${CATOP} /$CAREQ $EXTRA {ca}" ) if $RET == 0;
327+ $RET = run(@REQ , qw( -new -keyout) , " ${CATOP} /private/$CAKEY " ,
328+ " -out" , " ${CATOP} /$CAREQ " , @{$EXTRA {req }});
329+ $RET = run(@CA , qw( -create_serial -out) , " ${CATOP} /$CACERT " , @CADAYS ,
330+ qw( -batch -keyfile) , " ${CATOP} /private/$CAKEY " , " -selfsign" ,
331+ @EXTENSIONS , " -infiles" , " ${CATOP} /$CAREQ " , @{$EXTRA {ca }})
332+ if $RET == 0;
187333 print " CA certificate is in ${CATOP} /$CACERT \n " if $RET == 0;
188334 }
189335} elsif ($WHAT eq ' -pkcs12' ) {
190336 my $cname = $ARGV [0];
191337 $cname = " My Certificate" unless defined $cname ;
192- $RET = run(" $ PKCS12 -in $NEWCERT -inkey $NEWKEY "
193- . " -certfile ${CATOP} /$CACERT -out $NEWP12 "
194- . " -export -name \" $cname \" $EXTRA {pkcs12}" );
195- print " PKCS #12 file is in $NEWP12 \n " if $RET == 0;
338+ $RET = run(@ PKCS12, " -in" , $NEWCERT , " -inkey" , $NEWKEY ,
339+ " -certfile" , " ${CATOP} /$CACERT " , " -out" , $NEWP12 ,
340+ qw( -export -name) , $cname , @{ $EXTRA {pkcs12 }} );
341+ print " PKCS#12 file is in $NEWP12 \n " if $RET == 0;
196342} elsif ($WHAT eq ' -xsign' ) {
197- $RET = run(" $CA $ POLICY -infiles $NEWREQ $EXTRA {ca}" );
343+ $RET = run(@CA , @ POLICY, " -infiles" , $NEWREQ , @{ $EXTRA {ca }} );
198344} elsif ($WHAT eq ' -sign' ) {
199- $RET = run(" $CA $ POLICY -out $NEWCERT "
200- . " -infiles $NEWREQ $EXTRA {ca}" );
345+ $RET = run(@CA , @ POLICY, " -out" , $NEWCERT ,
346+ " -infiles" , $NEWREQ , @{ $EXTRA {ca }} );
201347 print " Signed certificate is in $NEWCERT \n " if $RET == 0;
202348} elsif ($WHAT eq ' -signCA' ) {
203- $RET = run(" $CA $ POLICY -out $NEWCERT "
204- . " $EXTENSIONS -infiles $NEWREQ $EXTRA {ca}" );
349+ $RET = run(@CA , @ POLICY, " -out" , $NEWCERT , @EXTENSIONS ,
350+ " -infiles" , $NEWREQ , @{ $EXTRA {ca }} );
205351 print " Signed CA certificate is in $NEWCERT \n " if $RET == 0;
206352} elsif ($WHAT eq ' -signcert' ) {
207- $RET = run(" $ X509 -x509toreq -in $NEWREQ -signkey $NEWREQ "
208- . " -out tmp.pem $EXTRA {x509}" );
209- $RET = run(" $CA $ POLICY -out $NEWCERT "
210- . " -infiles tmp.pem $EXTRA {ca}" ) if $RET == 0;
353+ $RET = run(@ X509, qw( -x509toreq -in) , $NEWREQ , " -signkey" , $NEWREQ ,
354+ qw( -out tmp.pem) , @{ $EXTRA {x509 }} );
355+ $RET = run(@CA , @ POLICY, " -out" , $NEWCERT ,
356+ qw( -infiles tmp.pem) , @{ $EXTRA {ca }} ) if $RET == 0;
211357 print " Signed certificate is in $NEWCERT \n " if $RET == 0;
212358} elsif ($WHAT eq ' -verify' ) {
213359 my @files = @ARGV ? @ARGV : ( $NEWCERT );
214360 foreach my $file (@files ) {
215- # -CAfile quoted for VMS, since the C RTL downcases all unquoted
216- # arguments to C programs
217- my $status = run(" $VERIFY \" -CAfile\" ${CATOP} /$CACERT $file $EXTRA {verify}" );
361+ my $status = run(@VERIFY , " -CAfile" , " ${CATOP} /$CACERT " , $file , @{$EXTRA {verify }});
218362 $RET = $status if $status != 0;
219363 }
220364} elsif ($WHAT eq ' -crl' ) {
221- $RET = run(" $CA -gencrl -out ${CATOP} /crl/$CACRL $EXTRA {ca}" );
365+ $RET = run(@CA , qw( -gencrl -out) , " ${CATOP} /crl/$CACRL " , @{ $EXTRA {ca }} );
222366 print " Generated CRL is in ${CATOP} /crl/$CACRL \n " if $RET == 0;
223367} elsif ($WHAT eq ' -revoke' ) {
224368 my $cname = $ARGV [0];
225369 if (!defined $cname ) {
226370 print " Certificate filename is required; reason optional.\n " ;
227371 exit 1;
228372 }
229- my $ reason = $ARGV [1] ;
230- $ reason = " -crl_reason $reason "
231- if defined $reason && crl_reason_ok($reason );
232- $RET = run(" $CA -revoke \" $cname \" " . $ reason . $EXTRA {ca });
373+ my @ reason ;
374+ @ reason = ( " -crl_reason" , $ARGV [1])
375+ if defined $ARGV [1] && crl_reason_ok($ARGV [1] );
376+ $RET = run(@CA , " -revoke" , $cname , @ reason, @{ $EXTRA {ca } });
233377} else {
234378 print STDERR " Unknown arg \" $WHAT \"\n " ;
235379 print STDERR " Use -help for help.\n " ;
0 commit comments