Crypt-PKCS10

 view release on metacpan or  search on metacpan

README  view on Meta::CPAN

4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
VERSION
 
    version 1.800201
 
SYNOPSIS
 
        use Crypt::PKCS10;
     
        Crypt::PKCS10->setAPIversion( 1 );
        my $decoded = Crypt::PKCS10->new( $csr ) or die Crypt::PKCS10->error;
     
        print $decoded;
     
        @names = $decoded->extensionValue('subjectAltName' );
        @names = $decoded->subject unless( @names );
     
        %extensions = map { $_ => $decoded->extensionValue( $_ ) } $decoded->extensions
 
DESCRIPTION
 
    Crypt::PKCS10 parses PKCS #10 certificate requests (CSRs) and provides
    accessor methods to extract the data in usable form.
 
    Common object identifiers will be translated to their corresponding
    names. Additionally, accessor methods allow extraction of single data
    fields. The format of returned data varies by accessor.

README  view on Meta::CPAN

101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
    For ECC: Crypt::PK::ECC
 
    Very old CSRs may require DIGEST::MD{5,4,2}
 
METHODS
 
    Access methods may exist for subject name components that are not
    listed here. To test for these, use code of the form:
 
      $locality = $decoded->localityName if( $decoded->can('localityName') );
 
    If a name component exists in a CSR, the method will be present. The
    converse is not (always) true.
 
 class method setAPIversion( $version )
 
    Selects the API version (0 or 1) expected.
 
    Must be called before calling any other method.

README  view on Meta::CPAN

164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
  If a file handle is supplied, the caller should specify acceptPEM => 0
  if the contents are DER.
 
  The request may be PEM or binary DER encoded. Only one request is
  processed.
 
  If PEM, other data (such as mail headers) may precede or follow the
  CSR.
 
      my $decoded = Crypt::PKCS10->new( $csr ) or die Crypt::PKCS10->error;
 
  Returns undef if there is an I/O error or the request can not be parsed
  successfully.
 
  Call error() to obtain more detail.
 
options
 
  The options are specified as name => value.

README  view on Meta::CPAN

334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
  In array context, returns an array of (componentName, [values]) pairs.
  Abbreviations are not used.
 
  Note that the order of components in a name is significant.
 
commonName
 
  Returns the common name(s) from the subject.
 
      my $cn = $decoded->commonName();
 
organizationalUnitName
 
  Returns the organizational unit name(s) from the subject
 
organizationName
 
  Returns the organization name(s) from the subject.
 
emailAddress

README  view on Meta::CPAN

439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
signatureAlgorithm
 
   Returns the signature algorithm according to its object identifier.
 
signatureParams
 
   Returns the parameters associated with the signatureAlgorithm as
   binary. Returns undef if none, or if NULL.
 
   Note: In the future, some signatureAlgorithms may return a hashref of
   decoded fields.
 
   Callers are advised to check for a ref before decoding...
 
signature( $format )
 
   The CSR's signature is returned.
 
   If $format is 1, in binary.
 
   If $format is 2, decoded as an ECDSA signature - returns hashref to r
   and s.
 
   Otherwise, in its hexadecimal representation.
 
attributes( $name )
 
   A request may contain a set of attributes. The attributes are OIDs with
   values. The most common is a list of requested extensions, but other
   OIDs can also occur. Of those, challengePassword is typical.

README  view on Meta::CPAN

485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
   In scalar context, a single string is returned, which may include lists
   and labels.
 
     cspName="Microsoft Strong Cryptographic Provider",keySpec=2,signature=("",0)
 
   Special characters are escaped as described in options.
 
   In array context, the value(s) are returned as a list of items, which
   may be references.
 
    print( " $_: ", scalar $decoded->attributes($_), "\n" )
                                      foreach ($decoded->attributes);
 
   See the module documentation for a list of known OID names.
 
   It is too long to include here.
 
extensions
 
   Returns an array containing the names of all extensions present in the
   CSR. If no extensions are present, the empty list is returned.
 
   The names vary depending on the API version; however, the returned
   names are acceptable to extensionValue, extensionPresent, and name2oid.
 
   The values of extensions vary, however the following code fragment will
   dump most extensions and their value(s).
 
    print( "$_: ", $decoded->extensionValue($_,1), "\n" ) foreach ($decoded->extensions);
 
   The sample code fragment is not guaranteed to handle all cases.
   Production code needs to select the extensions that it understands and
   should respect the critical boolean. critical can be obtained with
   extensionPresent.
 
extensionValue( $name, $format )
 
   Returns the value of an extension by name, e.g. extensionValue(
   'keyUsage' ). The name SHOULD be an API v1 name, but API v0 names are

README.md  view on Meta::CPAN

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# NAME
 
Crypt::PKCS10 - parse PKCS #10 certificate requests
 
# SYNOPSIS
 
    use Crypt::PKCS10;
 
    Crypt::PKCS10->setAPIversion( 1 );
    my $decoded = Crypt::PKCS10->new( $csr ) or die Crypt::PKCS10->error;
 
    print $decoded;
 
    @names = $decoded->extensionValue('subjectAltName' );
    @names = $decoded->subject unless( @names );
 
    %extensions = map { $_ => $decoded->extensionValue( $_ ) } $decoded->extensions
 
# DESCRIPTION
 
`Crypt::PKCS10` parses PKCS #10 certificate requests (CSRs) and provides accessor methods to extract the data in usable form.
 
Common object identifiers will be translated to their corresponding names.
Additionally, accessor methods allow extraction of single data fields.
The format of returned data varies by accessor.
 
The 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.
 
**true** values should be specified as 1 and **false** values as 0.  Future API changes may provide different functions when other values are used.
 
# METHODS
 
Access methods may exist for subject name components that are not listed here.  To test for these, use code of the form:
 
    $locality = $decoded->localityName if( $decoded->can('localityName') );
 
If a name component exists in a CSR, the method will be present.  The converse is not (always) true.
 
## class method setAPIversion( $version )
 
Selects the API version (0 or 1) expected.
 
Must be called before calling any other method.
 
The API version determines how a CSR is parsed.  Changing the API version after

README.md  view on Meta::CPAN

78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
`$csr` may be a scalar containing the request, a file name, or a file handle from which to read it.
 
If a file name is specified, the `readFile` option must be specified.
 
If a file handle is supplied, the caller should specify `acceptPEM => 0` if the contents are DER.
 
The request may be PEM or binary DER encoded.  Only one request is processed.
 
If PEM, other data (such as mail headers) may precede or follow the CSR.
 
    my $decoded = Crypt::PKCS10->new( $csr ) or die Crypt::PKCS10->error;
 
Returns `undef` if there is an I/O error or the request can not be parsed successfully.
 
Call `error()` to obtain more detail.
 
### options
 
The options are specified as `name => value`.
 
If the first option is a HASHREF, it is expanded and any remaining options are added.

README.md  view on Meta::CPAN

322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
        ]
    ];
 
This structure can be used directly to assemble ASN1 structures with
the OpenXPKI::Crypt::\* objects.
 
### commonName
 
Returns the common name(s) from the subject.
 
    my $cn = $decoded->commonName();
 
### organizationalUnitName
 
Returns the organizational unit name(s) from the subject
 
### organizationName
 
Returns the organization name(s) from the subject.
 
### emailAddress

README.md  view on Meta::CPAN

424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
## signatureAlgorithm
 
Returns the signature algorithm according to its object identifier.
 
## signatureParams
 
Returns the parameters associated with the **signatureAlgorithm** as binary.
Returns **undef** if none, or if **NULL**.
 
Note: In the future, some **signatureAlgorithm**s may return a hashref of decoded fields.
 
Callers are advised to check for a ref before decoding...
 
## signature( $format )
 
The CSR's signature is returned.
 
If `$format` is **1**, in binary.
 
If `$format` is **2**, decoded as an ECDSA signature - returns hashref to `r` and `s`.
 
Otherwise, in its hexadecimal representation.
 
## attributes( $name )
 
A request may contain a set of attributes. The attributes are OIDs with values.
The most common is a list of requested extensions, but other OIDs can also
occur.  Of those, **challengePassword** is typical.
 
For API version 0, this method returns a hash consisting of all

README.md  view on Meta::CPAN

465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
as a numeric OID.
 
In scalar context, a single string is returned, which may include lists and labels.
 
    cspName="Microsoft Strong Cryptographic Provider",keySpec=2,signature=("",0)
 
Special characters are escaped as described in options.
 
In array context, the value(s) are returned as a list of items, which may be references.
 
    print( " $_: ", scalar $decoded->attributes($_), "\n" )
                                      foreach ($decoded->attributes);
 
See the _Table of known OID names_ below for a list of names.
 
## extensions
 
Returns an array containing the names of all extensions present in the CSR.  If no extensions are present,
the empty list is returned.
 
The names vary depending on the API version; however, the returned names are acceptable to `extensionValue`, `extensionPresent`, and `name2oid`.
 
The values of extensions vary, however the following code fragment will dump most extensions and their value(s).
 
    print( "$_: ", $decoded->extensionValue($_,1), "\n" ) foreach ($decoded->extensions);
 
The sample code fragment is not guaranteed to handle all cases.
Production code needs to select the extensions that it understands and should respect
the **critical** boolean.  **critical** can be obtained with extensionPresent.
 
## extensionValue( $name, $format )
 
Returns the value of an extension by name, e.g. `extensionValue( 'keyUsage' )`.
The name SHOULD be an API v1 name, but API v0 names are accepted for compatibility.
The name can also be specified as a numeric OID.

lib/Crypt/PKCS10.pm  view on Meta::CPAN

896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
    $pkinfo->{algorithm}{algorithm}
        = $oids{$pkinfo->{algorithm}{algorithm}}
          if( defined $pkinfo->{algorithm}{algorithm}
            && exists $oids{$pkinfo->{algorithm}{algorithm}} );
    return $pkinfo;
}
 
# OIDs requiring some sort of special handling
#
# Called with decoded value, returns updated value.
# Key is ASN macro name
 
my %special;
%special =
(
 EnhancedKeyUsage => sub {
     my $self = shift;
     my( $value, $id ) = @_;
 
     foreach (@{$value}) {

lib/Crypt/PKCS10.pm  view on Meta::CPAN

1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
    }
    }
    return $typeandvalues;
}
 
sub _convert_extensionRequest {
    my $self = shift;
    my( $extensionRequest ) = @_;
 
    my $parser = $self->_init('extensionRequest');
    my $decoded = $parser->decode($extensionRequest) or return [];
 
    foreach my $entry (@{$decoded}) {
        my $name = $oids{ $entry->{extnID} };
        $name = $variantNames{$name} if( defined $name && exists $variantNames{$name} );
        if (defined $name) {
            my $asnName = $name;
            $asnName =~ tr/ //d;
            my $parser = $self->_init($asnName, 1);
            if(!$parser) {
                $entry = undef;
                next;
            }

lib/Crypt/PKCS10.pm  view on Meta::CPAN

1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
            $self->_scanvalue( $dec );
 
            if( exists $special{$asnName} ) {
                my $action = $special{$asnName};
                $dec = $action->( $self, $dec, $asnName, $entry );
            }
            $entry->{extnValue} = $dec;
        }
    }
    @{$decoded} = grep { defined } @{$decoded};
    return $decoded;
}
 
sub _convert_rdn {
    my $self = shift;
    my $typeandvalue = shift;
    my %hash = ( _subject => [], );
    foreach my $entry ( @$typeandvalue ) {
        foreach my $item (@$entry) {
            my $oid = $item->{type};
            my $name = (exists $variantNames{$oid})? $variantNames{$oid}[1]: $oids{ $oid };

lib/Crypt/PKCS10.pm  view on Meta::CPAN

1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
    # For RSA PSS the parameters have been parsed to a hash already
    if (ref $self->{signatureAlgorithm}{parameters} eq 'HASH') {
        return $self->{signatureAlgorithm}{parameters};
    }
 
    my( $tlen, undef, $tag ) = asn_decode_tag2( $self->{signatureAlgorithm}{parameters} );
    if( $tlen != 0 && $tag != ASN_NULL ) {
        return $self->{signatureAlgorithm}{parameters}
    }
    # Known algorithm's parameters MAY return a hash of decoded fields.
    # For now, leaving that to the caller...
 
    return;
}
 
sub signature {
    my $self = shift;
    my $format = shift;
 
    if( defined $format && $format == 2 ) { # Per keytype decoding

lib/Crypt/PKCS10.pm  view on Meta::CPAN

1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
Very old CSRs may require C<DIGEST::MD{5,4,2}>
 
=end :readme
 
=head1 SYNOPSIS
 
    use Crypt::PKCS10;
 
    Crypt::PKCS10->setAPIversion( 1 );
    my $decoded = Crypt::PKCS10->new( $csr ) or die Crypt::PKCS10->error;
 
    print $decoded;
 
    @names = $decoded->extensionValue('subjectAltName' );
    @names = $decoded->subject unless( @names );
 
    %extensions = map { $_ => $decoded->extensionValue( $_ ) } $decoded->extensions
 
=head1 DESCRIPTION
 
C<Crypt::PKCS10> parses PKCS #10 certificate requests (CSRs) and provides accessor methods to extract the data in usable form.
 
Common object identifiers will be translated to their corresponding names.
Additionally, accessor methods allow extraction of single data fields.
The format of returned data varies by accessor.
 
The 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.
 
B<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.
 
=head1 METHODS
 
Access methods may exist for subject name components that are not listed here.  To test for these, use code of the form:
 
  $locality = $decoded->localityName if( $decoded->can('localityName') );
 
If a name component exists in a CSR, the method will be present.  The converse is not (always) true.
 
=head2 class method setAPIversion( $version )
 
Selects the API version (0 or 1) expected.
 
Must be called before calling any other method.
 
The API version determines how a CSR is parsed.  Changing the API version after

lib/Crypt/PKCS10.pm  view on Meta::CPAN

1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
C<$csr> may be a scalar containing the request, a file name, or a file handle from which to read it.
 
If a file name is specified, the C<readFile> option must be specified.
 
If a file handle is supplied, the caller should specify C<< acceptPEM => 0 >> if the contents are DER.
 
The request may be PEM or binary DER encoded.  Only one request is processed.
 
If PEM, other data (such as mail headers) may precede or follow the CSR.
 
    my $decoded = Crypt::PKCS10->new( $csr ) or die Crypt::PKCS10->error;
 
Returns C<undef> if there is an I/O error or the request can not be parsed successfully.
 
Call C<error()> to obtain more detail.
 
=head3 options
 
The options are specified as C<< name => value >>.
 
If the first option is a HASHREF, it is expanded and any remaining options are added.

lib/Crypt/PKCS10.pm  view on Meta::CPAN

2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
        ]
    ];
 
This structure can be used directly to assemble ASN1 structures with
the OpenXPKI::Crypt::* objects.
 
=head3 commonName
 
Returns the common name(s) from the subject.
 
    my $cn = $decoded->commonName();
 
=head3 organizationalUnitName
 
Returns the organizational unit name(s) from the subject
 
=head3 organizationName
 
Returns the organization name(s) from the subject.
 
=head3 emailAddress

lib/Crypt/PKCS10.pm  view on Meta::CPAN

2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
=head2 signatureAlgorithm
 
Returns the signature algorithm according to its object identifier.
 
=head2 signatureParams
 
Returns the parameters associated with the B<signatureAlgorithm> as binary.
Returns B<undef> if none, or if B<NULL>.
 
Note: In the future, some B<signatureAlgorithm>s may return a hashref of decoded fields.
 
Callers are advised to check for a ref before decoding...
 
=head2 signature( $format )
 
The CSR's signature is returned.
 
If C<$format> is B<1>, in binary.
 
If C<$format> is B<2>, decoded as an ECDSA signature - returns hashref to C<r> and C<s>.
 
Otherwise, in its hexadecimal representation.
 
=head2 attributes( $name )
 
A request may contain a set of attributes. The attributes are OIDs with values.
The most common is a list of requested extensions, but other OIDs can also
occur.  Of those, B<challengePassword> is typical.
 
For API version 0, this method returns a hash consisting of all

lib/Crypt/PKCS10.pm  view on Meta::CPAN

2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
as a numeric OID.
 
In scalar context, a single string is returned, which may include lists and labels.
 
  cspName="Microsoft Strong Cryptographic Provider",keySpec=2,signature=("",0)
 
Special characters are escaped as described in options.
 
In array context, the value(s) are returned as a list of items, which may be references.
 
 print( " $_: ", scalar $decoded->attributes($_), "\n" )
                                   foreach ($decoded->attributes);
 
 
=for readme stop
 
See the I<Table of known OID names> below for a list of names.
 
=for readme continue
 
=begin :readme

lib/Crypt/PKCS10.pm  view on Meta::CPAN

2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
=head2 extensions
 
Returns an array containing the names of all extensions present in the CSR.  If no extensions are present,
the empty list is returned.
 
The names vary depending on the API version; however, the returned names are acceptable to C<extensionValue>, C<extensionPresent>, and C<name2oid>.
 
The values of extensions vary, however the following code fragment will dump most extensions and their value(s).
 
 print( "$_: ", $decoded->extensionValue($_,1), "\n" ) foreach ($decoded->extensions);
 
 
The sample code fragment is not guaranteed to handle all cases.
Production code needs to select the extensions that it understands and should respect
the B<critical> boolean.  B<critical> can be obtained with extensionPresent.
 
=head2 extensionValue( $name, $format )
 
Returns the value of an extension by name, e.g. C<extensionValue( 'keyUsage' )>.
The name SHOULD be an API v1 name, but API v0 names are accepted for compatibility.

t/02_base.t  view on Meta::CPAN

21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
 
use Test::More 0.94;
 
 
# Name of directory where data files are found
 
my @dirpath = (File::Spec->splitpath( $0 ))[0,1];
 
my $decoded;
 
plan  tests => 12;
 
# Basic functions test requires RSA
 
# Some useful information for automated testing reports
 
my $sslver = eval {
    local $SIG{__WARN__} = sub {};

t/02_base.t  view on Meta::CPAN

130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
FjpgWH5b3xQHVyjknpteOZJnICHmlMHcwqX1uk+ywC3hRTcC/+k+wtnbs0hvCh6c
t17iTm9qI8Tlf4xhHFrsXeCOCmtN3/HSjy3c9dYVB/je5JDesYWiDy1Ssp5D/Fg9
OwC37p57VNLEyCj397q/bdQtd9wkMQKbYTMOC1Wm3Mco9XOvGW/evs20t4xINjbk
xTf+NvadhsWn4CRnKkUEyqOivkjokf9Lg7SBXqaXL1Q2dGbezOa+lMZ67QQUU5Jo
RyYABCGHIzz=
-----END CERTIFICATE REQUEST-----
trailing junk
more junk
-CERT-
 
    $decoded = eval { Crypt::PKCS10->new( undef, dieOnError => 1, verifySignature => 0 ) };
    like( $@, qr/^\$csr argument to new\(\) is not defined at /, "dieOnError generates exception" ) or BAIL_OUT( Crypt::PKCS10->error );
 
    $decoded = eval { Crypt::PKCS10->new( undef, verifySignature => 0 ); 1 };
    like( $@, qr/^Value of Crypt::PKCS10->new ignored at /, "new() in void context generates exception" ) or BAIL_OUT( Crypt::PKCS10->error );
 
    $decoded = Crypt::PKCS10->new( $csr, PEMonly => 1, verifySignature => 0 );
 
    isnt( $decoded, undef, 'load PEM from variable' ) or BAIL_OUT( Crypt::PKCS10->error );
 
    isa_ok( $decoded, 'Crypt::PKCS10' ); # Make sure new objects are blessed
 
    is( $decoded->version, "v1", 'CSR version' );
 
    is( $decoded->commonName, "test", 'CSR commonName' );
 
    is( $decoded->emailAddress, 'test@test.com', 'emailAddress' );
 
    is( $decoded->subjectPublicKey, '3082010a0282010100e0484c12ee29a56fb72d2829fdf286859b049a007' .
        'd9030126bdd1e9d2319be38b4a40b00416dc54b000340ba580bb1c511e251e1a781ec2139c52d41e16226f6' .
        '07d1c4b3027a4a951eadc88e90b100de568f267902c694399d1392b27b7a7f31d84795e51d6833ee46132bf' .
        'c049f9f962cc6f50e511f1a56227d47d7e3d60f8756d914a8fcf5440a67e571e4106fa6e6cb1cbe6e46b94a' .
        'bed62f11bde4b2cfb2cf330558654e7f27ccc82b28708517a3336a36a9308b5683cbf3dcca492cd563ae3a7' .
        '4d2d337424a644771cce8704dc47e67ecff2389587a57e3a2197813114a58cecd260071bf03c97aa4b2d0bf' .
        '0e1f51310202bc7dee79ba9e884a6528a36f0203010001', 'hex subjectPublicKey' );
 
    is( $decoded->subjectPublicKey(1), << '_KEYPEM_', 'PEM subjectPublicKey' );
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4EhMEu4ppW+3LSgp/fKG
hZsEmgB9kDASa90enSMZvji0pAsAQW3FSwADQLpYC7HFEeJR4aeB7CE5xS1B4WIm
9gfRxLMCekqVHq3IjpCxAN5WjyZ5AsaUOZ0TkrJ7en8x2EeV5R1oM+5GEyv8BJ+f
lizG9Q5RHxpWIn1H1+PWD4dW2RSo/PVECmflceQQb6bmyxy+bka5Sr7WLxG95LLP
ss8zBVhlTn8nzMgrKHCFF6MzajapMItWg8vz3MpJLNVjrjp00tM3QkpkR3HM6HBN
xH5n7P8jiVh6V+OiGXgTEUpYzs0mAHG/A8l6pLLQvw4fUTECArx97nm6nohKZSij
bwIDAQAB
-----END PUBLIC KEY-----
_KEYPEM_
 
    is_deeply( $decoded->subjectPublicKeyParams,
               {keytype => 'RSA',
                keylen => 2048,
                modulus => 'e0484c12ee29a56fb72d2829fdf286859b049a007d9030126bdd1e9d2319be38b4a40b00416dc54b000340ba580bb1c511e251e1a781ec2139c52d41e16226f607d1c4b3027a4a951eadc88e90b100de568f267902c694399d1392b27b7a7f31d84795e51d6833ee46132bfc049f9f...
                publicExponent => '10001',
               }, 'subjectPublicKeyParams(RSA)' );
 
    is( $decoded->signature, 'dc8ba14eade00b952cdaacf0f48986407f01802b3d2c626c0cea42f977a39c3dbb5' .
        '8db01c1897fb72a3d46c811269e446c4c208f63030694216f84ecca99163a60587e5bdf14075728e49e9b5e3' .
        '992672021e694c1dcc2a5f5ba4fb2c02de1453702ffe93ec2d9dbb3486f0a1e9cb75ee24e6f6a23c4e57f8c6' .
        '11c5aec5de08e0a6b4ddff1d28f2ddcf5d61507f8dee490deb185a20f2d52b29e43fc583d3b00b7ee9e7b54d' .
        '2c4c828f7f7babf6dd42d77dc2431029b61330e0b55a6dcc728f573af196fdebecdb4b78c483636e4c537fe3' .
        '6f69d86c5a7e024672a4504caa3a2be48e891ff4b83b4815ea6972f54367466decce6be94c67aed04145392684726',
        'signature' );
 
    is( unpack( "H*", $decoded->certificationRequest ),
        '308201b602010030818831133011060a0992268993f22c64011916036f726731173015060a0992268993f22c' .
        '64011916074f70656e53534c31153013060a0992268993f22c640119160575736572733123300b0603550403' .
        '0c04746573743014060a0992268993f22c6401010c06313233343536311c301a06092a864886f70d01090116' .
        '0d7465737440746573742e636f6d30820122300d06092a864886f70d01010105000382010f003082010a0282' .
        '010100e0484c12ee29a56fb72d2829fdf286859b049a007d9030126bdd1e9d2319be38b4a40b00416dc54b00' .
        '0340ba580bb1c511e251e1a781ec2139c52d41e16226f607d1c4b3027a4a951eadc88e90b100de568f267902' .
        'c694399d1392b27b7a7f31d84795e51d6833ee46132bfc049f9f962cc6f50e511f1a56227d47d7e3d60f8756' .
        'd914a8fcf5440a67e571e4106fa6e6cb1cbe6e46b94abed62f11bde4b2cfb2cf330558654e7f27ccc82b2870' .
        '8517a3336a36a9308b5683cbf3dcca492cd563ae3a74d2d337424a644771cce8704dc47e67ecff2389587a57' .
        'e3a2197813114a58cecd260071bf03c97aa4b2d0bf0e1f51310202bc7dee79ba9e884a6528a36f0203010001a000',
        'certificationRequest' );
 
    is( scalar $decoded->subject, '/DC=org/DC=OpenSSL/DC=users/CN=test/UID=123456/emailAddress=test@test.com',
        'subject()' );
 
    is_deeply( $decoded->subjectRaw, [
          {
            'value' => 'org',
            'type' => '0.9.2342.19200300.100.1.25',
            'format' => 'ia5String'
          },
          {
            'value' => 'OpenSSL',
            'type' => '0.9.2342.19200300.100.1.25',
            'format' => 'ia5String'
          },

t/02_base.t  view on Meta::CPAN

235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
            }
          ],
          {
            'format' => 'ia5String',
            'value' => 'test@test.com',
            'type' => '1.2.840.113549.1.9.1'
          }
        ],
            'subjectRaw()' );
 
    is( scalar $decoded->userID, '123456', 'userID accessor autoloaded' );
 
    # Note that this is the input, but with junk removed.
 
    my $extcsr = << '~~~';
-----BEGIN CERTIFICATE REQUEST-----
MIICzjCCAbYCAQAwgYgxEzARBgoJkiaJk/IsZAEZFgNvcmcxFzAVBgoJkiaJk/Is
ZAEZFgdPcGVuU1NMMRUwEwYKCZImiZPyLGQBGRYFdXNlcnMxIzALBgNVBAMMBHRl
c3QwFAYKCZImiZPyLGQBAQwGMTIzNDU2MRwwGgYJKoZIhvcNAQkBFg10ZXN0QHRl
c3QuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4EhMEu4ppW+3
LSgp/fKGhZsEmgB9kDASa90enSMZvji0pAsAQW3FSwADQLpYC7HFEeJR4aeB7CE5

t/02_base.t  view on Meta::CPAN

285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
    "\340\13\225,\332\254\360\364\211\206\@\177\1\200+=,bl\f\352B\371w\243\234=\273X" .
    "\333\1\301\211\177\267*=F\310\21&\236DlL \217c\3\6\224!o\204\354\312\231\26:`X~[" .
    "\337\24\aW(\344\236\233^9\222g !\346\224\301\334\302\245\365\272O\262\300-\341E7\2" .
    "\377\351>\302\331\333\263Ho\n\36\234\267^\342Noj#\304\345\177\214a\34Z\354]\340\216" .
    "\nkM\337\361\322\217-\334\365\326\25\a\370\336\344\220\336\261\205\242\17-R\262" .
    "\236C\374X=;\0\267\356\236{T\322\304\310(\367\367\272\277m\324-w\334\$1\2\233a3\16" .
    "\13U\246\334\307(\365s\257\31o\336\276\315\264\267\214H66\344\3057\3766\366\235\206" .
    "\305\247\340\$g*E\4\312\243\242\276H\350\221\377K\203\264\201^\246\227/T6tf\336\314" .
    "\346\276\224\306z\355\4\24S\222hG&";
 
  is( $decoded->csrRequest(1), $extcsr, 'extracted PEM' );
 
  ok( $decoded->csrRequest eq $extder, 'extracted DER' );
 
  isnt( $decoded, undef, 'load PEM from variable' ) or BAIL_OUT( Crypt::PKCS10->error );
 
  #is( $decoded->pkAlgorithm, 'RSA encryption', 'correct encryption algorithm' );
  is( $decoded->pkAlgorithm, 'rsaEncryption', 'encryption algorithm' );
 
  #is( $decoded->signatureAlgorithm, 'SHA-256 with RSA encryption', 'correct signature algorithm' );
  is( $decoded->signatureAlgorithm, 'sha256WithRSAEncryption', 'signature algorithm' );
 
  is( $decoded->signatureParams, undef, 'signature parameters' ); # RSA is NULL
 
  is( $decoded->signature(2), undef, 'signature decoding' );
 
SKIP: {
      skip( "Crypt::PK::RSA not installed", 1 ) unless( eval { require Crypt::PK::RSA; } );
 
      ok( $decoded->checkSignature, 'verify RSA CSR signature' );
  }
 
  my $file = File::Spec->catpath( @dirpath, 'csr1.pem' );
 
  $decoded = Crypt::PKCS10->new( $file, readFile => 1, verifySignature => 0 );
 
  isnt( $decoded, undef, 'load PEM from filename' ) or BAIL_OUT( Crypt::PKCS10->error );
 
  my $der = $decoded->csrRequest;
 
  $file = File::Spec->catpath( @dirpath, 'csr1.cer' ); # N.B. Padding added to test removal
 
  if( open( my $csr, '<', $file ) ) {
      $decoded = Crypt::PKCS10->new( $csr, { verifySignature => 0, acceptPEM => 0, binaryMode => 1 }, escapeStrings => 0 );
  } else {
      BAIL_OUT( "$file: $!\n" );
  }
 
  isnt( $decoded, undef, 'load DER from file handle' ) or BAIL_OUT( Crypt::PKCS10->error );
 
  ok( $der eq $decoded->csrRequest, "DER from file matches DER from PEM" );
 
  subtest "subject name component access" => sub {
      plan tests => 9;
 
      is( join( ','$decoded->countryName ),            'AU',                       '/C' );
      is( join( ','$decoded->stateOrProvinceName ),    'Some-State',               '/ST' );
      is( join( ','$decoded->localityName ),           'my city',                  '/L' );
      is( join( ','$decoded->organizationName ),       'Internet Widgits Pty Ltd', '/O' );
      is( join( ','$decoded->organizationalUnitName ), 'Big org,Smaller org',      '/OU/OU' );
      is( join( ','$decoded->commonName ),             'My Name',                  '/CN' );
      is( join( ','$decoded->emailAddress ),           'none@no-email.com',        '/emailAddress' );
      is( join( ','$decoded->domainComponent ),        'domainComponent',          '/DC' );
 
      is_deeply( [ $decoded->subject ],
                 [
                  'countryName',
                  [
                   'AU'
                  ],
                  'stateOrProvinceName',
                  [
                   'Some-State'
                  ],
                  'localityName',

t/02_base.t  view on Meta::CPAN

437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
9tEF1Lm3u9U8cmTZAvUNO9A1NlPX8e660ra6WQN2IKfDZp4XX5qisg3tus7WTfG7
aLNx7HGTQt7c2f7AlhuoQJZsCpGrcxIFmsY3yB/bTw==
-----END CERTIFICATE REQUEST-----
GOOD
    cmp_ok( $bad->csrRequest(1), 'eq', $good, 'correct invalid base64' );
};
 
subtest 'attribute functions' => sub {
    plan tests => 7;
 
    is_deeply( [ $decoded->attributes ], [qw/challengePassword unstructuredName/],
               'attributes list is correct' );
    is( scalar $decoded->attributes( 'missing' ), undef, 'missing attribute ' );
    is( scalar $decoded->attributes( '1.2.840.113549.1.9.7' ), 'Secret',
        'challengePassword string by OID' );
    is( scalar $decoded->attributes( 'challengePassword' ), 'Secret',
        'challengePassword string' );
    is_deeply( [ $decoded->attributes( 'challengePassword' ) ], [ 'Secret' ],
               'challengePassword array' );
 
    is( scalar $decoded->attributes( 'unstructuredName' ), 'MyCoFoCo',
        'unstructuredName string' );
    is_deeply( [ $decoded->attributes( 'unstructuredName' ) ], [ 'MyCoFoCo' ],
               'unstructuredName array' );
};
 
subtest "basic extension functions" => sub {
    plan tests => 18;
 
    is_deeply( [ $decoded->extensions ],
               [ qw/basicConstraints keyUsage extKeyUsage subjectAltName
                    subjectKeyIdentifier certificatePolicies/ ],
               'extensions list is correct' );
 
    is( $decoded->extensionPresent( '-- I surely dont exist-' ), undef, 'extensionPresent undef' );
    is( $decoded->extensionPresent( '2.5.29.14' ), 1, 'extensionPresent by OID' ); # subjectKeyIdentifier
    is( $decoded->extensionPresent( 'basicConstraints' ), 2, 'extension present critical ' );
 
    is( $decoded->extensionValue( 'basicConstraints', 1 ), 'CA:TRUE',
        'basicConstraints string' );
    is( $decoded->extensionValue( '2.5.29.19', 1 ), 'CA:TRUE',
        'basicConstraints string by OID' );
    is_deeply( $decoded->extensionValue( 'basicConstraints' ), { CA => 'TRUE' },
               'basicConstraints hash' );
 
 
    is( $decoded->extensionValue( 'keyUsage', 1 ),
            'keyEncipherment,nonRepudiation,digitalSignature', 'keyUsage string' );
    ok( ref $decoded->extensionValue('KeyUsage') eq 'ARRAY', 'KeyUsage is an arrayref' );
 
    is_deeply( $decoded->extensionValue( 'keyUsage'), [
                                                       'keyEncipherment',
                                                       'nonRepudiation',
                                                       'digitalSignature',
                                                      ], 'keyUsage array' );
 
    is( $decoded->extensionValue( 'extKeyUsage', 1 ),
'emailProtection,serverAuth,clientAuth,codeSigning,emailProtection,timeStamping,OCSPSigning',
        'extKeyUsage string' );
    is_deeply( $decoded->extensionValue( 'extKeyUsage'), [
                                                          'emailProtection',
                                                          'serverAuth',
                                                          'clientAuth',
                                                          'codeSigning',
                                                          'emailProtection',
                                                          'timeStamping',
                                                          'OCSPSigning',
                                                         ], 'extKeyUsage array' );
 
    is( $decoded->extensionValue( 'subjectKeyIdentifier', 1 ), '0012459a',
        'subjectKeyIdentifier string' );
 
    is( $decoded->extensionValue( 'certificatePolicies', 1 ),
'(policyIdentifier=postOfficeBox,policyQualifier=((policyQualifierId=CPS,qualifier=http://there.example.net),'.
'(policyQualifierId=CPS,qualifier=http://here.example.net),(policyQualifierId=userNotice,'.
'qualifier=(explicitText="Trust but verify",userNotice=(noticeNumbers=(8,11),organization="Suspicious minds"))),'.
'(policyQualifierId=userNotice,qualifier=(explicitText="Trust but verify",userNotice=(noticeNumbers=(8,11),'.
'organization="Suspicious minds"))))),policyIdentifier=1.5.88.103',
        'certificatePolicies string' );
    is_deeply( $decoded->extensionValue( 'certificatePolicies' ),
            [
             {
              'policyIdentifier' => 'postOfficeBox',
              'policyQualifier' => [
                                    {
                                     'policyQualifierId' => 'CPS',
                                     'qualifier' => 'http://there.example.net'
                                    },
                                    {
                                     'policyQualifierId' => 'CPS',

t/02_base.t  view on Meta::CPAN

553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
                                                    }
                                    }
                                   ]
             },
             {
              'policyIdentifier' => '1.5.88.103'
             }
            ],
                   'certificatePolicies array' );
 
    is( $decoded->certificateTemplate, undef, 'certificateTemplate absent' );
 
    is( $decoded->extensionValue('foo'), undef, 'extensionValue when extension absent' );
 
    is( $decoded->extensionValue('subjectAltName', 1),
        'rfc822Name=noway@none.com,uniformResourceIdentifier=htt' .
        'ps://fred.example.net,rfc822Name=someday@nowhere.exampl' .
        'e.com,dNSName=www.example.net,dNSName=www.example.com,d' .
        'NSName=example.net,dNSName=example.com,iPAddress=10.2.3' .
        '.4,iPAddress=2001:0DB8:0741:0000:0000:0000:0000:0000', 'subjectAltName' );
};
 
subtest "subjectAltname" => sub {
    plan tests => 5;
 
    my $altname = $decoded->extensionValue('subjectAltName');
 
    my $correct =  [
                    {
                     'rfc822Name' => 'noway@none.com'
                    },
                    {
                     'uniformResourceIdentifier' => 'https://fred.example.net'
                    },
                    {
                     'rfc822Name' => 'someday@nowhere.example.com'

t/02_base.t  view on Meta::CPAN

601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
                    },
                    {
                     'iPAddress' => '10.2.3.4'
                    },
                    {
                     'iPAddress' => '2001:0DB8:0741:0000:0000:0000:0000:0000'
                    }
                   ];
    is_deeply( $correct, $altname, 'structure returned as extension' );
 
    is( $decoded->subjectAltName, 'rfc822Name:noway@none.com,' .
        'uniformResourceIdentifier:https://fred.example.net,rfc822Name:someday@nowhere.example.com,' .
        'dNSName:www.example.net,dNSName:www.example.com,dNSName:example.net,dNSName:example.com,' .
        'iPAddress:10.2.3.4,iPAddress:2001:0DB8:0741:0000:0000:0000:0000:0000',
        "subjectAltName returns string in scalar context" );
 
    is( join( ',', sort $decoded->subjectAltName ), 'dNSName,iPAddress,rfc822Name,uniformResourceIdentifier',
        'component list' );
 
    is( join( ',', $decoded->subjectAltName( 'iPAddress' )),
        '10.2.3.4,2001:0DB8:0741:0000:0000:0000:0000:0000', 'IP address list selection' );
 
    is( $decoded->subjectAltName( 'iPAddress' ), '10.2.3.4', 'extraction of first IP address' );
};
 
subtest 'oid mapping' => sub {
    plan tests => 6;
 
    is( Crypt::PKCS10->name2oid( 'houseIdentifier' ), '2.5.4.51', 'name2oid main table' );
    is( Crypt::PKCS10->name2oid( 'timeStamping' ), '1.3.6.1.5.5.7.3.8', 'name2oid extKeyUsages' );
    is( Crypt::PKCS10->name2oid( '-- I surely dont exist-' ), undef, 'name2oid returns undef if unknown' );
 
    is( Crypt::PKCS10->oid2name( '2.5.4.51' ), 'houseIdentifier', 'oid2name main table' );

t/02_base.t  view on Meta::CPAN

640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
plan tests => 14;
 
ok( !Crypt::PKCS10->registerOID( '1.3.6.1.4.1.25043.0' ), 'OID is not registered' );
ok( Crypt::PKCS10->registerOID( '2.5.4.51' ), 'OID is registered' );
ok( Crypt::PKCS10->registerOID( '1.3.6.1.5.5.7.3.1' ), 'KeyUsage OID registered' );
ok( Crypt::PKCS10->registerOID( '1.3.6.1.4.1.25043.0', 'SampleOID' ), 'Register longform OID' );
is( Crypt::PKCS10->name2oid( 'SampleOID' ),  '1.3.6.1.4.1.25043.0', 'Find by name' );
is( Crypt::PKCS10->oid2name( '1.3.6.1.4.1.25043.0' ), 'SampleOID', 'Find by OID' );
 
ok( Crypt::PKCS10->registerOID( '1.2.840.113549.1.9.1', undef, 'e' ), 'Register /E for emailAddress' );
cmp_ok( scalar $decoded->subject, 'eq', '/C=AU/ST=Some-State/L=my city/O=Internet Widgits Pty Ltd/OU=Big org/OU=Smaller org/CN=My Name/E=none@no-email.com/DC=domainComponent', 'Short name for /emailAddress' );
 
eval{ Crypt::PKCS10->registerOID( '2.5.4.6'undef, 'C' ) };
like( $@, qr/^C already registered/, 'Register duplicate shortname' );
 
eval{ Crypt::PKCS10->registerOID( 'A''name' ) };
like( $@, qr/^Invalid OID A/, 'Register invalid OID' );
 
eval{ Crypt::PKCS10->registerOID( '2.5.4.6''emailAddress', 'C' ) };
like( $@, qr/^2.5.4.6 already registered/, 'Register duplicate oid' );

t/02_base.t  view on Meta::CPAN

667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
    eval{ Crypt::PKCS10->registerOID( undef ) };
    like( $@, qr/^Not enough arguments/, 'Minimum arguments' );
};
 
subtest 'Microsoft extensions' => sub {
    plan tests => 10;
 
    my $file = File::Spec->catpath( @dirpath, 'csr2.pem' );
 
    if( open( my $csr, '<', $file ) ) {
        $decoded = Crypt::PKCS10->new( $csr, escapeStrings => 1, verifySignature => 0 );
    } else {
        BAIL_OUT( "$file: $!\n" );
    }
 
    isnt( $decoded, undef, 'load PEM from file handle' ) or BAIL_OUT( Crypt::PKCS10->error );
 
    is( scalar $decoded->subject, '/O=TestOrg/CN=TestCN', 'subject' );
    is_deeply( [ $decoded->attributes ],
               [
                'ClientInformation',
                'ENROLLMENT_CSP_PROVIDER',
                'ENROLLMENT_NAME_VALUE_PAIR',
                'OS_Version'
               ], 'attributes list' );
 
    is( scalar $decoded->attributes( 'ENROLLMENT_CSP_PROVIDER' ),
        'cspName="Microsoft Strong Cryptographic Provider",keySpec=2,signature=("",0)',
        'ENROLLMENT_CSP_PROVIDER string ' );
    is_deeply( $decoded->attributes( 'ENROLLMENT_CSP_PROVIDER' ),
               {
                'cspName' => 'Microsoft Strong Cryptographic Provider',
                'keySpec' => 2,
                'signature' => [
                                '',
                                0
                               ]
               }, 'ENROLLMENT_CSP_PROVIDER hash' );
 
    is( scalar $decoded->attributes('ENROLLMENT_NAME_VALUE_PAIR'),
        'name=CertificateTemplate,value=User', 'ENROLLMENT_NAME_VALUE_PAIR string' );
    is_deeply( $decoded->attributes('ENROLLMENT_NAME_VALUE_PAIR'),
               {
                'name' => 'CertificateTemplate',
                'value' => 'User'
               }, 'ENROLLMENT_NAME_VALUE_PAIR hash' );
 
    is( scalar $decoded->attributes('ClientInformation'),
        'MachineName=Scream,ProcessName=certreq,UserName="Scream\\\\timothe",clientId=9',
        'ClientInformation string' );
    is_deeply( $decoded->attributes('ClientInformation'),
               {
                'MachineName' => 'Scream',
                'ProcessName' => 'certreq',
                'UserName' => 'Scream\\timothe',
                'clientId' => 9
               }, 'ClientInformation hash' );
 
    is( scalar $decoded->attributes( 'OS_Version' ), '6.1.7601.2', 'OS_Version' );
};
 
subtest 'stringify object' => sub {
    plan tests => 9;
 
    my $string = eval {
        local $SIG{__WARN__} = sub { die $_[0] };
 
        return "$decoded";
    };
 
    cmp_ok( $@, 'eq', '', 'no exception' );
 
    isnt( $string, undef, 'returns something' );
 
    cmp_ok( length $string, '>=', 2800, 'approximate result length' ) or
      diag( sprintf( "actual length %u, value:\n%s\n", length $string, $string ) );
 
    # Perl 5.8.8 bug 39185: sometimes modifiers outside a qr don't work, but do when cloistered.

t/02_base.t  view on Meta::CPAN

748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
    like( $string, qr'(?ms:^publicExponent\s*: 10001)', 'string includes RSA public key' );
    like( $string, qr'(?ms:^-----BEGIN PUBLIC KEY-----$)', 'string includes public key PEM' );
    like( $string, qr'(?ms:^-----END PUBLIC KEY-----$)', 'string closes public key PEM' );
    like( $string, qr'(?ms:^-----BEGIN CERTIFICATE REQUEST-----$)', 'string includes CSR PEM' );
    like( $string, qr'(?ms:^-----END CERTIFICATE REQUEST-----$)', 'string closes CSR PEM' );
};
 
subtest 'DSA requests' => sub {
    plan tests => 5;
 
    $decoded = Crypt::PKCS10->new( File::Spec->catpath( @dirpath, 'csr5.pem' ),
                                   verifySignature => 0,
                                   readFile =>1, escapeStrings => 1 );
 
    isnt( $decoded, undef, 'load PEM from filename' ) or BAIL_OUT( Crypt::PKCS10->error );
 
    is( $decoded->signatureAlgorithm, 'dsaWithSha256', 'DSA signature' );
 
    is_deeply( $decoded->subjectPublicKeyParams,
               {keytype => 'DSA',
                keylen => 1024,
                Q => 'b2a130635bfe19dbb3e49d8f5c4bae8266126019',
                P => 'eb3ac7a7928f0a2ab9ef61288cfde11c13e932d3853803daeb2559e8a91abc9dc48577195a471026ef27741f24e60d93a42506f16cd8bd5aebdbf519b5baa3e6470484c3c3790ffc9b5617fbd38545cd07ff60da7846383c848f0ab447ac7ed5dcd35132d882e03269f3694330d41292d92e...
                G => 'd2a82fb32f303aab7c554c91096d233cd3e87b2c9e202172a5206c7a228a39195504fcf6266748ea1a212cef6b9632bdc2012a766875c93334f7dacc24fef6ed11c185af502b236637bfdb3f8fab1de2b4bc26b45d5bb6171b8c169eca77977b5b4b9c9ca7df4052c7717bd885db9436d098...
               }, 'subjectPublicKeyParams(DSA)' );
 
    is( $decoded->signature(2), undef, 'signature decoding' );
  SKIP: {
        skip( "Crypt::PK::DSA is not installed", 1 ) unless( eval { require Crypt::PK::DSA; } );
 
        ok( $decoded->checkSignature, "verify DSA signature" );
    }
};
 
subtest 'PSS Padding' => sub {
    plan tests => 16;
 
    foreach my $file ('csr9.pem','csr9a.pem','csr9b.pem','csr9c.pem') {
        $decoded = Crypt::PKCS10->new( File::Spec->catpath( @dirpath, $file ),
                        verifySignature => 0,
                        readFile =>1, escapeStrings => 1 );
 
        isnt( $decoded, undef, 'load PEM from filename' ) or BAIL_OUT( Crypt::PKCS10->error );
 
        is( $decoded->signatureAlgorithm, 'rsassaPss', 'RSA_PSS signature' );
 
        is( $decoded->signature(2), undef, 'signature decoding' );
        SKIP: {
            skip( "Crypt::PK::RSA is not installed", 1 ) unless( eval { require Crypt::PK::RSA; } );
            ok( $decoded->checkSignature, "verify RSA PSS signature" );
        }
    }
};
 
subtest 'API v0' => sub {
    plan tests => 6;
 
    Crypt::PKCS10->setAPIversion( 0 );
    my $csr = eval { Crypt::PKCS10->new( '', PEMonly => 1 ); };
    is( $csr, undef, 'new is undef' );

t/03_ecc.t  view on Meta::CPAN

31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
plan tests => 24;
 
pass( 'configuration' );
diag( sprintf( "Perl %s version %vd\n", $^X, $^V ) );
 
ok( Crypt::PKCS10->setAPIversion(1), 'setAPIversion 1' );
 
my @dirpath = (File::Spec->splitpath( $0 ))[0,1];
 
my $decoded;
$decoded = Crypt::PKCS10->new( File::Spec->catpath( @dirpath, 'csr4.pem' ),
                               readFile => 1, escapeStrings => 1, PEMonly => 1 );
 
isnt( $decoded, undef, 'load PEM from file' ) or BAIL_OUT( Crypt::PKCS10->error );
 
is( scalar $decoded->subject, '/C=AU/ST=Some-State/O=Internet Widgits Pty Ltd', 'subject' );
 
is( $decoded->commonName, "", 'CSR commonName' );
 
is( $decoded->subjectPublicKey, '048d0507a7ebf58a17910fe2b15b0c451e93bc948a4bafb7bf1204d6043e7de1394230befab9c5115cd3cd1e059a545788bb1e0830ee06300c4f3e8d87128f3ddc', 'hex subjectPublicKey' );
 
is( $decoded->subjectPublicKey(1), << '_KEYPEM_', 'PEM subjectPublicKey' );
-----BEGIN PUBLIC KEY-----
MFowFAYHKoZIzj0CAQYJKyQDAwIIAQEHA0IABI0FB6fr9YoXkQ/isVsMRR6TvJSK
S6+3vxIE1gQ+feE5QjC++rnFEVzTzR4FmlRXiLseCDDuBjAMTz6NhxKPPdw=
-----END PUBLIC KEY-----
_KEYPEM_
 
is( $decoded->signature, '30440220730d25ebe5f187c607577cc106d3141dc7f90827914f2a6a11ebc9de6fdf1d26022042c02e4819f2c16c56181205c6c2176902f20cbfcfdc1fa82b30f79bd15d2172',
    'signature' );
 
is( scalar $decoded->subject, '/C=AU/ST=Some-State/O=Internet Widgits Pty Ltd',
    'subject()' );
 
is( $decoded->pkAlgorithm, 'ecPublicKey', 'encryption algorithm' );
 
is_deeply( $decoded->subjectPublicKeyParams,
           {keytype => 'ECC',
            keylen => 256,
            curve => 'brainpoolP256r1',
            'pub_x' => '8D0507A7EBF58A17910FE2B15B0C451E93BC948A4BAFB7BF1204D6043E7DE139',
            'pub_y' => '4230BEFAB9C5115CD3CD1E059A545788BB1E0830EE06300C4F3E8D87128F3DDC',
           }, 'subjectPublicKeyParams(EC brainpool)' );
 
is( $decoded->signatureAlgorithm, 'ecdsa-with-SHA256', 'signature algorithm' );
 
my $sig = $decoded->signature( 2 );
ok( defined $sig &&
    substr( $sig->{r}->as_hex, 2 ) eq '730d25ebe5f187c607577cc106d3141dc7f90827914f2a6a11ebc9de6fdf1d26' &&
    substr( $sig->{s}->as_hex, 2 ) eq '42c02e4819f2c16c56181205c6c2176902f20cbfcfdc1fa82b30f79bd15d2172',
    'ECDSA signature components' );
 
 
my $key = $decoded->subjectPublicKey(1);
 
isnt( $key = Crypt::PK::ECC->new( \$key ), undef, 'parse EC key' );
# Seems curve_name can be reported in UPPERcase with old version of
# Crypt::PK::ECC.  curve_oid can also be missing...
my $kh = $key->key2hash;
if( $kh->{curve_name} =~ /^BRAINPOOLP256R1$/ || !exists $kh->{curve_oid}) {
    BAIL_OUT( "Crypt::PK::ECC version is too old" );
}
my $keyh = {
   'curve_A' => '7D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9',

t/03_ecc.t  view on Meta::CPAN

104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
   'curve_order' => 'A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A7',
   'curve_prime' => 'A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E5377',
   'k' => '',
   'pub_x' => '8D0507A7EBF58A17910FE2B15B0C451E93BC948A4BAFB7BF1204D6043E7DE139',
   'pub_y' => '4230BEFAB9C5115CD3CD1E059A545788BB1E0830EE06300C4F3E8D87128F3DDC',
   'size' => 32,
   'type' => 0,
           };
is_deeply( $kh, $keyh, 'extract EC parameters' );
 
is_deeply( $decoded->subjectPublicKeyParams(1), {
   'curve' => 'brainpoolP256r1',
   'detail' => $keyh,
   'keylen' => 256,
   'keytype' => 'ECC',
   'pub_x' => '8D0507A7EBF58A17910FE2B15B0C451E93BC948A4BAFB7BF1204D6043E7DE139',
   'pub_y' => '4230BEFAB9C5115CD3CD1E059A545788BB1E0830EE06300C4F3E8D87128F3DDC',
                                                }, 'detailed EC parameters' );
 
 
$decoded = Crypt::PKCS10->new( File::Spec->catpath( @dirpath, 'csr6.pem' ),
                               readFile => 1, PEMonly => 1, escapeStrings => 1 );
 
isnt( $decoded, undef, 'load PEM from file' ) or BAIL_OUT( Crypt::PKCS10->error );
 
is( $decoded->pkAlgorithm, 'ecPublicKey', 'encryption algorithm' );
 
is_deeply( $decoded->subjectPublicKeyParams,
           {keytype => 'ECC',
            keylen => 384,
            curve => 'secp384r1',
            'pub_x' => '43FCD15809728171AECA3029A002C13424E92F5D39C3FB7074B5B4B8802FA3E9AB79E1F6CC174596AA09C6BEA9DFAAFF',
            'pub_y' => '1891F7048842DF14F3FDCABB81C40BDDBFDA64A20FCEA13136DF8109AB56D205F857A295ED00C6B7FAFB6240D66447EB',
           }, 'subjectPublicKeyParams(EC secp)' );
 
is( $decoded->signatureAlgorithm, 'ecdsa-with-SHA384', 'signature algorithm' );
 
$decoded = Crypt::PKCS10->new( File::Spec->catpath( @dirpath, 'csr7.pem' ),
                               readFile => 1, PEMonly => 1, escapeStrings => 1 );
 
is( $decoded, undef, 'bad signature rejected' ) or BAIL_OUT( Crypt::PKCS10->error );
 
$decoded = Crypt::PKCS10->new( File::Spec->catpath( @dirpath, 'csr7.pem' ),
                               readFile => 1, PEMonly => 1, escapeStrings => 1, verifySignature => 0 );
 
isnt( $decoded, undef, 'bad signature loaded' ) or BAIL_OUT( Crypt::PKCS10->error );
 
ok( !$decoded->checkSignature, 'checkSignature returns false' );
ok( defined Crypt::PKCS10->error, 'checkSignature sets error string' );



( run in 0.291 second using v1.01-cache-2.11-cpan-496ff517765 )