| blib/lib/OpenCA/X509.pm | |||
|---|---|---|---|
| Criterion | Covered | Total | % | 
| statement | 9 | 270 | 3.3 | 
| branch | 0 | 106 | 0.0 | 
| condition | 0 | 30 | 0.0 | 
| subroutine | 3 | 19 | 15.7 | 
| pod | 0 | 16 | 0.0 | 
| total | 12 | 441 | 2.7 | 
| line | stmt | bran | cond | sub | pod | time | code | 
|---|---|---|---|---|---|---|---|
| 1 | ## OpenCA::X509 | ||||||
| 2 | ## | ||||||
| 3 | ## Copyright (C) 1998-1999 Massimiliano Pala (madwolf@openca.org) | ||||||
| 4 | ## All rights reserved. | ||||||
| 5 | ## | ||||||
| 6 | ## This library is free for commercial and non-commercial use as long as | ||||||
| 7 | ## the following conditions are aheared to. The following conditions | ||||||
| 8 | ## apply to all code found in this distribution, be it the RC4, RSA, | ||||||
| 9 | ## lhash, DES, etc., code; not just the SSL code. The documentation | ||||||
| 10 | ## included with this distribution is covered by the same copyright terms | ||||||
| 11 | ## | ||||||
| 12 | ## Copyright remains Massimiliano Pala's, and as such any Copyright notices | ||||||
| 13 | ## in the code are not to be removed. | ||||||
| 14 | ## If this package is used in a product, Massimiliano Pala should be given | ||||||
| 15 | ## attribution as the author of the parts of the library used. | ||||||
| 16 | ## This can be in the form of a textual message at program startup or | ||||||
| 17 | ## in documentation (online or textual) provided with the package. | ||||||
| 18 | ## | ||||||
| 19 | ## Redistribution and use in source and binary forms, with or without | ||||||
| 20 | ## modification, are permitted provided that the following conditions | ||||||
| 21 | ## are met: | ||||||
| 22 | ## 1. Redistributions of source code must retain the copyright | ||||||
| 23 | ## notice, this list of conditions and the following disclaimer. | ||||||
| 24 | ## 2. Redistributions in binary form must reproduce the above copyright | ||||||
| 25 | ## notice, this list of conditions and the following disclaimer in the | ||||||
| 26 | ## documentation and/or other materials provided with the distribution. | ||||||
| 27 | ## 3. All advertising materials mentioning features or use of this software | ||||||
| 28 | ## must display the following acknowledgement: | ||||||
| 29 | ## "This product includes OpenCA software written by Massimiliano Pala | ||||||
| 30 | ## (madwolf@openca.org) and the OpenCA Group (www.openca.org)" | ||||||
| 31 | ## 4. If you include any Windows specific code (or a derivative thereof) from | ||||||
| 32 | ## some directory (application code) you must include an acknowledgement: | ||||||
| 33 | ## "This product includes OpenCA software (www.openca.org)" | ||||||
| 34 | ## | ||||||
| 35 | ## THIS SOFTWARE IS PROVIDED BY OPENCA DEVELOPERS ``AS IS'' AND | ||||||
| 36 | ## ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||||||
| 37 | ## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||||||
| 38 | ## ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE | ||||||
| 39 | ## FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||||||
| 40 | ## DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | ||||||
| 41 | ## OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | ||||||
| 42 | ## HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | ||||||
| 43 | ## LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | ||||||
| 44 | ## OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | ||||||
| 45 | ## SUCH DAMAGE. | ||||||
| 46 | ## | ||||||
| 47 | ## The licence and distribution terms for any publically available version or | ||||||
| 48 | ## derivative of this code cannot be changed. i.e. this code cannot simply be | ||||||
| 49 | ## copied and put under another distribution licence | ||||||
| 50 | ## [including the GNU Public Licence.] | ||||||
| 51 | ## | ||||||
| 52 | |||||||
| 53 | ## the module's errorcode is 74 | ||||||
| 54 | ## | ||||||
| 55 | ## functions | ||||||
| 56 | ## | ||||||
| 57 | ## new 11 | ||||||
| 58 | ## init 12 | ||||||
| 59 | ## getHeader 21 | ||||||
| 60 | ## getKey 22 | ||||||
| 61 | ## getBody 23 | ||||||
| 62 | ## getParsed 31 | ||||||
| 63 | ## parseCert 13 | ||||||
| 64 | ## getPEM 41 | ||||||
| 65 | ## getPEMHeader 42 | ||||||
| 66 | ## getDER 43 | ||||||
| 67 | ## getTXT 44 | ||||||
| 68 | ## setHeaderAttribute 51 | ||||||
| 69 | ## getItem 61 | ||||||
| 70 | ## getSerial 62 | ||||||
| 71 | ## setParams 71 | ||||||
| 72 | |||||||
| 73 | 1 | 1 | 1212 | use strict; | |||
| 1 | 2 | ||||||
| 1 | 40 | ||||||
| 74 | 1 | 1 | 5 | use Digest::MD5; | |||
| 1 | 1 | ||||||
| 1 | 37 | ||||||
| 75 | 1 | 1 | 784 | use X500::DN; | |||
| 1 | 241046 | ||||||
| 1 | 6143 | ||||||
| 76 | |||||||
| 77 | package OpenCA::X509; | ||||||
| 78 | |||||||
| 79 | our ($errno, $errval); | ||||||
| 80 | |||||||
| 81 | ($OpenCA::X509::VERSION = '$Revision: 1.47 $' )=~ s/(?:^.*: (\d+))|(?:\s+\$$)/defined $1?"0\.9":""/eg; | ||||||
| 82 | |||||||
| 83 | my %params = ( | ||||||
| 84 | cert => undef, | ||||||
| 85 | item => undef, | ||||||
| 86 | pemCert => undef, | ||||||
| 87 | pemHeader => undef, | ||||||
| 88 | derCert => undef, | ||||||
| 89 | txtCert => undef, | ||||||
| 90 | backend => undef, | ||||||
| 91 | parsedItem => undef, | ||||||
| 92 | beginCert => undef, | ||||||
| 93 | endCert => undef, | ||||||
| 94 | beginHeader => undef, | ||||||
| 95 | endHeader => undef, | ||||||
| 96 | beginKey => undef, | ||||||
| 97 | endKey => undef, | ||||||
| 98 | beginAttribute => undef, | ||||||
| 99 | endAttribute => undef, | ||||||
| 100 | certFormat => undef, | ||||||
| 101 | ); | ||||||
| 102 | |||||||
| 103 | sub setError { | ||||||
| 104 | 0 | 0 | 0 | my $self = shift; | |||
| 105 | |||||||
| 106 | 0 | 0 | if (scalar (@_) == 4) { | ||||
| 107 | 0 | my $keys = { @_ }; | |||||
| 108 | 0 | $errval = $keys->{ERRVAL}; | |||||
| 109 | 0 | $errno = $keys->{ERRNO}; | |||||
| 110 | } else { | ||||||
| 111 | 0 | $errno = $_[0]; | |||||
| 112 | 0 | $errval = $_[1]; | |||||
| 113 | } | ||||||
| 114 | |||||||
| 115 | ## support for: return $self->setError (1234, "Something fails.") if (not $xyz); | ||||||
| 116 | 0 | return undef; | |||||
| 117 | } | ||||||
| 118 | |||||||
| 119 | ## Create an instance of the Class | ||||||
| 120 | sub new { | ||||||
| 121 | 0 | 0 | 0 | my $that = shift; | |||
| 122 | 0 | 0 | my $class = ref($that) || $that; | ||||
| 123 | |||||||
| 124 | 0 | my $self = { | |||||
| 125 | %params, | ||||||
| 126 | }; | ||||||
| 127 | |||||||
| 128 | 0 | bless $self, $class; | |||||
| 129 | |||||||
| 130 | 0 | my $keys = { @_ }; | |||||
| 131 | 0 | my ( $infile, $tmp ); | |||||
| 132 | |||||||
| 133 | 0 | $self->{item} = $keys->{DATA}; | |||||
| 134 | 0 | 0 | $self->{certFormat} = ( $keys->{FORMAT} or $keys->{INFORM} or "PEM" ); | ||||
| 135 | 0 | $infile = $keys->{INFILE}; | |||||
| 136 | |||||||
| 137 | 0 | $self->{backend} = $keys->{SHELL}; | |||||
| 138 | |||||||
| 139 | 0 | $self->{beginCert} = "-----BEGIN CERTIFICATE-----"; | |||||
| 140 | 0 | $self->{endCert} = "-----END CERTIFICATE-----"; | |||||
| 141 | 0 | $self->{beginHeader} = "-----BEGIN HEADER-----"; | |||||
| 142 | 0 | $self->{endHeader} = "-----END HEADER-----"; | |||||
| 143 | 0 | $self->{beginAttribute} = "-----BEGIN ATTRIBUTE-----"; | |||||
| 144 | 0 | $self->{endAttribute} = "-----END ATTRIBUTE-----"; | |||||
| 145 | 0 | $self->{beginKey} = "-----BEGIN ENCRYPTED PRIVATE KEY-----"; | |||||
| 146 | 0 | $self->{endKey} = "-----END ENCRYPTED PRIVATE KEY-----"; | |||||
| 147 | |||||||
| 148 | 0 | 0 | if( $infile ) { | ||||
| 149 | 0 | $self->{item} = ""; | |||||
| 150 | |||||||
| 151 | 0 | 0 | open( FD, "<$infile" ) | ||||
| 152 | or return $self->setError (7411011, "OpenCA::X509->new: Cannot open infile $infile for reading."); | ||||||
| 153 | 0 |  		while ( $tmp =  | 
|||||
| 154 | 0 | $self->{item} .= $tmp; | |||||
| 155 | } | ||||||
| 156 | 0 | close( FD ); | |||||
| 157 | } | ||||||
| 158 | |||||||
| 159 | 0 | 0 | 0 | if ( defined($self->{item}) and $self->{item} ne "" ) { | |||
| 160 | 0 | $self->{cert} = $self->getBody( ITEM=>$self->{item} ); | |||||
| 161 | |||||||
| 162 | 0 | 0 | if ( not $self->init() ) { | ||||
| 163 | 0 | return $self->setError (7411021, "OpenCA::X509->new: Cannot initialize certificate ". | |||||
| 164 | "($errno)\n$errval"); | ||||||
| 165 | } | ||||||
| 166 | |||||||
| 167 | } | ||||||
| 168 | |||||||
| 169 | 0 | return $self; | |||||
| 170 | } | ||||||
| 171 | |||||||
| 172 | sub init { | ||||||
| 173 | 0 | 0 | 0 | my $self = shift; | |||
| 174 | |||||||
| 175 | 0 | 0 | return $self->setError (7412011, "OpenCA::X509->init: No certificate present.") | ||||
| 176 | if (not $self->{cert}); | ||||||
| 177 | |||||||
| 178 | 0 | $self->{pemCert} = ""; | |||||
| 179 | |||||||
| 180 | 0 | $self->{derCert} = ""; | |||||
| 181 | |||||||
| 182 | 0 | $self->{txtCert} = ""; | |||||
| 183 | |||||||
| 184 | 0 | $self->{parsedItem} = $self->parseCert(); | |||||
| 185 | 0 | 0 | return $self->setError (7412031, "OpenCA::X509->init: Cannot parse certificate ($errno).\n$errval") | ||||
| 186 | if (not $self->{parsedItem}); | ||||||
| 187 | |||||||
| 188 | ## build pem-header | ||||||
| 189 | 0 | $self->{pemHeader} = $self->{beginHeader}; | |||||
| 190 | 0 | for my $h (keys %{$self->{parsedItem}->{HEADER}}) { | |||||
| 0 | |||||||
| 191 | 0 | $self->{pemHeader} .= "\n".$h."="; | |||||
| 192 | 0 | 0 | if ( $self->{parsedItem}->{HEADER}->{$h} =~ /\n/ ) { | ||||
| 193 | ## multirow attribute | ||||||
| 194 | 0 | $self->{pemHeader} .= "\n".$self->{beginAttribute}. | |||||
| 195 | "\n".$self->{parsedItem}->{HEADER}->{$h}. | ||||||
| 196 | "\n".$self->{endAttribute}; | ||||||
| 197 | } else { | ||||||
| 198 | 0 | $self->{pemHeader} .= $self->{parsedItem}->{HEADER}->{$h}; | |||||
| 199 | } | ||||||
| 200 | } | ||||||
| 201 | 0 | $self->{pemHeader} .= "\n".$self->{endHeader}."\n"; | |||||
| 202 | |||||||
| 203 | 0 | return 1; | |||||
| 204 | } | ||||||
| 205 | |||||||
| 206 | ## modified by michael bell to support multirow-values | ||||||
| 207 | sub getHeader { | ||||||
| 208 | 0 | 0 | 0 | my $self = shift; | |||
| 209 | 0 | my $keys = { @_ }; | |||||
| 210 | 0 | my $req = $keys->{ITEM}; | |||||
| 211 | |||||||
| 212 | 0 | my ( $txt, $ret, $i, $key, $val ); | |||||
| 213 | |||||||
| 214 | 0 | my $beginHeader = $self->{beginHeader}; | |||||
| 215 | 0 | my $endHeader = $self->{endHeader}; | |||||
| 216 | 0 | my $beginAttribute = $self->{beginAttribute}; | |||||
| 217 | 0 | my $endAttribute = $self->{endAttribute}; | |||||
| 218 | |||||||
| 219 | 0 | 0 | if( ($txt) = ( $req =~ /$beginHeader\n([\S\s\n]+)\n$endHeader/m) ) { | ||||
| 220 | 0 | my $active_multirow = 0; | |||||
| 221 | 0 | foreach $i ( split ( /\n/, $txt ) ) { | |||||
| 222 | 0 | 0 | if ($active_multirow) { | ||||
| 0 | |||||||
| 223 | ## multirow | ||||||
| 224 | 0 | 0 | if ($i =~ /^$endAttribute$/) { | ||||
| 225 | ## end of multirow | ||||||
| 226 | 0 | $active_multirow = 0; | |||||
| 227 | } else { | ||||||
| 228 | 0 | 0 | $ret->{$key} .= "\n" if ($ret->{$key}); | ||||
| 229 | ## additional data | ||||||
| 230 | 0 | $ret->{$key} .= $i; | |||||
| 231 | } | ||||||
| 232 | } elsif ($i =~ /^$beginAttribute$/) { | ||||||
| 233 | ## begin of multirow | ||||||
| 234 | 0 | $active_multirow = 1; | |||||
| 235 | } else { | ||||||
| 236 | ## no multirow | ||||||
| 237 | ## if multirow then $ret->{key} is initially empty) | ||||||
| 238 | 0 | $i =~ s/\s*=\s*/=/; | |||||
| 239 | 0 | ( $key, $val ) = ( $i =~ /(.*)\s*=\s*(.*)\s*/ ); | |||||
| 240 | 0 | $ret->{$key} = $val; | |||||
| 241 | } | ||||||
| 242 | } | ||||||
| 243 | } | ||||||
| 244 | |||||||
| 245 | 0 | 0 | if (not defined $ret->{CSR_SERIAL}) | ||||
| 246 | { | ||||||
| 247 | 0 | $ret->{CSR_SERIAL} = -1; | |||||
| 248 | } | ||||||
| 249 | |||||||
| 250 | 0 | return $ret; | |||||
| 251 | } | ||||||
| 252 | |||||||
| 253 | sub getKey { | ||||||
| 254 | 0 | 0 | 0 | my $self = shift; | |||
| 255 | 0 | my $keys = { @_ }; | |||||
| 256 | 0 | my $req = $keys->{ITEM}; | |||||
| 257 | |||||||
| 258 | 0 | my $beginKey = $self->{beginKey}; | |||||
| 259 | 0 | my $endKey = $self->{endKey}; | |||||
| 260 | |||||||
| 261 | 0 | my ( $ret ) = ( $req =~ /($beginKey[\S\s\n]+$endKey)/ ); | |||||
| 262 | 0 | return $ret; | |||||
| 263 | } | ||||||
| 264 | |||||||
| 265 | sub getBody { | ||||||
| 266 | 0 | 0 | 0 | my $self = shift; | |||
| 267 | 0 | my $keys = { @_ }; | |||||
| 268 | 0 | my $req = $keys->{ITEM}; | |||||
| 269 | |||||||
| 270 | 0 | my $beginCert = $self->{beginCert}; | |||||
| 271 | 0 | my $endCert = $self->{endCert}; | |||||
| 272 | |||||||
| 273 | 0 | my ( $ret ) = ( $req =~ /($beginCert[\S\s\n]+$endCert)/ ); | |||||
| 274 | 0 | return $ret; | |||||
| 275 | } | ||||||
| 276 | |||||||
| 277 | sub getParsed { | ||||||
| 278 | 0 | 0 | 0 | my $self = shift; | |||
| 279 | |||||||
| 280 | 0 | 0 | return $self->setError (7431011, "OpenCA::X509->getParsed: The certificate was not parsed.") | ||||
| 281 | if ( not $self->{parsedItem} ); | ||||||
| 282 | 0 | return $self->{parsedItem}; | |||||
| 283 | } | ||||||
| 284 | |||||||
| 285 | sub parseCert { | ||||||
| 286 | |||||||
| 287 | 0 | 0 | 0 | my $self = shift; | |||
| 288 | 0 | my $keys = { @_ }; | |||||
| 289 | |||||||
| 290 | 0 | my ( @ouList, @exts, $ret, $k, $v, $tmp, $md5 ); | |||||
| 291 | |||||||
| 292 | 0 | my @attList = ( "SERIAL", "DN", "ISSUER", "NOTBEFORE", "NOTAFTER", | |||||
| 293 | "ALIAS", "MODULUS", "PUBKEY", "FINGERPRINT", "HASH", "EMAILADDRESS", | ||||||
| 294 | "VERSION", "PUBKEY_ALGORITHM", "SIGNATURE_ALGORITHM", "EXPONENT", | ||||||
| 295 | "KEYSIZE", "EXTENSIONS" ); | ||||||
| 296 | 0 | 0 | if ($self->{certFormat} eq "DER") | ||||
| 297 | { | ||||||
| 298 | 0 | $ret = $self->{backend}->getCertAttribute( | |||||
| 299 | ATTRIBUTE_LIST => \@attList, | ||||||
| 300 | DATA => $self->getDER(), | ||||||
| 301 | INFORM => "DER"); | ||||||
| 302 | } else { | ||||||
| 303 | 0 | $ret = $self->{backend}->getCertAttribute( | |||||
| 304 | ATTRIBUTE_LIST => \@attList, | ||||||
| 305 | DATA => $self->getPEM(), | ||||||
| 306 | INFORM => "PEM"); | ||||||
| 307 | } | ||||||
| 308 | |||||||
| 309 | 0 | 0 |          print "OpenCA::X509->parseCert: DN: ".$ret->{SUBJECT}." \n" if ($self->{DEBUG});  | 
||||
| 310 | |||||||
| 311 | 0 | $ret->{DN} =~ s/(^\/|\/$)//g; | |||||
| 312 | 0 | $ret->{DN} =~ s/\/([A-Za-z0-9\-]+)=/, $1=/g; | |||||
| 313 | |||||||
| 314 | 0 | $ret->{ISSUER} =~ s/(^\/|\/$)//g; | |||||
| 315 | 0 | $ret->{ISSUER} =~ s/\/([A-Za-z0-9\-]+)=/, $1=/g; | |||||
| 316 | |||||||
| 317 | ## OpenSSL includes a bug in -nameopt RFC2253 | ||||||
| 318 | ## = signs are not escaped if they are normal values | ||||||
| 319 | 0 | my $i = 0; | |||||
| 320 | 0 | my $now = "name"; | |||||
| 321 | 0 | while ($i < length ($ret->{DN})) | |||||
| 322 | { | ||||||
| 323 | 0 | 0 | if (substr ($ret->{DN}, $i, 1) =~ /\\/) | ||||
| 0 | |||||||
| 0 | |||||||
| 324 | { | ||||||
| 325 | 0 | $i++; | |||||
| 326 | } elsif (substr ($ret->{DN}, $i, 1) =~ /=/) { | ||||||
| 327 | 0 | 0 | if ($now =~ /value/) | ||||
| 328 | { | ||||||
| 329 | ## OpenSSL forgets to escape = | ||||||
| 330 | 0 | $ret->{DN} = substr ($ret->{DN}, 0, $i)."\\".substr ($ret->{DN}, $i); | |||||
| 331 | 0 | $i++; | |||||
| 332 | } else { | ||||||
| 333 | 0 | $now = "value"; | |||||
| 334 | } | ||||||
| 335 | } elsif (substr ($ret->{DN}, $i, 1) =~ /[,+]/) { | ||||||
| 336 | 0 | $now = "name"; | |||||
| 337 | } | ||||||
| 338 | 0 | $i++; | |||||
| 339 | } | ||||||
| 340 | |||||||
| 341 | ## load the differnt parts of the DN into DN_HASH | ||||||
| 342 | 0 | 0 |  	print "OpenCA::X509->parseCert: DN: ".$ret->{DN}." \n" if ($self->{DEBUG});  | 
||||
| 343 | 0 | 0 | if ($ret->{DN} =~ /\\/) { | ||||
| 344 | 0 | my $x500_dn = X500::DN->ParseRFC2253 ($ret->{DN}); | |||||
| 345 | 0 | 0 | if (not $x500_dn) { | ||||
| 346 | 0 | 0 |         	 	print "OpenCA::X509->parseCert: X500::DN failed \n" if ($self->{DEBUG});  | 
||||
| 347 | 0 | return $self->setError (7413031, "OpenCA::X509->parseCert: X500::DN failed."); | |||||
| 348 | 0 | return undef; | |||||
| 349 | } | ||||||
| 350 | 0 | my $rdn; | |||||
| 351 | 0 | foreach $rdn ($x500_dn->getRDNs()) { | |||||
| 352 | 0 | 0 | next if ($rdn->isMultivalued()); | ||||
| 353 | 0 | my @attr_types = $rdn->getAttributeTypes(); | |||||
| 354 | 0 | my $type = $attr_types[0]; | |||||
| 355 | 0 | my $value = $rdn->getAttributeValue ($type); | |||||
| 356 | 0 | push (@{$ret->{DN_HASH}->{uc($type)}}, $value); | |||||
| 0 | |||||||
| 357 | 0 | 0 |  			print "OpenCA::X509->parseCert: DN_HASH: $type=$value \n" if ($self->{DEBUG});  | 
||||
| 358 | } | ||||||
| 359 | } else { | ||||||
| 360 | 0 | my @rdns = split /,/, $ret->{DN}; | |||||
| 361 | 0 | foreach my $rdn (@rdns) { | |||||
| 362 | 0 | my ($type, $value) = split /=/, $rdn; | |||||
| 363 | 0 | $type =~ s/^\s*//; | |||||
| 364 | 0 | $type =~ s/\s*$//; | |||||
| 365 | 0 | $value =~ s/^\s*//; | |||||
| 366 | 0 | $value =~ s/\s*$//; | |||||
| 367 | 0 | push (@{$ret->{DN_HASH}->{uc($type)}}, $value); | |||||
| 0 | |||||||
| 368 | 0 | 0 |  			print "OpenCA::REQ->parseReq: DN_HASH: $type=$value \n" if ($self->{DEBUG});  | 
||||
| 369 | } | ||||||
| 370 | } | ||||||
| 371 | |||||||
| 372 | 0 | 0 | if( exists $ret->{PUBKEY} ) { | ||||
| 373 | 0 | $md5 = new Digest::MD5; | |||||
| 374 | 0 | $md5->add( $ret->{PUBKEY} ); | |||||
| 375 | 0 | $ret->{KEY_DIGEST} = $md5->hexdigest(); | |||||
| 376 | } | ||||||
| 377 | |||||||
| 378 | ## Check if Email field is only present in subjectAltName | ||||||
| 379 | 0 | 0 | 0 | if (not $ret->{EMAILADDRESS} and | |||
| 0 | |||||||
| 380 | exists $ret->{DN_HASH}->{EMAILADDRESS} and | ||||||
| 381 | $ret->{DN_HASH}->{EMAILADDRESS}[0]) { | ||||||
| 382 | 0 | $ret->{EMAILADDRESS} = $ret->{DN_HASH}->{EMAILADDRESS}[0]; | |||||
| 383 | } | ||||||
| 384 | |||||||
| 385 | 0 | $ret->{SIG_ALGORITHM} = $ret->{SIGNATURE_ALGORITHM}; | |||||
| 386 | 0 | $ret->{PK_ALGORITHM} = $ret->{PUBKEY_ALGORITHM}; | |||||
| 387 | |||||||
| 388 | ## load all extensions | ||||||
| 389 | 0 | $ret->{PLAIN_EXTENSIONS} = $ret->{EXTENSIONS}; | |||||
| 390 | 0 | delete $ret->{EXTENSIONS}; | |||||
| 391 | 0 | $ret->{OPENSSL_EXTENSIONS} = {}; | |||||
| 392 | |||||||
| 393 | 0 | my ($c, $val, $key); | |||||
| 394 | 0 | my @lines = split(/\n/, $ret->{PLAIN_EXTENSIONS}); | |||||
| 395 | |||||||
| 396 | 0 | $i = 0; | |||||
| 397 | 0 | while($i < @lines) { | |||||
| 398 | 0 | 0 | if($lines[$i] =~ /^[\s\t]*[^:]+:\s*(critical|)\s*$/i) { | ||||
| 399 | 0 | $key = $lines[$i]; | |||||
| 400 | 0 | $key =~ s/[\s\t]*:.*$//g; | |||||
| 401 | 0 | $key =~ s/^[\s\t]*//g; | |||||
| 402 | 0 | $ret->{OPENSSL_EXTENSIONS}->{$key} = []; | |||||
| 403 | 0 | $i++; | |||||
| 404 | 0 | 0 | while($lines[$i] !~ /^[\s\t].+:\s*$/ && $i < @lines) { | ||||
| 405 | 0 | $val = $lines[$i]; | |||||
| 406 | 0 | $val =~ s/^[\s]+//g; | |||||
| 407 | 0 | $val =~ s/[\s]+$//g; | |||||
| 408 | 0 | $i++; | |||||
| 409 | 0 | 0 | next if $val =~ /^$/; | ||||
| 410 | 0 | push(@{$ret->{OPENSSL_EXTENSIONS}->{$key}}, $val); | |||||
| 0 | |||||||
| 411 | } | ||||||
| 412 | } else { | ||||||
| 413 | ## FIXME: can this every happen? | ||||||
| 414 | 0 | $i++; | |||||
| 415 | } | ||||||
| 416 | } | ||||||
| 417 | |||||||
| 418 | 0 | 0 | if ($self->{DEBUG}) { | ||||
| 419 | 0 |  		print "OpenCA::X509->parseCert: show all extensions and their values \n";  | 
|||||
| 420 | 0 | while(($key, $val) = each(%{$ret->{OPENSSL_EXTENSIONS}})) { | |||||
| 0 | |||||||
| 421 | 0 |  			print "OpenCA::X509->parseCert: found extension: $key \n";  | 
|||||
| 422 | 0 |  			print "OpenCA::X509->parseCert: with value(s):       $_ \n" foreach(@{$val});  | 
|||||
| 0 | |||||||
| 423 | } | ||||||
| 424 | } | ||||||
| 425 | |||||||
| 426 | ## load special extensions | ||||||
| 427 | 0 | my $h = $ret->{OPENSSL_EXTENSIONS}->{"X509v3 Basic Constraints"}[0]; | |||||
| 428 | 0 | 0 | $h ||= ""; | ||||
| 429 | 0 | $h =~ s/\s//g; | |||||
| 430 | 0 | 0 | if ($h =~ /CA:TRUE/i) { | ||||
| 431 | 0 | $ret->{IS_CA} = 1; | |||||
| 432 | 0 | $ret->{EXTENSIONS}->{BASIC_CONSTRAINTS}->{CA} = 1; | |||||
| 433 | } else { | ||||||
| 434 | 0 | $ret->{IS_CA} = 0; | |||||
| 435 | 0 | $ret->{EXTENSIONS}->{BASIC_CONSTRAINTS}->{CA} = 0; | |||||
| 436 | } | ||||||
| 437 | |||||||
| 438 | 0 | $ret->{BODY} = $self->getBody (ITEM => $self->{item}); | |||||
| 439 | 0 | $ret->{HEADER} = $self->getHeader (ITEM => $self->{item}); | |||||
| 440 | 0 | $ret->{KEY} = $self->getKey (ITEM => $self->{item}); | |||||
| 441 | 0 | $ret->{ITEM} = $ret->{BODY}; | |||||
| 442 | 0 | $ret->{FLAG_EXPORT_STATE} = 0; | |||||
| 443 | |||||||
| 444 | ## if email was not set then we check the subject alternative name | ||||||
| 445 | 0 | 0 | if (not $ret->{EMAILADDRESS}) { | ||||
| 446 | 0 | my $h = $ret->{OPENSSL_EXTENSIONS}->{"X509v3 Subject Alternative Name"}[0]; | |||||
| 447 | 0 | 0 | 0 | if ($h && $h =~ /^(.*,|)\s*email:/i) { | |||
| 448 | ## email steckt im subjectAltName | ||||||
| 449 | 0 | $h =~ s/^(.*,|)\s*email:\s*//ig; | |||||
| 450 | 0 | $h =~ s/\s*$//g; | |||||
| 451 | 0 | $h =~ s/,.*$//g; | |||||
| 452 | 0 | $ret->{EMAILADDRESS} = $h; | |||||
| 453 | } | ||||||
| 454 | } | ||||||
| 455 | |||||||
| 456 | 0 | return $ret; | |||||
| 457 | } | ||||||
| 458 | |||||||
| 459 | sub getPEM { | ||||||
| 460 | 0 | 0 | 0 | my $self = shift; | |||
| 461 | |||||||
| 462 | 0 | 0 | if ( $self->{certFormat} eq 'PEM' ) { | ||||
| 463 | 0 | $self->{cert} =~ s/^\n*//; | |||||
| 464 | 0 | $self->{cert} =~ s/\n*$/\n/; | |||||
| 465 | 0 | return $self->{cert}; | |||||
| 466 | } | ||||||
| 467 | 0 | 0 | if (not $self->{pemCert}) { | ||||
| 468 | 0 | $self->{pemCert} = $self->{backend}->dataConvert( DATA=>$self->{cert}, | |||||
| 469 | DATATYPE=>"CERTIFICATE", | ||||||
| 470 | INFORM=>$self->{certFormat}, | ||||||
| 471 | OUTFORM=>"PEM" ); | ||||||
| 472 | 0 | 0 | return $self->setError (7441005, "OpenCA::X509->getPEM: Cannot convert request to PEM-format ". | ||||
| 473 | "(".$OpenCA::OpenSSL::errno.")\n".$OpenCA::OpenSSL::errval) | ||||||
| 474 | if (not $self->{pemCert}); | ||||||
| 475 | } | ||||||
| 476 | |||||||
| 477 | ## return $self->setError (7441011, "OpenCA::X509->getPEM: The certificate is not available in PEM-format.") | ||||||
| 478 | ## if (not $self->{pemCert}); | ||||||
| 479 | 0 | return $self->{pemCert}; | |||||
| 480 | } | ||||||
| 481 | |||||||
| 482 | sub getPEMHeader { | ||||||
| 483 | 0 | 0 | 0 | my $self = shift; | |||
| 484 | |||||||
| 485 | 0 | 0 | return $self->setError (7442011, "OpenCA::X509->getPEMHeader: There is no PEM-header available.") | ||||
| 486 | if (not $self->{pemHeader}); | ||||||
| 487 | 0 | return $self->{pemHeader}; | |||||
| 488 | } | ||||||
| 489 | |||||||
| 490 | sub getDER { | ||||||
| 491 | 0 | 0 | 0 | my $self = shift; | |||
| 492 | |||||||
| 493 | 0 | 0 | if ( $self->{certFormat} eq 'DER' ) { | ||||
| 494 | 0 | return $self->{cert}; | |||||
| 495 | } | ||||||
| 496 | 0 | 0 | if (not $self->{derCert}) { | ||||
| 497 | 0 | $self->{derCert} = $self->{backend}->dataConvert( DATA=>$self->{cert}, | |||||
| 498 | DATATYPE=>"CERTIFICATE", | ||||||
| 499 | INFORM=>$self->{certFormat}, | ||||||
| 500 | OUTFORM=>"DER" ); | ||||||
| 501 | 0 | 0 | return $self->setError (7443005, "OpenCA::X509->getDER: Cannot convert request to DER-format ". | ||||
| 502 | "(".$OpenCA::OpenSSL::errno.")\n".$OpenCA::OpenSSL::errval) | ||||||
| 503 | if (not $self->{derCert}); | ||||||
| 504 | } | ||||||
| 505 | |||||||
| 506 | ## return $self->setError (7443011, "OpenCA::X509->getDER: The certificate is not available in DER-format.") | ||||||
| 507 | ## if( not $self->{derCert} ); | ||||||
| 508 | 0 | return $self->{derCert}; | |||||
| 509 | } | ||||||
| 510 | |||||||
| 511 | sub getTXT { | ||||||
| 512 | 0 | 0 | 0 | my $self = shift; | |||
| 513 | |||||||
| 514 | 0 | 0 | if (not $self->{txtCert}) { | ||||
| 515 | 0 | $self->{txtCert} = $self->{backend}->dataConvert( DATA=>$self->{cert}, | |||||
| 516 | DATATYPE=>"CERTIFICATE", | ||||||
| 517 | INFORM=>$self->{certFormat}, | ||||||
| 518 | OUTFORM=>"TXT" ); | ||||||
| 519 | 0 | 0 | return $self->setError (7444005, "OpenCA::X509->init: Cannot convert request to TXT-format ". | ||||
| 520 | "(".$OpenCA::OpenSSL::errno.")\n".$OpenCA::OpenSSL::errval) | ||||||
| 521 | if (not $self->{txtCert}); | ||||||
| 522 | } | ||||||
| 523 | |||||||
| 524 | ## return $self->setError (7444011, "OpenCA::X509->getTXT: The certificate is not available in TXT-format.") | ||||||
| 525 | ## if( not $self->{txtCert} ); | ||||||
| 526 | 0 | return $self->{txtCert}; | |||||
| 527 | } | ||||||
| 528 | |||||||
| 529 | ## by michael bell to support signature in the header | ||||||
| 530 | ## 1) works actually only with PEM because automatical | ||||||
| 531 | ## transformation to DER etc. is a high risc | ||||||
| 532 | ## for a failure | ||||||
| 533 | ## 2) please submit only one attribute | ||||||
| 534 | sub setHeaderAttribute { | ||||||
| 535 | |||||||
| 536 | 0 | 0 | 0 | my $self = shift; | |||
| 537 | 0 | my $keys = { @_ }; | |||||
| 538 | |||||||
| 539 | 0 | my $beginHeader = $self->{beginHeader}; | |||||
| 540 | 0 | my $endHeader = $self->{endHeader}; | |||||
| 541 | 0 | my $beginAttribute = $self->{beginAttribute}; | |||||
| 542 | 0 | my $endAttribute = $self->{endAttribute}; | |||||
| 543 | |||||||
| 544 | ## check certFormat to be PEM | ||||||
| 545 | 0 | 0 | return $self->setError (7451011, "OpenCA::X509->setHeaderAttribute: The request is not in PEM-format.") | ||||
| 546 | if ($self->{certFormat} !~ /^PEM$/i); | ||||||
| 547 | 0 | 0 |    print "X509->setHeaderAttribute: correct format - PEM \n" if ($self->{DEBUG});  | 
||||
| 548 | |||||||
| 549 | ## check for header | ||||||
| 550 | 0 | 0 | if ($self->{item} !~ /$beginHeader/) { | ||||
| 551 | ## create header | ||||||
| 552 | 0 | $self->{item} = $beginHeader."\n".$endHeader."\n".$self->{item}; | |||||
| 553 | } | ||||||
| 554 | |||||||
| 555 | 0 | for my $attribute (keys %{$keys}) { | |||||
| 0 | |||||||
| 556 | |||||||
| 557 | 0 | 0 |      print "X509->setHeaderAttribute: $attribute:=".$keys->{$attribute}." \n" if ($self->{DEBUG});  | 
||||
| 558 | |||||||
| 559 | ## insert into item | ||||||
| 560 | ## find last position in header | ||||||
| 561 | ## enter attributename | ||||||
| 562 | ## check fo multirow | ||||||
| 563 | 0 | 0 | if ($keys->{$attribute} =~ /\n/) { | ||||
| 564 | ## multirow | ||||||
| 565 | 0 | $self->{item} =~ s/${endHeader}/${attribute}=\n${beginAttribute}\n$keys->{$attribute}\n${endAttribute}\n${endHeader}/; | |||||
| 566 | } else { | ||||||
| 567 | ## single row | ||||||
| 568 | 0 | $self->{item} =~ s/${endHeader}/${attribute}=$keys->{$attribute}\n${endHeader}/; | |||||
| 569 | } | ||||||
| 570 | |||||||
| 571 | } | ||||||
| 572 | |||||||
| 573 | ## if you call init then all information is lost !!! | ||||||
| 574 | 0 | 0 | return $self->setError (7451021, "OpenCA::X509->setHeaderAttribute: Cannot re-initialize the certificate ". | ||||
| 575 | "($errno)\n$errval") | ||||||
| 576 | if (not $self->init ( CERTIFICATE => $self->{item}, | ||||||
| 577 | FORMAT => "PEM")); | ||||||
| 578 | |||||||
| 579 | 0 | return 1; | |||||
| 580 | } | ||||||
| 581 | |||||||
| 582 | sub getItem { | ||||||
| 583 | 0 | 0 | 0 | my $self = shift; | |||
| 584 | 0 | my $txtItem = ""; | |||||
| 585 | 0 | my $bH = $self->{beginHeader}; | |||||
| 586 | 0 | my $eH = $self->{endHeader}; | |||||
| 587 | |||||||
| 588 | ## remove empty header | ||||||
| 589 | 0 | 0 | if ($self->getPEMHeader() !~ /^\n*$bH\n*$eH\n*$/) { | ||||
| 590 | 0 | $txtItem .= $self->getPEMHeader ()."\n"; | |||||
| 591 | } | ||||||
| 592 | 0 | $txtItem .= $self->getPEM(); | |||||
| 593 | 0 | 0 | $txtItem .= $self->getParsed()->{KEY} || ""; | ||||
| 594 | |||||||
| 595 | 0 | return $txtItem; | |||||
| 596 | } | ||||||
| 597 | |||||||
| 598 | sub getSerial { | ||||||
| 599 | 0 | 0 | 0 | my $self = shift; | |||
| 600 | |||||||
| 601 | 0 | 0 | 0 | if (defined $_[0] and ( ($_[0] =~ /^CA/i) or ($_[0] =~ /CA_/i)) ) { | |||
| 0 | |||||||
| 602 | 0 | return $self->{backend}->getDigest ( DATA => $self->getPEM() ); | |||||
| 603 | } else { | ||||||
| 604 | 0 | return $self->getParsed()->{SERIAL}; | |||||
| 605 | } | ||||||
| 606 | } | ||||||
| 607 | |||||||
| 608 | sub setParams { | ||||||
| 609 | |||||||
| 610 | 0 | 0 | 0 | my $self = shift; | |||
| 611 | 0 | my $params = { @_ }; | |||||
| 612 | 0 | my $key; | |||||
| 613 | |||||||
| 614 | 0 | foreach $key ( keys %{$params} ) { | |||||
| 0 | |||||||
| 615 | ## we should place the parameters here | ||||||
| 616 | } | ||||||
| 617 | |||||||
| 618 | 0 | return 1; | |||||
| 619 | } | ||||||
| 620 | |||||||
| 621 | # Autoload methods go after =cut, and are processed by the autosplit program. | ||||||
| 622 | |||||||
| 623 | 1; |