/usr/local/CPAN/OpenCA-OpenSSL/OpenCA/OpenSSL.pm
## OpenCA::OpenSSL
##
## Copyright (C) 1998-2001 Massimiliano Pala (madwolf@openca.org)
## 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
## // (madwolf@openca.org) 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 <ccmartin@ust.hk>
## Uwe Gansert <ug@suse.de>
##
## General Errorcodes:
##
## The errorcodes consists of seven numbers:
## 1234567
## 12: module
## 34: function
## 567: errorcode
##
## The modules errorcode is 77.
##
## The functions use the following errorcodes:
##
## new 00
## setParams 01
## errno 02
## errval 03
## genKey 11
## genReq 12
## genCert 13
## crl2pkcs7 21
## dataConvert 22
## issueCert 31
## revoke 32
## issueCrl 33
## SPKAC 41
## getDigest 51
## verify 42
## sign 43
## decrypt 46
## encrypt 47
## getCertAttribute 61
## getReqAttribute 62
## getCRLAttribute 63
## pkcs7Certs 44
## updateDB 71
## getSMIME 52
## getPIN 53
## getOpenSSLDate 54
## getNumericDate 55
## getNumericDateDays 56
use strict;
package OpenCA::OpenSSL;
our ($errno, $errval);
use X500::DN;
use Carp;
use OpenCA::OpenSSL::SMIME;
## i18n stuff needs this
use Locale::Messages qw (:locale_h :libintl_h);
use POSIX qw (setlocale);
($OpenCA::OpenSSL::VERSION = '$Revision: 1.29 $' )=~ s/(?:^.*: (\d+))|(?:\s+\$$)/defined $1?"2\.0":""/eg;
## Global Variables Go HERE
my %params = (
shell => undef,
cnf => undef,
tmpDir => undef,
baseDir => undef,
verify => undef,
sign => undef,
decrypt => undef,
encrypt => undef,
errno => undef,
errval => undef,
OPENSSL => undef,
CALLBACK_HANDLER => undef,
PIN_CALLBACK => undef,
STDERR_CALLBACK => undef,
STDOUT_CALLBACK => 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->{openca_sv} ) {
$self->{openca_sv} = "$self->{binDir}/openca-sv";
}
if( not $self->{verify} ) {
$self->{verify} = "$self->{binDir}/openca-sv verify";
};
if( not $self->{sign} ) {
$self->{sign} = "$self->{binDir}/openca-sv sign";
};
if( not $self->{decrypt} ) {
$self->{decrypt} = "$self->{binDir}/openca-sv decrypt";
};
if( not $self->{encrypt} ) {
$self->{encrypt} = "$self->{binDir}/openca-sv encrypt";
};
if( not $self->{tmpDir} ) {
$self->{tmpDir} = '/tmp';
};
if( not $self->{gettext} ) {
$self->setError (7700110, "There is no translation function specified.");
return undef;
};
if( not -e $self->{openssl} ) {
$self->setError (7700120,
$self->{gettext} ("There is no path to OpenSSL specified."));
return undef;
};
$self->setError (0, "");
return $self;
}
sub setParams {
my $self = shift;
my $params = { @_ };
my $key;
my $rebuild_engine = 0;
## activate debugging
# $self->{DEBUG} = $params->{DEBUG} if (exists $params->{DEBUG});
$self->{DEBUG} = 0;
foreach $key ( keys %{$params} ) {
$self->_debug ("setParams: key: $key");
$self->_debug ("setParams: value: $params->{$key}");
$self->{cnf} = $params->{$key} if ( $key =~ /CONFIG/ );
$self->{gettext} = $params->{$key} if ( $key =~ /GETTEXT/ );
$self->{openssl} = $params->{$key} if ( $key =~ /SHELL/ );
$self->{wrapper} = $params->{$key} if ( $key =~ /WRAPPER/ );
$self->{ENGINE} = $params->{$key} if ( $key =~ /^ENGINE/ );
$self->{PRE_ENGINE} = $params->{$key} if ( $key =~ /PRE_ENGINE/ );
$self->{POST_ENGINE} = $params->{$key} if ( $key =~ /POST_ENGINE/ );
$self->{DYNAMIC_ENGINE} = $params->{$key} if ( $key =~ /DYNAMIC_ENGINE/ );
$self->{CALLBACK_HANDLER} = $params->{$key} if ( $key =~ /CALLBACK_HANDLER/);
$self->{GET_PIN_CALLBACK} = $params->{$key} if ( $key =~ /GET_PIN_CALLBACK/);
$self->{PIN_CALLBACK} = $params->{$key} if ( $key =~ /^PIN_CALLBACK/);
$self->{STDERR_CALLBACK} = $params->{$key} if ( $key =~ /STDERR_CALLBACK/);
$self->{STDOUT_CALLBACK} = $params->{$key} if ( $key =~ /STDOUT_CALLBACK/);
$self->{KEY} = $params->{$key} if ( $key eq "KEY" );
$self->{KEYFORM} = $params->{$key} if ( $key eq "KEYFORM" );
$self->{PASSWD} = $params->{$key} if ( $key =~ /PASSWD/ );
$self->{PEM_CERT} = $params->{$key} if ( $key =~ /PEM_CERT/ );
$self->{tmpDir} = $params->{$key} if ( $key =~ /TMPDIR/ );
$self->{binDir} = $params->{$key} if ( $key =~ /BINDIR/ );
if ( $key =~ /OPENCA_SV/ )
{
$self->{openca_sv} = $params->{$key};
$self->{verify} = $self->{openca_sv}." verify";
$self->{sign} = $self->{openca_sv}." sign";
$self->{decrypt} = $self->{openca_sv}." decrypt";
$self->{encrypt} = $self->{openca_sv}." encrypt";
$rebuild_engine = 1;
}
$ENV{RANDFILE} = $params->{$key} if ( $key =~ /RANDFILE/ );
$self->{DEBUG} = $params->{$key} if ( $key =~ /DEBUG/ );
open STDERR, $params->{$key} if ( $key =~ /STDERR/ );
}
$self->{shell} = $self->{openssl};
# add wrapper to commands that make use of private keys
if ((exists $self->{wrapper}) and $self->{wrapper})
{
foreach (qw(shell sign decrypt))
{
if (exists $self->{$_})
{
$self->{$_} = $self->{wrapper} . " " . $self->{$_};
}
}
}
# set keyform if engine is in use
if ($self->{ENGINE} and not $self->{KEYFORM})
{
$self->{KEYFORM} = "e";
}
return 1;
}
sub errno {
my $self = shift;
return $errno;
}
sub errval {
my $self = shift;
return $errval;
}
sub setError {
my $self = shift;
if (scalar (@_) == 4) {
my $keys = { @_ };
$errval = $keys->{ERRVAL};
$errno = $keys->{ERRNO};
} else {
$errno = $_[0];
$errval = $_[1];
}
$self->_debug ("setError: errno: $errno");
$self->_debug ("setError: errval: $errval");
## support for: return $self->setError (1234, "Something fails.") if (not $xyz);
return undef;
}
sub getRandomBytes {
my $self = shift;
my $bytes = shift;
my $ret = undef;
if ( $bytes <= 0 ) {
return undef;
}
$ret = OpenCA::OpenSSL::Misc::rand_bytes ( $bytes );
return $ret;
}
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};
$outfile = $self->{KEY} if (not $outfile);
my $alg = $keys->{ALGORITHM};
$alg = "aes256" if (not $alg);
my $type = lc($keys->{TYPE});
my $passwd = $keys->{PASSWD};
$passwd = $self->{PASSWD} if (not $passwd);
my $engine = $self->{ENGINE};
my $rand = $keys->{RAND};
## generate the parameter file if necessary
# $self->{DEBUG} = 1;
my $param_file = "";
if ( lc($type) eq "dsa" ) {
use File::Basename;
$param_file = $self->{tmpDir};
$param_file = dirname ($outfile) if (dirname ($outfile));
$param_file .= "/dsa_param.$$";
my $command = "dsaparam -out $param_file ";
if( $engine ) {
$command .= "-engine $engine ";
}
if ($bits) {
$command .= $bits;
undef $bits;
} else {
$command .= "2048";
}
if (not $self->_execute_command (COMMAND => $command, KEY_USAGE => $engine)) {
$self->setError (7711005,
$self->{gettext} ("OpenCA::OpenSSL->genKey: Cannot execute command (__ERRNO__). __ERRVAL__",
"__ERRVAL__", $self->errval,
"__ERRNO__", $self->errno));
return undef;
}
}
## generate the key
my $command = "";
if ($type) {
$self->_debug ( "genKey -> type is $type!");
if( $type =~ /^ecdsa$/i ) {
$command .= "ecparam -genkey -noout ";
} else {
$command .= "gen".lc($type)." ";
}
} else {
$command .= "genrsa ";
}
if( $engine ) {
$command .= "-engine $engine ";
}
if( ($passwd) and (lc($type) ne "ecdsa")) {
$command .= "-passout env:pwd ";
$alg = "aes256 " if ( not(defined($alg)) or $alg eq "" );
if ( defined($alg) && $alg ne "" ) {
$command .= "-$alg ";
}
}
if ( defined($outfile) && $outfile ne "" ) {
$command .= "-out $outfile ";
}
if ( defined($rand) && $rand ne "" ) {
$command .= "-rand $rand ";
} else {
$ENV{'RANDFILE'} = $self->{tmpDir}."/.rand_${$}";
}
if ($param_file) {
## DSA
$command .= $param_file;
} elsif ( $type =~ /^ecdsa$/i ) {
if( defined($bits) ) {
if ( $bits <= 112 ) {
$command .= " -name secp112r1 ";
} elsif ( $bits <= 128 ) {
$command .= " -name secp128r1 ";
} elsif ( $bits <= 160 ) {
$command .= " -name secp160r1 ";
} elsif ( $bits <= 192 ) {
$command .= " -name prime192v1 ";
} elsif ( $bits <= 224 ) {
$command .= " -name secp224r1 ";
} elsif ( $bits <= 256 ) {
$command .= " -name prime256v1 ";
} elsif ( $bits <= 384 ) {
$command .= " -name secp384r1 ";
} elsif ( $bits <= 521 ) {
$command .= " -name secp521r1 ";
} else {
#not supported, let's default to 224
$command .= " -name secp224r1 ";
$self->setError (7711006,
$self->{gettext} ("OpenCA::OpenSSL->genKey: You must specify a key length less or equal to 521 for ECDSA keys."));
$self->_debug ( "genKey -> ERROR::type is ECDSA but bits is $bits!");
return undef;
}
} else {
$self->setError (7711007,
$self->{gettext} ("OpenCA::OpenSSL->genKey: You must specify a key length for ECDSA keys."));
$self->_debug ( "genKey -> ERROR::type is ECDSA but bits is not defined ($bits)!");
return undef;
}
} elsif (defined($bits)) {
## RSA
$command .= $bits;
} else {
$self->setError (7711008,
$self->{gettext} ("OpenCA::OpenSSL->genKey: You must specify a parameterfile for DSA or a the key length for RSA keys."));
return undef;
}
$self->_debug ( "genKey -> INFO::Command is ($command)");
$ENV{'pwd'} = "$passwd" if (defined($passwd));
if (not $self->_execute_command (COMMAND => $command, KEY_USAGE => $engine)) {
$self->setError (7711011,
$self->{gettext} ("OpenCA::OpenSSL->genKey: Cannot execute command (__ERRNO__). __ERRVAL__" . "(__COMMAND__)",
"__ERRVAL__", $self->errval,
"__ERRNO__", $self->errno),
"__COMMAND__", $command );
delete ($ENV{'pwd'}) if( defined($passwd));
return undef;
}
if( ($passwd) and (lc($type) eq "ecdsa" )) {
my $encCommand = "";
$encCommand = "ec ";
$encCommand .= "-passout env:pwd ";
if( $alg ne "" ) {
$encCommand .= "-ecdsa ";
};
if ( $outfile ne "" ) {
$encCommand .= "-in \"$outfile\" -out \"$outfile\"";
}
$self->_execute_command( COMMAND => $encCommand,
KEY_USAGE => $engine );
}
delete ($ENV{'pwd'}) if( defined($passwd));
delete ($ENV{'RANDFILE'}) if (defined($ENV{'RANDFILE'}));
if( not defined( $rand )) {
unlink( $self->{tmpDir}."/.rand_${$}" );
}
if( $? != 0 ) {
$self->setError (7711021,
$self->{gettext} ("OpenCA::OpenSSL->genKey: OpenSSL fails (__ERRNO__).",
"__ERRNO__", $?));
return undef;
}
$self->{DEBUG} = 0;
return 1;
}
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 = $self->{ENGINE};
my $outfile = $keys->{OUTFILE};
my $outform = $keys->{OUTFORM};
my $keyfile = $keys->{KEYFILE};
$keyfile = $self->{KEY} if (not $keyfile);
my $subject = $keys->{SUBJECT};
my $noemaildn = $keys->{NOEMAILDN};
my $passwd = $keys->{PASSWD};
my $extensions = $keys->{EXTENSIONS};
$passwd = $self->{PASSWD} if (not $passwd);
my $command = "req -new ";
my $tmpfile = $self->{tmpDir} . "/${$}_req.pem";
my ( $ret, $tmp );
if( not $keyfile ) {
$self->setError (7712011,
$self->{gettext} ("OpenCA::OpenSSL->genReq: No keyfile specified."));
return undef;
}
## fix DN-handling of OpenSSL
if ($subject) {
$subject =~ s/\w+=\s*\,//g;
$subject =~ s/\w+=\s*$//;
# $self->setError(7712014,"NEW SUBJECT => $subject");
# return undef;
$self->_debug ("genReq: subject_rfc2253: $subject");
my $dn_obj = X500::DN->ParseRFC2253 ($subject);
if (not $dn_obj) {
$self->setError (7712013,
$self->{gettext} ("OpenCA::OpenSSL->genReq: Cannot build X500::DN-object from subject __SUBJECT__",
"__SUBJECT__", $subject));
return undef;
}
$subject = $dn_obj->getOpenSSLString ();
$self->_debug ("genReq: subject_x500: $subject");
} else {
$self->_debug ("genReq: the subject of the request is not defined");
$self->setError (7712015,
$self->{gettext} ("OpenCA::OpenSSL->genReq: The subject of the request is not defined."));
return undef;
}
if ( defined($self->{cnf}) && $self->{cnf} ne "" ) {
$command .= "-config " . $self->{cnf} . " ";
}
if (not $self->{PIN_CALLBACK} and not $self->{GET_PIN_CALLBACK})
{
$command .= "-passin env:pwd " if ( defined($passwd) && $passwd ne "" );
}
if( $keys->{NOEMAILDN} ) {
$subject =~ s/emailAddress=[^\/\,]+\,*\/*//gi;
$subject =~ s/(\,*|\/*)$//;
$subject =~ s/^\/\//\//;
};
$command .= "-subj \"$subject\" ";
$command .= "-multivalue-rdn " if ($subject =~ /[^\\](\\\\)*\+/);
if( $engine ) {
$command .= "-engine $engine -keyform ".$self->{KEYFORM}." ";
}
if( $extensions ) {
$command .= "-reqexts \"$extensions\" ";
}
if( defined($outform) ) {
$outform = uc( $outform );
if ( $outform =~ /(PEM|DER)/i ) {
$command .= "-outform $outform ";
} elsif ( $outform =~ /(TXT)/ ) {
$command .= "-text -noout ";
}
}
$command .= "-key $keyfile ";
if ( $outfile ne "" ) {
$command .= "-out $outfile ";
} else {
$command .= " -out $tmpfile ";
}
$ENV{'pwd'} = "$passwd" if( defined($passwd));
if (not $self->_execute_command (COMMAND => $command, KEY_USAGE => $engine)) {
$self->setError (7712071,
$self->{gettext} ("OpenCA::OpenSSL->genReq: Cannot execute command (__ERRNO__). __ERRVAL__",
"__ERRVAL__", $self->errval,
"__ERRNO__", $self->errno));
delete( $ENV{'pwd'} ) if( defined($passwd) );
return undef;
}
delete( $ENV{'pwd'} ) if( defined($passwd) );
if( not defined $outfile or $outfile eq "" ) {
if (not open( FD, "<$tmpfile" )) {
$self->setError (7712081,
$self->{gettext} ("OpenCA::OpenSSL->genReq: Cannot open tmpfile __FILENAME__ for reading.",
"__FILENAME__", $tmpfile));
return undef;
}
while( $tmp = <FD> ) {
$ret .= $tmp;
}
close(FD);
unlink( "$tmpfile" );
return $ret;
}
return 1;
}
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};
$keyfile = $self->{KEY} if (not $keyfile);
my $reqfile = $keys->{REQFILE};
my $subject = $keys->{SUBJECT};
my $noemail = $keys->{NOEMAILDN};
my $passwd = $keys->{PASSWD};
$passwd = $self->{PASSWD} if (not $passwd);
my $days = $keys->{DAYS};
my $tmpfile = $self->{tmpDir} . "/${$}_crt.tmp";
my $engine = $self->{ENGINE};
my $extfile = $keys->{EXTFILE};
my $extensions = $keys->{EXTENSIONS};
## fix DN-handling of OpenSSL
if ($subject) {
$subject =~ s/\w+=\s*\,//g;
$subject =~ s/\w+=\s*$//;
# $self->setError(7712014,"NEW SUBJECT => $subject");
# return undef;
$self->_debug ("genReq: subject_rfc2253: $subject");
my $dn_obj = X500::DN->ParseRFC2253 ($subject);
if (not $dn_obj) {
$self->setError (7713013,
$self->{gettext} ("OpenCA::OpenSSL->genCert: Cannot build X500::DN-object from subject __SUBJECT__.",
"__SUBJECT__", $subject));
return undef;
}
$subject = $dn_obj->getOpenSSLString ();
$self->_debug ("genReq: subject_x500: $subject");
}
my $command = "ca -batch -selfsign ";
my ( $ret, $tmp );
if (not $keyfile) {
$self->setError (7713015,
$self->{gettext} ("OpenCA::OpenSSL->genCert: No keyfile specified."));
return undef;
}
if (not $reqfile) {
$self->setError (7713016,
$self->{gettext} ("OpenCA::OpenSSL->genCert: No requestfile specified."));
return undef;
}
if( $engine ) {
$command .= "-engine $engine -keyform ".$self->{KEYFORM}." ";
}
if ( defined($subject) && ($subject ne "") ) {
# if( $keys->{NOEMAILDN} ) {
# $subject =~ s/emailAddress=[^\/\,]+\,*\/*//gi;
# $subject =~ s/(\,*|\/*)$//;
# $subject =~ s/^\/\//\//;
# };
$command .= " -subj \"$subject\" ";
};
$command .= "-multivalue-rdn " if ($subject and $subject =~ /[^\\](\\\\)*\+/);
if (not $self->{PIN_CALLBACK} and not $self->{GET_PIN_CALLBACK})
{
$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\" -keyfile \"$keyfile\" ";
$command .= "-extensions \"" . $extensions . "\" " if( $extensions );
$command .= qq{ -extfile "$extfile" } if ( $extfile );
if( defined($outfile) && $outfile ne "" ) {
$command .= "-out \"$outfile\" ";
} else {
$command .= "-out \"$tmpfile\" ";
}
$ENV{'pwd'} = "$passwd" if( defined($passwd) );
$ret = $self->_execute_command (COMMAND => $command,
KEY_USAGE => $engine);
delete( $ENV{'pwd'} ) if( defined($passwd) );
if( not $ret ) {
$self->setError (7713071,
$self->{gettext} ("OpenCA::OpenSSL->genCert: OpenSSL failed (__ERRNO__). __ERRVAL__",
"__ERRNO__", $self->errno,
"__ERRVAL__", $self->errval));
return undef;
}
if( not(defined($outfile)) or $outfile eq "" ) {
if (not open( FD, "<$tmpfile" )) {
$self->setError (7713081,
$self->{gettext} ("OpenCA::OpenSSL->genCert: Cannot open tmpfile __FILENAME__ for reading.",
"__FILENAME__", $tmpfile));
return undef;
}
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, $nocrl );
$command = "crl2pkcs7 ";
if( (not(defined($data)) or $data eq "") and
(not(defined($crlfile)) or $crlfile eq "" )) {
$nocrl = 1;
$command .= "-nocrl ";
} else {
$nocrl = 0;
}
if ( not defined $crlfile or $crlfile eq "" ){
$tmpfile = $self->{tmpDir} . "/${$}_incrl.tmp";
if (not open( FD, ">$tmpfile" )) {
$self->setError (7721011,
$self->{gettext} ("OpenCA::OpenSSL->crl2pkcs7: Cannot open tmpfile __FILENAME__ for writing.",
"__FILENAME__", $tmpfile));
return undef;
}
print FD "$data";
close( FD );
} else {
$tmpfile = $crlfile;
}
$command .= "-in $tmpfile " if( $nocrl == 1 );
$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 = $self->_execute_command (COMMAND => $command);
if(not $ret) {
$self->setError (7721071,
$self->{gettext} ("OpenCA::OpenSSL->crl2pkcs7: OpenSSL fails (__ERRNO__). __ERRVAL__",
"__ERRNO__", $self->errno,
"__ERRVAL__", $self->errval));
$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|KEY
## OUTFORM => PEM|DER|NET|TXT|PKCS12|PKCS8
## INFORM => PEM|DER|NET|TXT|PKCS12|PKCS8
## OUTFILE => $outfile
## INFILE => $infile
## DATA => $data
## KEYFILE => $keyfile
## CACERT => $cacert
## 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)
## INPASSWD = password for KEYFILE (optional)
## OUTPASSWD = password for KEYFILE (optional)
## OUTFILE = optional
## ALGO = optionl, default = des3
## DATATYPE must be 'CERTIFICATE'
## CACERT = add additional cacert to pkcs#12
## PKCS12 decode parameter
## INFILE or DATA (must be PKCS12 encoded)
## P12PASSWD
## PASSWD (PEM password optional)
## OUTFILE = optional
## DATATYPE must be 'CERTIFICATE'
## KEY encode/decode parameter
## PUBOUT = true value - output only the public key?
## PUBIN = true value - input is only the public key?
my $self = shift;
my $keys = { @_ };
my $data = $keys->{DATA};
my $type = $keys->{DATATYPE};
my $outform = $keys->{OUTFORM};
my $encoding= $keys->{ENCODING};
my $inform = $keys->{INFORM};
my $outfile = $keys->{OUTFILE};
my $infile = $keys->{INFILE};
my $keyfile = $keys->{KEYFILE};
$keyfile = $self->{KEY} if (not $keyfile);
my $passwd = $keys->{'PASSWD'};
$passwd = $self->{PASSWD} if (not $passwd and
not exists $keys->{'OUTPASSWD'} and
not exists $keys->{'INPASSWD'});
my $p12pass = $keys->{'P12PASSWD'};
my $inpwd = $keys->{'INPASSWD'};
my $outpwd = $keys->{'OUTPASSWD'};
my $algo = $keys->{'ALGO'} || 'des3';
my $nokeys = $keys->{'NOKEYS'};
my $cacert = $keys->{'CACERT'};
$cacert = $self->{PEM_CERT} if (not $cacert);
my $pubin = $keys->{'PUBIN'};
my $pubout = $keys->{'PUBOUT'};
my ( $command, $tmp, $ret, $tmpfile );
## rest errordetection
if( $? != 0 ) {
$self->_debug ("dataConvert: resetting error from ${?} to 0.");
$? = 0;
}
if( $errno != 0 ) {
$self->_debug ("dataConvert: resetting errno from $errno to 0.");
$self->setError (0, "");
}
if ( not $type) {
$self->setError (7722011,
$self->{gettext} ("OpenCA::OpenSSL->dataConvert: No datatype specified."));
return undef;
}
if ( (not $data) and (not $infile) and ($type =~ /KEY/)) {
$infile = $self->{KEY};
}
if ( (not $data) and (not $infile)) {
$self->setError (7722012,
$self->{gettext} ("OpenCA::OpenSSL->dataConvert: No input data specified."));
return undef;
}
if ( not $algo =~ /des3|des|idea/ ) {
$self->setError (7722013,
$self->{gettext} ("OpenCA::OpenSSL->dataConvert: Unsupported algorithm specified."));
return undef;
}
if ( defined($nokeys) and ($outform eq 'PKCS12') ) {
$self->setError (7722014,
$self->{gettext} ("OpenCA::OpenSSL->dataConvert: No keys available but the output format is PKCS#12."));
return undef;
}
## Return if $infile does not exists
if( $infile and ( not -e $infile )) {
$self->setError (7722015,
$self->{gettext} ("OpenCA::OpenSSL->dataConvert: The specified inputfile doesn't exist."));
return undef;
}
if (not $infile) {
$infile = $self->{tmpDir} . "/${$}_data.tmp";
$self->_debug ("dataConvert: create temporary infile $infile");
$self->_debug ("dataConvert: the data is like follows");
$self->_debug ("dataConvert: $data");
if (not open FD, ">".$infile) {
$self->_debug ("dataConvert: failed to open temporary infile $infile");
$self->setError (7722041,
$self->{gettext} ("OpenCA::OpenSSL->dataConvert: Cannot write inputdata to tmpfile __FILENAME__.",
"__FILENAME__", $infile));
return undef;
}
print FD $data;
close FD;
} else {
$data = 0;
}
$outform = "PEM" if( not $outform );
$inform = "PEM" if( not $inform );
$tmpfile = "$self->{tmpDir}/${$}_cnv.tmp";
$command = "";
if( $type =~ /CRL/i ) {
$command .= "crl ";
} elsif ( $type =~ /CERTIFICATE/i ) {
if( $outform eq 'PKCS12' or $inform eq 'PKCS12' ) {
$command .= 'pkcs12 ';
} else {
$command .= "x509 -nameopt RFC2253,-esc_msb ";
}
} elsif ( $type =~ /REQ/i ) {
$command .= "req -nameopt RFC2253,-esc_msb ";
if ( defined($self->{cnf}) && $self->{cnf} ne "" ) {
$command .= "-config " . $self->{cnf} . " ";
}
} elsif ( $type =~ /KEY/i ) {
## PKCS8 enforces PEM because the OpenSSL command req can
## only handle PEM-encoded PKCS#8 keys
if ( ($outform =~ /PKCS8/i) or ($inform =~ /PKCS8/i) ) {
$command .= "pkcs8 ";
} else {
$command .= "rsa ";
}
if ( $pubout ) {
$command .= " -pubout ";
}
if ( $pubin ) {
$command .= " -pubin ";
}
if (not $inpwd) {
$inpwd = $passwd;
}
if (not $inpwd) {
## unlink ($infile) if ($data);
## $self->setError (7722018,
## "OpenCA::OpenSSL->dataConvert: Cannot convert key without input passphrase.");
## return undef;
} else {
$command .= ' -passin env:inpwd ';
}
if (not $outpwd) {
$outpwd = $passwd;
}
if (not $outpwd) {
## unlink ($infile) if ($data);
## $self->setError (7722019,
## "OpenCA::OpenSSL->dataConvert: Cannot convert key without output passphrase.");
## return undef;
## I had to comment this one out. In my version of
## openssl (0.9.7a-1) it is not necessary nor
## recognized.
#$command .= ' -nocrypt ';
} else {
$command .= ' -passout env:outpwd ';
}
} else {
## if no known type is given...
$self->setError (7722021,
$self->{gettext} ("OpenCA::OpenSSL->dataConvert: The datatype which should be converted is not known."));
unlink ($infile) if ($data);
return undef;
}
$outfile = $tmpfile if ( not $outfile );
$command .= "-out $outfile ";
$command .= "-in $infile ";
$command .= "-inkey $keyfile " if( defined($keyfile) and ($inform eq 'PKCS12' or $outform eq 'PKCS12')); #PKCS12 only
# outform in PKCS12 is always PEM
if( $outform =~ /TXT/i ) {
## FIXME: noout was removed because of a bug in OpenSSL 0.9.7
## FIXME: the crl command returns an error if -noout is in use
## $command .= "-text -noout ";
$command .= "-text ";
} 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 ";
$command .= '-passout env:p12pwd ';
$command .= '-passin env:pempwd ' if( defined($passwd) );
$command .= "-certfile $cacert " if(defined($cacert));
} elsif ( $outform =~ /PKCS8/i ) {
$command .= " -topk8 ";
$command .= " -nocrypt " if (not $outpwd);
if ($encoding) {
$command .= " -outform ".uc($encoding)." ";
} else {
$command .= " -outform PEM ";
}
} else {
## no valid format received...
$self->_debug ("dataConvert: failed to determine the output format ($outform)");
unlink ($infile) if ($data);
$self->setError (7722024,
$self->{gettext} ("OpenCA::OpenSSL->dataConvert: The output format is unknown or unsupported."));
return undef;
}
if( $outform ne 'PKCS12' ) {
if( $inform =~ /(PEM|DER|NET)/i ) {
$command .= "-inform " . uc($inform) ." ";
} elsif( $inform eq 'PKCS12' ) {
# nothing to do here.
} elsif( $inform eq 'PKCS8' ) {
# nothing to do here.
} else {
## no valid format received ...
$self->_debug ("dataConvert: failed to determine the input format ($inform)");
unlink ($infile) if ($data);
$self->setError (7722026,
$self->{gettext} ("OpenCA::OpenSSL->dataConvert: You don't try to convert to PKCS#12 but the input format is unknown or unsupported."));
return undef;
}
}
$self->_debug ("dataConvert: p12pass is set") if( defined($p12pass) );
$self->_debug ("dataConvert: passwd is set") if( defined($passwd) );
$self->_debug ("dataConvert: inpwd is set") if( defined($inpwd) );
$self->_debug ("dataConvert: outpwd is set") if( defined($outpwd) );
$self->_debug ("dataConvert: command=$command");
if( $? != 0 ) {
$self->setError (7722069,
$self->{gettext} ("OpenCA::OpenSSL->dataConvert: Unkown Error detected before OpenSSL starts (__ERRNO__)",
"__ERRNO__", $?));
unlink ($infile) if ($data);
return undef;
}
$ENV{'p12pwd'} = "$p12pass" if( defined($p12pass) );
$ENV{'pempwd'} = "$passwd" if( defined($passwd) );
$ENV{'inpwd'} = "$inpwd" if( defined($inpwd) );
$ENV{'outpwd'} = "$outpwd" if( defined($outpwd) );
if( defined($infile) && $infile ne "" ) {
$self->_debug ("dataConvert: using infile");
$ret=$self->_execute_command (COMMAND => $command);
} else {
$self->_debug ("dataConvert: data piping is no longer supported - please use tmp files");
$self->setError (7722071,
$self->{gettext} ("OpenCA::OpenSSL->dataConvert: Data piping is no longer supported."));
return undef;
}
$self->_debug ("dataConvert: openssl itself successful");
delete($ENV{'pwd'});
delete($ENV{'pempwd'});
delete($ENV{'inpwd'});
delete($ENV{'outpwd'});
$self->_debug ("dataConvert: passphrases deleted");
if( not $ret ) {
$self->setError (7722073,
$self->{gettext} ("OpenCA::OpenSSL->dataConvert: OpenSSL failed (__ERRNO__). __ERRVAL__",
"__ERRNO__", $self->errno,
"__ERRVAL__", $self->errval));
unlink ($tmpfile) if (not $keys->{OUTFILE});
unlink ($infile) if ($data);
return undef;
}
unlink ($infile) if ($data);
if( $keys->{OUTFILE} ) {
$self->_debug ("dataConvert: return 1 and infile deleted if temporary");
return 1;
}
$ret = "";
if (not open( TMP, "<$outfile" )) {
$self->_debug ("dataConvert: cannot open outfile $outfile for reading");
$self->setError (7722081,
$self->{gettext} ("OpenCA::OpenSSL->dataConvert: Cannot open outfile __FILENAME__ for reading.",
"__FILENAME__", $outfile));
return undef;
}
while( $tmp = <TMP> ) {
$ret .= $tmp;
}
close( TMP );
unlink ($outfile);
$self->_debug ("dataConvert: return result like follows");
$self->_debug ("dataConvert: $ret");
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
## NOEMAILDN => -noemailDN
## NOUNIQUEDN => -nouniqueDN
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};
$cakey = $self->{KEY} if (not $cakey);
my $days = $keys->{DAYS};
my $startDate= $keys->{START_DATE};
my $endDate = $keys->{END_DATE};
my $passwd = $keys->{PASSWD};
$passwd = $self->{PASSWD} if (not $passwd);
my $exts = $keys->{EXTS};
my $extFile = $keys->{EXTFILE};
my $subject = $keys->{SUBJECT};
my $reqfiles =$keys->{REQFILES};
my $outdir =$keys->{OUTDIR};
my $caName = $keys->{CA_NAME};
my $engine = $self->{ENGINE};
my ( $ret, $tmpfile );
## fix DN-handling of OpenSSL
if ($subject) {
## OpenSSL includes a bug in -nameopt RFC2253
## = signs are not escaped if they are normal values
my $i = 0;
my $now = "name";
while ($i < length ($subject))
{
if (substr ($subject, $i, 1) =~ /\\/)
{
$i++;
} elsif (substr ($subject, $i, 1) =~ /=/) {
if ($now =~ /value/)
{
## OpenSSL forgets to escape =
$subject = substr ($subject, 0, $i)."\\".substr ($subject, $i);
$i++;
} else {
$now = "value";
}
} elsif (substr ($subject, $i, 1) =~ /[,+]/) {
$now = "name";
}
$i++;
}
$subject =~ s/\w+=\s*\,//g;
$subject =~ s/\w+=\s*$//;
# $self->setError(7712014,"NEW SUBJECT => $subject");
# return undef;
$self->_debug ("issueCert: subject_rfc2253: $subject");
my $dn_obj = X500::DN->ParseRFC2253 ($subject);
$self->_debug ("issueCert: subject parsed by X500::DN");
if (not $dn_obj) {
$self->_debug ("issueCert: cannot create X500::DN-object");
$self->setError (7731001,
$self->{gettext} ("OpenCA::OpenSSL->issueCert: Cannot create X500::DN-object."));
return undef;
}
$subject = $dn_obj->getOpenSSLString ();
$self->_debug ("issueCert: subject_x500: $subject");
}
#return if( (not $reqdata) and (not $reqfile));
# to make multi certs you need to tell openssl
# what directory to put it.
if( (not $reqdata) and (not $reqfile) and
((not $reqfiles) or (not $outdir)) ) {
$self->setError (7731011,
$self->{gettext} ("OpenCA::OpenSSL->issueCert: No request specified."));
return undef;
}
if (not $reqfile and not $reqfiles) {
$reqfile = $self->{tmpDir} . "/${$}_req.tmp";
$self->_debug ("issueCert: create temporary reqfile $reqfile");
$self->_debug ("issueCert: the data is like follows");
$self->_debug ("issueCert: $reqdata");
if (not open FD, ">".$reqfile) {
$self->_debug ("issueCertConvert: failed to open temporary reqfile $reqfile");
$self->setError (7731015,
$self->{gettext} ("OpenCA::OpenSSL->issueCert: Cannot write inputdata to tmpfile __FILENAME__.",
"__FILENAME__", $reqfile));
return undef;
}
print FD $reqdata;
close FD;
} else {
$reqdata = 0;
}
$inform = "PEM" if( not $inform );
my $command = "ca -batch ";
## activate this if you have a patched OpenSSL 0.9.8
## $command .= "-multivalue-rdn ";
if( $engine ) {
$command .= "-engine $engine -keyform ".$self->{KEYFORM}." ";
}
$command .= "-config " .$self->{cnf}." " if ( $self->{cnf} );
$command .= "-keyfile $cakey " if( $cakey );
if (not $self->{PIN_CALLBACK} and not $self->{GET_PIN_CALLBACK})
{
$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 .= "-multivalue-rdn " if ($subject and $subject =~ /[^\\](\\\\)*\+/);
$command .= "-noemailDN " if ( $keys->{NOEMAILDN} );
$command .= "-nouniqueDN " if ( $keys->{NOUNIQUEDN} );
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/ ) {
if ( not $reqfile ) {
$self->setError (7731012,
$self->{gettext} ("OpenCA::OpenSSL->issueCert: You must specify a requestfile if you use SPKAC."));
return undef;
}
$command .= "-spkac $reqfile ";
} else {
## no valid format received ...
$self->setError (7731013,
$self->{gettext} ("OpenCA::OpenSSL->issueCert: The requests format (__FORMAT__) is not supported.",
"__FORMAT__", $inform));
return undef;
}
## running the OpenSSL command
$self->_debug ("issueCert: openssl=$command");
$ENV{'pwd'} = "$passwd";
$ret = $self->_execute_command (COMMAND => $command, KEY_USAGE => $engine);
delete ($ENV{'pwd'});
unlink ($reqfile) if ($reqdata);
if( not $ret ) {
$self->setError (7731075,
$self->{gettext} ("OpenCA::OpenSSL->issueCert: OpenSSL fails (__ERRNO__). __ERRVAL__",
"__ERRNO__", $self->errno,
"__ERRVAL__", $self->errval));
return undef;
}
$self->_debug ("issueCert: certificate issued successfully");
return 1;
}
sub revoke {
## CAKEY => $CAkeyfile (Optional)
## CACERT => $CAcertfile (Optional)
## PASSWD => $passwd (Optional - if not needed)
## INFILE => $certFile (PEM Formatted certificate file);
## CRL_REASON => Reason for revocation
## unspecified
## keyCompromise
## CACompromise
## affiliationChanged
## superseded
## cessationOfOperation
## certificateHold
## removeFromCRL
## holdInstruction
## keyTime
## CAkeyTime
my $self = shift;
my $keys = { @_ };
my $cakey = $keys->{CAKEY};
$cakey = $self->{KEY} if (not $cakey);
my $cacert = $keys->{CACERT};
$cacert = $self->{PEM_CERT} if (not $cacert);
my $passwd = $keys->{PASSWD};
$passwd = $self->{PASSWD} if (not $passwd);
my $certFile = $keys->{INFILE};
my $crlReason= $keys->{CRL_REASON};
my $engine = $self->{ENGINE};
my ( $tmp, $ret );
my $command = "ca -revoke $certFile ";
if (not $certFile) {
$self->setError (7732011,
$self->{gettext} ("OpenCA::OpenSSL->revoke: No inputfile specified."));
return undef;
}
if( $engine ) {
$command .= "-engine $engine -keyform ".$self->{KEYFORM}." ";
}
$command .= "-config " . $self->{cnf}. " " if ( defined($self->{'cnf'}) && $self->{cnf} ne "" );
$command .= "-keyfile $cakey " if( defined($cakey) && $cakey ne "" );
if (not $self->{PIN_CALLBACK} and not $self->{GET_PIN_CALLBACK})
{
$command .= "-passin env:pwd " if ( defined($passwd) && $passwd ne "" );
}
$command .= "-cert $cacert " if ( defined($cacert) && $cacert ne "" );
$command .= "-nouniqueDN " if ( $keys->{NOUNIQUEDN} );
$command .= "-crl_reason $crlReason " if ( $keys->{CRL_REASON} );
$ENV{'pwd'} = "$passwd";
$ret = $self->_execute_command (COMMAND => $command, KEY_USAGE => $engine);
delete ($ENV{'pwd'});
if( not $ret ) {
$self->setError (7732073,
$self->{gettext} ("OpenCA::OpenSSL->revoke: OpenSSL failed (__ERRNO__). __ERRVAL__" . "<br /><br />(COMMAND=>$command)",
"__ERRNO__", $self->errno,
"__ERRVAL__", $self->errval));
return undef;
} else {
return 1;
}
}
sub issueCrl {
## CAKEY => $CAkeyfile
## CACERT => $CAcertfile
## PASSWD => $passwd
## DAYS => $days
## SECONDS => $seconds
## EXTS => $extentions
## OUTFILE => $outfile
## OUTFORM => PEM|DER|NET|TXT
my $self = shift;
my $keys = { @_ };
my $cakey = $keys->{CAKEY};
$cakey = $self->{KEY} if (not $cakey);
my $cacert = $keys->{CACERT};
$cacert = $self->{PEM_CERT} if (not $cacert);
my $hours = $keys->{HOURS};
my $days = $keys->{DAYS};
my $passwd = $keys->{PASSWD};
$passwd = $self->{PASSWD} if (not $passwd);
my $outfile = $keys->{OUTFILE};
my $outform = $keys->{OUTFORM};
my $exts = $keys->{EXTS};
my $extfile = $keys->{EXTFILE};
my $engine = $self->{ENGINE};
my ( $ret, $tmp, $tmpfile );
my $command = "ca -gencrl ";
if( $engine ) {
$command .= "-engine $engine -keyform ".$self->{KEYFORM}." ";
}
if ( not defined $outfile or $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 "" );
if (not $self->{PIN_CALLBACK} and not $self->{GET_PIN_CALLBACK})
{
$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 .= "-crlhours $hours " if ( defined($hours) && $hours ne "" );
$command .= "-crlexts $exts " if ( defined($exts) && $exts ne "" );
$command .= "-extfile $extfile " if ( defined($extfile) && $extfile ne "" );
$command .= "-nouniqueDN " if ( $keys->{NOUNIQUEDN} );
$ENV{'pwd'} = "$passwd";
$ret = $self->_execute_command (COMMAND => $command, KEY_USAGE => $engine);
delete( $ENV{'pwd'} );
if( not $ret ) {
$self->setError (7733071,
$self->{gettext} ("OpenCA::OpenSSL->issueCrl: OpenSSL failed (__ERRNO__). __ERRVAL__",
"__ERRNO__", $self->errno,
"__ERRVAL__", $self->errval));
return undef;
}
$ret = $self->dataConvert( INFILE =>$tmpfile,
OUTFORM =>$outform,
DATATYPE=>"CRL" );
if( not $ret ) {
$self->setError (7733082,
$self->{gettext} ("OpenCA::OpenSSL->issueCrl: data conversion failed (__ERRNO__). __ERRVAL__",
"__ERRNO__", $self->errno(),
"__ERRVAL__", $self->errval()));
return undef;
}
if( defined($outfile) && $outfile ne "" ) {
if (not open( FD, ">$outfile" )) {
$self->setError (7733084,
$self->{gettext} ("OpenCA::OpenSSL->issueCrl: Cannot open outfile __FILENAME__ for writing.",
"__FILENAME__", $outfile));
return undef;
}
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 = "spkac -verify ";
my $tmpfile = $self->{tmpDir} . "/${$}_SPKAC.tmp";
my $engine = $self->{ENGINE};
my $ret = "";
my $retVal = 0;
my $tmp;
if( defined($spkac) && $spkac ne "" ) {
$infile = $self->{tmpDir} . "/${$}_in_SPKAC.tmp";
if (not open( FD, ">$infile" )) {;
$self->setError (7741011,
$self->{gettext} ("OpenCA::OpenSSL->SPKAC: Cannot open infile __FILENAME__ for writing.",
"__FILENAME__", $infile));
return undef;
}
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 ";
}
$ret = $self->_execute_command (COMMAND => $command);
## Unlink the infile if it was temporary
unlink $infile if( defined($spkac) && $spkac ne "");
if (not $ret) {
$self->setError (7741073,
$self->{gettext} ("OpenCA::OpenSSL->SPKAC: OpenSSL failed (__ERRNO__). __ERRVAL__",
"__ERRNO__", $self->errno,
"__ERRVAL__", $self->errval));
return undef;
}
if( defined($outfile) && $outfile ne "" ) {
return 1;
}
## Get the output
if (not open( TMP, "$tmpfile" )) {
$self->setError (7741081,
$self->{gettext} ("OpenCA::OpenSSL->SPKAC: Cannot open tmpfile __FILENAME__.",
"__FILENAME__", $tmpfile));
return undef;
}
while ( $tmp = <TMP> ) {
$ret .= $tmp;
}
close( TMP );
unlink $tmpfile if (not defined $outfile or $outfile eq "");
if ( $? != 0 ) {
$self->setError (7741083,
$self->{gettext} ("OpenCA::OpenSSL->SPKAC: Cannot read tmpfile __FILENAME__ successfully (__ERRNO__).",
"__FILENAME__", $tmpfile,
"__ERRNO__", $?));
return undef;
}
return $ret;
}
sub getFingerprint {
my $self = shift;
my $keys = { @_ };
my $alg = lc ( $keys->{ALGORITHM} );
my $cert = $keys->{CERT};
if ( $alg eq "" ) {
$alg = "sha1";
}
if ( not $cert or not $cert->getPEM()) {
return undef;
}
my $cert_dat = OpenCA::OpenSSL::X509::_new_from_pem ($cert->getPEM());
return OpenCA::OpenSSL::X509::fingerprint ( $cert_dat, $alg );
# return getDigest ( DATA => $keys->{DATA},
# ALGORITHM => $alg,
# ENGINE => $keys->{ENGINE} );
}
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 $engine = $self->{ENGINE};
my ( $command, $ret );
$alg = "sha256" if( not $alg );
if (not $data) {
$self->setError (7751011,
$self->{gettext} ("OpenCA::OpenSSL->getDigest: No data specified."));
return undef;
}
$command = "dgst -$alg ";
if( defined($engine) and ($engine ne "")) {
$command .= "-engine $engine ";
}
$ret = $self->_execute_command (COMMAND => $command, INPUT => $data);
$ret =~ s/\n//g;
$ret =~ s/^[^=]+=\s+//;
if( not $ret ) {
$self->setError (7751071,
$self->{gettext} ("OpenCA::OpenSSL->getDigest: OpenSSL failed (__ERRNO__). __ERRVAL__",
"__ERRNO__", $self->errno,
"__ERRVAL__", $self->errval));
return undef;
} else {
return $ret;
}
}
# The common invocation mode requires a DATA specification (signed text)
# and a SIGNATURE (detached PKCS#7 signature).
# If OPAQUESIGNATURE is set, DATA and DATA_FILE must not be specified,
# and SIGNATURE or SIGNATURE_FILE must hold a PKCS#7 object containing
# both data and signature.
sub verify {
## Verify PKCS7 signatures (new OpenCA::verify command
## should be used )
my $self = shift;
my $keys = { @_ };
my $data = $keys->{DATA};
my $datafile= $keys->{DATA_FILE};
my $sig = $keys->{SIGNATURE};
my $sigfile = $keys->{SIGNATURE_FILE};
my $cacert = $keys->{CA_CERT};
$cacert = $self->{PEM_CERT} if (not $cacert);
my $cadir = $keys->{CA_DIR};
my $verbose = $keys->{VERBOSE};
my $out = $keys->{OUTFILE};
my $noChain = $keys->{NOCHAIN};
my $opaquesig = $keys->{OPAQUESIGNATURE};
my $tmpfile = $self->{tmpDir} . "/${$}_vrfy.tmp";
my $command = $self->{verify} . " ";
my ( $ret, $tmp );
if((not $opaquesig) and (not $data) and (not $datafile) ) {
$self->_debug ("verify: cannot open command");
$self->setError (7742011,
$self->{gettext} ("OpenCA::OpenSSL->verify: No input source specified."));
return undef;
}
if ((not $opaquesig) and (not $datafile)) {
$datafile = $self->{tmpDir} . "/${$}_data.tmp";
if (not open (FD, ">".$datafile)) {
$self->setError (7742023,
$self->{gettext} ("OpenCA::OpenSSL->verify: Cannot open datafile __FILENAME__ for writing.",
"__FILENAME__", $datafile));
return undef;
}
print FD $data;
close FD;
} else {
$data = 0;
}
if (not $sigfile) {
$sigfile = $self->{tmpDir} . "/${$}_sig.tmp";
if (not open (FD, ">".$sigfile)) {
$self->setError (7742025,
$self->{gettext} ("OpenCA::OpenSSL->verify: Cannot open sigfile __FILENAME__ for writing.",
"__FILENAME__", $sigfile));
unlink $datafile if ($data);
return undef;
}
print FD $sig;
close FD;
$sig = 1;
} else {
$sig = 0;
}
$command .= "-verbose " if ( $verbose );
$command .= "-cf $cacert " if ( $cacert );
$command .= "-cd $cadir " if ($cadir);
$command .= "-data $datafile " if ($datafile);
## the user should know what he is doing
## $command .= "-no_chain " if ( $noChain and not($cacert or $cadir));
$command .= "-no_chain " if ( $noChain );
$command .= "-in $sigfile" if ( $sigfile );
$command .= ">$out " if ( $out );
if( not $out ) {
$command .= " >$tmpfile";
}
$command .= " 2>\&1";
$self->_debug ("verify: command=$command");
$ret =`$command`;
my $org_err = $?;
unlink ($datafile ) if ($data);
unlink ($sigfile) if ($sig);
$ret = "";
if (not open( TMP, "<$tmpfile" )) {
$self->_debug ("verify: Cannot open tmpfile");
$self->setError (7742082,
$self->{gettext} ("OpenCA::OpenSSL->verify: Cannot open tmpfile __FILENAME__ for reading.",
"__FILENAME__", $tmpfile));
return undef;
}
while( not eof ( TMP ) ) {
$ret .= <TMP>;
}
close( TMP );
if ( $? == 256 ) {
$self->_debug ("verify: error detected");
$self->_debug ("verify: original errorcode: ${?}");
$self->_debug ("verify: deleting error");
$? = 0;
} elsif ( $? != 0 ) {
if ($? == -1)
{
$self->setError (7742071,
$self->{gettext} ("OpenCA::OpenSSL->verify: openca-sv failed with errorcode -1. This usually means that the command __COMMAND__ is not present.",
"__COMMAND__", $self->{verify}));
} elsif ($? == 32256)
{
$self->setError (7742074,
$self->{gettext} ("OpenCA::OpenSSL->verify: openca-sv failed with errorcode 32256. This usually means that the file permissions are wrong for the command __COMMAND__.",
"__COMMAND__", $self->{verify}));
} elsif ($? == 32512)
{
$self->setError (7742072,
$self->{gettext} ("OpenCA::OpenSSL->verify: openca-sv failed with errorcode 32512. This usually means that the command openca-sv is malformed or not present (__COMMAND__).",
"__COMMAND__", $self->{verify}));
} else
{
$self->_debug ("verify: error detected");
$self->_debug ("verify: original errorcode: ${?}");
(my $h) =
( $ret =~ /(Verify Error\s*.*?\s*:\s*.*?)\n/ );
$self->setError (7742073,
$self->{gettext} ("OpenCA::OpenSSL->verify: openca-sv failed (__ERRNO__). __ERRVAL__",
"__ERRNO__", $org_err,
"__ERRVAL__", $h));
$self->_debug ("verify: errorcode: $self->errno");
$self->_debug ("verify: errormsg: $self->errval");
}
unlink( $tmpfile ) if (not $out);
return undef;
}
unlink( $tmpfile ) if (not $out);
if ($ret =~ /\[Error\]/i)
{
$self->setError (7742075,
$self->{gettext} ("OpenCA::OpenSSL->verify: openca-sv failed. __ERRVAL__",
"__ERRVAL__", $ret));
return undef;
}
$self->_debug ("verify: returned data:\n$ret");
if( not $out) {
unlink( $tmpfile );
$self->_debug ("verify: finished successfully (return output)");
return $ret;
} else {
$self->_debug ("verify: finished successfully (return 1)");
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 $certfile= $keys->{CERT_FILE};
$certfile = $self->{PEM_CERT} if (not $certfile);
my $cert = $keys->{CERT};
my $keyfile = $keys->{KEY_FILE};
$keyfile = $self->{KEY} if (not $keyfile);
my $key = $keys->{KEY};
my $nonDetach = $keys->{INCLUDE_DATA};
my $pwd = ( $keys->{PWD} or $keys->{PASSWD} );
$pwd = $self->{PASSWD} if (not $pwd);
my $tmpfile = $self->{tmpDir} . "/${$}_sign.tmp";
my $command = $self->{sign} . " ";
if( $self->{ENGINE} ) {
my $init = $self->_build_engine_params(KEY_USAGE => "1");
$command .= " -engine $self->{ENGINE} -keyform ".$self->{KEYFORM}." $init ";
}
my ( $ret );
if( (not $data) and (not $datafile) ) {
$self->setError (7743011,
$self->{gettext} ("OpenCA::OpenSSL->sign: No input source."));
return undef;
}
if( (not $cert) and (not $certfile) ) {
$self->setError (7743012,
$self->{gettext} ("OpenCA::OpenSSL->sign: No certificate specified."));
return undef;
}
if( (not $key) and (not $keyfile) ) {
$self->setError (7743013,
$self->{gettext} ("OpenCA::OpenSSL->sign: No private key specified."));
return undef;
}
if ( not $datafile ) {
$datafile = $self->{tmpDir} . "/${$}_data.tmp";
if (not open FD, ">".$datafile) {
$self->setError (7743031,
$self->{gettext} ("OpenCA::OpenSSL->sign: Cannot open datafile __FILENAME__ for writing.",
"__FILENAME__", $datafile));
return undef;
}
print FD $data;
close FD;
} else {
$data = 0;
}
if ( not $keyfile ) {
$keyfile = $self->{tmpDir} . "/${$}_key.tmp";
if (not open FD, ">".$keyfile) {
$self->setError (7743033,
$self->{gettext} ("OpenCA::OpenSSL->sign: Cannot open keyfile __FILENAME__ for writing.",
"__FILENAME__", $keyfile));
unlink ($datafile) if ($data);
return undef;
}
print FD $key;
close FD;
} else {
$key = 0;
}
if ( not $certfile ) {
$certfile = $self->{tmpDir} . "/${$}_cert.tmp";
if (not open FD, ">".$certfile) {
$self->setError (7743035,
$self->{gettext} ("OpenCA::OpenSSL->sign: Cannot open certfile __FILENAME__ for writing.",
"__FILENAME__", $certfile));
unlink ($datafile) if ($data);
unlink ($keyfile) if ($key);
return undef;
}
print FD $cert;
close FD;
} else {
$cert = 0;
}
$command .= "-in $datafile ";
$command .= "-out $out " if ( $out );
if (not $self->{GET_PIN_CALLBACK})
{
$command .= "-passin env:pwd " if ( $pwd );
}
$command .= "-nd " if ( $nonDetach );
$command .= "-cert $certfile ";
$command .= " -keyfile $keyfile ";
if( not $out) {
$command .= " >$tmpfile";
};
$self->_debug ("sign: $command");
$ENV{pwd} = "$pwd" if ( $pwd );
$ret =`$command`;
delete ($ENV{pwd});
if ( $? == 256 ) {
$self->_debug ("sign: Error 256 detected");
$self->_debug ("sign: ignoring error");
} elsif ( $? ) {
unlink( $tmpfile ) if (not $out);
unlink( $datafile ) if ($data);
unlink( $keyfile ) if ($key);
unlink( $certfile ) if ($cert);
if ($? == -1)
{
$self->setError (7743073,
$self->{gettext} ("OpenCA::OpenSSL->sign: openca-sv failed with errorcode -1. This usually means that the command __COMMAND__ is not present.",
"__COMMAND__", $self->{openca_sv}));
} elsif ($? == 32256) {
$self->setError (7743074,
$self->{gettext} ("OpenCA::OpenSSL->sign: openca-sv failed with errorcode 32256. This usually means that the file permissions are wrong for the command __COMMAND__.",
"__COMMAND__", $self->{openca_sv}));
} elsif ($? == 32512) {
$self->setError (7743072,
$self->{gettext} ("OpenCA::OpenSSL->sign: openca-sv failed with errorcode 32512. This usually means that the command openca-sv is malformed or not present (__COMMAND__).",
"__COMMAND__", $self->{openca_sv}));
} else {
$self->setError (7743071,
$self->{gettext} ("OpenCA::OpenSSL->sign: openca-sv failed (__ERRNO__).",
"__ERRNO__", $?));
}
return undef;
}
unlink( $datafile ) if ($data);
unlink( $keyfile ) if ($key);
unlink( $certfile ) if ($cert);
if( not $out ) {
if (not open( TMP, "<$tmpfile" )) {
$self->setError (7743081,
$self->{gettext} ("OpenCA::OpenSSL->sign: Cannot open tmpfile __FILENAME__ for reading.",
"__FILENAME__", $tmpfile));
return undef;
}
do {
$ret .= <TMP>;
} while (not eof(TMP));
close(TMP);
unlink( $tmpfile );
}
## If we are here there have been no errors, so
## if $ret is empty, let us return a true value...
$ret = 1 if ( not $ret );
return $ret;
}
sub encrypt {
## Encrypt PKCS7 containers
my $self = shift;
my $keys = { @_ };
my $data = $keys->{DATA};
my $datafile= $keys->{DATA_FILE};
my $certfile= $keys->{CERT_FILE};
$certfile = $self->{PEM_CERT} if (not $certfile);
my $out = $keys->{OUTFILE};
my $tmpfile = $self->{tmpDir} . "/${$}_decrypt.tmp";
my $command = $self->{encrypt} . " ";
my ( $ret, $tmp );
if( (not $data) and (not $datafile) ) {
$self->setError (7747011,
$self->{gettext} ("OpenCA::OpenSSL->encrypt: No input source specified."));
return undef;
}
if( not $certfile ) {
$self->setError (7747012,
$self->{gettext} ("OpenCA::OpenSSL->encrypt: No certificate specified."));
return undef;
}
if (not $datafile) {
$datafile = $self->{tmpDir} . "/${$}_data.tmp";
if (not open (FD, ">".$datafile)) {
$self->setError (7747023,
$self->{gettext} ("OpenCA::OpenSSL->encrypt: Cannot open datafile __FILENAME__ for writing.",
"__FILENAME__", $datafile));
return undef;
}
print FD $data;
close FD;
} else {
$data = 0;
}
$command .= "-in $datafile " if ($datafile);
$command .= "-cert $certfile ";
$command .= ">$out " if ( $out );
if( not $out ) {
$command .= " $tmpfile";
}
$self->_debug ("encrypt: command=$command");
$ret =`$command`;
my $org_err = $?;
$self->_debug ("encrypt: question mark: ${?}");
$self->_debug ("encrypt: \@: ${@}");
$self->_debug ("encrypt: ret: $ret");
unlink ($datafile ) if ($data);
if (not $out)
{
$ret = "";
if (not open( TMP, "<$tmpfile" )) {
$self->_debug ("encrypt: Cannot open tmpfile for reading");
$self->setError (7747082,
$self->{gettext} ("OpenCA::OpenSSL->encrypt: Cannot open tmpfile __FILENAME__ for reading.",
"__FILENAME__", $tmpfile));
return undef;
}
while( not eof ( TMP ) ) {
$ret .= <TMP>;
}
close( TMP );
}
if ( $? == 256 and not $@ )
{
$self->_debug ("encrypt: error detected");
$self->_debug ("encrypt: original errorcode: ${?}");
$self->_debug ("encrypt: deleting error");
$? = 0;
} elsif ($? != 0)
{
if ($? == -1)
{
$self->setError (7747071,
$self->{gettext} ("OpenCA::OpenSSL->encrypt: openca-sv failed with errorcode -1. This usually means that the command __COMMAND__ is not present.",
"__COMMAND__", $self->{encrypt}));
} elsif ($? == 32256)
{
$self->setError (7747074,
$self->{gettext} ("OpenCA::OpenSSL->encrypt: openca-sv failed with errorcode 32256. This usually means that the file permissions are wrong for the command __COMMAND__.",
"__COMMAND__", $self->{encrypt}));
} elsif ($? == 32512)
{
$self->setError (7747072,
$self->{gettext} ("OpenCA::OpenSSL->encrypt: openca-sv failed with errorcode 32512. This usually means that the command openca-sv is malformed or not present (__COMMAND__).",
"__COMMAND__", $self->{encrypt}));
} else
{
$self->_debug ("encrypt: error detected");
$self->_debug ("encrypt: original errorcode: ${?}");
## (my $h) =
## ( $ret =~ /(Encrypt Error\s*.*?\s*:\s*.*?)\n/ );
my $h = $ret;
$self->setError (7747073,
$self->{gettext} ("OpenCA::OpenSSL->encrypt: openca-sv failed (__ERRNO__). __ERRVAL__",
"__ERRNO__", $org_err,
"__ERRVAL__", $h));
$self->_debug ("encrypt: errorcode: $self->errno");
$self->_debug ("encrypt: errormsg: $self->errval");
}
unlink( $tmpfile ) if (not $out);
return undef;
}
$self->_debug ("encrypt: returned data:\n$ret");
if( not $out) {
unlink( $tmpfile );
$self->_debug ("encrypt: finished successfully (return output)");
return $ret;
} else {
$self->_debug ("encrypt: finished successfully (return 1)");
return 1;
}
}
sub decrypt {
## Extract data from a PKCS7 structure.
my $self = shift;
my $keys = { @_ };
my $data = $keys->{DATA};
my $datafile= $keys->{DATA_FILE};
$datafile = $keys->{INFILE} if (not $datafile);
my $out = $keys->{OUT_FILE};
my $certfile= $keys->{CERT_FILE};
$certfile = $self->{PEM_CERT} if (not $certfile);
my $cert = $keys->{CERT};
my $keyfile = $keys->{KEY_FILE};
$keyfile = $self->{KEY} if (not $keyfile);
my $key = $keys->{KEY};
my $pwd = ( $keys->{PWD} or $keys->{PASSWD} );
$pwd = $self->{PASSWD} if (not $pwd);
my $tmpfile = $self->{tmpDir} . "/${$}_decrypt.tmp";
my $command = $self->{decrypt} . " ";
if( $self->{ENGINE} ) {
my $init = $self->_build_engine_params(KEY_USAGE => "1");
$command .= " -engine $self->{ENGINE} -keyform ".$self->{KEYFORM}." $init ";
}
my ( $ret );
if( (not $data) and (not $datafile) ) {
$self->setError (7746011,
$self->{gettext} ("OpenCA::OpenSSL->decrypt: No input source."));
return undef;
}
if( (not $cert) and (not $certfile) ) {
$self->setError (7746012,
$self->{gettext} ("OpenCA::OpenSSL->decrypt: No certificate specified."));
return undef;
}
if( (not $key) and (not $keyfile) ) {
$self->setError (7746012,
$self->{gettext} ("OpenCA::OpenSSL->decrypt: No private key specified."));
return undef;
}
if ( not $datafile ) {
$datafile = $self->{tmpDir} . "/${$}_data.tmp";
if (not open FD, ">".$datafile) {
$self->setError (7746031,
$self->{gettext} ("OpenCA::OpenSSL->decrypt: Cannot open datafile __FILENAME__ for writing.",
"__FILENAME__", $datafile));
return undef;
}
print FD $data;
close FD;
} else {
$data = 0;
}
if ( not $keyfile ) {
$keyfile = $self->{tmpDir} . "/${$}_key.tmp";
if (not open FD, ">".$keyfile) {
$self->setError (7746033,
$self->{gettext} ("OpenCA::OpenSSL->decrypt: Cannot open keyfile __FILENAME__ for writing.",
"__FILENAME__", $keyfile));
unlink ($datafile) if ($data);
return undef;
}
print FD $key;
close FD;
} else {
$key = 0;
}
if ( not $certfile ) {
$certfile = $self->{tmpDir} . "/${$}_cert.tmp";
if (not open FD, ">".$certfile) {
$self->setError (7746035,
$self->{gettext} ("OpenCA::OpenSSL->decrypt: Cannot open certfile __FILENAME__ for writing.",
"__FILENAME__", $certfile));
unlink ($datafile) if ($data);
unlink ($keyfile) if ($key);
return undef;
}
print FD $cert;
close FD;
} else {
$cert = 0;
}
$command .= "-in $datafile ";
$command .= "-out $out " if ( $out );
if (not $self->{GET_PIN_CALLBACK})
{
$command .= "-passin env:pwd " if ( $pwd );
}
$command .= "-cert $certfile ";
$command .= " -keyfile $keyfile ";
if( not $out) {
$command .= " >$tmpfile";
};
$self->_debug ("decrypt: $command");
$ENV{pwd} = "$pwd" if ( $pwd );
$ret =`$command`;
delete ($ENV{pwd});
if ( $? ) {
unlink( $tmpfile ) if (not $out);
unlink( $datafile ) if ($data);
unlink( $keyfile ) if ($key);
unlink( $certfile ) if ($cert);
if ($? == -1)
{
$self->setError (7746073,
$self->{gettext} ("OpenCA::OpenSSL->decrypt: openca-sv failed with errorcode -1. This usually means that the command __COMMAND__ is not present.",
"__COMMAND__", $self->{openca_sv}));
} elsif ($? == 32256)
{
$self->setError (7746074,
$self->{gettext} ("OpenCA::OpenSSL->decrypt: openca-sv failed with errorcode 32256. This usually means that the file permissions are wrong for the command __COMMAND__.",
"__COMMAND__", $self->{openca_sv}));
} elsif ($? == 32512)
{
$self->setError (7746072,
$self->{gettext} ("OpenCA::OpenSSL->decrypt: openca-sv failed with errorcode 32512. This usually means that the command openca-sv is malformed or not present (__COMMAND__).",
"__COMMAND__", $self->{openca_sv}));
} else {
$self->setError (7746071,
$self->{gettext} ("OpenCA::OpenSSL->decrypt: openca-sv failed (__ERRNO__).",
"__ERRNO__", $?));
}
return undef;
}
$self->_debug ("decrypt: openca-sv succeeded");
unlink( $datafile ) if ($data);
unlink( $keyfile ) if ($key);
unlink( $certfile ) if ($cert);
if( not $out ) {
if (not open( TMP, "<$tmpfile" )) {
$self->setError (7746081,
$self->{gettext} ("OpenCA::OpenSSL->decrypt: Cannot open tmpfile __FILENAME__ for reading.",
"__FILENAME__", $tmpfile));
return undef;
}
do {
$ret .= <TMP>;
} while (not eof(TMP));
close(TMP);
unlink( $tmpfile );
}
## If we are here there have been no errors, so
## if $ret is empty, let us return a true value...
$ret = 1 if ( not $ret );
return $ret;
}
sub getCertAttribute {
my $self = shift;
my $keys = { @_ };
my $cert;
# $self->_debug("OpenCA::OpenSSL::getCertAttribute() start");
if ($keys->{INFORM} and $keys->{INFORM} =~ /DER/) {
$cert = OpenCA::OpenSSL::X509::_new_from_der ($keys->{DATA});
} else {
$cert = OpenCA::OpenSSL::X509::_new_from_pem ($keys->{DATA});
}
# $self->_debug("OpenCA::OpenSSL::getCertAttribute() got cert");
my @attribute = ();
if( $keys->{ATTRIBUTE_LIST} && ref($keys->{ATTRIBUTE_LIST}) ) {
@attribute = @{$keys->{ATTRIBUTE_LIST}};
} else {
@attribute = ( $keys->{ATTRIBUTE} );
}
return undef if (not $cert);
my ( $ret );
# initialize additional OIDs not present in default OPENSSL
$cert->init_oids;
foreach my $attribute ( @attribute ) {
$_ = uc $attribute;
my $func;
SWITCH: {
$func = lc $attribute;
if (/^NOTBEFORE$/) {$func = "notBefore"};
if (/^NOTAFTER$/) {$func = "notAfter"};
if (/^DN$/) {$func = "subject"};
if (/^HASH$/) {$func = "subject_hash"};
}
# $self->_debug("OpenCA::OpenSSL::getCertAttribute() calling $func ($attribute) ");
$ret->{$attribute} = $cert->$func;
}
return $ret;
}
sub getReqAttribute {
my $self = shift;
my $keys = { @_ };
## timing test
##
## my $start;
## use Time::HiRes qw( usleep ualarm gettimeofday tv_interval );
## $start = [gettimeofday];
my $csr;
if ($keys->{INFORM} and $keys->{INFORM} =~ /DER/)
{
$csr = OpenCA::OpenSSL::PKCS10::_new_from_der ($keys->{DATA});
} elsif ($keys->{INFORM} and $keys->{INFORM} =~ /SPKAC/) {
$csr = OpenCA::OpenSSL::SPKAC::_new ($keys->{DATA});
} else {
$csr = OpenCA::OpenSSL::PKCS10::_new_from_pem ($keys->{DATA});
}
my @attribute = ();
if( $keys->{ATTRIBUTE_LIST} && ref($keys->{ATTRIBUTE_LIST}) ) {
@attribute = @{$keys->{ATTRIBUTE_LIST}};
} else {
@attribute = ( $keys->{ATTRIBUTE} );
}
return undef if (not $csr);
my ( $ret );
foreach my $attribute ( @attribute ) {
$_ = uc $attribute;
my $func;
SWITCH: {
$func = lc $attribute;
if (/^DN$/) {$func = "subject"};
}
$ret->{$attribute} = $csr->$func;
}
## timing test
##
## if ($self->{DEBUG}) {
## $errno += tv_interval ( $start ) if ($self->{DEBUG});
## print "OpenCA::OpenSSL::getReqAttribute: total_time=".$errno."<br>\n";
## }
return $ret;
}
sub getCRLAttribute {
my $self = shift;
my $keys = { @_ };
my $crl;
if ($keys->{INFORM} and $keys->{INFORM} =~ /DER/)
{
$crl = OpenCA::OpenSSL::CRL::_new_from_der ($keys->{DATA});
} else {
$crl = OpenCA::OpenSSL::CRL::_new_from_pem ($keys->{DATA});
}
my @attribute = ();
if( $keys->{ATTRIBUTE_LIST} && ref($keys->{ATTRIBUTE_LIST}) ) {
@attribute = @{$keys->{ATTRIBUTE_LIST}};
} else {
@attribute = ( $keys->{ATTRIBUTE} );
}
return undef if (not $crl);
my ( $ret );
foreach my $attribute ( @attribute ) {
$_ = uc $attribute;
my $func;
SWITCH: {
$func = lc $attribute;
if (/^LASTUPDATE$/) {$func = "lastUpdate"};
if (/^NEXTUPDATE$/) {$func = "nextUpdate"};
if (/^DN$/) {$func = "issuer"};
}
$ret->{$attribute} = $crl->$func;
}
return $ret;
}
sub pkcs7Certs {
my $self = shift;
my $keys = { @_ };
my $infile = $keys->{INFILE};
my $outfile = $keys->{OUTFILE};
my $pkcs7 = $keys->{PKCS7};
my $command = "pkcs7 -print_certs ";
my $tmpfile = $self->{tmpDir} . "/${$}_SPKAC.tmp";
my $engine = $self->{ENGINE};
my $ret = "";
my $retVal = 0;
my $tmp;
if( defined($pkcs7) && $pkcs7 ne "" ) {
$infile = $self->{tmpDir} . "/${$}_in_SPKAC.tmp";
if (not open( FD, ">$infile" )) {
$self->setError (7744021,
$self->{gettext} ("OpenCA::OpenSSL->pkcs7Certs: Cannot open infile __FILENAME__ for writing.",
"__FILENAME__", $infile));
return undef;
}
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 ";
}
$self->_debug ("pkcs7Certs: command=$command");
$ret = $self->_execute_command(COMMAND => $command);
if( not $ret ) {
$self->setError (7744071,
$self->{gettext} ("OpenCA::OpenSSL->pkcs7Certs: OpenSSL failed (__ERRNO__). __ERRVAL__",
"__ERRNO__", $self->errno,
"__ERRVAL__", $self->errval));
unlink $infile if( defined($pkcs7) && $pkcs7 ne "");
unlink $tmpfile if (not (defined($outfile)) or $outfile eq "");
return undef;
}
## Unlink the infile if it was temporary
unlink $infile if( defined($pkcs7) && $pkcs7 ne "");
## Get the output
if (not open( TMP, $tmpfile )) {
$self->setError (7744081,
$self->{gettext} ("OpenCA::OpenSSL->pkcs7Certs: Cannot open tmpfile __FILENAME__ for reading.",
"__FILENAME__", $tmpfile));
return undef;
}
$ret = "";
while ( $tmp = <TMP> ) {
$ret .= $tmp;
}
close( TMP );
unlink $tmpfile if (not (defined($outfile)) or $outfile eq "");
$self->_debug ("pkcs7Certs: finished successfully");
return $ret;
}
sub updateDB {
my $self = shift;
my $keys = { @_ };
my $cakey = $keys->{CAKEY};
$cakey = $self->{KEY} if (not $cakey);
my $cacert = $keys->{CACERT};
$cacert = $self->{PEM_CERT} if (not $cacert);
my $passwd = $keys->{PASSWD};
$passwd = $self->{PASSWD} if (not $passwd);
my $outfile = $keys->{OUTFILE};
my ( $ret, $tmp );
my $command = "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 = $self->_execute_command (COMMAND => $command);
delete( $ENV{'pwd'} );
if( not $ret ) {
$self->setError (7771071,
$self->{gettext} ("OpenCA::OpenSSL->updateDB: OpenSSL failed (__ERRNO__). __ERRVAL__",
"__ERRNO__", $self->errno,
"__ERRVAL__", $self->errval));
return undef;
}
if( defined($outfile) && $outfile ne "" ) {
if (not open( FD, ">$outfile" )) {
$self->setError (7771081,
$self->{gettext} ("OpenCA::OpenSSL->updateDB: Cannot open outfile __FILENAME__ for writing.",
"__FILENAME__", $outfile));
return undef;
}
print FD "$ret";
close( FD );
return 1;
}
return "$ret";
}
sub getSMIME {
## DECRYPT => a true value
## ENCRYPT => a true value
## SIGN => a true value
## CERT => $cert
## KEY => $key
## PASSWD => $passwd
## ENCRYPT_CERT => $enc_cert
## SIGN_CERT => $sign_cert
## INFILE => $infile
## OUTFILE => $outfile
## DATA => $message
## MESSAGE => $message (higher priority)
## TO => $to
## FROM => $from
## SUBJECT => $subject
my $self = shift;
my $keys = { @_ };
my $decrypt = $keys->{DECRYPT};
my $encrypt = $keys->{ENCRYPT};
my $sign = $keys->{SIGN};
my $cert = $keys->{CERT};
$cert = $self->{PEM_CERT} if (not $cert);
my $key = $keys->{KEY};
$key = $self->{KEY} if (not $key);
my $enc_cert = $keys->{ENCRYPT_CERT};
my $sign_cert = $keys->{SIGN_CERT};
my $passwd = $keys->{PASSWD};
$passwd = $self->{PASSWD} if (not $passwd);
my $infile = $keys->{INFILE};
my $outfile = $keys->{OUTFILE};
my $message = $keys->{DATA};
$message = $keys->{MESSAGE} if ($keys->{MESSAGE});
my $to = $keys->{TO};
my $cc = $keys->{CC};
my $from = $keys->{FROM};
my $subject = $keys->{SUBJECT};
my $engine = $self->{ENGINE};
my ( $ret, $tmp, $tmpfile );
## smime can only handle file and not stdin
if ($message) {
$infile = $self->{tmpDir} . "/${$}_data.msg";
$self->_debug ("getSMIME: create temporary infile $infile");
$self->_debug ("getSMIME: the data is like follows\n$message");
if (not open FD, ">".$infile) {
$self->setError (7752021,
$self->{gettext} ("OpenCA::OpenSSL->getSMIME: Cannot write message to tmpfile __FILENAME__.",
"__FILENAME__", $infile));
return undef;
}
print FD "From: $from\n";
print FD "To: $to\n";
foreach my $address (@{$cc})
{
print FD "Cc: $address\n";
}
print FD "Subject: $subject\n";
my $encoding = setlocale (LC_MESSAGES);
if ($encoding ne "C") {
$encoding =~ s/^.*\.//; ## remove language
} else {
$encoding = 'UTF-8';
}
$encoding =~ s/(UTF|ISO|EUC)([^-]{1})/$1-$2/i; ## on FreeBSD "locale" returns charset names
## which do not comply with IANA standard for
## MIME
print FD "Content-Type: text/plain; charset=$encoding; format=flowed\n".
"Content-Transfer-Encoding: 8bit\n";
print FD "\n".$message;
close FD;
} else {
$message = 0;
}
## setup file with smime-message
if ($outfile) {
$tmpfile = $outfile;
} else {
$tmpfile = $self->{tmpDir}."/".$$."_SMIME.msg";
}
$enc_cert = $cert if (not $enc_cert);
$sign_cert = $cert if (not $sign_cert);
my ($enc_x509, $sign_x509);
if ($enc_cert)
{
$enc_x509 = OpenCA::X509->new (
SHELL => $self,
GETTEXT => $self->{gettext},
INFILE => $enc_cert);
if (not $enc_x509)
{
unlink $infile if ($message);
return $self->setError ($OpenCA::X509::errno, $OpenCA::X509::errval);
}
}
$self->_debug ("getSMIME: enccert object ready");
if ($sign_cert) {
$sign_x509 = OpenCA::X509->new (
SHELL => $self,
GETTEXT => $self->{gettext},
INFILE => $sign_cert);
if (not $sign_x509) {
unlink $infile if ($message);
return $self->setError ($OpenCA::X509::errno, $OpenCA::X509::errval);
}
}
$self->_debug ("getSMIME: signcert object ready");
## use OpenCA::OpenSSL::SMIME
## this is only a wrapper for old code !!!
## decryption
my $smime = OpenCA::OpenSSL::SMIME->new(
INFILE => $infile,
SHELL => $self,
ENGINE => $engine,
KEYFORM => $self->{KEYFORM},
GETTEXT => $self->{gettext},
TMPDIR => $self->{tmpDir},
DEBUG => $self->{DEBUG}
# DEBUG => 1
);
if (not $smime) {
$self->_debug ("getSMIME: smime object failed");
unlink $infile if ($message);
return $self->setError ($OpenCA::OpenSSL::SMIME::errno,
$OpenCA::OpenSSL::SMIME::err);
}
$self->_debug ("getSMIME: smime object ready");
if ($decrypt) {
open(KEYF, '<', $key) or return;
if (not $smime->decrypt(
CERTIFICATE => $enc_x509,
KEY_PASSWORD => $passwd,
PRIVATE_KEY => \*KEYF)) {
close (KEYF);
unlink $infile if ($message);
return $self->setError ($smime->errno, $smime->errval);
}
close (KEYF);
} else {
## 1. signing
if ( $sign eq "1" ) {
open(KEYF, '<', $key) or return;
if (not $smime->sign(
CERTIFICATE => $sign_x509,
KEY_PASSWORD => $passwd,
PRIVATE_KEY => \*KEYF)) {
close (KEYF);
$self->_debug("ERROR::Can not sign SMIME (" .
$smime->errno . "::" .
$smime->errval );
unlink $infile if ($message);
return $self->setError ($smime->errno, $smime->errval);
}
close (KEYF);
}
if ($encrypt) {
if (not $smime->encrypt(CERTIFICATE => $enc_x509))
{
unlink $infile if ($message);
return $self->setError ($smime->errno, $smime->errval);
}
}
}
$self->_debug ("getSMIME: final steps ( $outfile / $smime )");
unlink $infile if ($message);
$self->_debug ("getSMIME: return data :: " . $smime->get_mime );
## if the caller want a file then we can finish
if( defined($outfile) && $outfile ne "" ) {
open (OUT, ">", $outfile);
$smime->get_mime->print(\*OUT);
close (OUT);
return 1;
}
if( defined ($smime) and defined($smime->get_mime)
and defined($smime->get_mime->stringify) ) {
return $smime->get_mime->stringify
} else {
return $message;
}
}
sub getPIN {
## PIN_LENGTH => $pin_length
## RANDOM_LENGTH => $random_length
## LENGTH => $pin_length
my $self = shift;
my $keys = { @_ };
my $pin_length = $keys->{LENGTH};
$pin_length = $keys->{PIN_LENGTH} if (defined $keys->{PIN_LENGTH});
my $length = $keys->{RANDOM_LENGTH};
my $hex = $keys->{HEX};
my $engine = $self->{ENGINE};
my ( $ret, $tmp, $tmpfile );
my $command = " rand ";
if ( $hex ) {
$command .= " -hex ";
} else {
$command .= " -base64 ";
}
if( $engine ) {
$command .= " -engine $engine ";
}
if ($length) {
$command .= $length;
} elsif ($pin_length) {
$command .= $pin_length;
} else {
return undef;
}
## create the PIN
my $pin = $self->_execute_command (COMMAND => $command,
HIDE_OUTPUT => 1);
if (not $pin) {
$self->setError (7753071,
$self->{gettext} ("OpenCA::OpenSSL->getPIN: OpenSSL failed (__ERRNO). __ERRVAL__",
"__ERRNO__", $self->errno,
"__ERRVAL__", $self->errval));
return undef;
}
## remove trailing newline
$pin =~ s/\n//gs;
$pin =~ s/=*$//gs;
if ($pin_length) {
## enforce the PIN-length
## SECURITY ADVICE: it is more secure to only set the
## number of randombytes
$pin = substr ($pin, 0, $pin_length);
} else {
## 2*$length is enough to encode $length randombytes in base64
my $hl = 2*$length;
$pin = substr ($pin, 0, $hl);
}
if ($pin) {
return $pin;
} else {
$self->setError (7753075,
$self->{gettext} ("OpenCA::OpenSSL->getPIN: PIN is empty."));
return undef;
}
}
sub getOpenSSLDate {
my $self = shift;
if (not defined $_[0]) {
$self->setError (7754011,
$self->{gettext} ("OpenCA::OpenSSL->getOpenSSLDate: No date specified."));
return undef;
}
my $date = $self->getNumericDate ( $_[0] );
if (not defined $date) {
$self->{errval} = $self->{gettext} ("OpenCA::OpenSSL->getOpenSSLDate: Errorcode 7754021: __ERRVAL__",
"__ERRVAL__", $self->{errval});
return undef;
}
## remove century
$date =~ s/^..//;
## add trailing Z
$date .= "Z";
return $date;
}
sub getNumericDate {
my $self = shift;
my %help;
my $new_date;
my $date = $_[0];
# if (not defined $_[0]) {
# $self->setError (7755011,
# $self->{gettext} ("OpenCA::OpenSSL->getNumericDate: No argument specified."));
# return undef;
# }
if (not $date) {
$date = gmtime();
# $self->setError (7755012,
# $self->{gettext} ("OpenCA::OpenSSL->getNumericDate: No date specified."));
# return undef;
}
## remove leading days like SUN or MON
if ( $date =~ /^\s*[^\s]+\s+(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)/i ) {
$date =~ s/^\s*[^\s]+//;
}
## Mar 10 19:36:45 2001 GMT
## Month
if ( $date =~ /^\s*JAN/i ) {
## january
$help {MONTH} = "01";
} elsif ( $date =~ /^\s*FEB/i ) {
## february
$help {MONTH} = "02";
} elsif ( $date =~ /^\s*MAR/i ) {
## march
$help {MONTH} = "03";
} elsif ( $date =~ /^\s*APR/i ) {
## april
$help {MONTH} = "04";
} elsif ( $date =~ /^\s*MAY/i ) {
## may
$help {MONTH} = "05";
} elsif ( $date =~ /^\s*JUN/i ) {
## june
$help {MONTH} = "06";
} elsif ( $date =~ /^\s*JUL/i ) {
## july
$help {MONTH} = "07";
} elsif ( $date =~ /^\s*AUG/i ) {
## august
$help {MONTH} = "08";
} elsif ( $date =~ /^\s*SEP/i ) {
## september
$help {MONTH} = "09";
} elsif ( $date =~ /^\s*OCT/i ) {
## october
$help {MONTH} = "10";
} elsif ( $date =~ /^\s*NOV/i ) {
## november
$help {MONTH} = "11";
} elsif ( $date =~ /^\s*DEC/i ) {
## december
$help {MONTH} = "12";
} else {
my @call = caller ( 1 );
## return illegal
$self->setError (7755022,
$self->{gettext} ("OpenCA::OpenSSL->getNumericDate: Illegal month."));
print STDERR $call[2] . "::" . $call[1] . "->" .
"[orig was $date]\n";
return undef;
}
## day
$date =~ s/^ *//;
$date = substr ($date, 4, length ($date)-4);
$help {DAY} = substr ($date, 0, 2);
$help {DAY} =~ s/ /0/;
## hour
$help {HOUR} = substr ($date, 3, 2);
## minute
$help {MINUTE} = substr ($date, 6, 2);
## second
$help {SECOND} = substr ($date, 9, 2);
## year
$help {YEAR} = substr ($date, 12, 4);
## build date
$new_date = $help {YEAR}.
$help {MONTH}.
$help {DAY}.
$help {HOUR}.
$help {MINUTE}.
$help {SECOND};
return $new_date;
}
sub getNumericDateDays {
my $self = shift;
my $date = shift;
my $tmpVal = undef;
my @monVals = ( 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 );
my $counter = 0;
my $ret = 0;
if( not $date ) {
$date = gmtime();
}
$date = $self->getNumericDate ( $date ) if ( $date =~ /[a-zA-Z]+/ );
# Years * 365
$tmpVal = substr ( $date, 0, 4) * 365;
$ret += $tmpVal;
# Months
$tmpVal = substr ( $date, 4, 2 );
for ( my $counter = 1; $counter < $tmpVal ; $counter++ ) {
$ret += $monVals[$counter - 1];
}
$tmpVal = substr ( $date, 6, 2 );
$ret += $tmpVal;
return $ret;
}
##############################################################
## OpenSSL execution environment ##
## BEGIN ##
##############################################################
sub _start_shell
{
my $self = shift;
$self->_debug ("_start_shell: try to start shell");
my $keys = { @_ };
return 1 if ($self->{OPENSSL});
my $open = "| ".$self->{shell}.
" 1>$self->{tmpDir}/${$}_stdout.log".
" 2>$self->{tmpDir}/${$}_stderr.log";
$self->_debug ("_start_shell: $open");
if (not open $self->{OPENSSL}, $open)
{
my $msg = $self->{gettext} ("Cannot start OpenSSL shell. (__ERRVAL__)",
"__ERRVAL__", $!);
$self->setError (7777030, $msg);
return undef;
}
$self->_debug ("_start_shell: shell started");
if ($self->{ENGINE} and
(
exists $self->{PRE_ENGINE} or
exists $self->{POST_ENGINE}
)
)
{
$self->_debug ("_start_shell: initializing engine");
my $command;
if ($self->{DYNAMIC_ENGINE}) {
$command = "engine dynamic -pre ID:".$self->{ENGINE};
} else {
$command = "engine ".$self->{ENGINE};
}
$command .= $self->_build_engine_params(KEY_USAGE => $keys->{KEY_USAGE});
$command .= "\n";
if (not print {$self->{OPENSSL}} $command)
{
my $msg = $self->{gettext} ("Cannot write to the OpenSSL shell. (__ERRVAL__)",
"__ERRVAL__", $!);
$self->setError (7777040, $msg);
return undef;
}
$self->_debug ("_start_shell: engine intialized");
}
return 1;
}
sub _stop_shell
{
my $self = shift;
$self->_debug ("_stop_shell: try to stop shell");
return 1 if (not $self->{OPENSSL});
print {$self->{OPENSSL}} "exit\n";
close $self->{OPENSSL};
$self->{OPENSSL} = undef;
return 1;
}
sub _execute_command
{
my $self = shift;
$self->_debug ("_execute_command: entering function");
my $keys = { @_ };
## initialize openssl (with engine if necessary)
return undef if (not $self->_start_shell(KEY_USAGE => $keys->{KEY_USAGE}));
## run command
my $command = $keys->{COMMAND};
my $input = undef;
$input = $keys->{INPUT} if (exists $keys->{INPUT});
$command =~ s/\n*$//;
$command .= "\n";
$self->_debug ("_execute_command: $command");
if (not print {$self->{OPENSSL}} $command)
{
my $msg = $self->{gettext} ("Cannot write to the OpenSSL shell. (__ERRVAL__)",
"__ERRVAL__", $!);
$self->setError (7777060, $msg);
return undef;
}
$self->_debug ("_execute_command: executed");
## if key is used and pin callback is set then use it
if ($keys->{KEY_USAGE} and
$self->{PIN_CALLBACK} and
$self->{CALLBACK_HANDLER})
{
$self->_debug ("_execute_command: executing pin callback");
$self->{PIN_CALLBACK} ($self->{CALLBACK_HANDLER});
$self->_debug ("_execute_command: pin callback executed");
}
## send the input
if ($input and not print {$self->{OPENSSL}} $input."\x00")
{
$self->_debug ("_execute_command: write input data");
my $msg = $self->{gettext} ("Cannot write to the OpenSSL shell. (__ERRVAL__)",
"__ERRVAL__", $!);
$self->setError (7777065, $msg);
return undef;
}
$self->_debug ("_execute_command: command executed - stopping shell");
return undef if (not $self->_stop_shell());
## check for errors
$self->_debug ("_execute_command: check for error");
if (-e "$self->{tmpDir}/${$}_stderr.log")
{
$self->_debug ("_execute_command: detected error log");
## there was an error
my $ret = "";
if (open FD, "$self->{tmpDir}/${$}_stderr.log")
{
while( my $tmp = <FD> ) {
$ret .= $tmp;
}
close(FD);
}
$self->_debug ("_execute_command: stderr: $ret");
if ($self->{ENGINE} and
(
exists $self->{PRE_ENGINE} or
exists $self->{POST_ENGINE}
) and
$self->{STDERR_CALLBACK} and
$self->{CALLBACK_HANDLER})
{
$self->_debug ("_execute_command: executing stderr callback");
$ret = $self->{STDERR_CALLBACK} ($self->{CALLBACK_HANDLER}, $ret);
$self->_debug ("_execute_command: stderr callback executed");
}
unlink ("$self->{tmpDir}/${$}_stderr.log");
if ($ret =~ /error/i)
{
unlink ("$self->{tmpDir}/${$}_stdout.log");
$self->setError (7777067, $ret);
return undef;
}
}
## load the output
my $ret = 1;
if (-e "$self->{tmpDir}/${$}_stdout.log" and
open FD, "$self->{tmpDir}/${$}_stdout.log")
{
## there was an output
$ret = "";
while( my $tmp = <FD> ) {
$ret .= $tmp;
}
close(FD);
if ($self->{ENGINE} and
(
exists $self->{PRE_ENGINE} or
exists $self->{POST_ENGINE}
) and
$self->{STDOUT_CALLBACK} and
$self->{CALLBACK_HANDLER})
{
$self->_debug ("_execute_command: executing stdout callback");
$ret = $self->{STDOUT_CALLBACK} ($self->{CALLBACK_HANDLER}, $ret);
$self->_debug ("_execute_command: stdout callback executed");
}
$ret =~ s/^(OpenSSL>\s)*//s;
$ret =~ s/OpenSSL>\s$//s;
$ret = 1 if ($ret eq "");
}
unlink ("$self->{tmpDir}/${$}_stdout.log");
my $msg = $ret;
$msg = "<NOT LOGGED>" if ($keys->{HIDE_OUTPUT});
$self->_debug ("_execute_command: leaving successful (return: $msg)");
return $ret;
}
sub _build_engine_params
{
my $self = shift;
my $keys = { @_ };
my $command = "";
## set the pre init commands for the engine
if (exists $self->{PRE_ENGINE})
{
if (ref $self->{PRE_ENGINE})
{
foreach my $item (@{$self->{PRE_ENGINE}})
{
$command .= " -pre $item";
}
} else
{
$command .= " -pre ".$self->{PRE_ENGINE};
}
}
## set the post init commands for the engine
if (exists $self->{POST_ENGINE})
{
if (ref $self->{POST_ENGINE})
{
foreach my $item (@{$self->{POST_ENGINE}})
{
$command .= " -post $item";
}
} else
{
$command .= " -post ".$self->{POST_ENGINE};
}
}
$self->_debug ("_build_engine_params: $command");
## set the pin if there is no pin callback for the engine
## this must be present after -pre SO_PATH:... because
## otherwise PIN is executed on
## the original pkcs11 engine for example which is from Bull and does
## not support PINs like OpenSC
if ($keys->{KEY_USAGE} and
$self->{GET_PIN_CALLBACK} and
$self->{CALLBACK_HANDLER}
)
{
$self->_debug ("_build_engine_params: GET_PIN_CALLBACK used");
$command .= " ".$self->{GET_PIN_CALLBACK} ($self->{CALLBACK_HANDLER});
} else {
$self->_debug ("_build_engine_params: GET_PIN_CALLBACK not present or not used");
}
return $command;
}
##############################################################
## END ##
## OpenSSL execution environment ##
##############################################################
sub _debug
{
my $self = shift;
return 1 if (not $self->{DEBUG});
my $text = join (" ", @_);
$text =~ s/PIN:[^\s]*/PIN:ERASING_FOR_SECURITY/g;
$text =~ s/-----BEGIN RSA PRIVATE KEY.*?END RSA PRIVATE KEY-----/RSA PRIVATE KEY ERASED FROM OUTPUT/gs;
print STDERR "OpenCA::OpenSSL->$text\n";
return 1;
}
sub DESTROY
{
my $self = shift;
$self->_stop_shell();
}
################################################################################
## OpenCA::OpenSSL::Fast area ##
################################################################################
require Exporter;
use AutoLoader;
our @ISA = qw(Exporter);
# Items to export into callers namespace by default. Note: do not export
# names by default without a very good reason. Use EXPORT_OK instead.
# Do not simply export all your public functions/methods/constants.
# This allows declaration use OpenCA::OpenSSL::Fast ':all';
# If you do not need this, moving things directly into @EXPORT or @EXPORT_OK
# will save memory.
our %EXPORT_TAGS = ( 'all' => [ qw(
CTX_TEST
EXFLAG_BCONS
EXFLAG_CA
EXFLAG_INVALID
EXFLAG_KUSAGE
EXFLAG_NSCERT
EXFLAG_SET
EXFLAG_SS
EXFLAG_V1
EXFLAG_XKUSAGE
GEN_DIRNAME
GEN_DNS
GEN_EDIPARTY
GEN_EMAIL
GEN_IPADD
GEN_OTHERNAME
GEN_RID
GEN_URI
GEN_X400
KU_CRL_SIGN
KU_DATA_ENCIPHERMENT
KU_DECIPHER_ONLY
KU_DIGITAL_SIGNATURE
KU_ENCIPHER_ONLY
KU_KEY_AGREEMENT
KU_KEY_CERT_SIGN
KU_KEY_ENCIPHERMENT
KU_NON_REPUDIATION
NS_OBJSIGN
NS_OBJSIGN_CA
NS_SMIME
NS_SMIME_CA
NS_SSL_CA
NS_SSL_CLIENT
NS_SSL_SERVER
X509V3_EXT_CTX_DEP
X509V3_EXT_DYNAMIC
X509V3_EXT_MULTILINE
X509V3_F_COPY_EMAIL
X509V3_F_COPY_ISSUER
X509V3_F_DO_EXT_CONF
X509V3_F_DO_EXT_I2D
X509V3_F_HEX_TO_STRING
X509V3_F_I2S_ASN1_ENUMERATED
X509V3_F_I2S_ASN1_INTEGER
X509V3_F_I2V_AUTHORITY_INFO_ACCESS
X509V3_F_NOTICE_SECTION
X509V3_F_NREF_NOS
X509V3_F_POLICY_SECTION
X509V3_F_R2I_CERTPOL
X509V3_F_S2I_ASN1_IA5STRING
X509V3_F_S2I_ASN1_INTEGER
X509V3_F_S2I_ASN1_OCTET_STRING
X509V3_F_S2I_ASN1_SKEY_ID
X509V3_F_S2I_S2I_SKEY_ID
X509V3_F_STRING_TO_HEX
X509V3_F_SXNET_ADD_ASC
X509V3_F_SXNET_ADD_ID_INTEGER
X509V3_F_SXNET_ADD_ID_ULONG
X509V3_F_SXNET_GET_ID_ASC
X509V3_F_SXNET_GET_ID_ULONG
X509V3_F_V2I_ACCESS_DESCRIPTION
X509V3_F_V2I_ASN1_BIT_STRING
X509V3_F_V2I_AUTHORITY_KEYID
X509V3_F_V2I_BASIC_CONSTRAINTS
X509V3_F_V2I_CRLD
X509V3_F_V2I_EXT_KU
X509V3_F_V2I_GENERAL_NAME
X509V3_F_V2I_GENERAL_NAMES
X509V3_F_V3_GENERIC_EXTENSION
X509V3_F_X509V3_ADD_VALUE
X509V3_F_X509V3_EXT_ADD
X509V3_F_X509V3_EXT_ADD_ALIAS
X509V3_F_X509V3_EXT_CONF
X509V3_F_X509V3_EXT_I2D
X509V3_F_X509V3_GET_VALUE_BOOL
X509V3_F_X509V3_PARSE_LIST
X509V3_F_X509_PURPOSE_ADD
X509V3_R_BAD_IP_ADDRESS
X509V3_R_BAD_OBJECT
X509V3_R_BN_DEC2BN_ERROR
X509V3_R_BN_TO_ASN1_INTEGER_ERROR
X509V3_R_DUPLICATE_ZONE_ID
X509V3_R_ERROR_CONVERTING_ZONE
X509V3_R_ERROR_IN_EXTENSION
X509V3_R_EXPECTED_A_SECTION_NAME
X509V3_R_EXTENSION_NAME_ERROR
X509V3_R_EXTENSION_NOT_FOUND
X509V3_R_EXTENSION_SETTING_NOT_SUPPORTED
X509V3_R_EXTENSION_VALUE_ERROR
X509V3_R_ILLEGAL_HEX_DIGIT
X509V3_R_INVALID_BOOLEAN_STRING
X509V3_R_INVALID_EXTENSION_STRING
X509V3_R_INVALID_NAME
X509V3_R_INVALID_NULL_ARGUMENT
X509V3_R_INVALID_NULL_NAME
X509V3_R_INVALID_NULL_VALUE
X509V3_R_INVALID_NUMBER
X509V3_R_INVALID_NUMBERS
X509V3_R_INVALID_OBJECT_IDENTIFIER
X509V3_R_INVALID_OPTION
X509V3_R_INVALID_POLICY_IDENTIFIER
X509V3_R_INVALID_SECTION
X509V3_R_INVALID_SYNTAX
X509V3_R_ISSUER_DECODE_ERROR
X509V3_R_MISSING_VALUE
X509V3_R_NEED_ORGANIZATION_AND_NUMBERS
X509V3_R_NO_CONFIG_DATABASE
X509V3_R_NO_ISSUER_CERTIFICATE
X509V3_R_NO_ISSUER_DETAILS
X509V3_R_NO_POLICY_IDENTIFIER
X509V3_R_NO_PUBLIC_KEY
X509V3_R_NO_SUBJECT_DETAILS
X509V3_R_ODD_NUMBER_OF_DIGITS
X509V3_R_UNABLE_TO_GET_ISSUER_DETAILS
X509V3_R_UNABLE_TO_GET_ISSUER_KEYID
X509V3_R_UNKNOWN_BIT_STRING_ARGUMENT
X509V3_R_UNKNOWN_EXTENSION
X509V3_R_UNKNOWN_EXTENSION_NAME
X509V3_R_UNKNOWN_OPTION
X509V3_R_UNSUPPORTED_OPTION
X509V3_R_USER_TOO_LONG
X509_PURPOSE_ANY
X509_PURPOSE_CRL_SIGN
X509_PURPOSE_DYNAMIC
X509_PURPOSE_DYNAMIC_NAME
X509_PURPOSE_MAX
X509_PURPOSE_MIN
X509_PURPOSE_NS_SSL_SERVER
X509_PURPOSE_SMIME_ENCRYPT
X509_PURPOSE_SMIME_SIGN
X509_PURPOSE_SSL_CLIENT
X509_PURPOSE_SSL_SERVER
XKU_CODE_SIGN
XKU_SGC
XKU_SMIME
XKU_SSL_CLIENT
XKU_SSL_SERVER
) ] );
our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
our @EXPORT = qw(
CTX_TEST
EXFLAG_BCONS
EXFLAG_CA
EXFLAG_INVALID
EXFLAG_KUSAGE
EXFLAG_NSCERT
EXFLAG_SET
EXFLAG_SS
EXFLAG_V1
EXFLAG_XKUSAGE
GEN_DIRNAME
GEN_DNS
GEN_EDIPARTY
GEN_EMAIL
GEN_IPADD
GEN_OTHERNAME
GEN_RID
GEN_URI
GEN_X400
KU_CRL_SIGN
KU_DATA_ENCIPHERMENT
KU_DECIPHER_ONLY
KU_DIGITAL_SIGNATURE
KU_ENCIPHER_ONLY
KU_KEY_AGREEMENT
KU_KEY_CERT_SIGN
KU_KEY_ENCIPHERMENT
KU_NON_REPUDIATION
NS_OBJSIGN
NS_OBJSIGN_CA
NS_SMIME
NS_SMIME_CA
NS_SSL_CA
NS_SSL_CLIENT
NS_SSL_SERVER
X509V3_EXT_CTX_DEP
X509V3_EXT_DYNAMIC
X509V3_EXT_MULTILINE
X509V3_F_COPY_EMAIL
X509V3_F_COPY_ISSUER
X509V3_F_DO_EXT_CONF
X509V3_F_DO_EXT_I2D
X509V3_F_HEX_TO_STRING
X509V3_F_I2S_ASN1_ENUMERATED
X509V3_F_I2S_ASN1_INTEGER
X509V3_F_I2V_AUTHORITY_INFO_ACCESS
X509V3_F_NOTICE_SECTION
X509V3_F_NREF_NOS
X509V3_F_POLICY_SECTION
X509V3_F_R2I_CERTPOL
X509V3_F_S2I_ASN1_IA5STRING
X509V3_F_S2I_ASN1_INTEGER
X509V3_F_S2I_ASN1_OCTET_STRING
X509V3_F_S2I_ASN1_SKEY_ID
X509V3_F_S2I_S2I_SKEY_ID
X509V3_F_STRING_TO_HEX
X509V3_F_SXNET_ADD_ASC
X509V3_F_SXNET_ADD_ID_INTEGER
X509V3_F_SXNET_ADD_ID_ULONG
X509V3_F_SXNET_GET_ID_ASC
X509V3_F_SXNET_GET_ID_ULONG
X509V3_F_V2I_ACCESS_DESCRIPTION
X509V3_F_V2I_ASN1_BIT_STRING
X509V3_F_V2I_AUTHORITY_KEYID
X509V3_F_V2I_BASIC_CONSTRAINTS
X509V3_F_V2I_CRLD
X509V3_F_V2I_EXT_KU
X509V3_F_V2I_GENERAL_NAME
X509V3_F_V2I_GENERAL_NAMES
X509V3_F_V3_GENERIC_EXTENSION
X509V3_F_X509V3_ADD_VALUE
X509V3_F_X509V3_EXT_ADD
X509V3_F_X509V3_EXT_ADD_ALIAS
X509V3_F_X509V3_EXT_CONF
X509V3_F_X509V3_EXT_I2D
X509V3_F_X509V3_GET_VALUE_BOOL
X509V3_F_X509V3_PARSE_LIST
X509V3_F_X509_PURPOSE_ADD
X509V3_R_BAD_IP_ADDRESS
X509V3_R_BAD_OBJECT
X509V3_R_BN_DEC2BN_ERROR
X509V3_R_BN_TO_ASN1_INTEGER_ERROR
X509V3_R_DUPLICATE_ZONE_ID
X509V3_R_ERROR_CONVERTING_ZONE
X509V3_R_ERROR_IN_EXTENSION
X509V3_R_EXPECTED_A_SECTION_NAME
X509V3_R_EXTENSION_NAME_ERROR
X509V3_R_EXTENSION_NOT_FOUND
X509V3_R_EXTENSION_SETTING_NOT_SUPPORTED
X509V3_R_EXTENSION_VALUE_ERROR
X509V3_R_ILLEGAL_HEX_DIGIT
X509V3_R_INVALID_BOOLEAN_STRING
X509V3_R_INVALID_EXTENSION_STRING
X509V3_R_INVALID_NAME
X509V3_R_INVALID_NULL_ARGUMENT
X509V3_R_INVALID_NULL_NAME
X509V3_R_INVALID_NULL_VALUE
X509V3_R_INVALID_NUMBER
X509V3_R_INVALID_NUMBERS
X509V3_R_INVALID_OBJECT_IDENTIFIER
X509V3_R_INVALID_OPTION
X509V3_R_INVALID_POLICY_IDENTIFIER
X509V3_R_INVALID_SECTION
X509V3_R_INVALID_SYNTAX
X509V3_R_ISSUER_DECODE_ERROR
X509V3_R_MISSING_VALUE
X509V3_R_NEED_ORGANIZATION_AND_NUMBERS
X509V3_R_NO_CONFIG_DATABASE
X509V3_R_NO_ISSUER_CERTIFICATE
X509V3_R_NO_ISSUER_DETAILS
X509V3_R_NO_POLICY_IDENTIFIER
X509V3_R_NO_PUBLIC_KEY
X509V3_R_NO_SUBJECT_DETAILS
X509V3_R_ODD_NUMBER_OF_DIGITS
X509V3_R_UNABLE_TO_GET_ISSUER_DETAILS
X509V3_R_UNABLE_TO_GET_ISSUER_KEYID
X509V3_R_UNKNOWN_BIT_STRING_ARGUMENT
X509V3_R_UNKNOWN_EXTENSION
X509V3_R_UNKNOWN_EXTENSION_NAME
X509V3_R_UNKNOWN_OPTION
X509V3_R_UNSUPPORTED_OPTION
X509V3_R_USER_TOO_LONG
X509_PURPOSE_ANY
X509_PURPOSE_CRL_SIGN
X509_PURPOSE_DYNAMIC
X509_PURPOSE_DYNAMIC_NAME
X509_PURPOSE_MAX
X509_PURPOSE_MIN
X509_PURPOSE_NS_SSL_SERVER
X509_PURPOSE_SMIME_ENCRYPT
X509_PURPOSE_SMIME_SIGN
X509_PURPOSE_SSL_CLIENT
X509_PURPOSE_SSL_SERVER
XKU_CODE_SIGN
XKU_SGC
XKU_SMIME
XKU_SSL_CLIENT
XKU_SSL_SERVER
);
## we take the version from OpenSSL.pm
## our $VERSION = '0.02';
sub AUTOLOAD {
# This AUTOLOAD is used to 'autoload' constants from the constant()
# XS function.
my $constname;
our $AUTOLOAD;
($constname = $AUTOLOAD) =~ s/.*:://;
croak "&OpenCA::OpenSSL::constant not defined" if $constname eq 'constant';
my ($error, $val) = constant($constname);
if ($error) { croak $error; }
{
no strict 'refs';
# Fixed between 5.005_53 and 5.005_61
#XXX if ($] >= 5.00561) {
#XXX *$AUTOLOAD = sub () { $val };
#XXX }
#XXX else {
*$AUTOLOAD = sub { $val };
#XXX }
}
goto &$AUTOLOAD;
}
require XSLoader;
XSLoader::load('OpenCA::OpenSSL', $OpenCA::OpenSSL::VERSION);
# Autoload methods go after =cut, and are processed by the autosplit program.
1;
__END__