"Sergey V. Udaltsov" schrieb: > > > can you be more specific, e.g. what password are you talking about, > like "abc#def" > > which function did you call (e.g. key generation on the CA)? > No. I mean the certificate generation. Key generation is OK (even with > this "bad" password), either no problems with request. But the > certificate generation itself has got problems here. Changing the > password to "abcdef" everywhere does the job.
I fixed OpenSSL.pm. All passphrases are now handled in a similar manner. So perhaps this fix your problem. Michael -- ------------------------------------------------------------------- Michael Bell Email (private): [EMAIL PROTECTED] Rechenzentrum - Datacenter Email: [EMAIL PROTECTED] Humboldt-University of Berlin Tel.: +49 (0)30-2093 2482 Unter den Linden 6 Fax: +49 (0)30-2093 2959 10099 Berlin Germany [OpenCA Core Developer] http://www.openca.org
## OpenCA::OpenSSL ## ## Copyright (C) 1998-2001 Massimiliano Pala ([EMAIL PROTECTED]) ## All rights reserved. ## ## This library is free for commercial and non-commercial use as long as ## the following conditions are aheared to. The following conditions ## apply to all code found in this distribution, be it the RC4, RSA, ## lhash, DES, etc., code; not just the SSL code. The documentation ## included with this distribution is covered by the same copyright terms ## ## Copyright remains Massimiliano Pala's, and as such any Copyright notices ## in the code are not to be removed. ## If this package is used in a product, Massimiliano Pala should be given ## attribution as the author of the parts of the library used. ## This can be in the form of a textual message at program startup or ## in documentation (online or textual) provided with the package. ## ## Redistribution and use in source and binary forms, with or without ## modification, are permitted provided that the following conditions ## are met: ## 1. Redistributions of source code must retain the copyright ## notice, this list of conditions and the following disclaimer. ## 2. Redistributions in binary form must reproduce the above copyright ## notice, this list of conditions and the following disclaimer in the ## documentation and/or other materials provided with the distribution. ## 3. All advertising materials mentioning features or use of this software ## must display the following acknowledgement: ## "This product includes OpenCA software written by Massimiliano Pala ## ([EMAIL PROTECTED]) and the OpenCA Group (www.openca.org)" ## 4. If you include any Windows specific code (or a derivative thereof) from ## some directory (application code) you must include an acknowledgement: ## "This product includes OpenCA software (www.openca.org)" ## ## THIS SOFTWARE IS PROVIDED BY OPENCA DEVELOPERS ``AS IS'' AND ## ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ## ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE ## FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ## DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS ## OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ## HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ## LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ## OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF ## SUCH DAMAGE. ## ## The licence and distribution terms for any publically available version or ## derivative of this code cannot be changed. i.e. this code cannot simply be ## copied and put under another distribution licence ## [including the GNU Public Licence.] ## ## Contributions by: ## Martin Leung <[EMAIL PROTECTED]> ## Uwe Gansert <[EMAIL PROTECTED]> use strict; package OpenCA::OpenSSL; $OpenCA::OpenSSL::VERSION = '0.8.8'; ## Global Variables Go HERE my %params = ( shell => undef, cnf => undef, tmpDir => undef, baseDir => undef, verify => undef, sign => undef, errno => undef, errval => undef ); ## Create an instance of the Class sub new { my $that = shift; my $class = ref($that) || $that; my $self = { %params, }; bless $self, $class; my $keys = { @_ }; $self->setParams( @_ ); if( not $self->{binDir} ) { $self->{binDir} = "/usr/bin"; }; if( not $self->{shell} ) { $self->{shell} = "$self->{binDir}/openssl"; }; if( not $self->{verify} ) { $self->{verify} = "$self->{binDir}/verify"; }; if( not $self->{sign} ) { $self->{sign} = "$self->{binDir}/sign"; }; if( not $self->{tmpDir} ) { $self->{tmpDir} = '/tmp'; }; if( not -e "$self->{shell}" ) { return; }; $self->{errno} = 0; $self->{errval} = ""; return $self; } sub setParams { my $self = shift; my $params = { @_ }; my $key; foreach $key ( keys %{$params} ) { $self->{cnf} = $params->{$key} if ( $key =~ /CONFIG/ ); $self->{shell} = $params->{$key} if ( $key =~ /SHELL/ ); $self->{tmpDir} = $params->{$key} if ( $key =~ /TMPDIR/ ); $self->{binDir} = $params->{$key} if ( $key =~ /BINDIR/ ); $self->{verify} = $params->{$key} if ( $key =~ /VERIFY/ ); $self->{sign} = $params->{$key} if ( $key =~ /SIGN/ ); open STDERR, $params->{$key} if ( $key =~ /STDERR/ ); } return 1; } sub errno { my $self = shift; return $self->{errno}; } sub errval { my $self = shift; return $self->{errval}; } sub genKey { ## Generate a new key, arguments accepted are, in order ## ( BITS=>$bits, OUTFILE=>$outfile, ALGORITHM=>$alg, PASSWD=>$passwd ) my $self = shift; my $keys = { @_ }; my $bits = $keys->{BITS}; my $outfile = $keys->{OUTFILE}; my $alg = $keys->{ALGORITHM}; my $passwd = $keys->{PASSWD}; my $engine = ( $ENV{'engine'} or $keys->{ENGINE} ); my $rand = $keys->{RAND}; my $command = "$self->{shell} genrsa "; if( $engine ) { $command .= "-engine $engine "; } if( $passwd ) { $command .= "-passout env:pwd "; $alg = "des" if ( not(defined($alg)) or $alg eq "" ); } if ( defined($alg) && $alg ne "" ) { $command .= "-$alg "; } if ( defined($outfile) && $outfile ne "" ) { $command .= "-out $outfile "; } $ENV{'pwd'} = "$passwd" if( defined($passwd) ); if ( $rand ne "" ) { $command .= "-rand \"$rand\" "; } else { $ENV{'RANDFILE'} = "/tmp/.rand_${$}"; } $command .= $bits if( defined($bits) ); $ENV{'pwd'} = "$passwd" if( defined( $passwd)); open(FD, "$command|" ) or return; ## Send Password ## if( $passwd ) { ## print FD "$passwd\n"; ## } ## Send Confirmation Password ## print FD "$passwd\n"; close(FD); delete ($ENV{'pwd'}) if( defined($passwd)); delete ($ENV{'RANDFILE'}) if (defined($ENV{'RANDFILE'})); if( not defined( $rand )) { unlink( "/tmp/.rand_${$}" ); } return if( $? != 0 ); return "$!"; } sub genReq { ## Generate a Request file, parameter accepted are ## ( $outfile, $keyfile, $passwd , [email, cn, ou, o, c ] ) ## To utilize null passwd simply pass a "" reference. my $self = shift; my $keys = { @_ }; my $engine = ( $ENV{'engine'} or $keys->{ENGINE} ); my $outfile = $keys->{OUTFILE}; my $outform = $keys->{OUTFORM}; my $keyfile = $keys->{KEYFILE}; my $subject = $keys->{SUBJECT}; my $passwd = $keys->{PASSWD}; my $command = "$self->{shell} req -new "; my $tmpfile = $self->{tmpDir} . "/${$}_req.pem"; my ( $ret, $tmp, @DN ); return if( not $keyfile ); if( defined $keys->{DN} ) { @DN = @{ $keys->{DN} }; } if ( defined($self->{cnf}) && $self->{cnf} ne "" ) { $command .= "-config " . $self->{cnf} . " "; } $command .= "-passin env:pwd " if ( defined($passwd) && $passwd ne "" ); $command .= "-subj \"$subject\" " if ( defined( $subject) && $subject ne "" ); if( $engine ) { $command .= "-engine $engine "; } if( defined($outform) ) { $outform = uc( $outform ); if ( $outform =~ /(PEM|DER)/i ) { $command .= "-outform $outform "; } elsif ( $outform =~ /(TXT)/ ) { $command .= "-text -noout "; } } if ( $outfile ne "" ) { $command .= "-out $outfile "; } else { $command .= " >$tmpfile "; } $command .= "-key $keyfile "; $ENV{'pwd'} = "$passwd" if( defined($passwd)); open( FD, "|$command" ) or return ; if( not defined ($subject) or ( $subject eq "") ) { foreach $tmp (@DN) { print FD "$tmp\n"; } } close(FD); delete( $ENV{'pwd'} ) if( defined($passwd) ); if( $? != 0 ) { return; } if( $outfile eq "" ) { open( FD, "<$tmpfile" ) or return; while( $tmp = <FD> ) { $ret .= $tmp; } close(FD); unlink( "$tmpfile" ); return $ret; } return defined; } sub genCert { ## Generate a new Certificate file, parameter accepted are ## (OUTFILE=>$outfile,KEYFILE=>$keyfile,REQFILE=>$reqfile, ## PASSWD=>$passwd, DN=>[ @list ] ) my $self = shift; my $keys = { @_ }; my $outfile = $keys->{OUTFILE}; my $keyfile = $keys->{KEYFILE}; my $reqfile = $keys->{REQFILE}; my $subject = $keys->{SUBJECT}; my $noemail = $keys->{NOEMAILDN}; my $passwd = $keys->{PASSWD}; my $days = $keys->{DAYS}; my $tmpfile = $self->{tmpDir} . "/${$}_crt.tmp"; my $engine = ( $ENV{'engine'} or $keys->{ENGINE} ); my $command = "$self->{shell} req -x509 "; my ( $ret, $tmp ); return if( (not $keyfile) or (not $reqfile) ); if( $engine ) { $command .= "-engine $engine "; } ## $command .= "-subj \"$subject\" " ## if ( defined($subject) && ($subject ne "") ); ## $command .= "-noemailDN " ## if ( defined($noemail) && ($noemail ne "") ); $command .= "-passin env:pwd " if ( defined($passwd) && $passwd ne "" ); $command .= "-config ". $self->{cnf} . " " if ( defined($self->{'cnf'}) && $self->{cnf} ne "" ); $command .= "-days $days " if ( defined($days) && $days =~ /\d+/ && $days > 0 ); $command .= "-in \"$reqfile\" -key \"$keyfile\" "; if( defined($outfile) && $outfile ne "" ) { $command .= "-out \"$outfile\" "; } else { $command .= "-out \"$tmpfile\" "; } $ENV{'pwd'} = "$passwd" if( defined($passwd) ); $ret = `$command`; delete( $ENV{'pwd'} ) if( defined($passwd) ); return if( $? != 0 ); if( $? != 0 ) { return; } ## if( defined($outfile) && $outfile eq "" ) { if( not(defined($outfile)) or $outfile eq "" ) { open( FD, "<$tmpfile" ) or return; while( $tmp = <FD> ) { $ret .= $tmp; } close(FD); unlink( "$tmpfile" ); } return "$ret"; } sub crl2pkcs7 { my $self = shift; my $keys = { @_ }; my $data = $keys->{DATA}; my $crlfile = $keys->{CRLFILE}; my $inform = $keys->{INFORM}; my $outfile = $keys->{OUTFILE}; my $outform = $keys->{OUTFORM}; my ( $ret, $tmp, $tmpfile, $command ); my $command = "$self->{shell} crl2pkcs7 "; if( (not(defined($data)) or $data eq "") and (not(defined($crlfile)) or $crlfile eq "" )) { $command .= "-nocrl "; } if ( not(defined($crlfile)) || $crlfile eq "" ){ $tmpfile = $self->{tmpDir} . "/${$}_incrl.tmp"; open( FD, ">$tmpfile" ) or exit 1; print FD "$data"; close( FD ); } else { $tmpfile = $crlfile; } $command .= "-in $tmpfile "; $command .= "-out $outfile " if ( defined($outfile) and $outfile ne ""); $command .= "-inform $inform " if ( defined($inform) and $inform ne ""); $command .= "-outform $outform " if ( defined($outform) and $outform ne ""); if( defined $keys->{CERTSLIST} ) { my @certs = @{ $keys->{CERTSLIST}}; for (@certs) { $command .= "-certfile \"$_\" " if( ("$_" ne "") and (-f "$_") ); } } $ret = `$command`; if($? != 0) { $self->{errno} = $?; $self->{errval} = "Impossible converting to pkcs7 structure"; $ret = undef; } else { $ret = 1 if( $outfile ne "" ); } unlink("$tmpfile") if ( $crlfile eq "" ); return $ret; } sub dataConvert { ## You can convert data structures to different formats ## Accepted parameters are: ## ## DATATYPE=> CRL|CERTIFICATE|REQUEST ## OUTFORM => PEM|DER|NET|TXT|PKCS12 ## INFORM => PEM|DER|NET|TXT|PKCS12 ## OUTFILE => $outfile ## INFILE => $infile ## DATA => $data ## KEYFILE => $keyfile ## PKCS12 encode parameter : ## INFILE or DATA (must be PEM encoded) ## KEYFILE (might be in front of the DATA or in INFILE) ## P12PASSWD = password for pkcs12 file (optional) ## PASSWD = password for KEYFILE (optional) ## OUTFILE = optional ## ALGO = optionl, default = des3 ## DATATYPE must be 'CERTIFICATE' ## PKCS12 decode parameter ## INFILE or DATA (must be PKCS12 encoded) ## P12PASSWD ## PASSWD (PEM password optional) ## OUTFILE = optional ## DATATYPE must be 'CERTIFICATE' my $self = shift; my $keys = { @_ }; my $data = $keys->{DATA}; my $type = $keys->{DATATYPE}; my $outform = $keys->{OUTFORM}; my $inform = $keys->{INFORM}; my $outfile = $keys->{OUTFILE}; my $infile = $keys->{INFILE}; my $keyfile = $keys->{KEYFILE}; my $passwd = $keys->{'PASSWD'}; my $p12pass = $keys->{'P12PASSWD'}; my $algo = $keys->{'ALGO'} || 'des3'; my $nokeys = $keys->{'NOKEYS'}; my ( $command, $tmp, $ret, $tmpfile ); return if ( not $type); return if ( (not $data) and (not $infile)); return if ( not $algo =~ /des3|des|idea/ ); return if ( defined($nokeys) and ($outform eq 'PKCS12') ); # impossible ## Return if $infile does not exists return if( defined($infile) and ( not -e $infile )); $outform = "PEM" if( not $outform ); $inform = "PEM" if( not $inform ); $tmpfile = "$self->{tmpDir}/${$}_cnv.tmp"; $command = "$self->{shell} "; if( $type =~ /CRL/i ) { $command .= " crl "; } elsif ( $type =~ /CERTIFICATE/i ) { if( $outform eq 'PKCS12' or $inform eq 'PKCS12' ) { $command .= ' pkcs12 '; } else { $command .= " x509 "; } } elsif ( $type =~ /REQ/i ) { $command .= " req "; } else { ## if no known type is given... return; } $outfile = $tmpfile if ( not $outfile ); $command .= "-out $outfile "; $command .= "-in $infile " if ( defined($infile) && $infile ne "" ); $command .= "-inkey $keyfile " if( defined($keyfile) ); #PKCS12 only # outform in PKCS12 is always PEM if( $outform =~ /TXT/i ) { $command .= "-text -noout "; } elsif ( $outform =~ /(PEM|DER|NET)/i ) { if( $inform eq 'PKCS12' ) { $command .= '-passout env:pempwd 'if( defined($passwd) ); $command .= '-passin env:p12pwd ' if( defined($p12pass) ); $command .= '-nokeys ' if( defined($nokeys) ); if( defined($passwd) ) { $command .= "-$algo " if( $algo eq 'des' or $algo eq 'des3' or $algo eq 'idea' ); } else { $command .= '-nodes' if( not defined($passwd) ); } } else { $command .= "-outform " . uc($outform) . " "; } } elsif ( $outform eq 'PKCS12' ) { $command .= "-export "; } else { ## no valid format received... return; } if( $outform eq 'PKCS12' ) { $command .= '-passout env:p12pwd '; $command .= '-passin env:pempwd ' if( defined($passwd) ); } elsif( $inform =~ /(PEM|DER|NET)/i ) { $command .= "-inform " . uc($inform) ." "; } elsif( $inform eq 'PKCS12' ) { # nothing to do here. } else { ## no valid format received ... return; } $ENV{'p12pwd'} = "$p12pass" if( defined($p12pass) ); $ENV{'pempwd'} = "$passwd" if( defined($passwd) ); if( defined($infile) && $infile ne "" ) { $ret=`$command`; } else { open( FD, "|$command" ) or return; print FD "$data"; close( FD ); } ## return if( $? != 0 ); delete($ENV{'pwd'}); delete($ENV{'pempwd'}); if( exists $keys->{OUTFILE} ) { return 1; } $ret = ""; open( TMP, "<$outfile" ) or return; while( $tmp = <TMP> ) { $ret .= $tmp; } close( TMP ); unlink( $outfile ); return $ret; ## if( $infile ne "" ) { ## $ret=`$command`; ## return if ( $? != 0 ); ## } else { ## if( $outfile eq "" ) { ## $tmpfile = $self->{tmpDir} . "/${$}_cnv.tmp"; ## $command .= " -out $tmpfile"; ## } else { ## $tmpfile = $outfile; ## } ## open( FD, "|$command" ) or return; ## print FD "$data"; ## close( FD ); ## return if( $? != 0 ); ## open( TMP, "<$tmpfile" ) or return; ## my $tmp; ## $ret = ""; ## while( $tmp = <TMP> ) { ## $ret .= $tmp; ## } ## close(TMP); ## unlink( $tmpfile ); ## } ## return "$ret"; } sub issueCert { ## Use this function to issue a certificate using the ## ca utility. Use this if you already own a valid CA ## certificate. Accepted parameters are: ## REQDATA => $data ## REQFILE => $reqfilename ## INFORM => PEM|DER|NET|SPKAC ; defaults to PEM ## PRESERVE_DN => Y/N ; defaults to Y/N ## CAKEY => $CAkeyfile ## CACERT => $CAcertfile ## DAYS => $days ## PASSWD => $passwd ## EXTS => $extentions ## REQTYPE => NETSCAPE|MSIE my $self = shift; my $keys = { @_ }; my $reqdata = $keys->{REQDATA}; my $reqfile = $keys->{REQFILE}; my $inform = $keys->{INFORM}; my $preserve = ( $keys->{PRESERVE_DN} or "N" ); my $cakey = $keys->{CAKEY}; my $days = $keys->{DAYS}; my $startDate= $keys->{START_DATE}; my $endDate = $keys->{END_DATE}; my $passwd = $keys->{PASSWD}; my $exts = $keys->{EXTS}; my $extFile = $keys->{EXTFILE}; my $reqtype = $keys->{REQTYPE}; my $subject = $keys->{SUBJECT}; my $noemail = $keys->{NOEMAILDN}; my $reqfiles =$keys->{REQFILES}; my $outdir =$keys->{OUTDIR}; my $caName = $keys->{CA_NAME}; my $engine = ( $ENV{'engine'} or $keys->{ENGINE} ); my ( $ret, $tmpfile ); #return if( (not $reqdata) and (not $reqfile)); # to make multi certs you need to tell openssl # what directory to put it. return if( (not $reqdata) and (not $reqfile) and ((not $reqfiles) and (not $outdir)) ); $inform = "PEM" if( not $inform ); $reqtype = "NETSCAPE" if( not $reqtype ); my $command = "$self->{shell} ca -batch "; if( $engine ) { $command .= "-engine $engine "; } $command .= "-config " .$self->{cnf}." " if ( $self->{cnf} ); $command .= "-keyfile $cakey " if( $cakey ); $command .= "-passin env:pwd " if ( $passwd ne "" ); $command .= "-days $days " if ( $days ); $command .= "-extfile $extFile " if ( $extFile ); $command .= "-extensions $exts " if ( $exts ); $command .= "-preserveDN " if ( $preserve =~ /Y/i ); $command .= "-startdate $startDate " if ( $startDate ); $command .= "-enddate $endDate " if ( $endDate ); $command .= "-name $caName " if ( $caName ); $command .= "-subj \"$subject\" " if ( $subject ); $command .= "-noemailDN " if ( exists $keys->{NOEMAILDN} ); # this got moved because if -infiles # is going to be used it has to be the last # option. if ( $reqtype =~ /MSIE/i ) { ## $command .= "-msie_hack "; } elsif ($reqtype =~ /NETSCAPE/i ) { ## Nothing to do... } else { return; } if( $inform =~ /(PEM|DER|NET)/i ) { #this has to be the last option $command .= "-outdir $outdir " if ($outdir); $command .= "-infiles @$reqfiles" if ($reqfiles); $command .= "-in $reqfile " if ( $reqfile ); } elsif ( $inform =~ /SPKAC/ ) { return if ( not $reqfile ); $command .= "-spkac $reqfile "; } else { ## no valid format received ... return; } if( $reqfile ne "" ) { $ENV{'pwd'} = "$passwd"; $ret = `$command`; $ENV{'pwd'} = ""; return if( $? != 0); } else { $ENV{'pwd'} = "$passwd"; open( FD, "|$command" ) or return; print "$reqdata"; close(FD); $ENV{'pwd'} = ""; return if( $? != 0); } return 1; } sub revoke { ## CAKEY => $CAkeyfile (Optional) ## CACERT => $CAcertfile (Optional) ## PASSWD => $passwd (Optional - if not needed) ## INFILE => $certFile (PEM Formatted certificate file); my $self = shift; my $keys = { @_ }; my $cakey = $keys->{CAKEY}; my $cacert = $keys->{CACERT}; my $passwd = $keys->{PASSWD}; my $certFile = $keys->{INFILE}; my $engine = ( $ENV{'engine'} or $keys->{ENGINE} ); my ( $tmp, $ret ); my $command = "$self->{shell} ca -revoke \"$certFile\" "; return if (not $certFile); if( $engine ) { $command .= "-engine $engine "; } $command .= "-config " . $self->{cnf}. " " if ( defined($self->{'cnf'}) && $self->{cnf} ne "" ); $command .= "-keyfile $cakey " if( defined($cakey) && $cakey ne "" ); $command .= "-passin env:pwd " if ( defined($passwd) && $passwd ne "" ); ## $command .= "-key $passwd " if ( $passwd ne "" ); $command .= "-cert $cacert " if ( defined($cacert) && $cacert ne "" ); $ENV{'pwd'} = "$passwd"; open( FD, "$command|" ) or return; while( $tmp = <FD> ) { $ret .= $tmp; } close(FD); #$ENV{'pwd'} = ""; delete( $ENV{'pwd'} ) if( defined($passwd) ); if( $? != 0) { return; } else { return 1; } } sub issueCrl { ## CAKEY => $CAkeyfile ## CACERT => $CAcertfile ## PASSWD => $passwd ## DAYS => $days ## EXTS => $extentions ## OUTFILE => $outfile ## OUTFORM => PEM|DER|NET|TXT my $self = shift; my $keys = { @_ }; my $cakey = $keys->{CAKEY}; my $cacert = $keys->{CACERT}; my $days = $keys->{DAYS}; my $passwd = $keys->{PASSWD}; my $outfile = $keys->{OUTFILE}; my $outform = $keys->{OUTFORM}; my $exts = $keys->{EXTS}; my $extfile = $keys->{EXTFILE}; my $engine = ( $ENV{'engine'} or $keys->{ENGINE} ); my ( $ret, $tmp, $tmpfile ); my $command = "$self->{shell} ca -gencrl "; if( $engine ) { $command .= "-engine $engine "; } if ( not(defined($outfile)) || $outfile eq "" ){ $tmpfile = $self->{tmpDir} . "/${$}_crl.tmp"; } else { $tmpfile = $outfile; } $command .= "-out $tmpfile "; $command .= "-config " . $self->{cnf}. " " if ( defined($self->{'cnf'}) && $self->{cnf} ne "" ); $command .= "-keyfile $cakey " if( defined($cakey) && $cakey ne "" ); $command .= "-passin env:pwd " if ( defined($passwd) && $passwd ne "" ); $command .= "-cert $cacert " if ( defined($cacert) && $cacert ne "" ); $command .= "-crldays $days " if ( defined($days) && $days ne "" ); $command .= "-crlexts $exts " if ( defined($exts) && $exts ne "" ); $command .= "-extfile $extfile " if ( defined($extfile) && $extfile ne "" ); $ENV{'pwd'} = "$passwd"; $ret = `$command`; delete( $ENV{'pwd'} ); return if( $? != 0); $ret = $self->dataConvert( INFILE =>$tmpfile, OUTFORM =>$outform, DATATYPE=>"CRL" ); return if( not $ret ); if( defined($outfile) && $outfile ne "" ) { open( FD, ">$outfile" ) or return; print FD "$ret"; close( FD ); return 1; } unlink( $tmpfile ); return "$ret"; } sub SPKAC { my $self = shift; my $keys = { @_ }; my $infile = $keys->{INFILE}; my $outfile = $keys->{OUTFILE}; my $spkac = $keys->{SPKAC}; my $command = $self->{shell} . " spkac -verify "; my $tmpfile = $self->{tmpDir} . "/${$}_SPKAC.tmp"; my $engine = ( $ENV{'engine'} or $keys->{ENGINE} ); my $ret = ""; my $retVal = 0; my $tmp; if( defined($spkac) && $spkac ne "" ) { $infile = $self->{tmpDir} . "/${$}_in_SPKAC.tmp"; open( FD, ">$infile" ) or return; print FD "$spkac\n"; close ( FD ); } if( $engine ) { $command .= "-engine $engine "; } $command .= "-in $infile " if( defined($infile) && $infile ne "" ); if( defined($outfile) && $outfile ne "" ) { $command .= "-out $outfile "; } else { $command .= "-out $tmpfile "; } open( FD, "|$command" ) or return; close( FD ); ## Store the ret value $retVal = $?; ## Unlink the infile if it was temporary unlink $infile if( defined($spkac) && $spkac ne ""); if( defined($outfile) && $outfile ne "" ) { return if ( $retVal != 0 ); return 1; } ## Get the output open( TMP, "$tmpfile" ) or return; while ( $tmp = <TMP> ) { $ret .= $tmp; } close( TMP ); unlink $tmpfile if ($outfile eq ""); return if ( $retVal != 0 ); return $ret; } sub getDigest { ## Returns Digest of the provided message ## DATA=>$data, ALGORITHM=>$alg my $self = shift; my $keys = { @_ }; my $data = $keys->{DATA}; my $alg = lc( $keys->{ALGORITHM} ); my $tmpfile = $self->{tmpDir} . "/${$}_dgst.tmp"; my $engine = ( $ENV{'engine'} or $keys->{ENGINE} ); my ( $command, $ret ); $alg = "md5" if( not $alg ); return if (not $data); open( FD, ">$tmpfile" ) or return; print FD $data; close( FD ); $command = "$self->{shell} dgst -$alg "; if( defined($engine) and ($engine ne "")) { $command .= "-engine $engine "; } $command .= "<\"$tmpfile\""; $ret = `$command`; $ret =~ s/\n//g; unlink( $tmpfile ); if( $? != 0 ) { return; } else { return $ret; } } sub verify { ## Verify PKCS7 signatures (new OpenCA::verify command ## should be used ) my $self = shift; my $keys = { @_ }; my $data = $keys->{DATA_FILE}; my $sig = $keys->{SIGNATURE}; my $sigfile = $keys->{SIGNATURE_FILE}; my $cacert = $keys->{CA_CERT}; my $cadir = $keys->{CA_DIR}; my $verbose = $keys->{VERBOSE}; my $out = $keys->{OUTFILE}; my $noChain = $keys->{NOCHAIN}; my $tmpfile = $self->{tmpDir} . "/${$}_vrfy.tmp"; my $command = $self->{verify} . " "; my ( $ret, $tmp ); $command .= "-verbose " if ( $verbose ); $command .= "-cf $cacert " if ( $cacert ); $command .= "-cd $cadir " if ($cadir); $command .= "-data $data " if ($data); $command .= ">\"$out\" " if ( $out ); $command .= "-no_chain " if ( $noChain and not($cacert or $cadir)); $command .= "-in $sigfile" if ( $sigfile ); ## if( $sigfile ) { ## open( SIG, "<$sigfile" ) or return; ## while( not eof( SIG ) ) { ## $sig .= <SIG>; ## } ## close( SIG ); ## } if( not $out ) { $command .= " >\"$tmpfile\""; } $command .= " 2>\&1"; open( FD, "|$command" ) or return; if( (defined($sig)) and ($sig ne "") ) { print FD "$sig"; } close( FD ); $self->{errno} = $?; $ret = ""; open( TMP, "<$tmpfile" ) or return; while( not eof ( TMP ) ) { $ret .= <TMP>; } close( TMP ); if ( $self->{errno} ) { unlink( $tmpfile ) if (not $out); ( $self->{errno}, $self->{errval} ) = ( $ret =~ /Verify Error\s*(.*?)\s*:\s*(.*?)\n/ ); return; } if( not $out) { unlink( $tmpfile ); return $ret; } else { return 1; } } sub sign { ## Generate a PKCS7 signature. my $self = shift; my $keys = { @_ }; my $data = $keys->{DATA}; my $datafile= $keys->{DATA_FILE}; my $out = $keys->{OUT_FILE}; my $cert = $keys->{CERT_FILE}; my $key = $keys->{KEY_FILE}; my $nonDetach = $keys->{INCLUDE_DATA}; my $pwd = ( $keys->{PWD} or $keys->{PASSWD} ); my $tmpfile = $self->{tmpDir} . "/${$}_sign.tmp"; my $command = $self->{sign} . " "; my ( $ret ); return if( (not $data) and (not $datafile)); return if( (not $cert) or (not $key)); $command .= "-in $datafile " if ($datafile); $command .= "-out $out " if ( $out ); $command .= "-key $pwd " if ( $pwd ); $command .= "-nd " if ( $nonDetach ); $command .= "-cert $cert -keyfile $key "; if( $datafile ) { $ret=`$command`; if( $? ) { return; }; } else { if( not $out) { $command .= " >$tmpfile"; }; open( FD, "|$command" ) or return; print FD "$data"; close(FD); if ( $? ) { unlink( $tmpfile ) if (not $out); return; } if( not $out ) { open( TMP, "<$tmpfile" ) or return; do { $ret .= <TMP>; } while (not eof($ret)); close(TMP); unlink( $tmpfile ); } } ## If we are here there have been no errors, so ## if $ret is empty, let's return a true value... $ret = 1 if ( not $ret ); return $ret; } sub getCertAttribute { my $self = shift; my $keys = { @_ }; my $cert = ( $keys->{DATA} or $keys->{FILE} ); my $inform = ( $keys->{INFORM} or "PEM" ); my @attribute = (); if( exists($keys->{ATTRIBUTE_LIST}) && ref($keys->{ATTRIBUTE_LIST}) ) { @attribute = @{$keys->{ATTRIBUTE_LIST}}; } else { @attribute = ( $keys->{ATTRIBUTE} ); } my $cmd = "$self->{shell} x509 -noout "; my $engine = ( $ENV{'engine'} or $keys->{ENGINE} ); my ( $ret ); my $tmpfile = $self->{tmpDir} . "/${$}_ATTRIBUTE.tmp"; if( exists $keys->{FILE} ) { $cmd .= "-in \"$cert\" "; } elsif ( exists $keys->{DATA} ) { $cmd .= "-in \"$tmpfile\" "; open ( FD, ">$tmpfile" ) or return; print FD "$cert"; close( FD ); } else { return; } foreach my $attribute ( @attribute ) { $attribute = "startdate" if( uc ($attribute) eq "NOTBEFORE" ); $attribute = "enddate" if( uc ($attribute) eq "NOTAFTER" ); $attribute = "subject" if( uc ($attribute) eq "DN" ); $attribute = "pubkey" if( uc ($attribute) eq "KEY"); $attribute = lc($attribute); $cmd .= "-$attribute "; } if( defined($engine) and ($engine ne "") ) { $cmd .= "-engine $engine "; } $ret = `$cmd`; my $return = {}; my $i=0; my @ret = split /\n/, $ret; foreach( @attribute ) { $_ = uc($_); if( $_ eq 'SUBJECT' ) { $_ = 'DN'; } elsif( $_ eq 'STARTDATE' ) { $_ = 'NOTBEFORE'; } elsif( $_ eq 'ENDDATE' ) { $_ = 'NOTAFTER'; } elsif( $_ eq 'PUBKEY' ) { $_ = 'KEY'; do { $return->{$_} .= $ret[$i] . "\n"; } while( $ret[$i] and $ret[++$i] !~ /^-----END PUBLIC KEY-----/ ); } $return->{$_} .= $ret[$i++]; } unlink( $tmpfile ); if ( $? != 0 ) { return; } else { foreach ( keys(%$return) ) { next if ( $_ eq "KEY" ); $return->{$_} =~ s/(.*?)=[\s]*//; $return->{$_} =~ s/$(\n|\r)//; } return (ref($keys->{ATTRIBUTE_LIST}))?($return):($return->{$attribute[0]}); } } sub getReqAttribute { my $self = shift; my $keys = { @_ }; my $req = ( $keys->{DATA} or $keys->{FILE} ); my $inform = ( $keys->{FORMAT} or $keys->{INFORM} or "PEM" ); my @attribute = (); my ( $cmd ); if( exists($keys->{ATTRIBUTE_LIST}) && ref($keys->{ATTRIBUTE_LIST}) ) { @attribute = @{$keys->{ATTRIBUTE_LIST}}; } else { @attribute = ( $keys->{ATTRIBUTE} ); } if( $inform =~ /SPKAC/ ) { $cmd = "$self->{shell} spkac -noout "; } else { $cmd = "$self->{shell} req -noout "; } my $engine = ( $ENV{'engine'} or $keys->{ENGINE} ); my ( $ret ); my $tmpfile = $self->{tmpDir} . "/${$}_ATTRIBUTE.tmp"; if( exists $keys->{FILE} ) { $cmd .= "-in \"$req\" "; } elsif ( exists $keys->{DATA} ) { $cmd .= "-in \"$tmpfile\" "; open ( FD, ">$tmpfile" ) or return; print FD "$req"; close( FD ); } else { return; } foreach my $attribute ( @attribute ) { $attribute = "startdate" if( uc ($attribute) eq "NOTBEFORE" ); $attribute = "enddate" if( uc ($attribute) eq "NOTAFTER" ); $attribute = "subject" if( uc ($attribute) eq "DN" ); $attribute = "pubkey" if( uc ($attribute) eq "KEY"); $attribute = lc($attribute); $cmd .= "-$attribute "; } if( defined($engine) and ($engine ne "") ) { $cmd .= "-engine $engine "; } $ret = `$cmd`; my $return = {}; my $i=0; my @ret = split /\n/, $ret; foreach( @attribute ) { $_ = uc($_); if( $_ eq 'SUBJECT' ) { $_ = 'DN'; } elsif( $_ eq 'STARTDATE' ) { $_ = 'NOTBEFORE'; } elsif( $_ eq 'ENDDATE' ) { $_ = 'NOTAFTER'; } elsif( $_ eq 'PUBKEY' ) { $_ = 'KEY'; do { $return->{$_} .= $ret[$i] . "\n"; } while( $ret[$i] and $ret[++$i] !~ /^-----END PUBLIC KEY-----/ ); } $return->{$_} .= $ret[$i++]; } # unlink( $tmpfile ); if ( $? != 0 ) { return; } else { foreach ( keys(%$return) ) { next if ( $_ eq "KEY" ); $return->{$_} =~ s/(.*?)=[\s]*//; $return->{$_} =~ s/$(\n|\r)//; } return (ref($keys->{ATTRIBUTE_LIST}))?($return):($return->{$attribute[0]}); } } sub pkcs7Certs { my $self = shift; my $keys = { @_ }; my $infile = $keys->{INFILE}; my $outfile = $keys->{OUTFILE}; my $pkcs7 = $keys->{PKCS7}; my $command = $self->{shell} . " pkcs7 -print_certs "; my $tmpfile = $self->{tmpDir} . "/${$}_SPKAC.tmp"; my $engine = ( $ENV{'engine'} or $keys->{ENGINE} ); my $ret = ""; my $retVal = 0; my $tmp; $self->{errno} = 0; if( defined($pkcs7) && $pkcs7 ne "" ) { $infile = $self->{tmpDir} . "/${$}_in_SPKAC.tmp"; open( FD, ">$infile" ) or return; print FD "$pkcs7\n"; close ( FD ); } if( defined($engine) and ($engine ne "")) { $command .= "-engine $engine "; } $command .= "-in $infile " if( defined($infile) && $infile ne "" ); if( defined($outfile) && $outfile ne "" ) { $command .= "-out $outfile "; } else { $command .= "-out $tmpfile "; } $ret = `$command 2>&1`; if( $? > 0 ) { $self->{errno} = "$?"; $self->{errval} = "$ret"; } ## Unlink the infile if it was temporary unlink $infile if( defined($pkcs7) && $pkcs7 ne ""); ## Get the output open( TMP, "$tmpfile" ) or return; while ( $tmp = <TMP> ) { $ret .= $tmp; } close( TMP ); unlink $tmpfile if (not (defined($outfile)) or $outfile eq ""); if ( $self->{errno} != 0 ) { return; } else { return $ret; } } sub updateDB { my $self = shift; my $keys = { @_ }; my $cakey = $keys->{CAKEY}; my $cacert = $keys->{CACERT}; my $passwd = $keys->{PASSWD}; my $outfile = $keys->{OUTFILE}; my ( $ret, $tmp ); my $command = "$self->{shell} ca -updatedb "; $command .= "-config " . $self->{cnf}. " " if ( defined($self->{'cnf'}) && $self->{cnf} ne "" ); $command .= "-keyfile $cakey " if( defined($cakey) && $cakey ne "" ); $command .= "-passin env:pwd " if ( defined($passwd) && $passwd ne "" ); $command .= "-cert $cacert " if ( defined($cacert) && $cacert ne "" ); $ENV{'pwd'} = "$passwd"; $ret = `$command`; delete( $ENV{'pwd'} ); return if( $? != 0); if( defined($outfile) && $outfile ne "" ) { open( FD, ">$outfile" ) or return; print FD "$ret"; close( FD ); return 1; } return "$ret"; } # Autoload methods go after =cut, and are processed by the autosplit program. 1; __END__ # Below is the stub of documentation for your module. You better edit it! =head1 NAME OpenCA::OpenSSL - Perl Crypto Extention to OpenSSL =head1 SYNOPSIS use OpenCA::OpenSSL; =head1 DESCRIPTION This Perl Module implements an interface to the openssl backend program. It actually uses the openssl command and it is not fully integrated as PERL/C mixture. Passing parameters to functions should be very simple as them have no particular order and have, often, self-explaining name. Each parameter should be passed to the function like this: ... ( NAME=>VALUE, NAME=>VALUE, ... ); =head1 FUNCTIONS =head2 sub new () - Creates a new Class instance. This functions creates a new instance of the class. It accepts only one parameter: the path to the backend command (openssl). This is due because if it cannot find the openssl command it will return an uninitialized class (default value is /usr/bin/ openssl which may not fit many distributions/OSs) EXAMPLE: my $openssl->new OpenCA::OpenSSL( $path ); =head2 sub setParams () - Set internal module variables. This function can handle the internal module data such as the backend path or the tmp dir. Accepted parameters are: SHELL - Path to the openssl command. CONFIG - Path to the openssl config file. TMPDIR - Temporary files directory. STDERR - Where to redirect the STDERR file. (*) - Optional parameters; EXAMPLE: $openssl->setParams( SHELL=>'/usr/local/ssl/bin/openssl', CONFIG=>$ca/stuff/openssl.cnf, TMPDIR=>'/tmp', STDERR=>'/dev/null' ); =head2 sub errno () - Get last command errno value. This functions returns last operation's errno value. Non zero value means there has been an error. EXAMPLE: print $openssl->errno; =head2 sub errval () - Get last command errval value. This functions returns last operation's errval value. This value usually has a brief error description. EXAMPLE: print $openssl->errval; =head2 sub genKey () - Generate a private Key. This functions let you generate a new private key. Accepted parameters are: BITS - key lengh in bits(*); OUTFILE - Output file name(*); ALGORITHM - Encryption Algorithm to be used(*); PASSWD - Password to be used when encrypting(*); (*) - Optional parameters; EXAMPLE: my $key = $openssl->genKey( BITS=>1024 ); =head2 sub genReq () - Generate a new Request. This function generate a new certificate request. Accepted parameters are: OUTFILE - Output file(*); KEYFILE - File containing the key; PASSWD - Password to decript key (if needed) (*); DN - Subject list (as required by openssl, see the openssl.cnf doc on policy); SUBJECT - DN string (use this instead of passing separate attributes list)(*); (*) - Optional parameters; EXAMPLE: my $req = $openssl->genReq( KEYFILE=>"00_key.pem", DN => [ "[EMAIL PROTECTED]","Max","","","" ] ); my $req = $openssl->genReq( KEYFILE=>"00_key.pem", SUBJECT => "CN=Madwolf, O=OpenCA, C=IT" ); =head2 sub genCert () - Generate a certificate from a request. This function let you generate a new certificate starting from the request file. It is used for self-signed certificate as it simply converts the request into a x509 structure. Accepted parameters are: OUTFILE - Output file(*); KEYFILE - File containing the private key; REQFILE - Request File; PASSWD - Password to decrypt private key(*); DAYS - Validity days(*); (*) - Optional parameters; EXAMPLE: $cert = $openssl->genCert( KEYFILE=>"priv_key.pem", REQFILE=>"req.pem", DAYS=>"720" ); =head2 sub crl2pkcs7 () - Convert a crl and/or certs into pkcs7 structure. This function converts certificates (optional) and a crl (also optional) into a pkcs7 structure. It is used to build strucutures to load certificates/crls into some browsers. Accepted parameters are: DATA - PEM|DER formatted CRL(*); INFORM - Input crl format (DER|PEM) (*); OUTFORM - Output pkcs7 structure format (DER|PEM) (*); INFILE - Input crl file (*); OUTFILE - Output pkcs7 file (*); CERTSLIST - List of files containing certificates to be added to the pkcs7 structure (*); (*) - Optional parameters; EXAMPLE: $pkcs7 = $openssl->crl2pkcs7( DATA=>$crl->getPEM(), CERTSLIST=>[ "cert1.pem", "cert2.pem" ]); =head2 sub dataConvert () - Convert data to different format. This functions will convert data you pass to another format. Ir requires you to provide with the data's type and IN/OUT format. Accepted parameters are: DATA - Data to be processed; INFILE - Data file to be processed (one of DATA and INFILE are required and exclusive); KEYFILE - file with the priv. key (* PEM to PKCS12 only). Not needed if key is presented in DATA or INFILE too. DATATYPE - Data type ( CRL | CERTIFICATE | REQUEST ); OUTFORM - Output format (PEM|DER|NET|TXT)(*); INFORM - Input format (PEM|DER|NET|TXT)(*); OUTFILE - Output file(*); PASSWD - priv. key password (* PKCS12 to PEM only) omitting the PASSWD leads into an unencrypted priv. key ALGO - des,des3 or idea. Default is des3 encryption for priv. key P12PASSWD - PKCS12 export password (* only needed for PKCS12) NOKEYS - extract only the certificate (* PKCS12 to PEM only) No need for the PASSWD parameter with this option. (*) - Optional parameters; EXAMPLES: # PEM file to TXT format print $openssl->dataConvert( INFILE=>"crl.pem", OUTFORM=>"TXT" ); # PEM file to PKCS12 format, priv key will be des3 encrypted print $openssl->dataConvert( INFILE=>"crl.pem", DATATYPE=>'CERTIFICATE', OUTFORM=>"PKCS12", PASSWD=>$pem_pass, P12PASSWD=>$export_pass ); # PKCS12 data to PEM formated certificate (no key) print $openssl->dataConvert( DATA=>$pkcs12_cert, DATATYPE=>'CERTIFICATE', INFORM=>"PKCS12", NOKEYS=>1, P12PASSWD=>$export_pass ); =head2 sub issueCert () - Issue a certificate. This function should be used when you have a CA certificate and a request (either DER|PEM|SPKAC) and want to issue the certificate. Parameters used will override the configuration values (remember to set to appropriate value the CONFIG with the setParams func). Accepted parameters are: REQDATA - Request; REQFILE - File containing the request (one of REQDATA, REQFILE or REQFILES are required); REQFILES - An array ref to an array of files that contain the request. OUTDIR - What directory to put the files from REQFILES. (This is required iff you use REQFILES.) INFORM - Input format (PEM|DER|NET|SPKAC)(*); PRESERVE_DN - Preserve DN order (Y|N)(*); CA_NAME - CA sub section to be used (take a look at the OpenSSL docs for adding support of multiple CAs to the conf file)(*); CAKEY - CA key file; CACERT - CA certificate file; DAYS - Days the certificate will be valid(*); START_DATE - Starting validity date (YYMMDDHHMMSSZ)(*); END_DATE - Ending validity date (YYMMDDHHMMSSZ)(*); PASSWD - Password to decrypt priv. CA key(*); EXTS - Extentions to be used (configuration section of the openssl.cnf file)(*); REQTYPE - Request type (NETSCAPE|MSIE)(*); (*) - Optional parameters; EXAMPLE: $openssl->issueCert( REQFILE=>"myreq", INFORM=>SPKAC, PRESERVE_DN=>Y, CAKEY=>$ca/private/cakey.pem, CACERT=>$ca/cacert.pem, PASSWD=>$passwd, REQTYPE=>NETSCAPE ); =head2 sub revoke () - Revoke a certificate. This function is used to revoke a certificate. Accepted parameters are: CAKEY - CA private key file(*); CACERT - CA certificate file(*); PASSWD - Password to decrypt priv. CA key(*); INFILE - Input PEM formatted certificate filename(*); (*) - Optional parameters; EXAMPLE: if( not $openssl->revoke( INFILE=>$certFile ) ) { print "Error while revoking certificate!"; } =head2 sub issueCrl () - Issue a CRL. This function is used to issue a CRL. Accepted parameters are: CAKEY - CA private key file; CACERT - CA certificate file; PASSWD - Password to decrypt priv. CA key(*); DAYS - Days the CRL will be valid for(*); EXTS - Extentions to be added ( see the openssl.cnf pages for more help on this )(*); EXTFILE - Extensions file to be used (*); OUTFILE - Output file(*); OUTFORM - Output format (PEM|DER|NET|TXT)(*); (*) - Optional parameters; EXAMPLE: print $openssl->issueCrl( CAKEY=>"$ca/private/cakey.pem", CACERT=>"$ca/cacert.pem", DAYS=>7, OUTFORM=>TXT ); =head2 sub SPKAC () - Get SPKAC infos. This function returns a text containing all major info about an spkac structure. Accepted parameters are: SPKAC - spkac data ( SPKAC = .... ) (*); INFILE - An spkac request file (*); OUTFILE - Output file (*); (*) - Optional parameters; EXAMPLE: print $openssl->SPKAC( SPKAC=>$data, OUTFILE=>$target ); =head2 sub pkcs7Certs () - Get PKCS7 structure certificate(s). This function returns a PEM formatted (file or ret value) contained in the pkcs7 structure. Accepted parameters are: PKCS7 - pkcs7 data (*); INFILE - A pkcs7 (signature?) file (*); OUTFILE - Output file (*); (*) - Optional parameters; EXAMPLE: print $openssl->pkcs7Cert( PKCS7=>$data, OUTFILE=>$target ); =head2 sub getDigest () - Get a message digest. This function returns a message digest. Default digest algorithm used is MD5. Accepted parameters are: DATA - Data on which to perform digest; ALGORITHM - Algorithm to be used(*); (*) - Optional parameters; EXAMPLE: print $openssl->getDigest( DATA=>$data, ALGORITHM=>sha1); =head2 sub updateDB () - Updates the OpenSSL index.txt This functions updates the index.txt file and returns the output of the command in the form: <SER>=Expired Accepted parameters are: CAKEY - CA private key file; CACERT - CA certificate file; PASSWD - Password to decrypt priv. CA key(*); OUTFILE - Output file(*); (*) - Optional parameters; EXAMPLE: $ret = $openssl->updateDB(); =head1 AUTHOR Massimiliano Pala <[EMAIL PROTECTED]> =head1 SEE ALSO OpenCA::X509, OpenCA::CRL, OpenCA::REQ, OpenCA::TRIStateCGI, OpenCA::Configuration =cut