XML::Sig::OO - Modern XML Signatured validation
use XML::Sig::OO; # Sign our xml my $s=new XML::Sig::OO( xml=>'<?xml version="1.0" standalone="yes"?><data><test ID="A" /><test ID="B" /></data>', key_file=>'rsa_key.pem' cert_file=>'cert.pem', ); my $result=$s->sign; die "Failed to sign the xml, error was: $result" unless $result; my $xml=$result->get_data; # Example checking a signature my $v=new XML::Sig::OO(xml=>$xml); # validate our xml my $result=$v->validate; if($result) { print "everything checks out!\n"; } else { foreach my $chunk (@{$result->get_data}) { my ($nth,$signature,$digest)=@{$chunk}{qw(nth signature digest)}; print "Results for processing chunk $nth\n"; print "Signature State: ".($signature ? "OK\n" : "Failed, error was $signature\n"); print "Digest State: ".($digest ? "OK\n" : "Failed, error was $digest\n"); } }
XML::Sig::OO is a project to create a stand alone perl module that does a good job creating and validating xml signatures. At its core This module is written around libxml2 better known as XML::LibXML.
In the case of signing multiple //@ID elements, it is possible to sign each chunk with a different key, in fact you can even use completly different key types.
use Modern::Perl; use XML::Sig::OO; use File::Spec; use FindBin qw($Bin); use Crypt::OpenSSL::DSA; use Crypt::OpenSSL::RSA; # create our signign object my $s=new XML::Sig::OO( xml=>'<?xml version="1.0" standalone="yes"?><data><test ID="A" /><test ID="B" /></data>', ); my $x=$s->build_xpath; # sign our first xml chunk with our rsa key! my $rsa_str=join '',IO::File->new(File::Spec->catfile($Bin,'x509_key.pem'))->getlines; my $rsa=Crypt::OpenSSL::RSA->new_private_key($rsa_str); $rsa->use_pkcs1_padding(); my $cert_str=join '',IO::File->new(File::Spec->catfile($Bin,'x509_cert.pem'))->getlines; $s->sign_cert($rsa); $s->key_type('rsa'); $s->cert_string($cert_str); my $result=$s->sign_chunk($x,1); die $result unless $result; # Sign our 2nd chunk with our dsa key my $dsa = Crypt::OpenSSL::DSA->read_priv_key(File::Spec->catfile($Bin,'dsa_priv.pem')); $s->cert_string(undef); $s->sign_cert($dsa); $s->key_type('dsa'); $result=$s->sign_chunk($x,2); die $result unless $result; my ($node)=$x->findnodes($s->xpath_Root); my $xml=$node->toString; print "Our Signed XML IS: \n",$xml,"\n"; # Example checking a signature my $v=new XML::Sig::OO(xml=>$xml); $result=$v->validate; die $result unless $result; print "Our signed and xml passes validation\n";
Net::SAML2 has many problems when it comes to signature validation of xml strings. This section documents how to use this module in place of the Net::SAML2 built ins.
use Net::SAML2::Protocol::Assertion; use XML::Sig::OO; use MIME::Base64; # Lets assume we have a post binding response my $saml_response=..... my $xml=decode_base64($saml_response); my $v=XML::Sig::OO->new(xml=>$xml,cacert=>'idp_cert.pem'); my $result=$v->validate; die $result unless $result; # we can now use the asertion knowing it was from our idp my $assertion=Net::SAML2::Protocol::Assertion->new_from_xml(xml=>$xml)
Although this package does not directly support encrypted keys, it is possible to use encrypted keys by loading and exporting them with the Crypt::PK::RSA and Crypt::PK::DSA packages.
xml=>'...'
The base xml string to validate or sign. This option is always required.
cacert=>'/path/to/your/cacert.pem'
Optional, used to validate X509 certs.
build_parser=>sub { return XML::LibXML->new() }
Callback that returns a new XML Parser
namespaces=>{ ds=>'http://www.w3.org/2000/09/xmldsig#', ec=>'http://www.w3.org/2001/10/xml-exc-c14n#'}
Contains the list of namespaces to set in our XML::LibXML::XPathContext object.
digest_cbs=>{ ... }
Contains the digest callbacks. The default handlers can be found in %XML::SIG::OO::DIGEST.
digest_method=>'http://www.w3.org/2000/09/xmldsig#sha1'
Sets the digest method to be used when signing xml
key_type=>'rsa'
The signature method we will use
signature_method=>'http://www.w3.org/2000/09/xmldsig#rsa-sha1'
Sets the signature method.
tune_cert_cbs=>{ ...}
A collection of callbacks to tune a certificate object for signing
mutate_cbs=>{....}
Transform and Canonization callbacks. The default callbacks are defined in %XML::Sig::OO::MUTATE.
Callbacks are usied in the following context
$cb->($self,$xpath_element);
The xpaths in this package are not hard coded, each xpath can be defined as an argument to the constructor. Since xml can contain multiple elements with signatures or multiple id elements to sign, most xpaths are prefixed with the $nth signature
Some cases the xpaths are used in the following context:
(/xpath)[$nth]
In special cases like finding a list of transforms or which key, signature, or digest:
(//ds::Signature)[$nth]/xpath
xpath_SignatureValue=>//ds:SignatureValue
Xpath used to find the signature value.
xpath_SignatureMethod=>'//ds:SignatureMethod/@Algorithm'
Xpath used to find the signature method algorithm.
xpath_CanonicalizationMethod=>'//ds:CanonicalizationMethod/@Algorithm'
Xpath used to find the list of canonicalization method(s).
xpath_SignedInfo=>'//ds:SignedInfo'
Xpath used to find the singed info.
xpath_Signature=>'//ds:Signature'
Xpath used to fetch the signature value
xpath_Transforms=>//ds:Transforms
Xpath Transform path
xpath_Transform=>'/ds:Transform/@Algorithm'
Xpath used to find the transform Algorithm
xpath_DigestValue=>'//ds:DigestValue'
Xpath used to fetch the digest value
xpath_DigestMethod=>'//ds:DigestMethod/@Algorithm'
Xpath used to find the digest method.
xpath_DigestId=>'//ds:Reference/@URI'
Xpath used to find the id of the node that should contain our digest.
digest_id_convert_cb=>sub { my ($self,$xpath_object,$id)=@_;$id =~ s/^#//;return "//*[\@ID='$id']" }
Code ref that converts the xpath_DigestId into the xpath lookup ised to find the digest node
xpath_ToSign=>'//[@ID]'
Xpath used to find what nodes to sign.
xpath_IdValue=>'//@ID'
Xpath used to find the value of the current id.
xpath_Root=>'/'
Root of the document expath
This section documents all xpaths/options related to certs.
xpath_x509Data=>'/ds:KeyInfo/ds:X509Data/ds:X509Certificate'
Xpath used to find the x509 cert value. In reality the nth signature will be prepended to this xpath.
Actual xpath used:
(//ds:Signature)[$nth]/ds:KeyInfo/ds:X509Data/ds:X509Certificate
xpath_RSAKeyValue=>'/ds:KeyInfo/ds:KeyValue/ds:RSAKeyValue'
Xpath used to find the RSA value tree.
xpath_RSA_Modulus=>'/ds:KeyInfo/ds:KeyValue/ds:RSAKeyValue/ds:Modulus'
Xpath used to find the RSA Modulus.
xpath_RSA_Exponent=>'/ds:KeyInfo/ds:KeyValue/ds:RSAKeyValue/ds:Exponent'
Xpath used to find the RSA Exponent.
xpath_DSAKeyValue=>'/ds:KeyInfo/ds:KeyValue/ds:DSAKeyValue'
Xpath used for DSA key tree discovery.
xpath_DSA_P=>'/ds:KeyInfo/ds:KeyValue/ds:DSAKeyValue/ds:P'
Xpath used to find DSA_P.
xpath_DSA_Q=>''
Xpath used to find DSA_Q.
xpath_DSA_G=>'/ds:KeyInfo/ds:KeyValue/ds:DSAKeyValue/ds:G'
Xpath used to find DSA_G.
xpath_DSA_Y=>'/ds:KeyInfo/ds:KeyValue/ds:DSAKeyValue/ds:Y'
Xpath used to find DSA_Y
The following Signature options can be passed to the constructor object.
key_file=>'path/to/my.key'
Key file only used when signing.
envelope_method=>"http://www.w3.org/2000/09/xmldsig#enveloped-signature"
Sets the envelope method; This value most likely is the only valid value.
canon_method=>'http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments'
Sets the canonization method used when signing the code
tag_namespace=>'ds'
Default namespace of the tags being created. This must be defined in $self->namespaces.
sign_cert=>$cert_object
Optional: The Certificate object used to sign xml. If this option is set it is recomended that you set the "key_type" option as well.
cert_file=>'/path/to/cert.pem'
The path that contains the cert file used for signing.
cert_string=>undef
This optional argument lets you define the x509 pem text that will be used to generate the x509 portion of the xml.
Creates a new xpath object based on our current object state.
Returns a Data::Result Object. When true validation passed, when false it contains why validation failed.
A better use case would be this:
my $result=$self->validate; if($result) { print "everything checks out\n"; } else { foreach my $chunk (@{$result->get_data}) { my ($nth,$signature,$digest)=@{$chunk}{qw(nth signature digest)}; print "Results for processing chunk $nth\n"; print "Signature State: ".($signature ? "OK\n" : "Failed, error was $signature\n"; print "Digest State: ".($digest ? "OK\n" : "Failed, error was $digest\n"; } }
Returns a Data::Result object: when true, the signature was verified, when false it contains why it failed.
Returns a Data::Reslt object, when true it contains an array ref that contains each digest transform, when false it contains why it failed.
Please note, the xpath generate is a concatination of $self->context($self->xpath_Transforms,$nth).$self->xpath_Transform, so keep that in mind when trying to change how transforms are looked up.
Returns a Data::Result Object, when true it contains the Digest Node, when false it contains why it failed.
Returns a Data::Result Object, when true it contains the Digest Method
Returns a Data::Result Object, when true it contains the Digest Value.
Returns a Data::Result Object, when true the signature was validated, when fails it contains why it failed.
Returns a Data::Result object, when true it validated the DSA signature.
Returns an xpath wrapped in the nth instance syntax.
Example
my $xpath="//something" my $nth=2; my $xpath_string=$self->context($xpath,$nth); $xpath_string eq '(//something)[2]';
Note: if nth is not set it defaults to 1
Returns a Data::Result object, when true it contains the canon xml of the $nth signature node.
Returns a Data::Result Object, when true the x509 signature was validated.
Returns a Data::Result object, when true it contains the following hashref
Structure:
cert: the tuned cert sig: the binary signature to verify xml: the xml to be verified against the signature
Returns a Data::Result Object, when true the the rsa key verification passed.
Retruns a Data::Result Object, when true it contains the xml string of the context node.
Returns a Data::Result Object, when true it contains the canonized string.
Returns a Data::Result Object, when true it contains an array ref of the canon methods.
Special note, the xpath is generated as follows
my $xpath=$self->context($self->xpath_SignedInfo,$nth).$self->xpath_CanonicalizationMethod;
Returns a Data::Result object, when true it contains the base64 decoded signature
Given $xpath_object, Returns a Data::Result when true it will contains the signed info node
Returns a Data::Result object, when true it contains the SignatureMethod. If $cert is passed in, it will cert the hashing mode for the cert
Returns a Data::Result Object, when true Sets the hashing method for the $cert object.
Converts a given string to an x509 certificate.
Given the $node XML::LibXML::Element and $transformType, returns a Data::Result object. When true the call to $result->get_data will return the xml, when false it will contain a string that shows why it failed.
Returns an ArrayRef that contains the list of transform methods we will use when signing the xml.
This list is built out of the following:
0: $self->envelope_method 1: $self->canon_method
Produces a text xml fragment to be used for an xml digest.
Produces text xml fragment to be used for an xml signature
Creates our common xmlns string based on our namespaces.
Creates the signature xml for signing.
Returns a Data::Result structure, when true it contains a hasref with the following elements:
type: 'dsa|rsa|x509' cert: $cert_object
Returns a Data::Result object, when true it contains the following hashref:
type: 'rsa' cert: $cert_object
type: 'x509' cert: $cert_object
type: 'dsa' cert: $cert_object
Returns a Data::Result object, when true it contains the xml object to sign.
Returns a Data::Result object, when true it contains the id value
Returns a Data::Result Object, when true it contains the signed xml string.
Returns a Data::Result object, when true, the nth element with //@ID was signed and updated in $xpath_object. This method provides absolute granular control over what node is signed.
Creates the xml from the Certificate Object.
Given the base64 encoded key, create a block of x509 xml.
Returns a Data::Result Object, when true it contains the x509 cert xml.
Creates the xml for the Key Object.
This package currently has some limitations.
Currently this module only supports RSA and DSA keys in pem format.
Currently CaCert validation only works with RSA keys.
This code is based on the following modules: XML::Sig, Net::SAML2::XML::Sig, Authen::NZRealMe::XMLSig, and Mojo::XMLSig and would not exist today withot them.
Currently there are no known bugs, but if any are found please report them on our github project. Patches and pull requests are welcomed!
https://github.com/akalinux/xml-sig-oo
AKALINUX <AKALINUX@CPAN.ORG>
To install XML::Sig::OO, copy and paste the appropriate command in to your terminal.
cpanm
cpanm XML::Sig::OO
CPAN shell
perl -MCPAN -e shell install XML::Sig::OO
For more information on module installation, please visit the detailed CPAN module installation guide.