1# 2# Crypt::PKCS10 3# 4# ABSTRACT: parse PKCS #10 certificate requests 5# 6# This software is copyright (c) 2014 by Gideon Knocke. 7# Copyright (c) 2016 Gideon Knocke, Timothe Litt 8# 9# See LICENSE for details. 10# 11 12package Crypt::PKCS10; 13 14use strict; 15use warnings; 16use Carp; 17 18use overload( q("") => 'as_string' ); 19 20use Convert::ASN1( qw/:tag :const/ ); 21use Encode (); 22use MIME::Base64; 23use Scalar::Util (); 24 25our $VERSION = '2.002'; 26 27my $apiVersion = undef; # 0 for compatibility. 1 for prefered 28my $error; 29 30# N.B. Names are exposed in the API. 31# %shortnames follows & depends on (some) values. 32# When adding OIDs, re-generate the documentation (see "for MAINTAINER" below) 33# 34# New OIDs don't need the [ ] syntax, which is [ prefered name, deprecated name ] 35# Some of the deprecated names are used in the ASN.1 definition. and in the $self 36# structure, which unfortunately is exposed with the attributes() method. 37# Dealing with the deprecated names causes some messy code. 38 39my %oids = ( 40 '2.5.4.6' => 'countryName', 41 '2.5.4.8' => 'stateOrProvinceName', 42 '2.5.4.10' => 'organizationName', 43 '2.5.4.11' => 'organizationalUnitName', 44 '2.5.4.3' => 'commonName', 45 '1.2.840.113549.1.9.1' => 'emailAddress', 46 '1.2.840.113549.1.9.2' => 'unstructuredName', 47 '1.2.840.113549.1.9.7' => 'challengePassword', 48 '1.2.840.113549.1.1.1' => [ 'rsaEncryption', 'RSA encryption' ], 49 '1.2.840.113549.1.1.5' => [ 'sha1WithRSAEncryption', 'SHA1 with RSA encryption' ], 50 '1.2.840.113549.1.1.4' => [ 'md5WithRSAEncryption', 'MD5 with RSA encryption' ], 51 '1.2.840.113549.1.9.14' => 'extensionRequest', 52 '1.3.6.1.4.1.311.13.2.3' => 'OS_Version', # Microsoft 53 '1.3.6.1.4.1.311.13.2.2' => 'EnrollmentCSP', # Microsoft 54 '1.3.6.1.4.1.311.21.20' => 'ClientInformation', # Microsoft REQUEST_CLIENT_INFO 55 '1.3.6.1.4.1.311.21.7' => 'certificateTemplate', # Microsoft 56 '2.5.29.37' => [ 'extKeyUsage', 'EnhancedKeyUsage' ], 57 '2.5.29.15' => [ 'keyUsage', 'KeyUsage' ], 58 '1.3.6.1.4.1.311.21.10' => 'ApplicationCertPolicies', # Microsoft APPLICATION_CERT_POLICIES 59 '2.5.29.14' => [ 'subjectKeyIdentifier', 'SubjectKeyIdentifier' ], 60 '2.5.29.17' => 'subjectAltName', 61 '1.3.6.1.4.1.311.20.2' => 'certificateTemplateName', # Microsoft 62 '2.16.840.1.113730.1.1' => 'netscapeCertType', 63 '2.16.840.1.113730.1.2' => 'netscapeBaseUrl', 64 '2.16.840.1.113730.1.4' => 'netscapeCaRevocationUrl', 65 '2.16.840.1.113730.1.7' => 'netscapeCertRenewalUrl', 66 '2.16.840.1.113730.1.8' => 'netscapeCaPolicyUrl', 67 '2.16.840.1.113730.1.12' => 'netscapeSSLServerName', 68 '2.16.840.1.113730.1.13' => 'netscapeComment', 69 70 #untested 71 '2.5.29.19' => [ 'basicConstraints', 'Basic Constraints' ], 72 '1.2.840.10040.4.1' => [ 'dsa', 'DSA' ], 73 '1.2.840.10040.4.3' => [ 'dsaWithSha1', 'DSA with SHA1' ], 74 '1.2.840.10045.2.1' => 'ecPublicKey', 75 '1.2.840.10045.4.3.1' => 'ecdsa-with-SHA224', 76 '1.2.840.10045.4.3.2' => 'ecdsa-with-SHA256', 77 '1.2.840.10045.4.3.3' => 'ecdsa-with-SHA384', 78 '1.2.840.10045.4.3.4' => 'ecdsa-with-SHA512', 79 '1.3.36.3.3.2.8.1.1.1' => 'brainpoolP160r1', 80 '1.3.36.3.3.2.8.1.1.2' => 'brainpoolP160t1', 81 '1.3.36.3.3.2.8.1.1.3' => 'brainpoolP192r1', 82 '1.3.36.3.3.2.8.1.1.4' => 'brainpoolP192t1', 83 '1.3.36.3.3.2.8.1.1.5' => 'brainpoolP224r1', 84 '1.3.36.3.3.2.8.1.1.6' => 'brainpoolP224t1', 85 '1.3.36.3.3.2.8.1.1.7' => 'brainpoolP256r1', 86 '1.3.36.3.3.2.8.1.1.8' => 'brainpoolP256t1', 87 '1.3.36.3.3.2.8.1.1.9' => 'brainpoolP320r1', 88 '1.3.36.3.3.2.8.1.1.10' => 'brainpoolP320t1', 89 '1.3.36.3.3.2.8.1.1.11' => 'brainpoolP384r1', 90 '1.3.36.3.3.2.8.1.1.12' => 'brainpoolP384t1', 91 '1.3.36.3.3.2.8.1.1.13' => 'brainpoolP512r1', 92 '1.3.36.3.3.2.8.1.1.14' => 'brainpoolP512t1', 93 '1.2.840.10045.3.1.1' => 'secp192r1', 94 '1.3.132.0.1' => 'sect163k1', 95 '1.3.132.0.15' => 'sect163r2', 96 '1.3.132.0.33' => 'secp224r1', 97 '1.3.132.0.26' => 'sect233k1', 98 '1.3.132.0.27' => 'sect233r1', 99 '1.3.132.0.16' => 'sect283k1', 100 '1.3.132.0.17' => 'sect283r1', 101 '1.2.840.10045.3.1.7' => 'secp256r1', 102 '1.3.132.0.34' => 'secp384r1', 103 '1.3.132.0.36' => 'sect409k1', 104 '1.3.132.0.37' => 'sect409r1', 105 '1.3.132.0.35' => 'secp521r1', 106 '1.3.132.0.38' => 'sect571k1', 107 '1.3.132.0.39' => 'sect571r1', 108#not std yet '1.3.6.1.4.1.3029.1.5.1' => 'curve25519', # GNU TLS 109# '1.3.6.1.4.1.11591.7' => 'curve25519', #ID josefsson-pkix-newcurves-00 110# '1.3.6.1.4.1.11591.8' => 'curve448', #ID josefsson-pkix-newcurves-00 111 '0.9.2342.19200300.100.1.25' => 'domainComponent', 112 '0.9.2342.19200300.100.1.1' => 'userID', 113 '2.5.4.7' => 'localityName', 114 '1.2.840.113549.1.1.11' => [ 'sha256WithRSAEncryption', 'SHA-256 with RSA encryption' ], 115 '1.2.840.113549.1.1.12' => 'sha384WithRSAEncryption', 116 '1.2.840.113549.1.1.13' => [ 'sha512WithRSAEncryption', 'SHA-512 with RSA encryption' ], 117 '1.2.840.113549.1.1.14' => 'sha224WithRSAEncryption', 118 '1.2.840.113549.1.1.2' => [ 'md2WithRSAEncryption', 'MD2 with RSA encryption' ], 119 '1.2.840.113549.1.1.3' => 'md4WithRSAEncryption', 120 '1.2.840.113549.1.1.6' => 'rsaOAEPEncryptionSET', 121 '1.2.840.113549.1.1.7' => 'RSAES-OAEP', 122 '1.2.840.113549.1.9.15' => [ 'smimeCapabilities', 'SMIMECapabilities' ], 123 '1.3.14.3.2.29' => [ 'sha1WithRSAEncryption', 'SHA1 with RSA signature' ], 124 '1.3.6.1.4.1.311.13.1' => 'RENEWAL_CERTIFICATE', # Microsoft 125 '1.3.6.1.4.1.311.13.2.1' => 'ENROLLMENT_NAME_VALUE_PAIR', # Microsoft 126 '1.3.6.1.4.1.311.13.2.2' => 'ENROLLMENT_CSP_PROVIDER', # Microsoft 127 '1.3.6.1.4.1.311.2.1.14' => 'CERT_EXTENSIONS', # Microsoft 128 '1.3.6.1.5.2.3.5' => [ 'keyPurposeKdc', 'KDC Authentication' ], 129 '1.3.6.1.5.5.7.9.5' => 'countryOfResidence', 130 '2.16.840.1.101.3.4.2.1' => [ 'sha256', 'SHA-256' ], 131 '2.16.840.1.101.3.4.2.2' => [ 'sha384', 'SHA-384' ], 132 '2.16.840.1.101.3.4.2.3' => [ 'sha512', 'SHA-512' ], 133 '2.16.840.1.101.3.4.2.4' => [ 'sha224', 'SHA-224' ], 134 '2.16.840.1.101.3.4.3.1' => 'dsaWithSha224', 135 '2.16.840.1.101.3.4.3.2' => 'dsaWithSha256', 136 '2.16.840.1.101.3.4.3.3' => 'dsaWithSha384', 137 '2.16.840.1.101.3.4.3.4' => 'dsaWithSha512', 138 '2.5.4.12' => [ 'title', 'Title' ], 139 '2.5.4.13' => [ 'description', 'Description' ], 140 '2.5.4.14' => 'searchGuide', 141 '2.5.4.15' => 'businessCategory', 142 '2.5.4.16' => 'postalAddress', 143 '2.5.4.17' => 'postalCode', 144 '2.5.4.18' => 'postOfficeBox', 145 '2.5.4.19', => 'physicalDeliveryOfficeName', 146 '2.5.4.20', => 'telephoneNumber', 147 '2.5.4.23', => 'facsimileTelephoneNumber', 148 '2.5.4.4' => [ 'surname', 'Surname' ], 149 '2.5.4.41' => [ 'name', 'Name' ], 150 '2.5.4.42' => 'givenName', 151 '2.5.4.43' => 'initials', 152 '2.5.4.44' => 'generationQualifier', 153 '2.5.4.45' => 'uniqueIdentifier', 154 '2.5.4.46' => 'dnQualifier', 155 '2.5.4.51' => 'houseIdentifier', 156 '2.5.4.65' => 'pseudonym', 157 '2.5.4.5' => 'serialNumber', 158 '2.5.4.9' => 'streetAddress', 159 '2.5.29.32' => 'certificatePolicies', 160 '2.5.29.32.0' => 'anyPolicy', 161 '1.3.6.1.5.5.7.2.1' => 'CPS', 162 '1.3.6.1.5.5.7.2.2' => 'userNotice', 163); 164 165my %variantNames; 166 167foreach (keys %oids) { 168 my $val = $oids{$_}; 169 if( ref $val ) { 170 $variantNames{$_} = $val; # OID to [ new, trad ] 171 $variantNames{$val->[0]} = $val->[1]; # New name to traditional for lookups 172 $variantNames{'$' . $val->[1]} = $val->[0]; # \$Traditional to new 173 $oids{$_} = $val->[!$apiVersion || 0]; 174 } 175} 176 177my %oid2extkeyusage = ( 178 '1.3.6.1.5.5.7.3.1' => 'serverAuth', 179 '1.3.6.1.5.5.7.3.2' => 'clientAuth', 180 '1.3.6.1.5.5.7.3.3' => 'codeSigning', 181 '1.3.6.1.5.5.7.3.4' => 'emailProtection', 182 '1.3.6.1.5.5.7.3.8' => 'timeStamping', 183 '1.3.6.1.5.5.7.3.9' => 'OCSPSigning', 184 185 '1.3.6.1.5.5.7.3.21' => 'sshClient', 186 '1.3.6.1.5.5.7.3.22' => 'sshServer', 187 188 # Microsoft usages 189 190 '1.3.6.1.4.1.311.10.3.1' => 'msCTLSign', 191 '1.3.6.1.4.1.311.10.3.2' => 'msTimeStamping', 192 '1.3.6.1.4.1.311.10.3.3' => 'msSGC', 193 '1.3.6.1.4.1.311.10.3.4' => 'msEFS', 194 '1.3.6.1.4.1.311.10.3.4.1' => 'msEFSRecovery', 195 '1.3.6.1.4.1.311.10.3.5' => 'msWHQLCrypto', 196 '1.3.6.1.4.1.311.10.3.6' => 'msNT5Crypto', 197 '1.3.6.1.4.1.311.10.3.7' => 'msOEMWHQLCrypto', 198 '1.3.6.1.4.1.311.10.3.8' => 'msEmbeddedNTCrypto', 199 '1.3.6.1.4.1.311.10.3.9' => 'msRootListSigner', 200 '1.3.6.1.4.1.311.10.3.10' => 'msQualifiedSubordination', 201 '1.3.6.1.4.1.311.10.3.11' => 'msKeyRecovery', 202 '1.3.6.1.4.1.311.10.3.12' => 'msDocumentSigning', 203 '1.3.6.1.4.1.311.10.3.13' => 'msLifetimeSigning', 204 '1.3.6.1.4.1.311.10.3.14' => 'msMobileDeviceSoftware', 205 206 '1.3.6.1.4.1.311.2.1.21' => 'msCodeInd', 207 '1.3.6.1.4.1.311.2.1.22' => 'msCodeCom', 208 '1.3.6.1.4.1.311.20.2.2' => 'msSmartCardLogon', 209 210 211 # Netscape 212 '2.16.840.1.113730.4.1' => 'nsSGC', 213); 214 215my %shortnames = ( 216 countryName => 'C', 217 stateOrProvinceName => 'ST', 218 organizationName => 'O', 219 organizationalUnitName => 'OU', 220 commonName => 'CN', 221# emailAddress => 'E', # Deprecated & not recognized by some software 222 domainComponent => 'DC', 223 localityName => 'L', 224 userID => 'UID', 225 surname => 'SN', 226 givenName => 'GN', 227); 228 229my %name2oid; 230 231# For generating documentation, not part of API 232 233sub _cmpOID { 234 my @a = split( /\./, $a ); 235 my @b = split( /\./, $b ); 236 237 while( @a && @b ) { 238 my $c = shift @a <=> shift @b; 239 return $c if( $c ); 240 } 241 return @a <=> @b; 242} 243 244sub __listOIDs { 245 my $class = shift; 246 my ( $hash ) = @_; 247 248 my @max = (0) x 3; 249 foreach my $oid ( keys %$hash ) { 250 my $len; 251 252 $len = length $oid; 253 $max[0] = $len if( $len > $max[0] ); 254 if( exists $variantNames{$oid} ) { 255 $len = length $variantNames{$oid}[0]; 256 $max[1] = $len if( $len > $max[1] ); 257 $len = length $variantNames{$oid}[1]; 258 $max[2] = $len if( $len > $max[2] ); 259 } else { 260 $len = length $hash->{$oid}; 261 $max[1] = $len if( $len > $max[1] ); 262 } 263 } 264 265 printf( " %-*s %-*s %s\n %s %s %s\n", $max[0], 'OID', 266 $max[1], 'Name (API v1)', 'Old Name (API v0)', 267 '-' x $max[0], '-' x $max[1], '-' x $max[2] ); 268 269 foreach my $oid ( sort _cmpOID keys %$hash ) { 270 printf( " %-*s %-*s", $max[0], $oid, $max[1], (exists $variantNames{$oid})? 271 $variantNames{$oid}[0]: $hash->{$oid} ); 272 printf( " (%-s)", $variantNames{$oid}[1] ) if( exists $variantNames{$oid} ); 273 print( "\n" ); 274 } 275 return; 276} 277 278sub _listOIDs { 279 my $class = shift; 280 281 $class->setAPIversion(1); 282 $class-> __listOIDs( { %oids, %oid2extkeyusage } ); 283 284 return; 285} 286 287sub setAPIversion { 288 my( $class, $version ) = @_; 289 290 croak( substr(($error = "Wrong number of arguments\n"), 0, -1) ) unless( @_ == 2 && defined $class && !ref $class ); 291 $version = 0 unless( defined $version ); 292 croak( substr(($error = "Unsupported API version $version\n"), 0, -1) ) unless( $version >= 0 && $version <= 1 ); 293 $apiVersion = $version; 294 295 $version = !$version || 0; 296 297 foreach (keys %variantNames) { 298 $oids{$_} = $variantNames{$_}[$version] if( /^\d/ ); # Map OID to selected name 299 } 300 %name2oid = reverse (%oids, %oid2extkeyusage); 301 302 return 1; 303} 304 305sub getAPIversion { 306 my( $class ) = @_; 307 308 croak( "Class not specified for getAPIversion()" ) unless( defined $class ); 309 310 return $class->{_apiVersion} if( ref $class && $class->isa( __PACKAGE__ ) ); 311 312 return $apiVersion; 313} 314 315sub name2oid { 316 my $class = shift; 317 my( $oid ) = @_; 318 319 croak( "Class not specifed for name2oid()" ) unless( defined $class ); 320 321 return unless( defined $oid && defined $apiVersion && $apiVersion > 0 ); 322 323 return $name2oid{$oid}; 324} 325 326sub oid2name { 327 my $class = shift; 328 my( $oid ) = @_; 329 330 croak( "Class not specifed for oid2name()" ) unless( defined $class ); 331 332 return $oid unless( defined $apiVersion && $apiVersion > 0 ); 333 334 return $class->_oid2name( @_ ); 335} 336 337# Should not be exported, as overloading may break ASN lookups 338 339sub _oid2name { 340 my $class = shift; 341 my( $oid ) = @_; 342 343 return unless($oid); 344 345 if( exists $oids{$oid} ) { 346 $oid = $oids{$oid}; 347 }elsif( exists $oid2extkeyusage{$oid} ) { 348 $oid = $oid2extkeyusage{$oid}; 349 } 350 return $oid; 351} 352 353# registerOID( $oid ) => true if $oid is registered, false if not 354# registerOID( $oid, $longname ) => Register an OID with its name 355# registerOID( $oid, $longname, $shortname ) => Register an OID with an abbreviation for RDNs. 356# registerOID( $oid, undef, $shortname ) => Register an abbreviation for RDNs for an existing OID 357 358sub registerOID { 359 my( $class, $oid, $longname, $shortname ) = @_; 360 361 croak( "Class not specifed for registerOID()" ) unless( defined $class ); 362 363 unless( defined $apiVersion ) { 364 carp( "${class}::setAPIversion MUST be called before registerOID(). Defaulting to legacy mode" ); 365 $class->setAPIversion(0); 366 } 367 368 return exists $oids{$oid} || exists $oid2extkeyusage{$oid} if( @_ == 2 && defined $oid ); 369 370 croak( "Not enough arguments" ) unless( @_ >= 3 && defined $oid && ( defined $longname || defined $shortname ) ); 371 croak( "Invalid OID $oid" ) unless( defined $oid && $oid =~ /^\d+(?:\.\d+)*$/ ); 372 373 if( defined $longname ) { 374 croak( "$oid already registered" ) if( exists $oids{$oid} || exists $oid2extkeyusage{$oid} ); 375 croak( "$longname already registered" ) if( grep /^$longname$/, values %oids ); 376 } else { 377 croak( "$oid not registered" ) unless( exists $oids{$oid} || exists $oid2extkeyusage{$oid} ); 378 } 379 croak( "$shortname already registered" ) if( defined $shortname && grep /^\U$shortname\E$/, 380 values %shortnames ); 381 382 if( defined $longname ) { 383 $oids{$oid} = $longname; 384 $name2oid{$longname} = $oid; 385 } else { 386 $longname = $class->_oid2name( $oid ); 387 } 388 $shortnames{$longname} = uc $shortname if( defined $shortname ); 389 return 1; 390} 391 392sub new { 393 my $class = shift; 394 395 undef $error; 396 397 $class = ref $class if( defined $class && ref $class && $class->isa( __PACKAGE__ ) ); 398 399 unless( defined $apiVersion ) { 400 carp( "${class}::setAPIversion MUST be called before new(). Defaulting to legacy mode" ); 401 $class->setAPIversion(0); 402 } 403 404 my( $void, $die ) = ( !defined wantarray, 0 ); 405 my $self = eval { 406 die( "Insufficient arguments for new\n" ) unless( defined $class && @_ >= 1 ); 407 die( "Value of Crypt::PKCS10->new ignored\n" ) if( $void ); 408 return $class->_new( \$die, @_ ); 409 }; if( $@ ) { 410 $error = $@; 411 if( !$apiVersion || $die || !defined wantarray ) { 412 1 while( chomp $@ ); 413 croak( $@ ); 414 } 415 return; 416 } 417 418 return $self; 419} 420 421sub error { 422 my $class = shift; 423 424 croak( "Class not specifed for error()" ) unless( defined $class ); 425 426 if( ref $class && $class->isa( __PACKAGE__ ) ) { 427 return $class->{_error}; 428 } 429 return $error; 430} 431 432my $pemre = qr/(?ms:^\r?-----BEGIN\s(?:NEW\s)?CERTIFICATE\sREQUEST-----\s*\r?\n\s*(.*?)\s*^\r?-----END\s(?:NEW\s)?CERTIFICATE\sREQUEST-----\r?$)/; 433 434sub _new { 435 my( $class, $die, $der ) = splice( @_, 0, 3 ); 436 437 my %options = ( 438 acceptPEM => 1, 439 PEMonly => 0, 440 escapeStrings => 1, 441 readFile => 0, 442 ignoreNonBase64 => 0, 443 verifySignature => ($apiVersion >= 1), 444 dieOnError => 0, 445 ); 446 447 %options = ( %options, %{ shift @_ } ) if( @_ >= 1 && ref( $_[0] ) eq 'HASH' ); 448 449 die( "Every option to new() must have a value\n" ) unless( @_ % 2 == 0 ); 450 451 %options = ( %options, @_ ) if( @_ ); 452 453 my $self = { _apiVersion => $apiVersion }; 454 455 my $keys = join( '|', qw/escapeStrings acceptPEM PEMonly binaryMode readFile verifySignature ignoreNonBase64 warnings dieOnError/ ); 456 457 $self->{"_$_"} = delete $options{$_} foreach (grep { /^(?:$keys)$/ } keys %options); 458 459 $$die = $self->{_dieOnError} &&= $apiVersion >= 1; 460 461 die( "\$csr argument to new() is not defined\n" ) unless( defined $der ); 462 463 if( keys %options ) { 464 die( "Invalid option(s) specified: " . join( ', ', sort keys %options ) . "\n" ); 465 } 466 467 $self->{_binaryMode} = !$self->{_acceptPEM} unless( exists $self->{_binaryMode} ); 468 469 my $parser; 470 471 # malformed requests can produce various warnings; don't proceed in that case. 472 473 local $SIG{__WARN__} = sub { my $msg = $_[0]; $msg =~ s/\A(.*?) at .*\Z/$1/s; 1 while( chomp $msg ); die "$msg\n" }; 474 475 if( $self->{_readFile} ) { 476 open( my $fh, '<', $der ) or die( "Failed to open $der: $!\n" ); 477 $der = $fh; 478 } 479 480 if( Scalar::Util::openhandle( $der ) ) { 481 local $/; 482 483 binmode $der if( $self->{_binaryMode} ); 484 485 $der = <$der>; # Note: this closes files opened by readFile 486 die( "Failed to read request: $!\n" ) unless( defined $der ); 487 } 488 489 my $isPEM; 490 491 if( $self->{_PEMonly} ) { 492 if( $der =~ $pemre ) { 493 $der = $1; 494 $isPEM = 1; 495 } else { 496 die( "No certificate request found\n" ); 497 } 498 } elsif( $self->{_acceptPEM} && $der =~ $pemre ) { 499 $der = $1; 500 $isPEM = 1; 501 } 502 if( $isPEM ) { 503 # Some versions of MIME::Base64 check the input. Some don't. Those that do 504 # seem to obey -w, but not 'use warnings'. So we'll check here. 505 506 $der =~ s/\s+//g; # Delete whitespace, which is legal but meaningless 507 $der =~ tr~A-Za-z0-9+=/~~cd if( $self->{_ignoreNonBase64} ); 508 509 unless( $der =~ m{\A[A-Za-z0-9+/]+={0,2}\z} && ( length( $der ) % 4 == 0 ) ) { 510 warn( "Invalid base64 encoding\n" ); # Invalid character or length 511 } 512 $der = decode_base64( $der ); 513 } 514 515 # some requests may contain information outside of the regular ASN.1 structure. 516 # This padding must be removed. 517 518 $der = eval { # Catch out of range errors caused by bad DER & report as format errors. 519 # SEQUENCE <len> -- CertificationRequest 520 521 my( $tlen, undef, $tag ) = asn_decode_tag2( $der ); 522 die( "SEQUENCE not present\n" ) unless( $tlen && $tag == ASN_SEQUENCE ); 523 524 my( $llen, $len ) = asn_decode_length( substr( $der, $tlen ) ); 525 die( "Invalid SEQUENCE length\n" ) unless( $llen && $len ); 526 527 $len += $tlen + $llen; 528 $tlen = length $der; 529 die( "DER too short to contain request\n" ) if( $tlen < $len ); 530 531 if( $tlen != $len && $self->{_warnings} ) { # Debugging 532 local $SIG{__WARN__}; 533 carp( sprintf( "DER length of %u contains %u bytes of padding", 534 $tlen, $tlen - $len ) ); 535 } 536 return substr( $der, 0, $len ); 537 }; if( $@ ) { 538 1 while( chomp $@ ); 539 die( "Invalid format for request: $@\n" ); 540 } 541 542 $self->{_der} = $der; 543 544 bless( $self, $class ); 545 546 $self->{_bmpenc} = Encode::find_encoding('UCS2-BE'); 547 548 my $asn = Convert::ASN1->new; 549 $self->{_asn} = $asn; 550 $asn->prepare(<<ASN1) or die( "Internal error in " . __PACKAGE__ . ": " . $asn->error ); 551 552 DirectoryString ::= CHOICE { 553 teletexString TeletexString, 554 printableString PrintableString, 555 bmpString BMPString, 556 universalString UniversalString, 557 utf8String UTF8String, 558 ia5String IA5String, 559 integer INTEGER} 560 561 Algorithms ::= ANY 562 563 Name ::= SEQUENCE OF RelativeDistinguishedName 564 RelativeDistinguishedName ::= SET OF AttributeTypeAndValue 565 AttributeTypeAndValue ::= SEQUENCE { 566 type OBJECT IDENTIFIER, 567 value DirectoryString} 568 569 Attributes ::= SET OF Attribute 570 Attribute ::= SEQUENCE { 571 type OBJECT IDENTIFIER, 572 values SET OF ANY} 573 574 575 AlgorithmIdentifier ::= SEQUENCE { 576 algorithm OBJECT IDENTIFIER, 577 parameters Algorithms OPTIONAL} 578 579 SubjectPublicKeyInfo ::= SEQUENCE { 580 algorithm AlgorithmIdentifier, 581 subjectPublicKey BIT STRING} 582 583 --- Certificate Request --- 584 585 CertificationRequest ::= SEQUENCE { 586 certificationRequestInfo CertificationRequestInfo, 587 signatureAlgorithm AlgorithmIdentifier, 588 signature BIT STRING}, 589 590 CertificationRequestInfo ::= SEQUENCE { 591 version INTEGER , 592 subject Name OPTIONAL, 593 subjectPKInfo SubjectPublicKeyInfo, 594 attributes [0] Attributes OPTIONAL} 595 596 --- Extensions --- 597 598 BasicConstraints ::= SEQUENCE { 599 cA BOOLEAN OPTIONAL, -- DEFAULT FALSE, 600 pathLenConstraint INTEGER OPTIONAL} 601 602 OS_Version ::= IA5String 603 emailAddress ::= IA5String 604 605 EnrollmentCSP ::= SEQUENCE { 606 KeySpec INTEGER, 607 Name BMPString, 608 Signature BIT STRING} 609 610 ENROLLMENT_CSP_PROVIDER ::= SEQUENCE { -- MSDN 611 keySpec INTEGER, 612 cspName BMPString, 613 signature BIT STRING} 614 615 ENROLLMENT_NAME_VALUE_PAIR ::= EnrollmentNameValuePair -- MSDN: SEQUENCE OF 616 617 EnrollmentNameValuePair ::= SEQUENCE { -- MSDN 618 name BMPString, 619 value BMPString} 620 621 ClientInformation ::= SEQUENCE { -- MSDN 622 clientId INTEGER, 623 MachineName UTF8String, 624 UserName UTF8String, 625 ProcessName UTF8String} 626 627 extensionRequest ::= SEQUENCE OF Extension 628 Extension ::= SEQUENCE { 629 extnID OBJECT IDENTIFIER, 630 critical BOOLEAN OPTIONAL, 631 extnValue OCTET STRING} 632 633 SubjectKeyIdentifier ::= OCTET STRING 634 635 certificateTemplate ::= SEQUENCE { 636 templateID OBJECT IDENTIFIER, 637 templateMajorVersion INTEGER OPTIONAL, -- (0..4294967295) 638 templateMinorVersion INTEGER OPTIONAL} -- (0..4294967295) 639 640 EnhancedKeyUsage ::= SEQUENCE OF OBJECT IDENTIFIER 641 KeyUsage ::= BIT STRING 642 netscapeCertType ::= BIT STRING 643 644 ApplicationCertPolicies ::= SEQUENCE OF PolicyInformation -- Microsoft 645 646 PolicyInformation ::= SEQUENCE { 647 policyIdentifier OBJECT IDENTIFIER, 648 policyQualifiers SEQUENCE OF PolicyQualifierInfo OPTIONAL} 649 650 PolicyQualifierInfo ::= SEQUENCE { 651 policyQualifierId OBJECT IDENTIFIER, 652 qualifier ANY} 653 654 certificatePolicies ::= SEQUENCE OF certPolicyInformation -- RFC 3280 655 656 certPolicyInformation ::= SEQUENCE { 657 policyIdentifier CertPolicyId, 658 policyQualifier SEQUENCE OF certPolicyQualifierInfo OPTIONAL} 659 660 CertPolicyId ::= OBJECT IDENTIFIER 661 662 certPolicyQualifierInfo ::= SEQUENCE { 663 policyQualifierId CertPolicyQualifierId, 664 qualifier ANY DEFINED BY policyQualifierId} 665 666 CertPolicyQualifierId ::= OBJECT IDENTIFIER 667 668 CertPolicyQualifier ::= CHOICE { 669 cPSuri CPSuri, 670 userNotice UserNotice } 671 672 CPSuri ::= IA5String 673 674 UserNotice ::= SEQUENCE { 675 noticeRef NoticeReference OPTIONAL, 676 explicitText DisplayText OPTIONAL} 677 678 NoticeReference ::= SEQUENCE { 679 organization DisplayText, 680 noticeNumbers SEQUENCE OF INTEGER } 681 682 DisplayText ::= CHOICE { 683 ia5String IA5String, 684 visibleString VisibleString, 685 bmpString BMPString, 686 utf8String UTF8String } 687 688 unstructuredName ::= CHOICE { 689 Ia5String IA5String, 690 directoryString DirectoryString} 691 692 challengePassword ::= DirectoryString 693 694 subjectAltName ::= SEQUENCE OF GeneralName 695 696 GeneralName ::= CHOICE { 697 otherName [0] AnotherName, 698 rfc822Name [1] IA5String, 699 dNSName [2] IA5String, 700 x400Address [3] ANY, --ORAddress, 701 directoryName [4] Name, 702 ediPartyName [5] EDIPartyName, 703 uniformResourceIdentifier [6] IA5String, 704 iPAddress [7] OCTET STRING, 705 registeredID [8] OBJECT IDENTIFIER} 706 707 AnotherName ::= SEQUENCE { 708 type OBJECT IDENTIFIER, 709 value [0] EXPLICIT ANY } 710 711 EDIPartyName ::= SEQUENCE { 712 nameAssigner [0] DirectoryString OPTIONAL, 713 partyName [1] DirectoryString } 714 715 certificateTemplateName ::= CHOICE { 716 octets OCTET STRING, 717 directoryString DirectoryString} 718 719 rsaKey ::= SEQUENCE { 720 modulus INTEGER, 721 publicExponent INTEGER} 722 723 dsaKey ::= INTEGER 724 725 dsaPars ::= SEQUENCE { 726 P INTEGER, 727 Q INTEGER, 728 G INTEGER} 729 730 eccName ::= OBJECT IDENTIFIER 731 732 ecdsaSigValue ::= SEQUENCE { 733 r INTEGER, 734 s INTEGER} 735ASN1 736 737 $asn->registertype( 'qualifier', '1.3.6.1.5.5.7.2.1', $self->_init('CPSuri') ); 738 $asn->registertype( 'qualifier', '1.3.6.1.5.5.7.2.2', $self->_init('UserNotice') ); 739 740 $parser = $self->_init( 'CertificationRequest' ); 741 742 my $top = 743 $parser->decode( $der ) or 744 confess( "decode: " . $parser->error . 745 "Cannot handle input or missing ASN.1 definitions" ); 746 747 $self->{certificationRequestInfo}{subject_raw} 748 = $top->{certificationRequestInfo}{subject}; 749 750 $self->{certificationRequestInfo}{subject} 751 = $self->_convert_rdn( $top->{certificationRequestInfo}{subject} ); 752 753 $self->{certificationRequestInfo}{version} 754 = $top->{certificationRequestInfo}{version}; 755 756 $self->{certificationRequestInfo}{attributes} = $self->_convert_attributes( 757 $top->{certificationRequestInfo}{attributes} ); 758 759 $self->{_pubkey} = "-----BEGIN PUBLIC KEY-----\n" . 760 _encode_PEM( $self->_init('SubjectPublicKeyInfo')-> 761 encode( $top->{certificationRequestInfo}{subjectPKInfo} ) ) . 762 "-----END PUBLIC KEY-----\n"; 763 764 $self->{certificationRequestInfo}{subjectPKInfo} = $self->_convert_pkinfo( 765 $top->{certificationRequestInfo}{subjectPKInfo} ); 766 767 $self->{signature} = $top->{signature}; 768 769 $self->{signatureAlgorithm} 770 = $self->_convert_signatureAlgorithm( $top->{signatureAlgorithm} ); 771 772 # Extract bundle of bits that is signed 773 # The DER is SEQUENCE -- CertificationRequest 774 # SEQUENCE -- CertificationRequestInfo [SIGNED] 775 776 my( $CRtaglen, $CRtag, $CRllen, $CRlen ); 777 ($CRtaglen, undef, $CRtag) = asn_decode_tag2( $der ); 778 die( "Invalid CSR format: missing SEQUENCE 1\n" ) unless( $CRtag == ASN_SEQUENCE ); 779 ($CRllen, $CRlen) = asn_decode_length( substr( $der, $CRtaglen ) ); 780 781 my( $CItaglen, $CItag, $CIllen, $CIlen ); 782 ($CItaglen, undef, $CItag) = asn_decode_tag2( substr( $der, $CRtaglen + $CRllen ) ); 783 die( "Invalid CSR format: missing SEQUENCE 2\n" ) unless( $CItag == ASN_SEQUENCE ); 784 ($CIllen, $CIlen) = asn_decode_length( substr( $der, $CRtaglen + $CRllen + $CItaglen ) ); 785 786 $self->{_signed} = substr( $der, $CRtaglen + $CRllen, $CItaglen + $CIllen + $CIlen ); 787 788 die( $error ) if( $self->{_verifySignature} && !$self->checkSignature ); 789 790 return $self; 791} 792 793# Convert::ASN1 returns BMPStrings as 16-bit fixed-width characters, e.g. UCS2-BE 794 795sub _bmpstring { 796 my $self = shift; 797 798 my $enc = $self->{_bmpenc}; 799 800 $_ = $enc->decode( $_ ) foreach (@_); 801 802 return; 803} 804 805# Find the obvious BMPStrings in a value and convert them 806# This doesn't catch direct values, but does find them in hashes 807# (generally as a result of a CHOICE) 808# 809# Convert iPAddresses as well 810 811sub _scanvalue { 812 my $self = shift; 813 814 my( $value ) = @_; 815 816 return unless( ref $value ); 817 if( ref $value eq 'ARRAY' ) { 818 foreach (@$value) { 819 $self->_scanvalue( $_ ); 820 } 821 return; 822 } 823 if( ref $value eq 'HASH' ) { 824 foreach my $k (keys %$value) { 825 if( $k eq 'bmpString' ) { 826 $self->_bmpstring( $value->{bmpString} ); 827 next; 828 } 829 if( $k eq 'iPAddress' ) { 830 use bytes; 831 my $addr = $value->{iPAddress}; 832 if( length $addr == 4 ) { 833 $value->{iPAddress} = sprintf( '%vd', $addr ); 834 } else { 835 $addr = sprintf( '%*v02X', ':', $addr ); 836 $addr =~ s/([[:xdigit:]]{2}):([[:xdigit:]]{2})/$1$2/g; 837 $value->{iPAddress} = $addr; 838 } 839 next; 840 } 841 $self->_scanvalue( $value->{$k} ); 842 } 843 return; 844 } 845 return; 846} 847 848sub _convert_signatureAlgorithm { 849 my $self = shift; 850 851 my $signatureAlgorithm = shift; 852 $signatureAlgorithm->{algorithm} 853 = $oids{$signatureAlgorithm->{algorithm}} 854 if( defined $signatureAlgorithm->{algorithm} 855 && exists $oids{$signatureAlgorithm->{algorithm}} ); 856 857 return $signatureAlgorithm; 858} 859 860sub _convert_pkinfo { 861 my $self = shift; 862 863 my $pkinfo = shift; 864 865 $pkinfo->{algorithm}{algorithm} 866 = $oids{$pkinfo->{algorithm}{algorithm}} 867 if( defined $pkinfo->{algorithm}{algorithm} 868 && exists $oids{$pkinfo->{algorithm}{algorithm}} ); 869 return $pkinfo; 870} 871 872# OIDs requiring some sort of special handling 873# 874# Called with decoded value, returns updated value. 875# Key is ASN macro name 876 877my %special; 878%special = 879( 880 EnhancedKeyUsage => sub { 881 my $self = shift; 882 my( $value, $id ) = @_; 883 884 foreach (@{$value}) { 885 $_ = $oid2extkeyusage{$_} if(defined $oid2extkeyusage{$_}); 886 } 887 return $value; 888 }, 889 KeyUsage => sub { 890 my $self = shift; 891 my( $value, $id ) = @_; 892 893 my $bit = unpack('C*', @{$value}[0]); #get the decimal representation 894 my $length = int(log($bit) / log(2) + 1); #get its bit length 895 my @usages = reverse( $id eq 'KeyUsage'? # Following are in order from bit 0 upwards 896 qw(digitalSignature nonRepudiation keyEncipherment dataEncipherment 897 keyAgreement keyCertSign cRLSign encipherOnly decipherOnly) : 898 qw(client server email objsign reserved sslCA emailCA objCA) ); 899 my $shift = ($#usages + 1) - $length; # computes the unused area in @usages 900 901 @usages = @usages[ grep { $bit & (1 << $_ - $shift) } 0 .. $#usages ]; #transfer bitmap to barewords 902 903 return [ @usages ] if( $self->{_apiVersion} >= 1 ); 904 905 return join( ', ', @usages ); 906 }, 907 netscapeCertType => sub { 908 goto &{$special{KeyUsage}}; 909 }, 910 SubjectKeyIdentifier => sub { 911 my $self = shift; 912 my( $value, $id ) = @_; 913 914 return unpack( "H*", $value ); 915 }, 916 ApplicationCertPolicies => sub { 917 goto &{$special{certificatePolicies}} if( $_[0]->{_apiVersion} > 0 ); 918 919 my $self = shift; 920 my( $value, $id ) = @_; 921 922 foreach my $entry (@{$value}) { 923 $entry->{policyIdentifier} = $self->_oid2name( $entry->{policyIdentifier} ); 924 } 925 926 return $value; 927 }, 928 certificateTemplate => sub { 929 my $self = shift; 930 my( $value, $id ) = @_; 931 932 $value->{templateID} = $self->_oid2name( $value->{templateID} ) if( $self->{_apiVersion} > 0 ); 933 return $value; 934 }, 935 ENROLLMENT_NAME_VALUE_PAIR => sub { 936 my $self = shift; 937 my( $value, $id ) = @_; 938 939 $self->_bmpstring( @{$value}{qw/name value/} ); 940 941 return $value; 942 }, 943 EnrollmentCSP => sub { 944 my $self = shift; 945 my( $value, $id ) = @_; 946 947 $self->_bmpstring( $value->{Name} ); 948 949 return $value; 950 }, 951 ENROLLMENT_CSP_PROVIDER => sub { 952 my $self = shift; 953 my( $value, $id ) = @_; 954 955 $self->_bmpstring( $value->{cspName} ); 956 957 return $value; 958 }, 959 certificatePolicies => sub { 960 my $self = shift; 961 my( $value, $id ) = @_; 962 963 foreach my $policy (@$value) { 964 $policy->{policyIdentifier} = $self->_oid2name( $policy->{policyIdentifier} ); 965 if( exists $policy->{policyQualifier} ) { 966 foreach my $qualifier (@{$policy->{policyQualifier}}) { 967 $qualifier->{policyQualifierId} = $self->_oid2name( $qualifier->{policyQualifierId} ); 968 my $qv = $qualifier->{qualifier}; 969 if( ref $qv eq 'HASH' ) { 970 foreach my $qt (keys %$qv) { 971 if( $qt eq 'explicitText' ) { 972 $qv->{$qt} = (values %{$qv->{$qt}})[0]; 973 } elsif( $qt eq 'noticeRef' ) { 974 my $userNotice = $qv->{$qt}; 975 $userNotice->{organization} = (values %{$userNotice->{organization}})[0]; 976 } 977 } 978 $qv->{userNotice} = delete $qv->{noticeRef} 979 if( exists $qv->{noticeRef} ); 980 } 981 } 982 } 983 } 984 return $value; 985 }, 986 CERT_EXTENSIONS => sub { 987 my $self = shift; 988 my( $value, $id, $entry ) = @_; 989 990 return $self->_convert_extensionRequest( [ $value ] ) if( $self->{_apiVersion} > 0 ); # Untested 991 }, 992 BasicConstraints => sub { 993 my $self = shift; 994 my( $value, $id, $entry ) = @_; 995 996 my $r = { 997 CA => $value->{cA}? 'TRUE' : 'FALSE', 998 }; 999 my $string = "CA:$r->{CA}"; 1000 1001 if( exists $value->{pathLenConstraint} ) { 1002 $r->{pathlen} = $value->{pathLenConstraint}; 1003 $string .= sprintf( ',pathlen:%u', $value->{pathLenConstraint} ); 1004 } 1005 $entry->{_FMT} = [ $r, $string ]; # [ Raw, formatted ] 1006 return $value; 1007 }, 1008 unstructuredName => sub { 1009 my $self = shift; 1010 my( $value, $id ) = @_; 1011 1012 return $self->_hash2string( $value ); 1013 }, 1014 challengePassword => sub { 1015 my $self = shift; 1016 my( $value, $id ) = @_; 1017 1018 return $self->_hash2string( $value ); 1019 }, 1020); # %special 1021 1022sub _convert_attributes { 1023 my $self = shift; 1024 my( $typeandvalues ) = @_; 1025 1026 foreach my $entry ( @{$typeandvalues} ) { 1027 my $oid = $entry->{type}; 1028 my $name = $oids{$oid}; 1029 $name = $variantNames{$name} if( defined $name && exists $variantNames{$name} ); 1030 1031 next unless( defined $name ); 1032 1033 $entry->{type} = $name; 1034 1035 if ($name eq 'extensionRequest') { 1036 $entry->{values} = $self->_convert_extensionRequest($entry->{values}[0]); 1037 } else { 1038 my $parser = $self->_init( $name, 1 ) or next; # Skip unknown attributes 1039 1040 if($entry->{values}[1]) { 1041 confess( "Incomplete parsing of attribute type: $name" ); 1042 } 1043 my $value = $entry->{values} = $parser->decode( $entry->{values}[0] ) or 1044 confess( "Looks like damaged input parsing attribute $name" ); 1045 1046 if( exists $special{$name} ) { 1047 my $action = $special{$name}; 1048 $entry->{values} = $action->( $self, $value, $name, $entry ); 1049 } 1050 } 1051 } 1052 return $typeandvalues; 1053} 1054 1055sub _convert_extensionRequest { 1056 my $self = shift; 1057 my( $extensionRequest ) = @_; 1058 1059 my $parser = $self->_init('extensionRequest'); 1060 my $decoded = $parser->decode($extensionRequest) or return []; 1061 1062 foreach my $entry (@{$decoded}) { 1063 my $name = $oids{ $entry->{extnID} }; 1064 $name = $variantNames{$name} if( defined $name && exists $variantNames{$name} ); 1065 if (defined $name) { 1066 my $asnName = $name; 1067 $asnName =~ tr/ //d; 1068 my $parser = $self->_init($asnName, 1); 1069 if(!$parser) { 1070 $entry = undef; 1071 next; 1072 } 1073 $entry->{extnID} = $name; 1074 my $dec = $parser->decode($entry->{extnValue}) or 1075 confess( $parser->error . ".. looks like damaged input parsing extension $asnName" ); 1076 1077 $self->_scanvalue( $dec ); 1078 1079 if( exists $special{$asnName} ) { 1080 my $action = $special{$asnName}; 1081 $dec = $action->( $self, $dec, $asnName, $entry ); 1082 } 1083 $entry->{extnValue} = $dec; 1084 } 1085 } 1086 @{$decoded} = grep { defined } @{$decoded}; 1087 return $decoded; 1088} 1089 1090sub _convert_rdn { 1091 my $self = shift; 1092 my $typeandvalue = shift; 1093 my %hash = ( _subject => [], ); 1094 foreach my $entry ( @$typeandvalue ) { 1095 foreach my $item (@$entry) { 1096 my $oid = $item->{type}; 1097 my $name = (exists $variantNames{$oid})? $variantNames{$oid}[1]: $oids{ $oid }; 1098 if( defined $name ) { 1099 push @{$hash{$name}}, sort values %{$item->{value}}; 1100 push @{$hash{_subject}}, $name, [ sort values %{$item->{value}} ]; 1101 my @names = (exists $variantNames{$oid})? @{$variantNames{$oid}} : ( $name ); 1102 foreach my $name ( @names ) { 1103 unless( $self->can( $name ) ) { 1104 no strict 'refs'; ## no critic 1105 *$name = sub { 1106 my $self = shift; 1107 return @{ $self->{certificationRequestInfo}{subject}{$name} } if( wantarray ); 1108 return $self->{certificationRequestInfo}{subject}{$name}[0] || ''; 1109 } 1110 } 1111 } 1112 } 1113 } 1114 } 1115 1116 return \%hash; 1117} 1118 1119sub _init { 1120 my $self = shift; 1121 my( $node, $optional ) = @_; 1122 1123 my $parsed = $self->{_asn}->find($node); 1124 1125 unless( defined $parsed || $optional ) { 1126 croak( "Missing node $node in ASN.1" ); 1127 } 1128 return $parsed; 1129} 1130 1131########################################################################### 1132# interface methods 1133 1134sub csrRequest { 1135 my $self = shift; 1136 my $format = shift; 1137 1138 return( "-----BEGIN CERTIFICATE REQUEST-----\n" . 1139 _encode_PEM( $self->{_der} ) . 1140 "-----END CERTIFICATE REQUEST-----\n" ) if( $format ); 1141 1142 return $self->{_der}; 1143} 1144 1145# Common subject components documented to be always present: 1146 1147foreach my $component (qw/commonName organizationalUnitName organizationName 1148 emailAddress stateOrProvinceName countryName domainComponent/ ) { 1149 no strict 'refs'; ## no critic 1150 1151 unless( defined &$component ) { 1152 *$component = sub { 1153 my $self = shift; 1154 return @{ $self->{certificationRequestInfo}{subject}{$component} || [] } if( wantarray ); 1155 return $self->{certificationRequestInfo}{subject}{$component}[0] || ''; 1156 } 1157 } 1158} 1159 1160# Complete subject 1161 1162sub subject { 1163 my $self = shift; 1164 my $long = shift; 1165 1166 return @{ $self->{certificationRequestInfo}{subject}{_subject} } if( wantarray ); 1167 1168 my @subject = @{ $self->{certificationRequestInfo}{subject}{_subject} }; 1169 1170 my $subj = ''; 1171 while( @subject ) { 1172 my( $name, $value ) = splice( @subject, 0, 2 ); 1173 $name = $shortnames{$name} if( !$long && exists $shortnames{$name} ); 1174 $subj .= "/$name=" . join( ',', @$value ); 1175 } 1176 1177 return $subj; 1178} 1179 1180 1181sub subjectRaw { 1182 1183 my $self = shift; 1184 my @subject; 1185 foreach my $rdn (@{$self->{certificationRequestInfo}{subject_raw}}) { 1186 my @sequence = map { 1187 $_->{format} = (keys %{$_->{value}})[0]; 1188 $_->{value} = (values %{$_->{value}})[0]; 1189 $_; 1190 } @{$rdn}; 1191 if (scalar @sequence > 1) { 1192 push @subject, \@sequence; 1193 } else { 1194 push @subject, $sequence[0]; 1195 } 1196 } 1197 return \@subject; 1198 1199} 1200 1201 1202sub subjectAltName { 1203 my $self = shift; 1204 my( $type ) = @_; 1205 1206 my $san = $self->extensionValue( 'subjectAltName' ); 1207 unless( defined $san ) { 1208 return () if( wantarray ); 1209 return undef; ## no critic 1210 } 1211 1212 if( !defined $type ) { 1213 if( wantarray ) { 1214 my %comps; 1215 $comps{$_} = 1 foreach (map { keys %$_ } @$san); 1216 return sort keys %comps; ## no critic 1217 } 1218 my @string; 1219 foreach my $comp (@$san) { 1220 push @string, join( '+', map { "$_:$comp->{$_}" } sort keys %$comp ); 1221 } 1222 return join( ',', @string ); 1223 } 1224 1225 my $result = [ map { $_->{$type} } grep { exists $_->{$type} } @$san ]; 1226 1227 return @$result if( wantarray ); 1228 return $result->[0]; 1229} 1230 1231sub version { 1232 my $self = shift; 1233 my $v = $self->{certificationRequestInfo}{version}; 1234 return sprintf( "v%u", $v+1 ); 1235} 1236 1237sub pkAlgorithm { 1238 my $self = shift; 1239 return $self->{certificationRequestInfo}{subjectPKInfo}{algorithm}{algorithm}; 1240} 1241 1242sub subjectPublicKey { 1243 my $self = shift; 1244 my $format = shift; 1245 1246 return $self->{_pubkey} if( $format ); 1247 return unpack('H*', $self->{certificationRequestInfo}{subjectPKInfo}{subjectPublicKey}[0]); 1248} 1249 1250sub subjectPublicKeyParams { 1251 my $self = shift; 1252 my $detail = shift; 1253 1254 croak( "Requires API version 1" ) unless( $self->{_apiVersion} >= 1 ); 1255 1256 undef $error; 1257 delete $self->{_error}; 1258 1259 my $rv = {}; 1260 my $at = $self->pkAlgorithm; 1261 $at = 'undef' unless( defined $at ); 1262 1263 if( $at eq 'rsaEncryption' ) { 1264 $rv->{keytype} = 'RSA'; 1265 my $par = $self->_init( 'rsaKey' ); 1266 my $rsa = $par->decode( $self->{certificationRequestInfo}{subjectPKInfo}{subjectPublicKey}[0] ); 1267 $rv->{keylen} = 4 * ( length( $rsa->{modulus}->as_hex ) -2 ); # 2 == length( '0x' ) 1268 $rv->{modulus} = substr( $rsa->{modulus}->as_hex, 2 ); 1269 $rv->{publicExponent} = ( ref( $rsa->{publicExponent} )? 1270 $rsa->{publicExponent}->as_hex : 1271 sprintf( '%x', $rsa->{publicExponent} ) ); 1272 } elsif( $at eq 'ecPublicKey' ) { 1273 $rv->{keytype} = 'ECC'; 1274 1275 eval { require Crypt::PK::ECC; }; 1276 if( $@ ) { 1277 $rv->{keytype} = undef; 1278 $self->{_error} = 1279 $error = "ECC public key requires Crypt::PK::ECC\n"; 1280 croak( $error ) if( $self->{_dieOnError} ); 1281 return $rv; 1282 } 1283 my $key = $self->subjectPublicKey(1); 1284 $key = Crypt::PK::ECC->new( \$key )->key2hash; 1285 $rv->{keylen} = $key->{curve_bits}; 1286 $rv->{pub_x} = $key->{pub_x}; 1287 $rv->{pub_y} = $key->{pub_y}; 1288 $rv->{detail} = { %$key } if( $detail ); 1289 1290 my $par = $self->_init( 'eccName' ); 1291 $rv->{curve} = $par->decode( $self->{certificationRequestInfo}{subjectPKInfo}{algorithm}{parameters} ); 1292 $rv->{curve} = $self->_oid2name( $rv->{curve} ) if ($rv->{curve}); 1293 } elsif( $at eq 'dsa' ) { 1294 $rv->{keytype} = 'DSA'; 1295 my $par = $self->_init( 'dsaKey' ); 1296 my $dsa = $par->decode( $self->{certificationRequestInfo}{subjectPKInfo}{subjectPublicKey}[0] ); 1297 $rv->{keylen} = 4 * ( length( $dsa->as_hex ) -2 ); 1298 if( exists $self->{certificationRequestInfo}{subjectPKInfo}{algorithm}{parameters} ) { 1299 $par = $self->_init('dsaPars'); 1300 $dsa = $par->decode($self->{certificationRequestInfo}{subjectPKInfo}{algorithm}{parameters}); 1301 $rv->{G} = substr( $dsa->{G}->as_hex, 2 ); 1302 $rv->{P} = substr( $dsa->{P}->as_hex, 2 ); 1303 $rv->{Q} = substr( $dsa->{Q}->as_hex, 2 ); 1304 } 1305 } else { 1306 $rv->{keytype} = undef; 1307 $self->{_error} = 1308 $error = "Unrecognized public key type $at\n"; 1309 croak( $error ) if( $self->{_dieOnError} ); 1310 } 1311 return $rv; 1312} 1313 1314sub signatureAlgorithm { 1315 my $self = shift; 1316 return $self->{signatureAlgorithm}{algorithm}; 1317} 1318 1319sub signatureParams { 1320 my $self = shift; 1321 1322 if( exists $self->{signatureAlgorithm}{parameters} ) { 1323 my( $tlen, undef, $tag ) = asn_decode_tag2( $self->{signatureAlgorithm}{parameters} ); 1324 if( $tlen != 0 && $tag != ASN_NULL ) { 1325 return $self->{signatureAlgorithm}{parameters} 1326 } 1327 } 1328 # Known algorithm's parameters MAY return a hash of decoded fields. 1329 # For now, leaving that to the caller... 1330 1331 return; 1332} 1333 1334sub signature { 1335 my $self = shift; 1336 my $format = shift; 1337 1338 if( defined $format && $format == 2 ) { # Per keytype decoding 1339 if( $self->pkAlgorithm eq 'ecPublicKey' ) { # ECDSA 1340 my $par = $self->_init( 'ecdsaSigValue' ); 1341 return $par->decode( $self->{signature}[0] ); 1342 } 1343 return; # Unknown 1344 } 1345 return $self->{signature}[0] if( $format ); 1346 1347 return unpack('H*', $self->{signature}[0]); 1348} 1349 1350sub certificationRequest { 1351 my $self = shift; 1352 1353 return $self->{_signed}; 1354} 1355 1356sub _attributes { 1357 my $self = shift; 1358 1359 my $attributes = $self->{certificationRequestInfo}{attributes}; 1360 return unless( defined $attributes ); 1361 1362 return { map { $_->{type} => $_->{values} } @$attributes }; 1363} 1364 1365sub attributes { 1366 my $self = shift; 1367 my( $name ) = @_; 1368 1369 if( $self->{_apiVersion} < 1 ) { 1370 my $attributes = $self->{certificationRequestInfo}{attributes}; 1371 return () unless( defined $attributes ); 1372 1373 my %hash = map { $_->{type} => $_->{values} } 1374 @{$attributes}; 1375 return %hash; 1376 } 1377 1378 my $attributes = $self->_attributes; 1379 unless( defined $attributes ) { 1380 return () if( wantarray ); 1381 return undef; ## no critic 1382 } 1383 1384 unless( defined $name ) { 1385 return grep { $_ ne 'extensionRequest' } sort keys %$attributes; 1386 } 1387 1388 $name = $self->_oid2name( $name ); 1389 1390 if( $name eq 'extensionRequest' ) { # Meaningless, and extensions/extensionValue handle 1391 return () if( wantarray ); 1392 return undef; ## no critic 1393 } 1394 1395 # There can only be one matching the name. 1396 # If the match becomes wider, sort the keys. 1397 1398 1399 my @attrs = grep { $_ eq $name } keys %$attributes; 1400 unless( @attrs ) { 1401 return () if( wantarray ); 1402 return undef; ## no critic 1403 } 1404 1405 my @values; 1406 foreach my $attr (@attrs) { 1407 my $values = $attributes->{$attr}; 1408 $values = [ $values ] unless( ref $values eq 'ARRAY' ); 1409 foreach my $value (@$values) { 1410 my $value = $self->_hash2string( $value ); 1411 push @values, (wantarray? $value : $self->_value2strings( $value )); 1412 } 1413 } 1414 return @values if( wantarray ); 1415 1416 if( @values == 1 ) { 1417 $values[0] =~ s/^\((.*)\)$/$1/; 1418 return $values[0]; 1419 } 1420 return join( ',', @values ); 1421} 1422 1423sub certificateTemplate { 1424 my $self = shift; 1425 1426 return $self->extensionValue( 'certificateTemplate', @_ ); 1427} 1428 1429# If a hash contains one string (e.g. a CHOICE containing type=>value), return the string. 1430# If the hash is nested, try recursing. 1431# If the string can't be identified (clutter in the hash), return the hash 1432# Some clutter can be filtered by specifying $exclude (a regexp) 1433 1434sub _hash2string { 1435 my $self = shift; 1436 my( $hash, $exclude ) = @_; 1437 1438 return $hash unless( ref $hash eq 'HASH' ); 1439 1440 my @keys = keys %$hash; 1441 1442 @keys = grep { $_ !~ /$exclude/ } @keys if( defined $exclude ); 1443 1444 return $hash if( @keys != 1 ); 1445 1446 return $self->_hash2string( $hash->{$keys[0]} ) if( ref $hash->{$keys[0]} eq 'HASH' ); 1447 1448 return $hash->{$keys[0]}; 1449} 1450 1451# Convert a value to a printable string 1452 1453sub _value2strings { 1454 my $self = shift; 1455 my( $value ) = @_; 1456 1457 my @strings; 1458 if( ref $value eq 'ARRAY' ) { 1459 foreach my $value (@$value) { 1460 push @strings, $self->_value2strings( $value ); 1461 } 1462 return '(' . join( ',', @strings ) . ')' if( @strings > 1 ); 1463 return join( ',', @strings ); 1464 } 1465 if( ref $value eq 'HASH' ) { 1466 foreach my $k (sort keys %$value) { 1467 push @strings, "$k=" . $self->_value2strings( $value->{$k} ); 1468 } 1469 return '(' . join( ',', @strings ) . ')' if( @strings > 1 ); 1470 return join( ',', @strings ); 1471 } 1472 1473 return $value if( $value =~ /^\d+$/ ); 1474 1475 # OpenSSL and Perl-compatible string syntax 1476 1477 $value =~ s/(["\\\$])/\\$1/g if( $self->{_escapeStrings} ); 1478 1479 return $value if( $value =~ m{\A[\w!\@$%^&*_=+\[\]\{\}:;|<>./?"'-]+\z} ); # Barewords 1480 1481 return '"' . $value . '"'; # Must quote: whitespace, non-printable, comma, (), \, null string 1482} 1483 1484sub extensions { 1485 my $self = shift; 1486 1487 my $attributes = $self->_attributes; 1488 return () unless( defined $attributes && exists $attributes->{extensionRequest} ); 1489 1490 my @present = map { $_->{extnID} } @{$attributes->{extensionRequest}}; 1491 if( $self->{_apiVersion} >= 1 ) { 1492 foreach my $ext (@present) { 1493 $ext = $variantNames{'$' . $ext} if( exists $variantNames{'$' . $ext} ); 1494 } 1495 } 1496 return @present; 1497} 1498 1499sub extensionValue { 1500 my $self = shift; 1501 my( $extensionName, $format ) = @_; 1502 1503 my $attributes = $self->_attributes; 1504 my $value; 1505 return unless( defined $attributes && exists $attributes->{extensionRequest} ); 1506 1507 $extensionName = $self->_oid2name( $extensionName ); 1508 1509 $extensionName = $variantNames{$extensionName} if( exists $variantNames{$extensionName} ); 1510 1511 foreach my $entry (@{$attributes->{extensionRequest}}) { 1512 if ($entry->{extnID} eq $extensionName) { 1513 $value = $entry->{extnValue}; 1514 if( $self->{_apiVersion} == 0 ) { 1515 while (ref $value eq 'HASH') { 1516 my @keys = sort keys %{$value}; 1517 $value = $value->{ shift @keys } ; 1518 } 1519 } else { 1520 if( $entry->{_FMT} ) { # Special formatting 1521 $value = $entry->{_FMT}[$format? 1:0]; 1522 } else { 1523 $value = $self->_hash2string( $value, '(?i:^(?:critical|.*id)$)' ); 1524 $value = $self->_value2strings( $value ) if( $format ); 1525 } 1526 } 1527 last; 1528 } 1529 } 1530 $value =~ s/^\((.*)\)$/$1/ if( $format ); 1531 1532 return $value; 1533} 1534 1535sub extensionPresent { 1536 my $self = shift; 1537 my( $extensionName ) = @_; 1538 1539 my $attributes = $self->_attributes; 1540 return unless( defined $attributes && exists $attributes->{extensionRequest} ); 1541 1542 $extensionName = $self->_oid2name( $extensionName ); 1543 1544 $extensionName = $variantNames{$extensionName} if( exists $variantNames{$extensionName} ); 1545 1546 foreach my $entry (@{$attributes->{extensionRequest}}) { 1547 if ($entry->{extnID} eq $extensionName) { 1548 return 2 if ($entry->{critical}); 1549 return 1; 1550 } 1551 } 1552 return; 1553} 1554 1555sub checkSignature { 1556 my $self = shift; 1557 1558 undef $error; 1559 delete $self->{_error}; 1560 1561 my $ok = eval { 1562 die( "checkSignature requires API version 1\n" ) unless( $self->{_apiVersion} >= 1 ); 1563 1564 my $key = $self->subjectPublicKey(1); # Key as PEM 1565 my $sig = $self->signature(1); # Signature as DER 1566 my $alg = $self->signatureAlgorithm; # Algorithm name 1567 1568 # Determine the signature hash type from the algorithm name 1569 1570 my( $hash, $hashmod, $hashfcn ); # hashnnn, Digest::mod, Digest::mod::fcn 1571 if( $alg =~ /sha-?(\d+)/i ) { 1572 $hash = "sha$1"; 1573 $hashmod = 'Digest::SHA'; 1574 $hashfcn = "Digest::SHA::$hash"; 1575 } elsif( $alg =~ /md-?(\d)/i ) { 1576 $hash = "md$1"; 1577 $hashmod = "Digest::MD$1"; 1578 $hashfcn = "Digest::MD$1::$hash"; 1579 } else { 1580 die( "Unknown hash in signature algorithm $alg\n" ); 1581 } 1582 1583 my $keyp = $self->subjectPublicKeyParams; 1584 1585 die( "Unknown public key type\n" ) unless( defined $keyp->{keytype} ); 1586 1587 # Verify signature using the correct module and hash type. 1588 1589 if( $keyp->{keytype} eq 'RSA' ) { 1590 1591 eval { require Crypt::PK::RSA; }; 1592 die( "Unable to load Crypt::PK::RSA\n" ) if( $@ ); 1593 1594 $key = Crypt::PK::RSA->new( \$key ); 1595 return $key->verify_message( $sig, $self->certificationRequest, uc($hash), "v1.5" ); 1596 1597 } 1598 1599 if( $keyp->{keytype} eq 'DSA' ) { 1600 1601 eval { require Crypt::PK::DSA; }; 1602 die( "Unable to load Crypt::PK::DSA\n" ) if( $@ ); 1603 1604 $key = Crypt::PK::DSA->new( \$key ); 1605 return $key->verify_message( $sig, $self->certificationRequest, uc($hash) ); 1606 } 1607 1608 if( $keyp->{keytype} eq 'ECC' ) { 1609 eval { require Crypt::PK::ECC; }; 1610 die( "Unable to load Crypt::PK::ECC\n" ) if( $@ ); 1611 1612 $key = Crypt::PK::ECC->new( \$key ); 1613 return $key->verify_message( $sig, $self->certificationRequest, uc($hash) ); 1614 } 1615 1616 die( "Unknown key type $keyp->{keytype}\n" ); 1617 }; 1618 if( $@ ) { 1619 $self->{_error} = 1620 $error = $@; 1621 croak( $error ) if( $self->{_dieOnError} ); 1622 return; 1623 } 1624 return 1 if( $ok ); 1625 1626 $self->{_error} = 1627 $error = "Incorrect signature\n"; 1628 croak( $error ) if( $self->{_dieOnError} ); 1629 1630 return 0; 1631} 1632 1633sub _wrap { 1634 my( $to, $text ) = @_; 1635 1636 my $wid = 76 - $to; 1637 1638 my $out = substr( $text, 0, $wid, '' ); 1639 1640 while( length $text ) { 1641 $out .= "\n" . (' ' x $to) . substr( $text, 0, $wid, '' ); 1642 } 1643 return $out; 1644} 1645 1646sub _encode_PEM { 1647 my $text = encode_base64( $_[0] ); 1648 return $text if( length $text <= 65 ); 1649 $text =~ tr/\n//d; 1650 my $out = ''; 1651 $out .= substr( $text, 0, 64, '' ) . "\n" while( length $text ); 1652 return $out; 1653} 1654 1655sub as_string { 1656 my $self = shift; 1657 1658 local $self->{_escapeStrings} = 0; 1659 local( $@, $_, $! ); 1660 1661 my $v = $apiVersion; 1662 ref( $self )->setAPIversion( 1 ) unless( defined $v && $v == 1 ); 1663 1664 my $string = eval { 1665 $self = ref( $self )->new( $self->{_der}, acceptPEM => 0, verifySignature => 0, escapeStrings => 0 ); 1666 return $error if( !defined $self ); 1667 1668 $self->__stringify; 1669 }; 1670 my $at = $@; 1671 ref( $self )->setAPIversion( $v ) unless( defined $v && $v == 1 ); 1672 1673 $string = '' unless( defined $string ); 1674 $string .= $at if( $at ); 1675 1676 return $string; 1677} 1678 1679sub __stringify { 1680 my $self = shift; 1681 1682 my $max = 0; 1683 foreach ($self->attributes, $self->extensions, 1684 qw/Version Subject Key_algorithm Public_key Signature_algorithm Signature/) { 1685 $max = length if( length > $max ); 1686 } 1687 1688 my $string = sprintf( "%-*s: %s\n", $max, 'Version', $self->version ) ; 1689 1690 $string .= sprintf( "%-*s: %s\n", $max, 'Subject', _wrap( $max+2, scalar $self->subject ) ); 1691 1692 $string .= "\n --Attributes--\n"; 1693 1694 $string .= " --None--" unless( $self->attributes ); 1695 1696 foreach ($self->attributes) { 1697 $string .= sprintf( "%-*s: %s\n", $max, $_, _wrap( $max+2, scalar $self->attributes($_) ) ); 1698 } 1699 1700 $string .= "\n --Extensions--\n"; 1701 1702 $string .= " --None--" unless( $self->extensions ); 1703 1704 foreach ($self->extensions) { 1705 my $critical = $self->extensionPresent($_) == 2? 'critical,': ''; 1706 1707 $string .= sprintf( "%-*s: %s\n", $max, $_, 1708 _wrap( $max+2, $critical . ($_ eq 'subjectAltName'? 1709 scalar $self->subjectAltName: 1710 $self->extensionValue($_, 1) ) ) ); 1711 } 1712 1713 $string .= "\n --Key and signature--\n"; 1714 $string .= sprintf( "%-*s: %s\n", $max, 'Key algorithm', $self->pkAlgorithm ); 1715 $string .= sprintf( "%-*s: %s\n", $max, 'Public key', _wrap( $max+2, $self->subjectPublicKey ) ); 1716 $string .= $self->subjectPublicKey(1); 1717 my $kp = $self->subjectPublicKeyParams(1); 1718 foreach (sort keys %$kp) { 1719 my $v = $kp->{$_}; 1720 1721 if( !defined $v && !defined( $v = $self->error ) ) { 1722 $v = 'undef'; 1723 } elsif( ref $v ) { 1724 next; 1725 } 1726 $string .= sprintf( "%-*s: %s\n", $max, $_, _wrap( $max+2, $v ) ); 1727 } 1728 if( exists $kp->{detail} ) { 1729 $kp = $kp->{detail}; 1730 $string .= "Key details\n-----------\n"; 1731 foreach (sort keys %$kp) { 1732 next if( ref $kp->{$_} ); 1733 $string .= sprintf( "%-*s: %s\n", $max, $_, _wrap( $max+2, $kp->{$_} ) ); 1734 } 1735 } 1736 1737 $string .= sprintf( "\n%-*s: %s\n", $max, 'Signature algorithm', $self->signatureAlgorithm ); 1738 $string .= sprintf( "%-*s: %s\n", $max, 'Signature', _wrap( $max+2, $self->signature ) ); 1739 my $sp = $self->signature(2); 1740 if( $sp ) { 1741 foreach (sort keys %$sp) { 1742 my $v = $sp->{$_}; 1743 1744 if( ref $v ) { 1745 if( $v->can('as_hex') ) { 1746 $v = substr( $v->as_hex, 2 ); 1747 } else { 1748 next; 1749 } 1750 } 1751 $string .= sprintf( "%-*s: %s\n", $max, $_, _wrap( $max+2, $v ) ); 1752 } 1753 } 1754 1755 $string .= "\n --Request--\n" . $self->csrRequest(1); 1756 1757 return $string; 1758} 1759 17601; 1761 1762__END__ 1763 1764=encoding utf-8 1765 1766=pod 1767 1768=begin :readme 1769 1770 This file is automatically generated by pod2readme from PKCS10.pm and Changes. 1771 1772=end :readme 1773 1774=head1 NAME 1775 1776Crypt::PKCS10 - parse PKCS #10 certificate requests 1777 1778=begin :readme 1779 1780=head1 RELEASE NOTES 1781 1782Version 1.4 made several API changes. Most users should have a painless migration. 1783 1784ALL users must call Crypt::PKCS10->setAPIversion. If not, a warning will be generated 1785by the first class method called. This warning will be made a fatal exception in a 1786future release. 1787 1788Other than that requirement, the legacy mode is compatible with previous versions. 1789 1790C<new> will no longer generate exceptions. C<undef> is returned on all errors. Use 1791the error class method to retrieve the reason. 1792 1793new will accept an open file handle in addition to a request. 1794 1795Users are encouraged to migrate to the version 1 API. It is much easier to use, 1796and does not require the application to navigate internal data structures. 1797 1798Version 1.7 provides support for DSA and ECC public keys. By default, it verifies 1799the signature of CSRs. It also allows the caller to verify the signature of a CSR. 1800subjectPublicKeyParams and signatureParams provide additional information. 1801The readFile option to new() will open() a file containing a CSR by name. 1802The ignoreNonBase64 option allows PEM to contain extraneous characters. 1803F<Changes> describes additional improvements. Details follow. 1804 1805=head1 INSTALLATION 1806 1807C<Crypt::PKCS10> supports DSA, RSA and ECC public keys in CSRs. 1808 1809It depends on C<Crypt::PK::*> (provided by CryptX) for some operations. 1810All are recommended. Some methods will return errors if 1811Crypt::PKCS10 is presented with a CSR containing an unsupported public key type. 1812 1813To install this module type the following: 1814 1815 perl Makefile.PL 1816 make 1817 make test 1818 make install 1819 1820=head1 REQUIRES 1821 1822C<Convert::ASN1> 1823 1824C<Crypt::PK::DSA> 1825 1826C<Crypt::PK::RSA> 1827 1828C<Crypt::PK::ECC> 1829 1830C<Digest::SHA> 1831 1832Very old CSRs may require C<DIGEST::MD{5,4,2}> 1833 1834=end :readme 1835 1836=head1 SYNOPSIS 1837 1838 use Crypt::PKCS10; 1839 1840 Crypt::PKCS10->setAPIversion( 1 ); 1841 my $decoded = Crypt::PKCS10->new( $csr ) or die Crypt::PKCS10->error; 1842 1843 print $decoded; 1844 1845 @names = $decoded->extensionValue('subjectAltName' ); 1846 @names = $decoded->subject unless( @names ); 1847 1848 %extensions = map { $_ => $decoded->extensionValue( $_ ) } $decoded->extensions 1849 1850=head1 DESCRIPTION 1851 1852C<Crypt::PKCS10> parses PKCS #10 certificate requests (CSRs) and provides accessor methods to extract the data in usable form. 1853 1854Common object identifiers will be translated to their corresponding names. 1855Additionally, accessor methods allow extraction of single data fields. 1856The format of returned data varies by accessor. 1857 1858The access methods return the value corresponding to their name. If called in scalar context, they return the first value (or an empty string). If called in array context, they return all values. 1859 1860B<true> values should be specified as 1 and B<false> values as 0. Future API changes may provide different functions when other values are used. 1861 1862=head1 METHODS 1863 1864Access methods may exist for subject name components that are not listed here. To test for these, use code of the form: 1865 1866 $locality = $decoded->localityName if( $decoded->can('localityName') ); 1867 1868If a name component exists in a CSR, the method will be present. The converse is not (always) true. 1869 1870=head2 class method setAPIversion( $version ) 1871 1872Selects the API version (0 or 1) expected. 1873 1874Must be called before calling any other method. 1875 1876The API version determines how a CSR is parsed. Changing the API version after 1877parsing a CSR will cause accessors to produce unpredictable results. 1878 1879=over 4 1880 1881=item Version 0 - B<DEPRECATED> 1882 1883Some OID names have spaces and descriptions 1884 1885This is the format used for C<Crypt::PKCS10> version 1.3 and lower. The attributes method returns legacy data. 1886 1887Some new API functions are disabled. 1888 1889=item Version 1 1890 1891OID names from RFCs - or at least compatible with OpenSSL and ASN.1 notation. The attributes method conforms to version 1. 1892 1893=back 1894 1895If not called, a warning will be generated and the API will default to version 0. 1896 1897In a future release, the warning will be changed to a fatal exception. 1898 1899To ease migration, both old and new names are accepted by the API. 1900 1901Every program should call C<setAPIversion(1)>. 1902 1903=cut 1904 1905=head2 class method getAPIversion 1906 1907Returns the current API version. 1908 1909Returns C<undef> if setAPIversion has never been called. 1910 1911=head2 class method new( $csr, %options ) 1912 1913Constructor, creates a new object containing the parsed PKCS #10 certificate request. 1914 1915C<$csr> may be a scalar containing the request, a file name, or a file handle from which to read it. 1916 1917If a file name is specified, the C<readFile> option must be specified. 1918 1919If a file handle is supplied, the caller should specify C<< acceptPEM => 0 >> if the contents are DER. 1920 1921The request may be PEM or binary DER encoded. Only one request is processed. 1922 1923If PEM, other data (such as mail headers) may precede or follow the CSR. 1924 1925 my $decoded = Crypt::PKCS10->new( $csr ) or die Crypt::PKCS10->error; 1926 1927Returns C<undef> if there is an I/O error or the request can not be parsed successfully. 1928 1929Call C<error()> to obtain more detail. 1930 1931=head3 options 1932 1933The options are specified as C<< name => value >>. 1934 1935If the first option is a HASHREF, it is expanded and any remaining options are added. 1936 1937=over 4 1938 1939=item acceptPEM 1940 1941If B<false>, the input must be in DER format. C<binmode> will be called on a file handle. 1942 1943If B<true>, the input is checked for a C<CERTIFICATE REQUEST> header. If not found, the csr 1944is assumed to be in DER format. 1945 1946Default is B<true>. 1947 1948=item PEMonly 1949 1950If B<true>, the input must be in PEM format. An error will be returned if the input doesn't contain a C<CERTIFICATE REQUEST> header. 1951If B<false>, the input is parsed according to C<acceptPEM>. 1952 1953Default is B<false>. 1954 1955=item binaryMode 1956 1957If B<true>, an input file or file handle will be set to binary mode prior to reading. 1958 1959If B<false>, an input file or file handle's C<binmode> will not be modified. 1960 1961Defaults to B<false> if B<acceptPEM> is B<true>, otherwise B<true>. 1962 1963=item dieOnError 1964 1965If B<true>, any API function that sets an error string will also C<die>. 1966 1967If B<false>, exceptions are only generated for fatal conditions. 1968 1969The default is B<false>. API version 1 only.. 1970 1971=item escapeStrings 1972 1973If B<true>, strings returned for extension and attribute values are '\'-escaped when formatted. 1974This is compatible with OpenSSL configuration files. 1975 1976The special characters are: '\', '$', and '"' 1977 1978If B<false>, these strings are not '\'-escaped. This is useful when they are being displayed 1979to a human. 1980 1981The default is B<true>. 1982 1983=item ignoreNonBase64 1984 1985If B<true>, most invalid base64 characters in PEM data will be ignored. For example, this will 1986accept CSRs prefixed with '> ', as e-mail when the PEM is inadvertently quoted. Note that the 1987BEGIN and END lines may not be corrupted. 1988 1989If B<false>, invalid base64 characters in PEM data will cause the CSR to be rejected. 1990 1991The default is B<false>. 1992 1993=item readFile 1994 1995If B<true>, C<$csr> is the name of a file containing the CSR. 1996 1997If B<false>, C<$csr> contains the CSR or is an open file handle. 1998 1999The default is B<false>. 2000 2001=item verifySignature 2002 2003If B<true>, the CSR's signature is checked. If verification fails, C<new> will fail. Requires API version 1. 2004 2005If B<false>, the CSR's signature is not checked. 2006 2007The default is B<true> for API version 1 and B<false> for API version 0. 2008 2009See C<checkSignature> for requirements and limitations. 2010 2011=back 2012 2013No exceptions are generated, unless C<dieOnError> is set or C<new()> is called in 2014void context. 2015 2016The defaults will accept either PEM or DER from a string or file hande, which will 2017not be set to binary mode. Automatic detection of the data format may not be 2018reliable on file systems that represent text and binary files differently. Set 2019C<acceptPEM> to B<false> and C<PEMonly> to match the file type on these systems. 2020 2021The object will stringify to a human-readable representation of the CSR. This is 2022useful for debugging and perhaps for displaying a request. However, the format 2023is not part of the API and may change. It should not be parsed by automated tools. 2024 2025Exception: The public key and extracted request are PEM blocks, which other tools 2026can extract. 2027 2028If another object inherits from C<Crypt::PKCS10>, it can extend the representation 2029by overloading or calling C<as_string>. 2030 2031=head2 {class} method error 2032 2033Returns a string describing the last error encountered; 2034 2035If called as an instance method, last error encountered by the object. 2036 2037If called as a class method, last error encountered by the class. 2038 2039Any method can reset the string to B<undef>, so the results are 2040only valid immediately after a method call. 2041 2042=head2 class method name2oid( $oid ) 2043 2044Returns the OID corresponding to a name returned by an access method. 2045 2046Not in API v0; 2047 2048=head2 csrRequest( $format ) 2049 2050Returns the binary (ASN.1) request (after conversion from PEM and removal of any data beyond the length of the ASN.1 structure. 2051 2052If $format is B<true>, the request is returned as a PEM CSR. Otherwise as a binary string. 2053 2054=head2 certificationRequest 2055 2056Returns the binary (ASN.1) section of the request that is signed by the requestor. 2057 2058The caller can verify the signature using B<signatureAlgorithm>, B<certificationRequest> and B<signature(1)>. 2059 2060=head2 Access methods for the subject's distinguished name 2061 2062Note that B<subjectAltName> is prefered, and that modern certificate users will ignore the subject if B<subjectAltName> is present. 2063 2064=head3 subject( $format ) 2065 2066Returns the entire subject of the CSR. 2067 2068In scalar context, returns the subject as a string in the form C</componentName=value,value>. 2069 If format is B<true>, long component names are used. By default, abbreviations are used when they exist. 2070 2071 e.g. /countryName=AU/organizationalUnitName=Big org/organizationalUnitName=Smaller org 2072 or /C=AU/OU=Big org/OU=Smaller org 2073 2074In array context, returns an array of C<(componentName, [values])> pairs. Abbreviations are not used. 2075 2076Note that the order of components in a name is significant. 2077 2078 2079=head3 subjectRaw 2080 2081Returns the subjects RDNs as sequence of hashes without OID any mapping applied. 2082 2083The result is an array ref where each item is a hash: 2084 2085 [ 2086 { 2087 'format' => 'ia5String', 2088 'value' => 'Org', 2089 'type' => '0.9.2342.19200300.100.1.25' 2090 }, 2091 { 2092 'format' => 'utf8String', 2093 'value' => 'ACME', 2094 'type' => '2.5.4.10' 2095 }, 2096 { 2097 'format' => 'utf8String', 2098 'type' => '2.5.4.3', 2099 'value' => 'Foobar' 2100 } 2101 ] 2102 2103If a component contains a SET, the component will become an array on the 2104second level, too: 2105 2106 [ 2107 { 2108 'format' => 'ia5String', 2109 'value' => 'Org', 2110 'type' => '0.9.2342.19200300.100.1.25' 2111 }, 2112 { 2113 'format' => 'utf8String', 2114 'value' => 'ACME', 2115 'type' => '2.5.4.10' 2116 }, 2117 [ 2118 { 2119 'format' => 'utf8String', 2120 'type' => '2.5.4.3', 2121 'value' => 'Foobar' 2122 }, 2123 { 2124 'format' => 'utf8String', 2125 'type' => '0.9.2342.19200300.100.1.1', 2126 'value' => 'foobar' 2127 } 2128 ] 2129 ]; 2130 2131=head3 commonName 2132 2133Returns the common name(s) from the subject. 2134 2135 my $cn = $decoded->commonName(); 2136 2137=head3 organizationalUnitName 2138 2139Returns the organizational unit name(s) from the subject 2140 2141=head3 organizationName 2142 2143Returns the organization name(s) from the subject. 2144 2145=head3 emailAddress 2146 2147Returns the email address from the subject. 2148 2149=head3 stateOrProvinceName 2150 2151Returns the state or province name(s) from the subject. 2152 2153=head3 countryName 2154 2155Returns the country name(s) from the subject. 2156 2157=head2 subjectAltName( $type ) 2158 2159Convenience method. 2160 2161When $type is specified: returns the subject alternate name values of the specified type in list context, or the first value 2162of the specified type in scalar context. 2163 2164Returns undefined/empty list if no values of the specified type are present, or if the B<subjectAltName> 2165extension is not present. 2166 2167Types can be any of: 2168 2169 otherName 2170 * rfc822Name 2171 * dNSName 2172 x400Address 2173 directoryName 2174 ediPartyName 2175 * uniformResourceIdentifier 2176 * iPAddress 2177 * registeredID 2178 2179The types marked with '*' are the most common. 2180 2181If C<$type> is not specified: 2182 In list context returns the types present in the subjectAlternate name. 2183 In scalar context, returns the SAN as a string. 2184 2185=head2 version 2186 2187Returns the structure version as a string, e.g. "v1" "v2", or "v3" 2188 2189=head2 pkAlgorithm 2190 2191Returns the public key algorithm according to its object identifier. 2192 2193=head2 subjectPublicKey( $format ) 2194 2195If C<$format> is B<true>, the public key will be returned in PEM format. 2196 2197Otherwise, the public key will be returned in its hexadecimal representation 2198 2199=head2 subjectPublicKeyParams 2200 2201Returns a hash describing the public key. The contents vary depending on 2202the public key type. 2203 2204=head3 Standard items: 2205 2206C<keytype> - ECC, RSA, DSA or C<undef> 2207 2208C<keytype> will be C<undef> if the key type is not supported. In 2209this case, C<error()> returns a diagnostic message. 2210 2211C<keylen> - Approximate length of the key in bits. 2212 2213Other items include: 2214 2215For RSA, C<modulus> and C<publicExponent>. 2216 2217For DSA, C<G, P and Q>. 2218 2219For ECC, C<curve>, C<pub_x> and C<pub_y>. C<curve> is an OID name. 2220 2221=head3 Additional detail 2222 2223C<subjectPublicKeyParams(1)> returns the standard items, and may 2224also return C<detail>, which is a hashref. 2225 2226For ECC, the C<detail> hash includes the curve definition constants. 2227 2228=head2 signatureAlgorithm 2229 2230Returns the signature algorithm according to its object identifier. 2231 2232=head2 signatureParams 2233 2234Returns the parameters associated with the B<signatureAlgorithm> as binary. 2235Returns B<undef> if none, or if B<NULL>. 2236 2237Note: In the future, some B<signatureAlgorithm>s may return a hashref of decoded fields. 2238 2239Callers are advised to check for a ref before decoding... 2240 2241=head2 signature( $format ) 2242 2243The CSR's signature is returned. 2244 2245If C<$format> is B<1>, in binary. 2246 2247If C<$format> is B<2>, decoded as an ECDSA signature - returns hashref to C<r> and C<s>. 2248 2249Otherwise, in its hexadecimal representation. 2250 2251=head2 attributes( $name ) 2252 2253A request may contain a set of attributes. The attributes are OIDs with values. 2254The most common is a list of requested extensions, but other OIDs can also 2255occur. Of those, B<challengePassword> is typical. 2256 2257For API version 0, this method returns a hash consisting of all 2258attributes in an internal format. This usage is B<deprecated>. 2259 2260For API version 1: 2261 2262If $name is not specified, a list of attribute names is returned. The list does not 2263include the requestedExtensions attribute. For that, use extensions(); 2264 2265If no attributes are present, the empty list (C<undef> in scalar context) is returned. 2266 2267If $name is specified, the value of the extension is returned. $name can be specified 2268as a numeric OID. 2269 2270In scalar context, a single string is returned, which may include lists and labels. 2271 2272 cspName="Microsoft Strong Cryptographic Provider",keySpec=2,signature=("",0) 2273 2274Special characters are escaped as described in options. 2275 2276In array context, the value(s) are returned as a list of items, which may be references. 2277 2278 print( " $_: ", scalar $decoded->attributes($_), "\n" ) 2279 foreach ($decoded->attributes); 2280 2281 2282=for readme stop 2283 2284See the I<Table of known OID names> below for a list of names. 2285 2286=for readme continue 2287 2288=begin :readme 2289 2290See the module documentation for a list of known OID names. 2291 2292It is too long to include here. 2293 2294=end :readme 2295 2296=head2 extensions 2297 2298Returns an array containing the names of all extensions present in the CSR. If no extensions are present, 2299the empty list is returned. 2300 2301The names vary depending on the API version; however, the returned names are acceptable to C<extensionValue>, C<extensionPresent>, and C<name2oid>. 2302 2303The values of extensions vary, however the following code fragment will dump most extensions and their value(s). 2304 2305 print( "$_: ", $decoded->extensionValue($_,1), "\n" ) foreach ($decoded->extensions); 2306 2307 2308The sample code fragment is not guaranteed to handle all cases. 2309Production code needs to select the extensions that it understands and should respect 2310the B<critical> boolean. B<critical> can be obtained with extensionPresent. 2311 2312=head2 extensionValue( $name, $format ) 2313 2314Returns the value of an extension by name, e.g. C<extensionValue( 'keyUsage' )>. 2315The name SHOULD be an API v1 name, but API v0 names are accepted for compatibility. 2316The name can also be specified as a numeric OID. 2317 2318If C<$format> is 1, the value is a formatted string, which may include lists and labels. 2319Special characters are escaped as described in options; 2320 2321If C<$format> is 0 or not defined, a string, or an array reference may be returned. 2322The array many contain any Perl variable type. 2323 2324To interpret the value(s), you need to know the structure of the OID. 2325 2326=for readme stop 2327 2328See the I<Table of known OID names> below for a list of names. 2329 2330=for readme continue 2331 2332=begin :readme 2333 2334See the module documentation for a list of known OID names. 2335 2336It is too long to include here. 2337 2338=end :readme 2339 2340=head2 extensionPresent( $name ) 2341 2342Returns B<true> if a named extension is present: 2343 If the extension is B<critical>, returns 2. 2344 Otherwise, returns 1, indicating B<not critical>, but present. 2345 2346If the extension is not present, returns C<undef>. 2347 2348The name can also be specified as a numeric OID. 2349 2350=for readme stop 2351 2352See the I<Table of known OID names> below for a list of names. 2353 2354=for readme continue 2355 2356=begin :readme 2357 2358See the module documentation for a list of known OID names. 2359 2360It is too long to include here. 2361 2362=end :readme 2363 2364=head2 registerOID( ) 2365 2366Class method. 2367 2368Register a custom OID, or a public OID that has not been added to Crypt::PKCS10 yet. 2369 2370The OID may be an extension identifier or an RDN component. 2371 2372The oid is specified as a string in numeric form, e.g. C<'1.2.3.4'> 2373 2374=head3 registerOID( $oid ) 2375 2376Returns B<true> if the specified OID is registered, B<false> otherwise. 2377 2378=head3 registerOID( $oid, $longname, $shortname ) 2379 2380Registers the specified OID with the associated long name. This 2381enables the OID to be translated to a name in output. 2382 2383The long name should be Hungarian case (B<commonName>), but this is not currently 2384enforced. 2385 2386Optionally, specify the short name used for extracting the subject. 2387The short name should be upper-case (and will be upcased). 2388 2389E.g. built-in are C<< $oid => '2.4.5.3', $longname => 'commonName', $shortname => 'CN' >> 2390 2391To register a shortname for an existing OID without one, specify C<$longname> as C<undef>. 2392 2393E.g. To register /E for emailAddress, use: 2394 C<< Crypt::PKCS10->registerOID( '1.2.840.113549.1.9.1', undef, 'e' ) >> 2395 2396 2397Generates an exception if any argument is not valid, or is in use. 2398 2399Returns B<true> otherwise. 2400 2401=head2 checkSignature 2402 2403Verifies the signature of a CSR. (Useful if new() specified C<< verifySignature => 0 >>.) 2404 2405Returns B<true> if the signature is OK. 2406 2407Returns B<false> if the signature is incorrect. C<< error() >> returns 2408the reason. 2409 2410Returns B<undef> if it was not possible to complete the verification process (e.g. a required 2411Perl module could not be loaded or an unsupported key/signature type is present.) 2412 2413I<Note>: Requires Crypt::PK::* for the used algorithm to be installed. For RSA 2414v1.5 padding is assumed, PSS is not supported (validation fails). 2415 2416 2417=head2 certificateTemplate 2418 2419C<CertificateTemplate> returns the B<certificateTemplate> attribute. 2420 2421Equivalent to C<extensionValue( 'certificateTemplate' )>, which is prefered. 2422 2423=for readme stop 2424 2425=head2 Table of known OID names 2426 2427The following OID names are known. They are used in returned strings and 2428structures, and as names by methods such as B<extensionValue>. 2429 2430Unknown OIDs are returned in numeric form, or can be registered with 2431B<registerOID>. 2432 2433=begin MAINTAINER 2434 2435 To generate the following table, use: 2436 perl -Mwarnings -Mstrict -MCrypt::PKCS10 -e'Crypt::PKCS10->_listOIDs' 2437 2438=end MAINTAINER 2439 2440 OID Name (API v1) Old Name (API v0) 2441 -------------------------- -------------------------- --------------------------- 2442 0.9.2342.19200300.100.1.1 userID 2443 0.9.2342.19200300.100.1.25 domainComponent 2444 1.2.840.10040.4.1 dsa (DSA) 2445 1.2.840.10040.4.3 dsaWithSha1 (DSA with SHA1) 2446 1.2.840.10045.2.1 ecPublicKey 2447 1.2.840.10045.3.1.1 secp192r1 2448 1.2.840.10045.3.1.7 secp256r1 2449 1.2.840.10045.4.3.1 ecdsa-with-SHA224 2450 1.2.840.10045.4.3.2 ecdsa-with-SHA256 2451 1.2.840.10045.4.3.3 ecdsa-with-SHA384 2452 1.2.840.10045.4.3.4 ecdsa-with-SHA512 2453 1.2.840.113549.1.1.1 rsaEncryption (RSA encryption) 2454 1.2.840.113549.1.1.2 md2WithRSAEncryption (MD2 with RSA encryption) 2455 1.2.840.113549.1.1.3 md4WithRSAEncryption 2456 1.2.840.113549.1.1.4 md5WithRSAEncryption (MD5 with RSA encryption) 2457 1.2.840.113549.1.1.5 sha1WithRSAEncryption (SHA1 with RSA encryption) 2458 1.2.840.113549.1.1.6 rsaOAEPEncryptionSET 2459 1.2.840.113549.1.1.7 RSAES-OAEP 2460 1.2.840.113549.1.1.11 sha256WithRSAEncryption (SHA-256 with RSA encryption) 2461 1.2.840.113549.1.1.12 sha384WithRSAEncryption 2462 1.2.840.113549.1.1.13 sha512WithRSAEncryption (SHA-512 with RSA encryption) 2463 1.2.840.113549.1.1.14 sha224WithRSAEncryption 2464 1.2.840.113549.1.9.1 emailAddress 2465 1.2.840.113549.1.9.2 unstructuredName 2466 1.2.840.113549.1.9.7 challengePassword 2467 1.2.840.113549.1.9.14 extensionRequest 2468 1.2.840.113549.1.9.15 smimeCapabilities (SMIMECapabilities) 2469 1.3.6.1.4.1.311.2.1.14 CERT_EXTENSIONS 2470 1.3.6.1.4.1.311.2.1.21 msCodeInd 2471 1.3.6.1.4.1.311.2.1.22 msCodeCom 2472 1.3.6.1.4.1.311.10.3.1 msCTLSign 2473 1.3.6.1.4.1.311.10.3.2 msTimeStamping 2474 1.3.6.1.4.1.311.10.3.3 msSGC 2475 1.3.6.1.4.1.311.10.3.4 msEFS 2476 1.3.6.1.4.1.311.10.3.4.1 msEFSRecovery 2477 1.3.6.1.4.1.311.10.3.5 msWHQLCrypto 2478 1.3.6.1.4.1.311.10.3.6 msNT5Crypto 2479 1.3.6.1.4.1.311.10.3.7 msOEMWHQLCrypto 2480 1.3.6.1.4.1.311.10.3.8 msEmbeddedNTCrypto 2481 1.3.6.1.4.1.311.10.3.9 msRootListSigner 2482 1.3.6.1.4.1.311.10.3.10 msQualifiedSubordination 2483 1.3.6.1.4.1.311.10.3.11 msKeyRecovery 2484 1.3.6.1.4.1.311.10.3.12 msDocumentSigning 2485 1.3.6.1.4.1.311.10.3.13 msLifetimeSigning 2486 1.3.6.1.4.1.311.10.3.14 msMobileDeviceSoftware 2487 1.3.6.1.4.1.311.13.1 RENEWAL_CERTIFICATE 2488 1.3.6.1.4.1.311.13.2.1 ENROLLMENT_NAME_VALUE_PAIR 2489 1.3.6.1.4.1.311.13.2.2 ENROLLMENT_CSP_PROVIDER 2490 1.3.6.1.4.1.311.13.2.3 OS_Version 2491 1.3.6.1.4.1.311.20.2 certificateTemplateName 2492 1.3.6.1.4.1.311.20.2.2 msSmartCardLogon 2493 1.3.6.1.4.1.311.21.7 certificateTemplate 2494 1.3.6.1.4.1.311.21.10 ApplicationCertPolicies 2495 1.3.6.1.4.1.311.21.20 ClientInformation 2496 1.3.6.1.5.2.3.5 keyPurposeKdc (KDC Authentication) 2497 1.3.6.1.5.5.7.2.1 CPS 2498 1.3.6.1.5.5.7.2.2 userNotice 2499 1.3.6.1.5.5.7.3.1 serverAuth 2500 1.3.6.1.5.5.7.3.2 clientAuth 2501 1.3.6.1.5.5.7.3.3 codeSigning 2502 1.3.6.1.5.5.7.3.4 emailProtection 2503 1.3.6.1.5.5.7.3.8 timeStamping 2504 1.3.6.1.5.5.7.3.9 OCSPSigning 2505 1.3.6.1.5.5.7.3.21 sshClient 2506 1.3.6.1.5.5.7.3.22 sshServer 2507 1.3.6.1.5.5.7.9.5 countryOfResidence 2508 1.3.14.3.2.29 sha1WithRSAEncryption (SHA1 with RSA signature) 2509 1.3.36.3.3.2.8.1.1.1 brainpoolP160r1 2510 1.3.36.3.3.2.8.1.1.2 brainpoolP160t1 2511 1.3.36.3.3.2.8.1.1.3 brainpoolP192r1 2512 1.3.36.3.3.2.8.1.1.4 brainpoolP192t1 2513 1.3.36.3.3.2.8.1.1.5 brainpoolP224r1 2514 1.3.36.3.3.2.8.1.1.6 brainpoolP224t1 2515 1.3.36.3.3.2.8.1.1.7 brainpoolP256r1 2516 1.3.36.3.3.2.8.1.1.8 brainpoolP256t1 2517 1.3.36.3.3.2.8.1.1.9 brainpoolP320r1 2518 1.3.36.3.3.2.8.1.1.10 brainpoolP320t1 2519 1.3.36.3.3.2.8.1.1.11 brainpoolP384r1 2520 1.3.36.3.3.2.8.1.1.12 brainpoolP384t1 2521 1.3.36.3.3.2.8.1.1.13 brainpoolP512r1 2522 1.3.36.3.3.2.8.1.1.14 brainpoolP512t1 2523 1.3.132.0.1 sect163k1 2524 1.3.132.0.15 sect163r2 2525 1.3.132.0.16 sect283k1 2526 1.3.132.0.17 sect283r1 2527 1.3.132.0.26 sect233k1 2528 1.3.132.0.27 sect233r1 2529 1.3.132.0.33 secp224r1 2530 1.3.132.0.34 secp384r1 2531 1.3.132.0.35 secp521r1 2532 1.3.132.0.36 sect409k1 2533 1.3.132.0.37 sect409r1 2534 1.3.132.0.38 sect571k1 2535 1.3.132.0.39 sect571r1 2536 2.5.4.3 commonName 2537 2.5.4.4 surname (Surname) 2538 2.5.4.5 serialNumber 2539 2.5.4.6 countryName 2540 2.5.4.7 localityName 2541 2.5.4.8 stateOrProvinceName 2542 2.5.4.9 streetAddress 2543 2.5.4.10 organizationName 2544 2.5.4.11 organizationalUnitName 2545 2.5.4.12 title (Title) 2546 2.5.4.13 description (Description) 2547 2.5.4.14 searchGuide 2548 2.5.4.15 businessCategory 2549 2.5.4.16 postalAddress 2550 2.5.4.17 postalCode 2551 2.5.4.18 postOfficeBox 2552 2.5.4.19 physicalDeliveryOfficeName 2553 2.5.4.20 telephoneNumber 2554 2.5.4.23 facsimileTelephoneNumber 2555 2.5.4.41 name (Name) 2556 2.5.4.42 givenName 2557 2.5.4.43 initials 2558 2.5.4.44 generationQualifier 2559 2.5.4.45 uniqueIdentifier 2560 2.5.4.46 dnQualifier 2561 2.5.4.51 houseIdentifier 2562 2.5.4.65 pseudonym 2563 2.5.29.14 subjectKeyIdentifier (SubjectKeyIdentifier) 2564 2.5.29.15 keyUsage (KeyUsage) 2565 2.5.29.17 subjectAltName 2566 2.5.29.19 basicConstraints (Basic Constraints) 2567 2.5.29.32 certificatePolicies 2568 2.5.29.32.0 anyPolicy 2569 2.5.29.37 extKeyUsage (EnhancedKeyUsage) 2570 2.16.840.1.101.3.4.2.1 sha256 (SHA-256) 2571 2.16.840.1.101.3.4.2.2 sha384 (SHA-384) 2572 2.16.840.1.101.3.4.2.3 sha512 (SHA-512) 2573 2.16.840.1.101.3.4.2.4 sha224 (SHA-224) 2574 2.16.840.1.101.3.4.3.1 dsaWithSha224 2575 2.16.840.1.101.3.4.3.2 dsaWithSha256 2576 2.16.840.1.101.3.4.3.3 dsaWithSha384 2577 2.16.840.1.101.3.4.3.4 dsaWithSha512 2578 2.16.840.1.113730.1.1 netscapeCertType 2579 2.16.840.1.113730.1.2 netscapeBaseUrl 2580 2.16.840.1.113730.1.4 netscapeCaRevocationUrl 2581 2.16.840.1.113730.1.7 netscapeCertRenewalUrl 2582 2.16.840.1.113730.1.8 netscapeCaPolicyUrl 2583 2.16.840.1.113730.1.12 netscapeSSLServerName 2584 2.16.840.1.113730.1.13 netscapeComment 2585 2.16.840.1.113730.4.1 nsSGC 2586 2587=for readme continue 2588 2589=begin :readme 2590 2591=head1 CHANGES 2592 2593=for readme include file=Changes type=text start=^1\.0 stop=^__END__ 2594 2595For a more detailed list of changes, see F<Commitlog> in the distribution. 2596 2597=end :readme 2598 2599=head1 EXAMPLES 2600 2601In addition to the code snippets contained in this document, the F<examples/> directory of the distribution 2602contains some sample utilitiles. 2603 2604Also, the F<t/> directory of the distribution contains a number of tests that exercise the 2605API. Although artificial, they are another good source of examples. 2606 2607Note that the type of data returned when extracting attributes and extensions is dependent 2608on the specific OID used. 2609 2610Also note that some functions not listed in this document are tested. The fact that they are 2611tested does not imply that they are stable, or that they will be present in any future release. 2612 2613The test data was selected to exercise the API; the CSR contents are not representative of 2614realistic certificate requests. 2615 2616=head1 ACKNOWLEDGEMENTS 2617 2618Martin Bartosch contributed preliminary EC support: OIDs and tests. 2619 2620Timothe Litt made most of the changes for V1.4+ 2621 2622C<Crypt::PKCS10> is based on the generic ASN.1 module by Graham Barr and on the 2623 x509decode example by Norbert Klasen. It is also based upon the 2624works of Duncan Segrest's C<Crypt-X509-CRL> module. 2625 2626=head1 AUTHORS 2627 2628Gideon Knocke <gknocke@cpan.org> 2629Timothe Litt <tlhackque@cpan.org> 2630 2631=head1 LICENSE 2632 2633GPL v1 -- See LICENSE file for details 2634 2635=cut 2636