## OpenCA::PKCS7 ## ## Copyright (C) 1998-1999 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.] ## ## the module's errorcode is 79 ## ## functions ## ## new 11 ## initSignature 12 ## getParsed 21 ## getSigner 22 ## verifyChain 31 ## parseDepth 32 ## getSignature 23 use strict; use X500::DN; package OpenCA::PKCS7; our ($errno, $errval); ($OpenCA::PKCS7::VERSION = '$Revision: 1.13 $' )=~ s/(?:^.*: (\d+))|(?:\s+\$$)/defined $1?"0\.9":""/eg; my %params = ( inFile => undef, signature => undef, dataFile => undef, caCert => undef, caDir => undef, parsed => undef, context => undef, backend => undef, status => undef, nochain => undef, ); sub setError { my $self = shift; if (scalar (@_) == 4) { my $keys = { @_ }; $errval = $keys->{ERRVAL}; $errno = $keys->{ERRNO}; } else { $errno = $_[0]; $errval = $_[1]; } ## support for: return $self->setError (1234, "Something fails.") if (not $xyz); return 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 = { @_ }; my $tmp; $self->{caCert} = $keys->{CA_CERT}; $self->{caDir} = $keys->{CA_DIR}; $self->{dataFile} = $keys->{DATAFILE}; $self->{data} = $keys->{DATA}; $self->{inFile} = $keys->{INFILE}; $self->{signature} = $keys->{SIGNATURE}; $self->{backend} = $keys->{SHELL}; $self->{nochain} = $keys->{NOCHAIN}; if( ($self->{inFile}) and ( -e "$self->{inFile}") ) { $self->{signature} = ""; open(FD, "<$self->{inFile}" ) or return $self->setError (7911021, "OpenCA::PKCS7->new: Cannot open infile ". $self->{inFile}." for reading."); while ( $tmp = ) { $self->{signature} .= $tmp; } close(FD); }; if( ($self->{dataFile}) and ( -e "$self->{dataFile}") ) { $self->{data} = ""; open(FD, "<$self->{dataFile}" ) or return $self->setError (7911023, "OpenCA::PKCS7->new: Cannot open datafile ". $self->{dataFile}." for reading."); while ( $tmp = ) { $self->{data} .= $tmp; } close(FD); }; if (not $self->{data} and $self->{inFile} and ( -e "$self->{inFile}")) { $self->{data} = ""; open(FD, "<$self->{inFile}" ) or return $self->setError (7911025, "OpenCA::PKCS7->new: Cannot open infile ". $self->{inFile}." for reading."); while ( $tmp = ) { $self->{data} .= $tmp; } close(FD); }; return $self->setError (7911031, "OpenCA::PKCS7->new: Cannot initialize signature ($errno)\n$errval") if (not $self->initSignature() ); return $self; } sub initSignature { my $self = shift; my $keys = { @_ }; my $tmp; return $self->setError (7912011, "OpenCA::PKCS7->initSignature: No signature specified.") if (not $self->{signature}); return $self->setError (7912012, "OpenCA::PKCS7->initSignature: No data specified.") if (not $self->{data}); if( $self->getParsed() ) { return 1; } else { return $self->setError (7912021, "OpenCA::PKCS7->initSignature: Cannot parse signature ($errno)\n$errval.") } } sub getParsed { my $self = shift; my $keys = { @_ }; my ( $ret, $tmp ); $tmp = $self->{backend}->verify( SIGNATURE=>$self->{signature}, ## old: DATA_FILE=>$self->{dataFile}, DATA => $self->{data}, CA_CERT => $self->{caCert}, CA_DIR => $self->{caDir}, VERBOSE => 1, NOCHAIN => $self->{nochain} ); ## why should verify the signature twice? #$tmp = $self->{backend}->verify( SIGNATURE=>$self->{signature}, # DATA_FILE=>$self->{dataFile}, # NOCHAIN=>1, # VERBOSE=>1 ); if (not $tmp) { $self->{status} = $OpenCA::OpenSSL::errno; return $self->setError (7921021, "OpenCA::PKCS7->getParsed: The crypto-backend cannot verify the signature ". "(".$OpenCA::OpenSSL::errno.")\n".$OpenCA::OpenSSL::errval); } if ( not $ret = $self->parseDepth( DEPTH=>"0", DATA=>$tmp ) ) { $self->{status} = $OpenCA::OpenSSL::errno; return $self->setError (7921031, "OpenCA::PKCS7->getParsed: Cannot parse the signer ($errno)\n$errval"); } $self->{parsed}->{SIGNER} = $ret->{0}; if ( ( $tmp ) and ( $ret = $self->parseDepth( DATA=>$tmp )) ) { $self->{parsed}->{CHAIN} = $ret; } $self->{parsed}->{SIGNER}->{CERTIFICATE} = $self->{backend}->pkcs7Certs( PKCS7=>$self->{signature}); $self->{parsed}->{SIGNATURE} = $self->{signature}; return $self->{parsed}; } sub status { my $self = shift; return $self->{status}; } sub getSigner { my $self = shift; my $keys = { @_ }; my ( $tmp, $ret ); return $self->setError (7922011, "OpenCA::PKCS7->getSigner: The signature was not parsed.") if( not $self->{parsed} ); return $self->{parsed}->{SIGNER}; } sub verifyChain { my $self = shift; my $keys = { @_ }; my ( $tmp, $ret ); #unnecessary because the signature was already loaded #if ( $self->{inFile} ) { # $tmp=$self->{backend}->verify( SIGNATURE_FILE => $self->{inFile}, # DATA => $self->{data}, # CA_CERT => $self->{caCert}, # CA_DIR => $self->{caDir}, # VERBOSE => 1 ); #} else { $tmp=$self->{backend}->verify( SIGNATURE => $self->{signature}, DATA => $self->{data}, CA_CERT => $self->{caCert}, CA_DIR => $self->{caDir}, VERBOSE => 1 ); #}; return $self->setError (7931021, "OpenCA::PKCS7->verifyChain: The crypto-backend fails ". "(".$OpenCA::OpenSSL::errno.")\n".$OpenCA::OpenSSL::errval) if (not $tmp); ## Returns if signature is not valid (verify returned an error) return $self->setError (7931022, "OpenCA::PKCS7->verifyChain: The crypto-backend fails.") if( $? != 0 ); if ( not $ret = $self->parseDepth( DEPTH=>"0", DATA=>$tmp ) ) { return $self->setError (7931031, "OpenCA::PKCS7->verifyChain: Cannot parse the signer ($errno)\n$errval"); } return $ret; } sub parseDepth { my $self = shift; my $keys = { @_ }; my $depth = $keys->{DEPTH}; my $data = $keys->{DATA}; my @dnList = (); my @ouList = (); my ( $serial, $dn, $email, $cn, @ou, $o, $c ); my ( $currentDepth, $subject, $tmp, $line, $ret ); return $self->setError (7932011, "OpenCA::PKCS7->parseDepth: No data specified.") if (not $data); my @lines = split ( /(\n|\r)/ , $data ); while( $line = shift @lines ) { if ($line =~ /^\s*error:20:/i) { $self->setError (7932021, "OpenCA::PKCS7->parseDepth: The chain is not complete."); $self->{status} = 20; } elsif ($line =~ /^\s*error:18:/i) { if (!$self->{nochain}) { $self->setError (7932023, "OpenCA::PKCS7->parseDepth: Selfsigned certificate at depth 0."); $self->{status} = 18; } } elsif ($line =~ /^\s*error:/i) { $self->setError (7932039, "OpenCA::PKCS7->parseDepth: ". "There is a problem with the verification of the chain ($line)."); ($self->{status}) = ( $line =~ /^\s*error:([^:]*):/ ); } next if( $line != /^depth/i ); ( $currentDepth, $serial, $dn ) = ( $line =~ /depth:([\d]+) serial:([a-f\d]+) subject:(.*)/ ); $ret->{$currentDepth}->{SERIAL} = hex ($serial) ; $ret->{$currentDepth}->{DN} = $dn; ## Split the Subject into separate fields @dnList = split( /[\,\/]+/, $dn ); @ouList = (); my $tmpOU; ## load the differnt parts of the DN into DN_HASH print "OpenCA::PKCS7->parseDepth: DN: ".$dn."
\n" if ($self->{DEBUG}); my $x500_dn = X500::DN->ParseRFC2253 ($dn); if (not $x500_dn) { print "OpenCA::PKCS7->parseDepth: X500::DN failed
\n" if ($self->{DEBUG}); return $self->setError (7932081, "OpenCA::PKCS7->parseDepth: X500::DN failed."); return undef; } my $rdn; foreach $rdn ($x500_dn->getRDNs()) { next if ($rdn->isMultivalued()); my @attr_types = $rdn->getAttributeTypes(); my $type = $attr_types[0]; my $value = $rdn->getAttributeValue ($type); push (@{$ret->{$currentDepth}->{DN_HASH}->{uc($type)}}, $value); print "OpenCA::PKCS7->parseDepth: DN_HASH: $type=$value
\n" if ($self->{DEBUG}); } } return $ret; } sub getSignature { my $self = shift; return $self->setError (7923011, "OpenCA::PKCS7->getSignature: There is no signature present.") if( not $self->{signature} ); return $self->{signature}; } sub getSignerCert { my $self = shift; my $keys = { @_ }; } # Autoload methods go after =cut, and are processed by the autosplit program. 1;