view release on metacpan or search on metacpan
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.
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.
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.
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
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.
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
# 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
`$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.
]
];
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
## 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
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
$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
}
}
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
$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
# 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
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
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
]
];
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
=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
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
=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
use warnings;
use Test::More 0.94;
use File::Spec;
# 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
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
}
],
{
'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
"\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
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
}
}
]
},
{
'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
},
{
'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
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
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
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' );
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',
'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' );