| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package Net::SAML2::XML::Sig; | 
| 2 |  |  |  |  |  |  |  | 
| 3 | 13 |  |  | 13 |  | 103 | use strict; | 
|  | 13 |  |  |  |  | 30 |  | 
|  | 13 |  |  |  |  | 450 |  | 
| 4 | 13 |  |  | 13 |  | 65 | use warnings; | 
|  | 13 |  |  |  |  | 32 |  | 
|  | 13 |  |  |  |  | 773 |  | 
| 5 |  |  |  |  |  |  |  | 
| 6 |  |  |  |  |  |  | # ABSTRACT: Net::SAML2::XML::Sig - A toolkit to help sign and verify XML Digital Signatures | 
| 7 |  |  |  |  |  |  |  | 
| 8 |  |  |  |  |  |  |  | 
| 9 |  |  |  |  |  |  | # use 'our' on v5.6.0 | 
| 10 | 13 |  |  | 13 |  | 70 | use vars qw($VERSION @EXPORT_OK %EXPORT_TAGS $DEBUG); | 
|  | 13 |  |  |  |  | 37 |  | 
|  | 13 |  |  |  |  | 1091 |  | 
| 11 |  |  |  |  |  |  |  | 
| 12 |  |  |  |  |  |  | $DEBUG = 0; | 
| 13 |  |  |  |  |  |  | # Based on XML::Sig VERSION = '0.47'; | 
| 14 |  |  |  |  |  |  | our $VERSION = '0.43'; | 
| 15 |  |  |  |  |  |  |  | 
| 16 | 13 |  |  | 13 |  | 101 | use base qw(Class::Accessor); | 
|  | 13 |  |  |  |  | 38 |  | 
|  | 13 |  |  |  |  | 7980 |  | 
| 17 |  |  |  |  |  |  | Net::SAML2::XML::Sig->mk_accessors(qw(key)); | 
| 18 |  |  |  |  |  |  |  | 
| 19 |  |  |  |  |  |  | # We are exporting functions | 
| 20 | 13 |  |  | 13 |  | 25483 | use base qw/Exporter/; | 
|  | 13 |  |  |  |  | 36 |  | 
|  | 13 |  |  |  |  | 1412 |  | 
| 21 |  |  |  |  |  |  |  | 
| 22 |  |  |  |  |  |  | # Export list - to allow fine tuning of export table | 
| 23 |  |  |  |  |  |  | @EXPORT_OK = qw( sign verify ); | 
| 24 |  |  |  |  |  |  |  | 
| 25 |  |  |  |  |  |  |  | 
| 26 | 13 |  |  | 13 |  | 7968 | use Digest::SHA qw(sha1 sha224 sha256 sha384 sha512); | 
|  | 13 |  |  |  |  | 44210 |  | 
|  | 13 |  |  |  |  | 1461 |  | 
| 27 | 13 |  |  | 13 |  | 119 | use XML::LibXML; | 
|  | 13 |  |  |  |  | 28 |  | 
|  | 13 |  |  |  |  | 149 |  | 
| 28 | 13 |  |  | 13 |  | 8631 | use Net::SAML2::XML::Util qw/ no_comments /; | 
|  | 13 |  |  |  |  | 39 |  | 
|  | 13 |  |  |  |  | 815 |  | 
| 29 | 13 |  |  | 13 |  | 6548 | use MIME::Base64; | 
|  | 13 |  |  |  |  | 8763 |  | 
|  | 13 |  |  |  |  | 785 |  | 
| 30 | 13 |  |  | 13 |  | 96 | use Carp; | 
|  | 13 |  |  |  |  | 36 |  | 
|  | 13 |  |  |  |  | 721 |  | 
| 31 |  |  |  |  |  |  |  | 
| 32 |  |  |  |  |  |  |  | 
| 33 | 13 |  |  | 13 |  | 81 | use constant TRANSFORM_ENV_SIG           => 'http://www.w3.org/2000/09/xmldsig#enveloped-signature'; | 
|  | 13 |  |  |  |  | 37 |  | 
|  | 13 |  |  |  |  | 906 |  | 
| 34 | 13 |  |  | 13 |  | 86 | use constant TRANSFORM_C14N              => 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315'; | 
|  | 13 |  |  |  |  | 37 |  | 
|  | 13 |  |  |  |  | 697 |  | 
| 35 | 13 |  |  | 13 |  | 84 | use constant TRANSFORM_C14N_COMMENTS     => 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments'; | 
|  | 13 |  |  |  |  | 28 |  | 
|  | 13 |  |  |  |  | 685 |  | 
| 36 | 13 |  |  | 13 |  | 80 | use constant TRANSFORM_C14N_V1_1         => 'http://www.w3.org/TR/2008/REC-xml-c14n11-20080502'; | 
|  | 13 |  |  |  |  | 26 |  | 
|  | 13 |  |  |  |  | 663 |  | 
| 37 | 13 |  |  | 13 |  | 82 | use constant TRANSFORM_C14N_V1_1_COMMENTS => 'http://www.w3.org/TR/2008/REC-xml-c14n11-20080502#WithComments'; | 
|  | 13 |  |  |  |  | 23 |  | 
|  | 13 |  |  |  |  | 742 |  | 
| 38 | 13 |  |  | 13 |  | 88 | use constant TRANSFORM_EXC_C14N          => 'http://www.w3.org/2001/10/xml-exc-c14n#'; | 
|  | 13 |  |  |  |  | 30 |  | 
|  | 13 |  |  |  |  | 689 |  | 
| 39 | 13 |  |  | 13 |  | 77 | use constant TRANSFORM_EXC_C14N_COMMENTS => 'http://www.w3.org/2001/10/xml-exc-c14n#WithComments'; | 
|  | 13 |  |  |  |  | 39 |  | 
|  | 13 |  |  |  |  | 60750 |  | 
| 40 |  |  |  |  |  |  |  | 
| 41 |  |  |  | 0 |  |  | sub DESTROY { } | 
| 42 |  |  |  |  |  |  |  | 
| 43 |  |  |  |  |  |  | $SIG{INT} = sub { die "Interrupted\n"; }; | 
| 44 |  |  |  |  |  |  |  | 
| 45 |  |  |  |  |  |  | $| = 1;  # autoflush | 
| 46 |  |  |  |  |  |  |  | 
| 47 |  |  |  |  |  |  |  | 
| 48 |  |  |  |  |  |  |  | 
| 49 |  |  |  |  |  |  |  | 
| 50 |  |  |  |  |  |  | sub new { | 
| 51 | 7 |  |  | 7 | 1 | 6674 | my $class = shift; | 
| 52 | 7 |  |  |  |  | 13 | my $params = shift; | 
| 53 | 7 |  |  |  |  | 15 | my $self = {}; | 
| 54 | 7 |  |  |  |  | 23 | foreach my $prop ( qw/ key cert cert_text / ) { | 
| 55 | 21 | 100 |  |  |  | 61 | if ( exists $params->{ $prop } ) { | 
| 56 | 9 |  |  |  |  | 23 | $self->{ $prop } = $params->{ $prop }; | 
| 57 |  |  |  |  |  |  | } | 
| 58 |  |  |  |  |  |  | #        else { | 
| 59 |  |  |  |  |  |  | #            confess "You need to provide the $prop parameter!"; | 
| 60 |  |  |  |  |  |  | #        } | 
| 61 |  |  |  |  |  |  | } | 
| 62 | 7 |  |  |  |  | 18 | bless $self, $class; | 
| 63 | 7 | 100 |  |  |  | 93 | $self->{ 'x509' } = exists $params->{ x509 } ? 1 : 0; | 
| 64 | 7 | 100 |  |  |  | 31 | if ( exists $params->{ 'key' } ) { | 
| 65 | 4 |  |  |  |  | 25 | $self->_load_key( $params->{ 'key' } ); | 
| 66 |  |  |  |  |  |  | } | 
| 67 | 7 | 100 |  |  |  | 32 | if ( exists $params->{ 'cert' } ) { | 
| 68 | 4 |  |  |  |  | 21 | $self->_load_cert_file( $params->{ 'cert' } ); | 
| 69 |  |  |  |  |  |  | } | 
| 70 | 7 | 100 |  |  |  | 25 | if ( exists $params->{ 'cert_text' } ) { | 
| 71 | 1 |  |  |  |  | 6 | $self->_load_cert_text( $params->{ 'cert_text' } ); | 
| 72 |  |  |  |  |  |  | } | 
| 73 |  |  |  |  |  |  |  | 
| 74 | 7 | 100 | 66 |  |  | 36 | if ( exists $params->{ sig_hash } && grep { $_ eq $params->{ sig_hash } } ('sha224', 'sha256', 'sha384', 'sha512')) | 
|  | 4 |  |  |  |  | 12 |  | 
| 75 |  |  |  |  |  |  | { | 
| 76 | 1 |  |  |  |  | 4 | $self->{ sig_hash } = $params->{ sig_hash }; | 
| 77 |  |  |  |  |  |  | } | 
| 78 |  |  |  |  |  |  | else { | 
| 79 | 6 |  |  |  |  | 19 | $self->{ sig_hash } = 'sha1'; | 
| 80 |  |  |  |  |  |  | } | 
| 81 |  |  |  |  |  |  |  | 
| 82 | 7 | 100 | 66 |  |  | 41 | if ( exists $params->{ digest_hash } && grep { $_ eq $params->{ digest_hash } } ('sha1', 'sha224', 'sha256', 'sha384',, 'sha512')) | 
|  | 5 |  |  |  |  | 15 |  | 
| 83 |  |  |  |  |  |  | { | 
| 84 | 1 |  |  |  |  | 3 | $self->{ digest_hash } = $params->{ digest_hash }; | 
| 85 |  |  |  |  |  |  | } | 
| 86 |  |  |  |  |  |  | else { | 
| 87 | 6 |  |  |  |  | 16 | $self->{ digest_hash } = 'sha1'; | 
| 88 |  |  |  |  |  |  | } | 
| 89 |  |  |  |  |  |  |  | 
| 90 | 7 | 50 | 66 |  |  | 52 | if (defined $self->{ key_type } && $self->{ key_type } eq 'dsa') { | 
| 91 | 0 | 0 | 0 |  |  | 0 | if ( defined $params->{ sig_hash } && grep { $_ eq $params->{ sig_hash } } ('sha1', 'sha256')) { | 
|  | 0 |  |  |  |  | 0 |  | 
| 92 | 0 |  |  |  |  | 0 | $self->{ sig_hash } = $params->{ sig_hash }; | 
| 93 |  |  |  |  |  |  | } | 
| 94 |  |  |  |  |  |  | else { | 
| 95 | 0 |  |  |  |  | 0 | $self->{ sig_hash } = 'sha1'; | 
| 96 |  |  |  |  |  |  | } | 
| 97 |  |  |  |  |  |  | } | 
| 98 |  |  |  |  |  |  |  | 
| 99 | 7 | 100 | 66 |  |  | 46 | if ( exists $params->{ no_xml_declaration } && $params->{ no_xml_declaration } == 1 ) { | 
| 100 | 4 |  |  |  |  | 11 | $self->{ no_xml_declaration } = 1; | 
| 101 |  |  |  |  |  |  | } else { | 
| 102 | 3 |  |  |  |  | 10 | $self->{ no_xml_declaration } = 0; | 
| 103 |  |  |  |  |  |  | } | 
| 104 |  |  |  |  |  |  |  | 
| 105 | 7 |  |  |  |  | 28 | return $self; | 
| 106 |  |  |  |  |  |  | } | 
| 107 |  |  |  |  |  |  |  | 
| 108 |  |  |  |  |  |  |  | 
| 109 |  |  |  |  |  |  | sub sign { | 
| 110 | 4 |  |  | 4 | 1 | 448 | my $self = shift; | 
| 111 | 4 |  |  |  |  | 12 | my ($xml) = @_; | 
| 112 |  |  |  |  |  |  |  | 
| 113 | 4 | 50 |  |  |  | 28 | die "You cannot sign XML without a private key." unless $self->key; | 
| 114 |  |  |  |  |  |  |  | 
| 115 | 4 |  |  |  |  | 117 | local $XML::LibXML::skipXMLDeclaration = $self->{ no_xml_declaration }; | 
| 116 |  |  |  |  |  |  |  | 
| 117 | 4 |  |  |  |  | 21 | my $dom = no_comments($xml); | 
| 118 |  |  |  |  |  |  | #my $dom = XML::LibXML->load_xml( | 
| 119 |  |  |  |  |  |  | #                string => $xml, | 
| 120 |  |  |  |  |  |  | #                no_network => 1, | 
| 121 |  |  |  |  |  |  | #                load_ext_dtd => 0, | 
| 122 |  |  |  |  |  |  | #                expand_entities => 0 ); | 
| 123 |  |  |  |  |  |  |  | 
| 124 | 4 |  |  |  |  | 71 | $self->{ parser } = XML::LibXML::XPathContext->new($dom); | 
| 125 | 4 |  |  |  |  | 30 | $self->{ parser }->registerNs('dsig', 'http://www.w3.org/2000/09/xmldsig#'); | 
| 126 | 4 |  |  |  |  | 21 | $self->{ parser }->registerNs('ec', 'http://www.w3.org/2001/10/xml-exc-c14n#'); | 
| 127 | 4 |  |  |  |  | 20 | $self->{ parser }->registerNs('saml', 'urn:oasis:names:tc:SAML:2.0:assertion'); | 
| 128 |  |  |  |  |  |  |  | 
| 129 | 4 | 50 |  |  |  | 17 | print ("Signing XML\n") if $DEBUG; | 
| 130 |  |  |  |  |  |  |  | 
| 131 | 4 |  |  |  |  | 19 | my @ids_to_sign = $self->_get_ids_to_sign(); | 
| 132 |  |  |  |  |  |  |  | 
| 133 | 4 |  |  |  |  | 14 | foreach (@ids_to_sign) { | 
| 134 | 5 |  |  |  |  | 53 | my $signid = $_; | 
| 135 |  |  |  |  |  |  | # Temporarily create the Signature XML from the part | 
| 136 |  |  |  |  |  |  | # TODO: ths section needs a rewrite to create the xml in | 
| 137 |  |  |  |  |  |  | # a better way. | 
| 138 |  |  |  |  |  |  |  | 
| 139 |  |  |  |  |  |  | # Create a Reference xml fragment including digest section | 
| 140 | 5 |  |  |  |  | 34 | my $digest_xml    = $self->_reference_xml( $signid, "REPLACE DIGEST " . $signid ); | 
| 141 |  |  |  |  |  |  |  | 
| 142 |  |  |  |  |  |  | # Create a SignedInfo xml fragment including digest section | 
| 143 | 5 |  |  |  |  | 22 | my $signed_info   = $self->_signedinfo_xml( $digest_xml ); | 
| 144 |  |  |  |  |  |  |  | 
| 145 |  |  |  |  |  |  | # Create a Signature xml fragment including SignedInfo section | 
| 146 | 5 |  |  |  |  | 31 | my $signature_xml = $self->_signature_xml( $signed_info, 'REPLACE SIGNATURE ' . $signid ); | 
| 147 |  |  |  |  |  |  |  | 
| 148 | 5 | 50 |  |  |  | 27 | print ("Sign ID: $signid\n") if $DEBUG; | 
| 149 |  |  |  |  |  |  |  | 
| 150 |  |  |  |  |  |  | # Get the XML note to sign base on the ID | 
| 151 | 5 |  |  |  |  | 25 | my $xml = $self->_get_xml_to_sign($signid); | 
| 152 |  |  |  |  |  |  |  | 
| 153 |  |  |  |  |  |  | # Set the namespace but do not apply it to the XML | 
| 154 | 5 |  |  |  |  | 30 | $xml->setNamespace("http://www.w3.org/2000/09/xmldsig#", "dsig", 0); | 
| 155 |  |  |  |  |  |  |  | 
| 156 |  |  |  |  |  |  | # Canonicalize the XML to http://www.w3.org/2001/10/xml-exc-c14n# | 
| 157 |  |  |  |  |  |  | # TODO Change the Canonicalization method in the xml fragment from _signedinfo_xml | 
| 158 |  |  |  |  |  |  | #    <dsig:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" /> | 
| 159 |  |  |  |  |  |  | #    <dsig:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/> | 
| 160 | 5 |  |  |  |  | 123 | my $xml_canon        = $xml->toStringEC14N(); | 
| 161 |  |  |  |  |  |  |  | 
| 162 | 5 | 50 |  |  |  | 1937 | if(my $ref = Digest::SHA->can($self->{ digest_hash })) { | 
| 163 | 5 |  |  |  |  | 19 | $self->{digest_method} = $ref; | 
| 164 |  |  |  |  |  |  | } | 
| 165 |  |  |  |  |  |  | else { | 
| 166 | 0 |  |  |  |  | 0 | die("Can't handle $self->{ digest_hash }"); | 
| 167 |  |  |  |  |  |  | } | 
| 168 |  |  |  |  |  |  |  | 
| 169 |  |  |  |  |  |  | # Calculate the digest of the XML being signed | 
| 170 | 5 |  |  |  |  | 148 | my $bin_digest    = $self->{digest_method}->( $xml_canon ); | 
| 171 | 5 |  |  |  |  | 28 | my $digest        = encode_base64( $bin_digest, '' ); | 
| 172 | 5 | 50 |  |  |  | 21 | print ("   Digest: $digest\n") if $DEBUG; | 
| 173 |  |  |  |  |  |  |  | 
| 174 |  |  |  |  |  |  | # Display the ID of the XML being signed for debugging | 
| 175 | 5 |  |  |  |  | 11 | my $reference = $signid; #$self->{parser}->findvalue('//@ID', $xml); | 
| 176 | 5 | 50 |  |  |  | 16 | print ("   Reference URI: $reference\n") if $DEBUG; | 
| 177 |  |  |  |  |  |  |  | 
| 178 |  |  |  |  |  |  | # Add the Signature to the xml being signed | 
| 179 | 5 |  |  |  |  | 31 | $xml->appendWellBalancedChunk($signature_xml, 'UTF-8'); | 
| 180 |  |  |  |  |  |  |  | 
| 181 |  |  |  |  |  |  | # Canonicalize the SignedInfo to http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments | 
| 182 |  |  |  |  |  |  | # TODO Change the Canonicalization method in the xml fragment from _signedinfo_xml | 
| 183 |  |  |  |  |  |  |  | 
| 184 | 5 |  |  |  |  | 1188 | my ($signature_node) = $xml->findnodes( | 
| 185 |  |  |  |  |  |  | './dsig:Signature', $xml); | 
| 186 | 5 |  |  |  |  | 231 | my ($signed_info_node) = $xml->findnodes( | 
| 187 |  |  |  |  |  |  | './dsig:Signature/dsig:SignedInfo',$xml); | 
| 188 |  |  |  |  |  |  |  | 
| 189 |  |  |  |  |  |  | # Add the digest value to the Signed info | 
| 190 | 5 |  |  |  |  | 144 | my ($digest_value_node) = $xml->findnodes( | 
| 191 |  |  |  |  |  |  | './dsig:Signature/dsig:SignedInfo/dsig:Reference/dsig:DigestValue', $signature_node); | 
| 192 | 5 |  |  |  |  | 164 | $digest_value_node->removeChildNodes(); | 
| 193 | 5 |  |  |  |  | 28 | $digest_value_node->appendText($digest); | 
| 194 |  |  |  |  |  |  |  | 
| 195 |  |  |  |  |  |  | # At this point the SignedInfo includes the information | 
| 196 |  |  |  |  |  |  | # to allow us to use the _canonicalize_xml with the $signature_node | 
| 197 | 5 |  |  |  |  | 22 | my $signed_info_canon = $self->_canonicalize_xml($signed_info_node, $signature_node); | 
| 198 |  |  |  |  |  |  |  | 
| 199 |  |  |  |  |  |  | # Calculate the signature of the Canonical Form of SignedInfo | 
| 200 | 5 |  |  |  |  | 183 | my $signature; | 
| 201 | 5 | 50 |  |  |  | 30 | if ($self->{key_type} eq 'dsa') { | 
|  |  | 50 |  |  |  |  |  | 
| 202 | 0 | 0 |  |  |  | 0 | print ("    Signing SignedInfo using DSA key type\n") if $DEBUG; | 
| 203 | 0 | 0 |  |  |  | 0 | if(my $ref = Digest::SHA->can($self->{ sig_hash })) { | 
| 204 | 0 |  |  |  |  | 0 | $self->{sig_method} = $ref; | 
| 205 |  |  |  |  |  |  | } | 
| 206 |  |  |  |  |  |  | else { | 
| 207 | 0 |  |  |  |  | 0 | die("Can't handle $self->{ sig_hash }"); | 
| 208 |  |  |  |  |  |  | } | 
| 209 |  |  |  |  |  |  |  | 
| 210 |  |  |  |  |  |  | # DSA 1024-bit only permits the signing of 20 bytes or less, hence the sha1 | 
| 211 |  |  |  |  |  |  | # DSA 2048-bit only permits the signing sha256 | 
| 212 | 0 |  |  |  |  | 0 | my $bin_signature = $self->{key_obj}->do_sign( $self->{ sig_method }($signed_info_canon) ); | 
| 213 |  |  |  |  |  |  |  | 
| 214 |  |  |  |  |  |  | # https://www.w3.org/TR/2002/REC-xmldsig-core-20020212/#sec-SignatureAlg | 
| 215 |  |  |  |  |  |  | # The output of the DSA algorithm consists of a pair of integers | 
| 216 |  |  |  |  |  |  | # The signature value consists of the base64 encoding of the | 
| 217 |  |  |  |  |  |  | # concatenation of r and s in that order ($r . $s) | 
| 218 | 0 |  |  |  |  | 0 | my $r = $bin_signature->get_r; | 
| 219 | 0 |  |  |  |  | 0 | my $s = $bin_signature->get_s; | 
| 220 |  |  |  |  |  |  |  | 
| 221 | 0 |  |  |  |  | 0 | my $sig_size = ($self->{key_obj}->get_sig_size - 8) * 8; | 
| 222 | 0 |  |  |  |  | 0 | my $rs = _zero_fill_buffer($sig_size); | 
| 223 | 0 |  |  |  |  | 0 | _concat_dsa_sig_r_s(\$rs, $r, $s, $sig_size); | 
| 224 |  |  |  |  |  |  |  | 
| 225 | 0 |  |  |  |  | 0 | $signature        = encode_base64( $rs, "\n" ); | 
| 226 |  |  |  |  |  |  | } elsif ($self->{key_type} eq 'ecdsa') { | 
| 227 | 0 | 0 |  |  |  | 0 | print ("    Signing SignedInfo using ECDSA key type\n") if $DEBUG; | 
| 228 |  |  |  |  |  |  |  | 
| 229 |  |  |  |  |  |  | my $bin_signature = $self->{key_obj}->sign_message_rfc7518( | 
| 230 |  |  |  |  |  |  | $signed_info_canon, uc($self->{sig_hash}) | 
| 231 | 0 |  |  |  |  | 0 | ); | 
| 232 |  |  |  |  |  |  | # The output of the ECDSA algorithm consists of a pair of integers | 
| 233 |  |  |  |  |  |  | # The signature value consists of the base64 encoding of the | 
| 234 |  |  |  |  |  |  | # concatenation of r and s in that order ($r . $s).  In this | 
| 235 |  |  |  |  |  |  | # case sign_message_rfc7518 produces that | 
| 236 |  |  |  |  |  |  |  | 
| 237 | 0 |  |  |  |  | 0 | $signature        = encode_base64( $bin_signature, "\n" ); | 
| 238 |  |  |  |  |  |  | } else { | 
| 239 | 5 | 50 |  |  |  | 18 | print ("    Signing SignedInfo using RSA key type\n") if $DEBUG; | 
| 240 | 5 |  |  |  |  | 28 | my $sig_hash = 'use_' . $self->{ sig_hash } . '_hash'; | 
| 241 | 5 |  |  |  |  | 30 | $self->{key_obj}->$sig_hash; | 
| 242 | 5 |  |  |  |  | 6514 | my $bin_signature = $self->{key_obj}->sign( $signed_info_canon ); | 
| 243 | 5 |  |  |  |  | 44 | $signature        = encode_base64( $bin_signature, "\n" ); | 
| 244 |  |  |  |  |  |  | } | 
| 245 |  |  |  |  |  |  |  | 
| 246 |  |  |  |  |  |  | # Add the Signature to the SignatureValue | 
| 247 | 5 |  |  |  |  | 26 | my ($signature_value_node) = $xml->findnodes( | 
| 248 |  |  |  |  |  |  | './dsig:Signature/dsig:SignatureValue', $signature_node); | 
| 249 | 5 |  |  |  |  | 210 | $signature_value_node->removeChildNodes(); | 
| 250 | 5 |  |  |  |  | 31 | $signature_value_node->appendText($signature); | 
| 251 |  |  |  |  |  |  |  | 
| 252 | 5 | 50 |  |  |  | 28 | print ("\n\n\n SignatureValue:\n" . $signature_value_node . "\n\n\n") if $DEBUG; | 
| 253 |  |  |  |  |  |  | } | 
| 254 |  |  |  |  |  |  |  | 
| 255 | 4 |  |  |  |  | 200 | return $dom->toString; | 
| 256 |  |  |  |  |  |  | } | 
| 257 |  |  |  |  |  |  |  | 
| 258 |  |  |  |  |  |  |  | 
| 259 |  |  |  |  |  |  | sub verify { | 
| 260 | 5 |  |  | 5 | 1 | 406 | my $self = shift; | 
| 261 | 5 |  |  |  |  | 13 | delete $self->{signer_cert}; | 
| 262 | 5 |  |  |  |  | 17 | my ($xml) = @_; | 
| 263 |  |  |  |  |  |  |  | 
| 264 | 5 |  |  |  |  | 23 | my $dom = no_comments($xml); | 
| 265 |  |  |  |  |  |  | #my $dom = XML::LibXML->load_xml( | 
| 266 |  |  |  |  |  |  | #                string => $xml, | 
| 267 |  |  |  |  |  |  | #                no_network => 1, | 
| 268 |  |  |  |  |  |  | #                load_ext_dtd => 0, | 
| 269 |  |  |  |  |  |  | #                expand_entities => 0 ); | 
| 270 |  |  |  |  |  |  |  | 
| 271 | 5 |  |  |  |  | 107 | $self->{ parser } = XML::LibXML::XPathContext->new($dom); | 
| 272 | 5 |  |  |  |  | 39 | $self->{ parser }->registerNs('dsig', 'http://www.w3.org/2000/09/xmldsig#'); | 
| 273 | 5 |  |  |  |  | 27 | $self->{ parser }->registerNs('ec', 'http://www.w3.org/2001/10/xml-exc-c14n#'); | 
| 274 | 5 |  |  |  |  | 22 | $self->{ parser }->registerNs('saml', 'urn:oasis:names:tc:SAML:2.0:assertion'); | 
| 275 | 5 |  |  |  |  | 69 | $self->{ parser }->registerNs('ecdsa', 'http://www.w3.org/2001/04/xmldsig-more#'); | 
| 276 |  |  |  |  |  |  |  | 
| 277 | 5 |  |  |  |  | 22 | my $signature_nodeset = $self->{ parser }->findnodes('//dsig:Signature'); | 
| 278 |  |  |  |  |  |  |  | 
| 279 | 5 |  |  |  |  | 275 | my $numsigs = $signature_nodeset->size(); | 
| 280 | 5 | 50 |  |  |  | 70 | print ("NodeSet Size: $numsigs\n") if $DEBUG; | 
| 281 |  |  |  |  |  |  |  | 
| 282 |  |  |  |  |  |  | # Loop through each Signature in the document checking each | 
| 283 | 5 |  |  |  |  | 13 | my $i; | 
| 284 | 5 |  |  |  |  | 24 | while (my $signature_node = $signature_nodeset->shift()) { | 
| 285 | 6 |  |  |  |  | 143 | $i++; | 
| 286 | 6 | 50 |  |  |  | 19 | print ("\nSignature $i\n") if $DEBUG; | 
| 287 |  |  |  |  |  |  |  | 
| 288 |  |  |  |  |  |  | # Get SignedInfo Reference ID | 
| 289 |  |  |  |  |  |  | my $reference = $self->{ parser }->findvalue( | 
| 290 | 6 |  |  |  |  | 27 | 'dsig:SignedInfo/dsig:Reference/@URI', $signature_node); | 
| 291 | 6 |  |  |  |  | 610 | $reference =~ s/#//g; | 
| 292 |  |  |  |  |  |  |  | 
| 293 | 6 | 50 |  |  |  | 25 | print ("   Reference URI: $reference\n") if $DEBUG; | 
| 294 |  |  |  |  |  |  |  | 
| 295 |  |  |  |  |  |  | # The reference ID must point to something in the document | 
| 296 |  |  |  |  |  |  | # if not disregard it and look for another signature | 
| 297 |  |  |  |  |  |  | # TODO check to ensure that if there is only a single reference | 
| 298 |  |  |  |  |  |  | # like this it won't accidentally validate | 
| 299 | 6 | 50 |  |  |  | 36 | if (! $self->{ parser }->findvalue('//*[@ID=\''. $reference . '\']')) { | 
| 300 | 0 | 0 |  |  |  | 0 | print ("   Signature reference $reference is not signing anything in this xml\n") if $DEBUG; | 
| 301 | 0 | 0 |  |  |  | 0 | if ($numsigs <= 1) { | 
| 302 | 0 |  |  |  |  | 0 | return 0; | 
| 303 |  |  |  |  |  |  | } | 
| 304 |  |  |  |  |  |  | else { | 
| 305 | 0 |  |  |  |  | 0 | next; | 
| 306 |  |  |  |  |  |  | } | 
| 307 |  |  |  |  |  |  | } | 
| 308 |  |  |  |  |  |  |  | 
| 309 |  |  |  |  |  |  | # Get SignedInfo DigestMethod Algorithim | 
| 310 |  |  |  |  |  |  | my $digest_method = $self->{ parser }->findvalue( | 
| 311 | 6 |  |  |  |  | 745 | 'dsig:SignedInfo/dsig:Reference/dsig:DigestMethod/@Algorithm', $signature_node); | 
| 312 | 6 |  |  |  |  | 628 | $digest_method =~ s/^.*[#]//; | 
| 313 | 6 | 50 |  |  |  | 24 | print ("   Digest Method: $digest_method\n") if $DEBUG; | 
| 314 |  |  |  |  |  |  |  | 
| 315 |  |  |  |  |  |  | # Get the DigestValue used to verify Canonical XML | 
| 316 |  |  |  |  |  |  | # Note that the digest may have embedded newlines in the XML | 
| 317 |  |  |  |  |  |  | # Decode the base64 and encode it with no newlines | 
| 318 |  |  |  |  |  |  | my $refdigest = encode_base64(decode_base64(_trim($self->{ parser }->findvalue( | 
| 319 | 6 |  |  |  |  | 33 | 'dsig:SignedInfo/dsig:Reference/dsig:DigestValue', $signature_node))), ""); | 
| 320 | 6 | 50 |  |  |  | 31 | print ("   Digest Value: $refdigest\n") if $DEBUG; | 
| 321 |  |  |  |  |  |  |  | 
| 322 |  |  |  |  |  |  | # Get the SignatureValue used to verify the SignedInfo | 
| 323 | 6 |  |  |  |  | 101 | my $signature = _trim($self->{ parser }->findvalue('dsig:SignatureValue', $signature_node)); | 
| 324 | 6 | 50 |  |  |  | 23 | print ("   Signature: $signature\n") if $DEBUG; | 
| 325 |  |  |  |  |  |  |  | 
| 326 |  |  |  |  |  |  | # Get SignatureMethod Algorithim | 
| 327 |  |  |  |  |  |  | my $signature_method = $self->{ parser }->findvalue( | 
| 328 | 6 |  |  |  |  | 95 | 'dsig:SignedInfo/dsig:SignatureMethod/@Algorithm', $signature_node); | 
| 329 | 6 |  |  |  |  | 436 | $signature_method =~ s/^.*[#]//; | 
| 330 | 6 |  |  |  |  | 41 | $signature_method =~ s/^rsa-//; | 
| 331 | 6 |  |  |  |  | 23 | $signature_method =~ s/^dsa-//; | 
| 332 | 6 |  |  |  |  | 11 | $signature_method =~ s/^ecdsa-//; | 
| 333 |  |  |  |  |  |  |  | 
| 334 | 6 |  |  |  |  | 15 | $self->{ sig_hash } = $signature_method; | 
| 335 | 6 | 50 |  |  |  | 21 | print ("   SignatureMethod: $signature_method\n") if $DEBUG; | 
| 336 |  |  |  |  |  |  |  | 
| 337 |  |  |  |  |  |  | # Get the SignedInfo and obtain its Canonical form | 
| 338 | 6 |  |  |  |  | 27 | my ($signed_info) = $self->{ parser }->findnodes('dsig:SignedInfo', $signature_node); | 
| 339 | 6 |  |  |  |  | 250 | my $signed_info_canon = $self->_canonicalize_xml($signed_info, $signature_node); | 
| 340 |  |  |  |  |  |  |  | 
| 341 | 6 | 50 |  |  |  | 112 | print "$signed_info_canon\n" if $DEBUG; | 
| 342 |  |  |  |  |  |  |  | 
| 343 | 6 | 50 |  |  |  | 65 | if(my $ref = Digest::SHA->can($signature_method)) { | 
| 344 | 6 |  |  |  |  | 20 | $self->{sig_method} = $ref; | 
| 345 |  |  |  |  |  |  | } | 
| 346 |  |  |  |  |  |  | else { | 
| 347 | 0 |  |  |  |  | 0 | die("Can't handle $signature_method"); | 
| 348 |  |  |  |  |  |  | } | 
| 349 |  |  |  |  |  |  |  | 
| 350 | 6 | 50 |  |  |  | 43 | if(my $ref = Digest::SHA->can($digest_method)) { | 
| 351 | 6 |  |  |  |  | 20 | $self->{digest_method} = $ref; | 
| 352 |  |  |  |  |  |  | } | 
| 353 |  |  |  |  |  |  | else { | 
| 354 | 0 |  |  |  |  | 0 | die("Can't handle $digest_method"); | 
| 355 |  |  |  |  |  |  | } | 
| 356 |  |  |  |  |  |  |  | 
| 357 |  |  |  |  |  |  | # If a cert was provided to Net::SAML2::XML::Sig->new() use it to | 
| 358 |  |  |  |  |  |  | # verify the SignedInfo signature | 
| 359 | 6 | 100 |  |  |  | 23 | if (defined $self->{cert_obj}) { | 
| 360 |  |  |  |  |  |  | # use the provided cert to verify | 
| 361 | 3 | 50 |  |  |  | 13 | unless ($self->_verify_x509_cert($self->{cert_obj},$signed_info_canon,$signature)) { | 
| 362 | 0 |  |  |  |  | 0 | print STDERR "not verified by x509\n"; | 
| 363 | 0 |  |  |  |  | 0 | return 0; | 
| 364 |  |  |  |  |  |  | } | 
| 365 |  |  |  |  |  |  | } | 
| 366 |  |  |  |  |  |  | # Extract the XML provided certificate and use it to | 
| 367 |  |  |  |  |  |  | # verify the SignedInfo signature | 
| 368 |  |  |  |  |  |  | else { | 
| 369 |  |  |  |  |  |  | # extract the certficate or key from the document | 
| 370 | 3 |  |  |  |  | 33 | my %verify_dispatch = ( | 
| 371 |  |  |  |  |  |  | 'X509Data' => '_verify_x509', | 
| 372 |  |  |  |  |  |  | 'RSAKeyValue' => '_verify_rsa', | 
| 373 |  |  |  |  |  |  | 'DSAKeyValue' => '_verify_dsa', | 
| 374 |  |  |  |  |  |  | 'ECDSAKeyValue' => '_verify_ecdsa', | 
| 375 |  |  |  |  |  |  | ); | 
| 376 | 3 |  |  |  |  | 5 | my $keyinfo_nodeset; | 
| 377 | 3 |  |  |  |  | 9 | foreach my $key_info_sig_type ( qw/X509Data RSAKeyValue DSAKeyValue ECDSAKeyValue/ ) { | 
| 378 | 3 | 50 |  |  |  | 11 | if ( $key_info_sig_type eq 'X509Data' ) { | 
| 379 |  |  |  |  |  |  | $keyinfo_nodeset = $self->{ parser }->find( | 
| 380 | 3 |  |  |  |  | 18 | "dsig:KeyInfo/dsig:$key_info_sig_type", $signature_node); | 
| 381 |  |  |  |  |  |  | #print ("   keyinfo_nodeset X509Data: $keyinfo_nodeset\n") if $DEBUG; | 
| 382 |  |  |  |  |  |  | } else { | 
| 383 |  |  |  |  |  |  | $keyinfo_nodeset = $self->{ parser }->find( | 
| 384 | 0 |  |  |  |  | 0 | "dsig:KeyInfo/dsig:KeyValue/dsig:$key_info_sig_type", $signature_node); | 
| 385 |  |  |  |  |  |  | #print ("   keyinfo_nodeset [DR]SAKeyValue: $keyinfo_nodeset\n") if $DEBUG; | 
| 386 |  |  |  |  |  |  | } | 
| 387 | 3 | 50 |  |  |  | 152 | if ( $keyinfo_nodeset->size ) { | 
| 388 | 3 |  |  |  |  | 21 | my $verify_method = $verify_dispatch{$key_info_sig_type}; | 
| 389 | 3 | 50 |  |  |  | 9 | print ("   Verify Method: $verify_method\n") if $DEBUG; | 
| 390 | 3 | 50 |  |  |  | 11 | if ( ! $self->$verify_method($keyinfo_nodeset->get_node(0), | 
| 391 |  |  |  |  |  |  | $signed_info_canon, $signature) ) { | 
| 392 | 0 | 0 |  |  |  | 0 | print ("keyinfo_nodeset->get_node: " . $keyinfo_nodeset->get_node(0) . "\n") if $DEBUG; | 
| 393 | 0 |  |  |  |  | 0 | print STDERR "Failed to verify using $verify_method\n"; | 
| 394 | 0 |  |  |  |  | 0 | return 0; | 
| 395 |  |  |  |  |  |  | } else { | 
| 396 | 3 | 50 |  |  |  | 12 | print ("Success Verifying\n") if $DEBUG; | 
| 397 |  |  |  |  |  |  | } | 
| 398 | 3 |  |  |  |  | 8 | last; | 
| 399 |  |  |  |  |  |  | } | 
| 400 |  |  |  |  |  |  | } | 
| 401 | 3 | 50 | 33 |  |  | 17 | die "Unrecognized key type or no KeyInfo in document" unless ( | 
| 402 |  |  |  |  |  |  | $keyinfo_nodeset && $keyinfo_nodeset->size > 0); | 
| 403 |  |  |  |  |  |  | } | 
| 404 |  |  |  |  |  |  |  | 
| 405 |  |  |  |  |  |  | # Signature of SignedInfo was verified above now obtain the | 
| 406 |  |  |  |  |  |  | # Canonical form of the XML and verify the DigestValue of the XML | 
| 407 |  |  |  |  |  |  |  | 
| 408 |  |  |  |  |  |  | # Remove the Signature from the signed XML | 
| 409 | 6 |  |  |  |  | 274 | my $signed_xml = $self->_get_signed_xml( $signature_node ); | 
| 410 | 6 |  |  |  |  | 89 | $signed_xml->removeChild( $signature_node ); | 
| 411 |  |  |  |  |  |  |  | 
| 412 |  |  |  |  |  |  | # Obtain the Canonical form of the XML | 
| 413 | 6 |  |  |  |  | 17 | my $canonical = $self->_transform($signed_xml, $signature_node); | 
| 414 |  |  |  |  |  |  |  | 
| 415 |  |  |  |  |  |  | # Add the $signature_node back to the $signed_xml to allow other | 
| 416 |  |  |  |  |  |  | # signatures to be validated if they exist | 
| 417 | 6 |  |  |  |  | 222 | $signed_xml->addChild( $signature_node ); | 
| 418 |  |  |  |  |  |  |  | 
| 419 |  |  |  |  |  |  | # Obtain the DigestValue of the Canonical XML | 
| 420 | 6 |  |  |  |  | 18 | my $digest = $self->{digest_method}->($canonical); | 
| 421 |  |  |  |  |  |  |  | 
| 422 | 6 | 50 |  |  |  | 227 | print ( "    Reference Digest:  " . _trim($refdigest) ."\n") if $DEBUG; | 
| 423 |  |  |  |  |  |  |  | 
| 424 | 6 | 50 |  |  |  | 19 | print ( "    Calculated Digest: ". _trim(encode_base64($digest, '')) ."\n") if $DEBUG; | 
| 425 |  |  |  |  |  |  |  | 
| 426 |  |  |  |  |  |  | # Return 0 - fail verification on the first XML signature that fails | 
| 427 | 6 | 50 |  |  |  | 56 | return 0 unless ($refdigest eq _trim(encode_base64($digest, ''))); | 
| 428 |  |  |  |  |  |  |  | 
| 429 | 6 | 50 |  |  |  | 28 | print ( "Signature $i Valid\n") if $DEBUG; | 
| 430 |  |  |  |  |  |  | } | 
| 431 |  |  |  |  |  |  |  | 
| 432 | 5 |  |  |  |  | 177 | return 1; | 
| 433 |  |  |  |  |  |  | } | 
| 434 |  |  |  |  |  |  |  | 
| 435 |  |  |  |  |  |  |  | 
| 436 |  |  |  |  |  |  | sub signer_cert { | 
| 437 | 3 |  |  | 3 | 1 | 7 | my $self = shift; | 
| 438 | 3 |  |  |  |  | 21 | return $self->{signer_cert}; | 
| 439 |  |  |  |  |  |  | } | 
| 440 |  |  |  |  |  |  |  | 
| 441 |  |  |  |  |  |  | ## | 
| 442 |  |  |  |  |  |  | ## _get_ids_to_sign() | 
| 443 |  |  |  |  |  |  | ## | 
| 444 |  |  |  |  |  |  | ## Arguments: | 
| 445 |  |  |  |  |  |  | ## | 
| 446 |  |  |  |  |  |  | ## Returns: array Value of ID attributes from XML | 
| 447 |  |  |  |  |  |  | ## | 
| 448 |  |  |  |  |  |  | ## Finds all the values of the ID attributes in the XML | 
| 449 |  |  |  |  |  |  | ## and return them in reverse order found.  Reverse order | 
| 450 |  |  |  |  |  |  | ## assumes that the Signatures should be performed on lower | 
| 451 |  |  |  |  |  |  | ## Nodes first. | 
| 452 |  |  |  |  |  |  | ## | 
| 453 |  |  |  |  |  |  | sub _get_ids_to_sign { | 
| 454 | 4 |  |  | 4 |  | 10 | my $self = shift; | 
| 455 | 4 |  |  |  |  | 31 | my @id = $self->{parser}->findnodes('//@ID'); | 
| 456 | 4 |  |  |  |  | 189 | my @ids; | 
| 457 | 4 |  |  |  |  | 14 | foreach (@id) { | 
| 458 | 5 |  |  |  |  | 14 | my $i = $_; | 
| 459 | 5 |  |  |  |  | 192 | $_ =~ m/^.*\"(.*)\".*$/; | 
| 460 | 5 |  |  |  |  | 180 | $i = $1; | 
| 461 |  |  |  |  |  |  | #//*[@ID='identifier_1'] | 
| 462 | 5 | 50 |  |  |  | 19 | die "You cannot sign an XML document without identifying the element to sign with an ID attribute" unless $i; | 
| 463 | 5 |  |  |  |  | 17 | unshift @ids, $i; | 
| 464 |  |  |  |  |  |  | } | 
| 465 | 4 |  |  |  |  | 30 | return @ids; | 
| 466 |  |  |  |  |  |  |  | 
| 467 |  |  |  |  |  |  |  | 
| 468 |  |  |  |  |  |  | } | 
| 469 |  |  |  |  |  |  |  | 
| 470 |  |  |  |  |  |  | ## | 
| 471 |  |  |  |  |  |  | ## _get_xml_to_sign() | 
| 472 |  |  |  |  |  |  | ## | 
| 473 |  |  |  |  |  |  | ## Arguments: | 
| 474 |  |  |  |  |  |  | ##    $id:     string ID of the Node for the XML to retrieve | 
| 475 |  |  |  |  |  |  | ## | 
| 476 |  |  |  |  |  |  | ## Returns: XML NodeSet to sign | 
| 477 |  |  |  |  |  |  | ## | 
| 478 |  |  |  |  |  |  | ## Find the XML node with the ID = $id and return the | 
| 479 |  |  |  |  |  |  | ## XML NodeSet | 
| 480 |  |  |  |  |  |  | ## | 
| 481 |  |  |  |  |  |  | sub _get_xml_to_sign { | 
| 482 | 5 |  |  | 5 |  | 10 | my $self = shift; | 
| 483 | 5 |  |  |  |  | 11 | my $id = shift; | 
| 484 | 5 | 50 |  |  |  | 22 | die "You cannot sign an XML document without identifying the element to sign with an ID attribute" unless $id; | 
| 485 |  |  |  |  |  |  |  | 
| 486 | 5 |  |  |  |  | 22 | my $xpath = "//*[\@ID='$id']"; | 
| 487 | 5 |  |  |  |  | 23 | my ($node) = $self->_get_node( $xpath ); | 
| 488 | 5 |  |  |  |  | 13 | return $node; | 
| 489 |  |  |  |  |  |  | } | 
| 490 |  |  |  |  |  |  |  | 
| 491 |  |  |  |  |  |  | ## | 
| 492 |  |  |  |  |  |  | ## _get_signed_xml($context) | 
| 493 |  |  |  |  |  |  | ## | 
| 494 |  |  |  |  |  |  | ## Arguments: | 
| 495 |  |  |  |  |  |  | ##    $context:     string XML NodeSet used as context | 
| 496 |  |  |  |  |  |  | ## | 
| 497 |  |  |  |  |  |  | ## Returns: XML NodeSet for with ID equal to the URI | 
| 498 |  |  |  |  |  |  | ## | 
| 499 |  |  |  |  |  |  | ## Find the XML node with the ID = $URI and return the | 
| 500 |  |  |  |  |  |  | ## XML NodeSet | 
| 501 |  |  |  |  |  |  | ## | 
| 502 |  |  |  |  |  |  | sub _get_signed_xml { | 
| 503 | 6 |  |  | 6 |  | 15 | my $self = shift; | 
| 504 | 6 |  |  |  |  | 16 | my ($context) = @_; | 
| 505 |  |  |  |  |  |  |  | 
| 506 | 6 |  |  |  |  | 27 | my $id = $self->{parser}->findvalue('./dsig:SignedInfo/dsig:Reference/@URI', $context); | 
| 507 | 6 |  |  |  |  | 545 | $id =~ s/^#//; | 
| 508 | 6 | 50 |  |  |  | 31 | print ("    Signed XML id: $id\n") if $DEBUG; | 
| 509 |  |  |  |  |  |  |  | 
| 510 | 6 |  |  |  |  | 19 | $self->{'sign_id'} = $id; | 
| 511 | 6 |  |  |  |  | 22 | my $xpath = "//*[\@ID='$id']"; | 
| 512 | 6 |  |  |  |  | 21 | return $self->_get_node( $xpath, $context ); | 
| 513 |  |  |  |  |  |  | } | 
| 514 |  |  |  |  |  |  |  | 
| 515 |  |  |  |  |  |  | ## | 
| 516 |  |  |  |  |  |  | ## _transform($xml, $context) | 
| 517 |  |  |  |  |  |  | ## | 
| 518 |  |  |  |  |  |  | ## Arguments: | 
| 519 |  |  |  |  |  |  | ##    $xml:     string XML NodeSet | 
| 520 |  |  |  |  |  |  | ##    $context: string XML Context | 
| 521 |  |  |  |  |  |  | ## | 
| 522 |  |  |  |  |  |  | ## Returns: string  Transformed XML | 
| 523 |  |  |  |  |  |  | ## | 
| 524 |  |  |  |  |  |  | ## Canonicalizes/Transforms xml based on the Transforms | 
| 525 |  |  |  |  |  |  | ## from the SignedInfo. | 
| 526 |  |  |  |  |  |  | ## | 
| 527 |  |  |  |  |  |  | sub _transform { | 
| 528 | 6 |  |  | 6 |  | 108 | my $self = shift; | 
| 529 | 6 |  |  |  |  | 14 | my ($xml, $context) = @_; | 
| 530 |  |  |  |  |  |  |  | 
| 531 | 6 |  |  |  |  | 25 | $context->setNamespace( 'http://www.w3.org/2000/09/xmldsig#', 'dsig' ); | 
| 532 |  |  |  |  |  |  | my $transforms = $self->{parser}->find( | 
| 533 | 6 |  |  |  |  | 159 | 'dsig:SignedInfo/dsig:Reference/dsig:Transforms/dsig:Transform', | 
| 534 |  |  |  |  |  |  | $context | 
| 535 |  |  |  |  |  |  | ); | 
| 536 |  |  |  |  |  |  |  | 
| 537 | 6 | 50 |  |  |  | 321 | print "_transform\n" if $DEBUG; | 
| 538 | 6 |  |  |  |  | 22 | foreach my $node ($transforms->get_nodelist) { | 
| 539 | 12 |  |  |  |  | 57 | my $alg = $node->getAttribute('Algorithm'); | 
| 540 |  |  |  |  |  |  |  | 
| 541 | 12 | 50 |  |  |  | 133 | print "    Algorithm: $alg\n" if $DEBUG; | 
| 542 | 12 | 100 |  |  |  | 73 | if ($alg eq TRANSFORM_ENV_SIG) { | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
| 543 |  |  |  |  |  |  | # TODO the xml being passed here currently has the | 
| 544 |  |  |  |  |  |  | # Signature removed.  May be better to do it all here | 
| 545 | 6 |  |  |  |  | 16 | next; | 
| 546 |  |  |  |  |  |  | } | 
| 547 |  |  |  |  |  |  | elsif ($alg eq TRANSFORM_C14N) { | 
| 548 | 0 | 0 |  |  |  | 0 | print "        toStringC14N" if $DEBUG; | 
| 549 | 0 |  |  |  |  | 0 | $xml = $xml->toStringC14N(); | 
| 550 |  |  |  |  |  |  | } | 
| 551 |  |  |  |  |  |  | elsif ($alg eq TRANSFORM_C14N_COMMENTS) { | 
| 552 | 0 | 0 |  |  |  | 0 | print "        toStringC14N(1)" if $DEBUG; | 
| 553 | 0 |  |  |  |  | 0 | $xml = $xml->toStringC14N(1); | 
| 554 |  |  |  |  |  |  | } | 
| 555 |  |  |  |  |  |  | elsif ($alg eq TRANSFORM_EXC_C14N) { | 
| 556 | 6 |  |  |  |  | 24 | my @prefixlist = $self->_find_prefixlist($node); | 
| 557 | 6 | 50 |  |  |  | 17 | print "        toStringEC14N(0, '', @prefixlist)\n" if $DEBUG; | 
| 558 | 6 |  |  |  |  | 25 | $xml = $xml->toStringEC14N(0, '', \@prefixlist); | 
| 559 |  |  |  |  |  |  | } | 
| 560 |  |  |  |  |  |  | elsif ($alg eq TRANSFORM_EXC_C14N_COMMENTS) { | 
| 561 | 0 |  |  |  |  | 0 | my @prefixlist = $self->_find_prefixlist($node); | 
| 562 | 0 | 0 |  |  |  | 0 | print "        toStringEC14N(1, '', @prefixlist)\n" if $DEBUG; | 
| 563 | 0 |  |  |  |  | 0 | $xml = $xml->toStringEC14N(1, '', \@prefixlist); | 
| 564 |  |  |  |  |  |  | } | 
| 565 |  |  |  |  |  |  | else { | 
| 566 | 0 |  |  |  |  | 0 | die "Unsupported transform: $alg"; | 
| 567 |  |  |  |  |  |  | } | 
| 568 |  |  |  |  |  |  | } | 
| 569 | 6 |  |  |  |  | 2043 | return $xml; | 
| 570 |  |  |  |  |  |  | } | 
| 571 |  |  |  |  |  |  |  | 
| 572 |  |  |  |  |  |  | ## | 
| 573 |  |  |  |  |  |  | ## _find_prefixlist($node) | 
| 574 |  |  |  |  |  |  | ## | 
| 575 |  |  |  |  |  |  | ## Arguments: | 
| 576 |  |  |  |  |  |  | ##    $node:    string XML NodeSet | 
| 577 |  |  |  |  |  |  | ## | 
| 578 |  |  |  |  |  |  | ## Returns: ARRAY of prefix lists | 
| 579 |  |  |  |  |  |  | ## | 
| 580 |  |  |  |  |  |  | ## Generate an array of prefix lists defined in InclusiveNamespaces | 
| 581 |  |  |  |  |  |  | ## | 
| 582 |  |  |  |  |  |  | sub _find_prefixlist { | 
| 583 | 6 |  |  | 6 |  | 13 | my $self = shift; | 
| 584 | 6 |  |  |  |  | 15 | my ($node) = @_; | 
| 585 | 6 |  |  |  |  | 23 | my @children = $node->getChildrenByLocalName('InclusiveNamespaces'); | 
| 586 |  |  |  |  |  |  |  | 
| 587 | 6 |  |  |  |  | 90 | my $prefixlist = ''; | 
| 588 | 6 |  |  |  |  | 18 | foreach my $child (@children) { | 
| 589 | 0 | 0 |  |  |  | 0 | if ($child) { | 
| 590 | 0 |  |  |  |  | 0 | $prefixlist .= $child->getAttribute('PrefixList'); | 
| 591 |  |  |  |  |  |  | } | 
| 592 | 0 |  |  |  |  | 0 | $prefixlist .= ' '; | 
| 593 |  |  |  |  |  |  | } | 
| 594 | 6 |  |  |  |  | 24 | return split / /, $prefixlist; | 
| 595 |  |  |  |  |  |  | } | 
| 596 |  |  |  |  |  |  |  | 
| 597 |  |  |  |  |  |  | ## | 
| 598 |  |  |  |  |  |  | ## _verify_rsa($context,$canonical,$sig) | 
| 599 |  |  |  |  |  |  | ## | 
| 600 |  |  |  |  |  |  | ## Arguments: | 
| 601 |  |  |  |  |  |  | ##    $context:     string XML Context to use | 
| 602 |  |  |  |  |  |  | ##    $canonical:   string Canonical XML to verify | 
| 603 |  |  |  |  |  |  | ##    $sig:         string Base64 encode of RSA Signature | 
| 604 |  |  |  |  |  |  | ## | 
| 605 |  |  |  |  |  |  | ## Returns: integer (1 True, 0 False) if signature is valid | 
| 606 |  |  |  |  |  |  | ## | 
| 607 |  |  |  |  |  |  | ## Verify the RSA signature of Canonical XML | 
| 608 |  |  |  |  |  |  | ## | 
| 609 |  |  |  |  |  |  | sub _verify_rsa { | 
| 610 | 0 |  |  | 0 |  | 0 | my $self = shift; | 
| 611 | 0 |  |  |  |  | 0 | my ($context,$canonical,$sig) = @_; | 
| 612 |  |  |  |  |  |  |  | 
| 613 |  |  |  |  |  |  | # Generate Public Key from XML | 
| 614 | 0 |  |  |  |  | 0 | my $mod = _trim($self->{parser}->findvalue('dsig:Modulus', $context)); | 
| 615 | 0 |  |  |  |  | 0 | my $modBin = decode_base64( $mod ); | 
| 616 | 0 |  |  |  |  | 0 | my $exp = _trim($self->{parser}->findvalue('dsig:Exponent', $context)); | 
| 617 | 0 |  |  |  |  | 0 | my $expBin = decode_base64( $exp ); | 
| 618 | 0 |  |  |  |  | 0 | my $n = Crypt::OpenSSL::Bignum->new_from_bin($modBin); | 
| 619 | 0 |  |  |  |  | 0 | my $e = Crypt::OpenSSL::Bignum->new_from_bin($expBin); | 
| 620 | 0 |  |  |  |  | 0 | my $rsa_pub = Crypt::OpenSSL::RSA->new_key_from_parameters( $n, $e ); | 
| 621 |  |  |  |  |  |  |  | 
| 622 |  |  |  |  |  |  | # Decode signature and verify | 
| 623 | 0 |  |  |  |  | 0 | my $sig_hash = 'use_' . $self->{ sig_hash } . '_hash'; | 
| 624 | 0 |  |  |  |  | 0 | $rsa_pub->$sig_hash; | 
| 625 | 0 |  |  |  |  | 0 | my $bin_signature = decode_base64($sig); | 
| 626 | 0 | 0 |  |  |  | 0 | return 1 if ($rsa_pub->verify( $canonical,  $bin_signature )); | 
| 627 | 0 |  |  |  |  | 0 | return 0; | 
| 628 |  |  |  |  |  |  | } | 
| 629 |  |  |  |  |  |  |  | 
| 630 |  |  |  |  |  |  | ## | 
| 631 |  |  |  |  |  |  | ## _clean_x509($cert) | 
| 632 |  |  |  |  |  |  | ## | 
| 633 |  |  |  |  |  |  | ## Arguments: | 
| 634 |  |  |  |  |  |  | ##    $cert:     string Certificate in base64 from XML | 
| 635 |  |  |  |  |  |  | ## | 
| 636 |  |  |  |  |  |  | ## Returns: string  Certificate in Valid PEM format | 
| 637 |  |  |  |  |  |  | ## | 
| 638 |  |  |  |  |  |  | ## Reformats Certifcate string into PEM format 64 characters | 
| 639 |  |  |  |  |  |  | ## with proper header and footer | 
| 640 |  |  |  |  |  |  | ## | 
| 641 |  |  |  |  |  |  | sub _clean_x509 { | 
| 642 | 3 |  |  | 3 |  | 47 | my $self = shift; | 
| 643 | 3 |  |  |  |  | 7 | my ($cert) = @_; | 
| 644 |  |  |  |  |  |  |  | 
| 645 | 3 | 50 |  |  |  | 11 | $cert = $cert->value() if(ref $cert); | 
| 646 | 3 |  |  |  |  | 11 | chomp($cert); | 
| 647 |  |  |  |  |  |  |  | 
| 648 |  |  |  |  |  |  | # rewrap the base64 data from the certificate; it may not be | 
| 649 |  |  |  |  |  |  | # wrapped at 64 characters as PEM requires | 
| 650 | 3 |  |  |  |  | 61 | $cert =~ s/\n//g; | 
| 651 |  |  |  |  |  |  |  | 
| 652 | 3 |  |  |  |  | 7 | my @lines; | 
| 653 | 3 |  |  |  |  | 38 | while (length $cert > 64) { | 
| 654 | 78 |  |  |  |  | 563 | push @lines, substr $cert, 0, 64, ''; | 
| 655 |  |  |  |  |  |  | } | 
| 656 | 3 |  |  |  |  | 8 | push @lines, $cert; | 
| 657 |  |  |  |  |  |  |  | 
| 658 | 3 |  |  |  |  | 23 | $cert = join "\n", @lines; | 
| 659 |  |  |  |  |  |  |  | 
| 660 | 3 |  |  |  |  | 18 | $cert = "-----BEGIN CERTIFICATE-----\n" . $cert . "\n-----END CERTIFICATE-----\n"; | 
| 661 | 3 |  |  |  |  | 15 | return $cert; | 
| 662 |  |  |  |  |  |  | } | 
| 663 |  |  |  |  |  |  |  | 
| 664 |  |  |  |  |  |  | ## | 
| 665 |  |  |  |  |  |  | ## _verify_x509($context,$canonical,$sig) | 
| 666 |  |  |  |  |  |  | ## | 
| 667 |  |  |  |  |  |  | ## Arguments: | 
| 668 |  |  |  |  |  |  | ##    $context:     string XML Context to use | 
| 669 |  |  |  |  |  |  | ##    $canonical:   string Canonical XML to verify | 
| 670 |  |  |  |  |  |  | ##    $sig:         string Base64 encode of RSA Signature | 
| 671 |  |  |  |  |  |  | ## | 
| 672 |  |  |  |  |  |  | ## Returns: integer (1 True, 0 False) if signature is valid | 
| 673 |  |  |  |  |  |  | ## | 
| 674 |  |  |  |  |  |  | ## Verify the RSA signature of Canonical XML using an X509 | 
| 675 |  |  |  |  |  |  | ## | 
| 676 |  |  |  |  |  |  | sub _verify_x509 { | 
| 677 | 3 |  |  | 3 |  | 29 | my $self = shift; | 
| 678 | 3 |  |  |  |  | 8 | my ($context,$canonical,$sig) = @_; | 
| 679 |  |  |  |  |  |  |  | 
| 680 | 3 |  |  |  |  | 5 | eval { | 
| 681 | 3 |  |  |  |  | 28 | require Crypt::OpenSSL::X509; | 
| 682 |  |  |  |  |  |  | }; | 
| 683 | 3 | 50 |  |  |  | 8 | confess "Crypt::OpenSSL::X509 needs to be installed so that we can handle X509 certificates" if $@; | 
| 684 |  |  |  |  |  |  |  | 
| 685 |  |  |  |  |  |  | # Generate Public Key from XML | 
| 686 | 3 |  |  |  |  | 12 | my $certificate = _trim($self->{parser}->findvalue('dsig:X509Certificate', $context)); | 
| 687 |  |  |  |  |  |  |  | 
| 688 |  |  |  |  |  |  | # This is added because the X509 parser requires it for self-identification | 
| 689 | 3 |  |  |  |  | 12 | $certificate = $self->_clean_x509($certificate); | 
| 690 |  |  |  |  |  |  |  | 
| 691 | 3 |  |  |  |  | 255 | my $cert = Crypt::OpenSSL::X509->new_from_string($certificate); | 
| 692 |  |  |  |  |  |  |  | 
| 693 | 3 |  |  |  |  | 16 | return $self->_verify_x509_cert($cert, $canonical, $sig); | 
| 694 |  |  |  |  |  |  | } | 
| 695 |  |  |  |  |  |  |  | 
| 696 |  |  |  |  |  |  | ## | 
| 697 |  |  |  |  |  |  | ## _verify_x509_cert($cert,$canonical,$sig) | 
| 698 |  |  |  |  |  |  | ## | 
| 699 |  |  |  |  |  |  | ## Arguments: | 
| 700 |  |  |  |  |  |  | ##    $cert:        string X509 Certificate | 
| 701 |  |  |  |  |  |  | ##    $canonical:   string Canonical XML to verify | 
| 702 |  |  |  |  |  |  | ##    $sig:         string Base64 encode of [EC|R]SA Signature | 
| 703 |  |  |  |  |  |  | ## | 
| 704 |  |  |  |  |  |  | ## Returns: integer (1 True, 0 False) if signature is valid | 
| 705 |  |  |  |  |  |  | ## | 
| 706 |  |  |  |  |  |  | ## Verify the X509 signature of Canonical XML | 
| 707 |  |  |  |  |  |  | ## | 
| 708 |  |  |  |  |  |  | sub _verify_x509_cert { | 
| 709 | 6 |  |  | 6 |  | 15 | my $self = shift; | 
| 710 | 6 |  |  |  |  | 16 | my ($cert, $canonical, $sig) = @_; | 
| 711 |  |  |  |  |  |  |  | 
| 712 |  |  |  |  |  |  | # Decode signature and verify | 
| 713 | 6 |  |  |  |  | 32 | my $bin_signature = decode_base64($sig); | 
| 714 |  |  |  |  |  |  |  | 
| 715 | 6 | 50 |  |  |  | 98 | if ($cert->key_alg_name eq 'id-ecPublicKey') { | 
|  |  | 50 |  |  |  |  |  | 
| 716 | 0 | 0 |  |  |  | 0 | eval {require Crypt::PK::ECC; CryptX->VERSION('0.036'); 1} | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
| 717 |  |  |  |  |  |  | or confess "Crypt::PK::ECC 0.036+ needs to be installed so | 
| 718 |  |  |  |  |  |  | that we can handle ECDSA signatures"; | 
| 719 | 0 |  |  |  |  | 0 | my $ecdsa_pub = Crypt::PK::ECC->new(\$cert->pubkey); | 
| 720 |  |  |  |  |  |  |  | 
| 721 | 0 |  |  |  |  | 0 | my $ecdsa_hash = $self->{rsa_hash}; | 
| 722 |  |  |  |  |  |  |  | 
| 723 |  |  |  |  |  |  | # Signature is stored as the concatenation of r and s. | 
| 724 |  |  |  |  |  |  | # verify_message_rfc7518 expects that format | 
| 725 | 0 | 0 |  |  |  | 0 | if ($ecdsa_pub->verify_message_rfc7518( $bin_signature, $canonical, uc($self->{sig_hash}) )) { | 
| 726 | 0 |  |  |  |  | 0 | $self->{signer_cert} = $cert; | 
| 727 | 0 |  |  |  |  | 0 | return 1; | 
| 728 |  |  |  |  |  |  | } | 
| 729 |  |  |  |  |  |  | } | 
| 730 |  |  |  |  |  |  | elsif ($cert->key_alg_name eq 'dsaEncryption') { | 
| 731 | 0 |  |  |  |  | 0 | eval { | 
| 732 | 0 |  |  |  |  | 0 | require Crypt::OpenSSL::DSA; | 
| 733 |  |  |  |  |  |  | }; | 
| 734 | 0 | 0 |  |  |  | 0 | confess "Crypt::OpenSSL::DSA needs to be installed so | 
| 735 |  |  |  |  |  |  | that we can handle DSA X509 certificates" if $@; | 
| 736 |  |  |  |  |  |  |  | 
| 737 | 0 |  |  |  |  | 0 | my $dsa_pub  = Crypt::OpenSSL::DSA->read_pub_key_str( $cert->pubkey ); | 
| 738 | 0 |  |  |  |  | 0 | my $sig_size = ($dsa_pub->get_sig_size - 8)/2; | 
| 739 |  |  |  |  |  |  | #my ($r, $s) = unpack('a20a20', $bin_signature); | 
| 740 | 0 |  |  |  |  | 0 | my $unpk = "a" . $sig_size . "a" . $sig_size; | 
| 741 | 0 |  |  |  |  | 0 | my ($r, $s) = unpack($unpk, $bin_signature); | 
| 742 |  |  |  |  |  |  |  | 
| 743 |  |  |  |  |  |  | # Create a new Signature Object from r and s | 
| 744 | 0 |  |  |  |  | 0 | my $sigobj = Crypt::OpenSSL::DSA::Signature->new(); | 
| 745 | 0 |  |  |  |  | 0 | $sigobj->set_r($r); | 
| 746 | 0 |  |  |  |  | 0 | $sigobj->set_s($s); | 
| 747 |  |  |  |  |  |  |  | 
| 748 | 0 | 0 |  |  |  | 0 | if ($dsa_pub->do_verify($self->{sig_method}->($canonical), $sigobj)) { | 
| 749 | 0 |  |  |  |  | 0 | $self->{signer_cert} = $cert; | 
| 750 | 0 |  |  |  |  | 0 | return 1; | 
| 751 |  |  |  |  |  |  | } | 
| 752 |  |  |  |  |  |  | } | 
| 753 |  |  |  |  |  |  | else { | 
| 754 | 6 |  |  |  |  | 15 | eval { | 
| 755 | 6 |  |  |  |  | 46 | require Crypt::OpenSSL::RSA; | 
| 756 |  |  |  |  |  |  | }; | 
| 757 | 6 | 50 |  |  |  | 23 | confess "Crypt::OpenSSL::RSA needs to be installed so | 
| 758 |  |  |  |  |  |  | that we can handle X509 certificates" if $@; | 
| 759 |  |  |  |  |  |  |  | 
| 760 | 6 |  |  |  |  | 320 | my $rsa_pub = Crypt::OpenSSL::RSA->new_public_key($cert->pubkey); | 
| 761 |  |  |  |  |  |  |  | 
| 762 | 6 |  |  |  |  | 2726 | my $sig_hash = 'use_' . $self->{sig_hash} . '_hash'; | 
| 763 | 6 |  |  |  |  | 40 | $rsa_pub->$sig_hash(); | 
| 764 |  |  |  |  |  |  | # If successful verify, store the signer's cert for validation | 
| 765 | 6 | 50 |  |  |  | 437 | if ($rsa_pub->verify( $canonical,  $bin_signature )) { | 
| 766 | 6 |  |  |  |  | 44 | $self->{signer_cert} = $cert; | 
| 767 | 6 |  |  |  |  | 75 | return 1; | 
| 768 |  |  |  |  |  |  | } | 
| 769 |  |  |  |  |  |  | } | 
| 770 |  |  |  |  |  |  |  | 
| 771 | 0 |  |  |  |  | 0 | return 0; | 
| 772 |  |  |  |  |  |  | } | 
| 773 |  |  |  |  |  |  |  | 
| 774 |  |  |  |  |  |  | ## | 
| 775 |  |  |  |  |  |  | ## _zero_fill_buffer($bits) | 
| 776 |  |  |  |  |  |  | ## | 
| 777 |  |  |  |  |  |  | ## Arguments: | 
| 778 |  |  |  |  |  |  | ##    $bits:     number of bits to set to zero | 
| 779 |  |  |  |  |  |  | ## | 
| 780 |  |  |  |  |  |  | ## Returns: Zero filled bit buffer of size $bits | 
| 781 |  |  |  |  |  |  | ## | 
| 782 |  |  |  |  |  |  | ## Create a buffer with all bits set to 0 | 
| 783 |  |  |  |  |  |  | ## | 
| 784 |  |  |  |  |  |  | sub _zero_fill_buffer { | 
| 785 | 0 |  |  | 0 |  | 0 | my $bits = shift; | 
| 786 |  |  |  |  |  |  | # set all bit to zero | 
| 787 | 0 |  |  |  |  | 0 | my $v = ''; | 
| 788 | 0 |  |  |  |  | 0 | for (my $i = 0; $i < $bits; $i++) { | 
| 789 | 0 |  |  |  |  | 0 | vec($v, $i, 1) = 0; | 
| 790 |  |  |  |  |  |  | } | 
| 791 | 0 |  |  |  |  | 0 | return $v; | 
| 792 |  |  |  |  |  |  | } | 
| 793 |  |  |  |  |  |  |  | 
| 794 |  |  |  |  |  |  | ## | 
| 795 |  |  |  |  |  |  | ## _concat_dsa_sig_r_s(\$buffer,$r,$s) | 
| 796 |  |  |  |  |  |  | ## | 
| 797 |  |  |  |  |  |  | ## Arguments: | 
| 798 |  |  |  |  |  |  | ##    $buffer:      Zero Filled bit buffer | 
| 799 |  |  |  |  |  |  | ##    $r:           octet stream | 
| 800 |  |  |  |  |  |  | ##    $s:           octet stream | 
| 801 |  |  |  |  |  |  | ## | 
| 802 |  |  |  |  |  |  | ## Combine r and s components of DSA signature | 
| 803 |  |  |  |  |  |  | ## | 
| 804 |  |  |  |  |  |  | sub _concat_dsa_sig_r_s { | 
| 805 |  |  |  |  |  |  |  | 
| 806 | 0 |  |  | 0 |  | 0 | my ($buffer, $r, $s, $sig_size) = @_; | 
| 807 | 0 |  |  |  |  | 0 | my $bits_r = (length($r)*8)-1; | 
| 808 | 0 |  |  |  |  | 0 | my $bits_s = (length($s)*8)-1; | 
| 809 |  |  |  |  |  |  |  | 
| 810 | 0 |  |  |  |  | 0 | my $halfsize = $sig_size / 2; | 
| 811 |  |  |  |  |  |  |  | 
| 812 |  |  |  |  |  |  | # Place $s right justified in $v starting at bit 319 | 
| 813 | 0 |  |  |  |  | 0 | for (my $i = $bits_s; $i >=0; $i--) { | 
| 814 | 0 |  |  |  |  | 0 | vec($$buffer, $halfsize + $i + (($halfsize -1) - $bits_s) , 1) = vec($s, $i, 1); | 
| 815 |  |  |  |  |  |  | } | 
| 816 |  |  |  |  |  |  |  | 
| 817 |  |  |  |  |  |  | # Place $r right justified in $v starting at bit 159 | 
| 818 | 0 |  |  |  |  | 0 | for (my $i = $bits_r; $i >= 0 ; $i--) { | 
| 819 | 0 |  |  |  |  | 0 | vec($$buffer, $i + (($halfsize -1) - $bits_r) , 1) = vec($r, $i, 1); | 
| 820 |  |  |  |  |  |  | } | 
| 821 |  |  |  |  |  |  |  | 
| 822 |  |  |  |  |  |  | } | 
| 823 |  |  |  |  |  |  |  | 
| 824 |  |  |  |  |  |  | ## | 
| 825 |  |  |  |  |  |  | ## _verify_dsa($context,$canonical,$sig) | 
| 826 |  |  |  |  |  |  | ## | 
| 827 |  |  |  |  |  |  | ## Arguments: | 
| 828 |  |  |  |  |  |  | ##    $context:     string XML Context to use | 
| 829 |  |  |  |  |  |  | ##    $canonical:   string Canonical XML to verify | 
| 830 |  |  |  |  |  |  | ##    $sig:         string Base64 encode 40 byte string of r and s | 
| 831 |  |  |  |  |  |  | ## | 
| 832 |  |  |  |  |  |  | ## Returns: integer (1 True, 0 False) if signature is valid | 
| 833 |  |  |  |  |  |  | ## | 
| 834 |  |  |  |  |  |  | ## Verify the DSA signature of Canonical XML | 
| 835 |  |  |  |  |  |  | ## | 
| 836 |  |  |  |  |  |  | sub _verify_dsa { | 
| 837 | 0 |  |  | 0 |  | 0 | my $self = shift; | 
| 838 | 0 |  |  |  |  | 0 | my ($context,$canonical,$sig) = @_; | 
| 839 |  |  |  |  |  |  |  | 
| 840 | 0 |  |  |  |  | 0 | eval { | 
| 841 | 0 |  |  |  |  | 0 | require Crypt::OpenSSL::DSA; | 
| 842 |  |  |  |  |  |  | }; | 
| 843 | 0 | 0 |  |  |  | 0 | confess "Crypt::OpenSSL::DSA needs to be installed so | 
| 844 |  |  |  |  |  |  | that we can handle DSA signatures" if $@; | 
| 845 |  |  |  |  |  |  |  | 
| 846 |  |  |  |  |  |  | # Generate Public Key from XML | 
| 847 | 0 |  |  |  |  | 0 | my $p = decode_base64(_trim($self->{parser}->findvalue('dsig:P', $context))); | 
| 848 | 0 |  |  |  |  | 0 | my $q = decode_base64(_trim($self->{parser}->findvalue('dsig:Q', $context))); | 
| 849 | 0 |  |  |  |  | 0 | my $g = decode_base64(_trim($self->{parser}->findvalue('dsig:G', $context))); | 
| 850 | 0 |  |  |  |  | 0 | my $y = decode_base64(_trim($self->{parser}->findvalue('dsig:Y', $context))); | 
| 851 | 0 |  |  |  |  | 0 | my $dsa_pub = Crypt::OpenSSL::DSA->new(); | 
| 852 | 0 |  |  |  |  | 0 | $dsa_pub->set_p($p); | 
| 853 | 0 |  |  |  |  | 0 | $dsa_pub->set_q($q); | 
| 854 | 0 |  |  |  |  | 0 | $dsa_pub->set_g($g); | 
| 855 | 0 |  |  |  |  | 0 | $dsa_pub->set_pub_key($y); | 
| 856 |  |  |  |  |  |  |  | 
| 857 |  |  |  |  |  |  | # Decode signature and verify | 
| 858 | 0 |  |  |  |  | 0 | my $bin_signature = decode_base64($sig); | 
| 859 |  |  |  |  |  |  |  | 
| 860 |  |  |  |  |  |  | # https://www.w3.org/TR/2002/REC-xmldsig-core-20020212/#sec-SignatureAlg | 
| 861 |  |  |  |  |  |  | # The output of the DSA algorithm consists of a pair of integers | 
| 862 |  |  |  |  |  |  | # The signature value consists of the base64 encoding of the | 
| 863 |  |  |  |  |  |  | # concatenation of r and s in that order ($r . $s) | 
| 864 |  |  |  |  |  |  | # Binary Signature is stored as a concatenation of r and s | 
| 865 | 0 |  |  |  |  | 0 | my $sig_size = ($dsa_pub->get_sig_size - 8)/2; | 
| 866 | 0 |  |  |  |  | 0 | my $unpk = "a" . $sig_size . "a" . $sig_size; | 
| 867 | 0 |  |  |  |  | 0 | my ($r, $s) = unpack($unpk, $bin_signature); | 
| 868 |  |  |  |  |  |  |  | 
| 869 |  |  |  |  |  |  | # Create a new Signature Object from r and s | 
| 870 | 0 |  |  |  |  | 0 | my $sigobj = Crypt::OpenSSL::DSA::Signature->new(); | 
| 871 | 0 |  |  |  |  | 0 | $sigobj->set_r($r); | 
| 872 | 0 |  |  |  |  | 0 | $sigobj->set_s($s); | 
| 873 |  |  |  |  |  |  |  | 
| 874 |  |  |  |  |  |  | # DSA signatures are limited to a message body of 20 characters, so a sha1 digest is taken | 
| 875 | 0 | 0 |  |  |  | 0 | return 1 if ($dsa_pub->do_verify( $self->{sig_method}->($canonical),  $sigobj )); | 
| 876 | 0 |  |  |  |  | 0 | return 0; | 
| 877 |  |  |  |  |  |  | } | 
| 878 |  |  |  |  |  |  |  | 
| 879 |  |  |  |  |  |  | ## | 
| 880 |  |  |  |  |  |  | ## _verify_ecdsa($context,$canonical,$sig) | 
| 881 |  |  |  |  |  |  | ## | 
| 882 |  |  |  |  |  |  | ## Arguments: | 
| 883 |  |  |  |  |  |  | ##    $context:     string XML Context to use | 
| 884 |  |  |  |  |  |  | ##    $canonical:   string Canonical XML to verify | 
| 885 |  |  |  |  |  |  | ##    $sig:         string Base64 encoded | 
| 886 |  |  |  |  |  |  | ## | 
| 887 |  |  |  |  |  |  | ## Returns: integer (1 True, 0 False) if signature is valid | 
| 888 |  |  |  |  |  |  | ## | 
| 889 |  |  |  |  |  |  | ## Verify the ECDSA signature of Canonical XML | 
| 890 |  |  |  |  |  |  | ## | 
| 891 |  |  |  |  |  |  | sub _verify_ecdsa { | 
| 892 | 0 |  |  | 0 |  | 0 | my $self = shift; | 
| 893 | 0 |  |  |  |  | 0 | my ($context,$canonical,$sig) = @_; | 
| 894 |  |  |  |  |  |  |  | 
| 895 | 0 | 0 |  |  |  | 0 | eval {require Crypt::PK::ECC; CryptX->VERSION('0.036'); 1} | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
| 896 |  |  |  |  |  |  | or confess "Crypt::PK::ECC 0.036+ needs to be installed so | 
| 897 |  |  |  |  |  |  | that we can handle ECDSA signatures"; | 
| 898 |  |  |  |  |  |  | # Generate Public Key from XML | 
| 899 | 0 |  |  |  |  | 0 | my $oid = _trim($self->{parser}->findvalue('//dsig:NamedCurve/@URN', $context)); | 
| 900 |  |  |  |  |  |  |  | 
| 901 | 13 |  |  | 13 |  | 158 | use URI (); | 
|  | 13 |  |  |  |  | 40 |  | 
|  | 13 |  |  |  |  | 36068 |  | 
| 902 | 0 |  |  |  |  | 0 | my $u1 = URI->new($oid); | 
| 903 | 0 |  |  |  |  | 0 | $oid = $u1->nss; | 
| 904 |  |  |  |  |  |  |  | 
| 905 | 0 |  |  |  |  | 0 | my %curve_name = ( | 
| 906 |  |  |  |  |  |  | '1.2.840.10045.3.1.1'   => 'secp192r1', | 
| 907 |  |  |  |  |  |  | '1.3.132.0.33'          => 'secp224r1', | 
| 908 |  |  |  |  |  |  | '1.2.840.10045.3.1.7'   => 'secp256r1', | 
| 909 |  |  |  |  |  |  | '1.3.132.0.34'          => 'secp384r1', | 
| 910 |  |  |  |  |  |  | '1.3.132.0.35'          => 'secp521r1', | 
| 911 |  |  |  |  |  |  | '1.3.36.3.3.2.8.1.1.1'  => 'brainpoolP160r1', | 
| 912 |  |  |  |  |  |  | '1.3.36.3.3.2.8.1.1.3'  => 'brainpoolP192r1', | 
| 913 |  |  |  |  |  |  | '1.3.36.3.3.2.8.1.1.5'  => 'brainpoolP224r1', | 
| 914 |  |  |  |  |  |  | '1.3.36.3.3.2.8.1.1.7'  => 'brainpoolP256r1', | 
| 915 |  |  |  |  |  |  | '1.3.36.3.3.2.8.1.1.9'  => 'brainpoolP320r1', | 
| 916 |  |  |  |  |  |  | '1.3.36.3.3.2.8.1.1.11' => 'brainpoolP384r1', | 
| 917 |  |  |  |  |  |  | '1.3.36.3.3.2.8.1.1.13' => 'brainpoolP512r1', | 
| 918 |  |  |  |  |  |  | ); | 
| 919 |  |  |  |  |  |  |  | 
| 920 | 0 |  |  |  |  | 0 | my $x = $self->{parser}->findvalue('//dsig:PublicKey/dsig:X/@Value', $context); | 
| 921 | 0 |  |  |  |  | 0 | my $y = $self->{parser}->findvalue('//dsig:PublicKey/dsig:Y/@Value', $context); | 
| 922 |  |  |  |  |  |  |  | 
| 923 | 0 |  |  |  |  | 0 | my $ecdsa_pub = Crypt::PK::ECC->new(); | 
| 924 |  |  |  |  |  |  |  | 
| 925 |  |  |  |  |  |  | $ecdsa_pub->import_key({ | 
| 926 |  |  |  |  |  |  | kty => "EC", | 
| 927 | 0 |  |  |  |  | 0 | curve_name => $curve_name{ $oid }, | 
| 928 |  |  |  |  |  |  | pub_x   => $x, | 
| 929 |  |  |  |  |  |  | pub_y   => $y, | 
| 930 |  |  |  |  |  |  | }); | 
| 931 |  |  |  |  |  |  |  | 
| 932 | 0 |  |  |  |  | 0 | my $bin_signature = decode_base64($sig); | 
| 933 |  |  |  |  |  |  |  | 
| 934 |  |  |  |  |  |  | # verify_message_rfc7518 is used to verify signature stored as a | 
| 935 |  |  |  |  |  |  | # concatenation of integers r and s | 
| 936 |  |  |  |  |  |  | return 1 if ($ecdsa_pub->verify_message_rfc7518( | 
| 937 |  |  |  |  |  |  | $bin_signature, | 
| 938 |  |  |  |  |  |  | $canonical, | 
| 939 | 0 | 0 |  |  |  | 0 | uc($self->{sig_hash})) | 
| 940 |  |  |  |  |  |  | ); | 
| 941 | 0 |  |  |  |  | 0 | return 0; | 
| 942 |  |  |  |  |  |  | } | 
| 943 |  |  |  |  |  |  |  | 
| 944 |  |  |  |  |  |  | ## | 
| 945 |  |  |  |  |  |  | ## _get_node($xpath, context) | 
| 946 |  |  |  |  |  |  | ## | 
| 947 |  |  |  |  |  |  | ## Arguments: | 
| 948 |  |  |  |  |  |  | ##    $xpath:       string XML XPath to use | 
| 949 |  |  |  |  |  |  | ##    $context:     string XML context | 
| 950 |  |  |  |  |  |  | ## | 
| 951 |  |  |  |  |  |  | ## Returns: string  XML NodeSet | 
| 952 |  |  |  |  |  |  | ## | 
| 953 |  |  |  |  |  |  | ## Return a NodeSet based on the xpath string | 
| 954 |  |  |  |  |  |  | ## | 
| 955 |  |  |  |  |  |  | sub _get_node { | 
| 956 | 11 |  |  | 11 |  | 23 | my $self = shift; | 
| 957 | 11 |  |  |  |  | 43 | my ($xpath, $context) = @_; | 
| 958 | 11 |  |  |  |  | 19 | my $nodeset; | 
| 959 | 11 | 100 |  |  |  | 43 | if ($context) { | 
| 960 | 6 |  |  |  |  | 46 | $nodeset = $self->{parser}->find($xpath, $context); | 
| 961 |  |  |  |  |  |  | } else { | 
| 962 | 5 |  |  |  |  | 31 | $nodeset = $self->{parser}->find($xpath); | 
| 963 |  |  |  |  |  |  | } | 
| 964 | 11 |  |  |  |  | 926 | foreach my $node ($nodeset->get_nodelist) { | 
| 965 | 11 |  |  |  |  | 149 | return $node; | 
| 966 |  |  |  |  |  |  | } | 
| 967 |  |  |  |  |  |  | } | 
| 968 |  |  |  |  |  |  |  | 
| 969 |  |  |  |  |  |  | # TODO remove unused? | 
| 970 |  |  |  |  |  |  | sub _get_node_as_text { | 
| 971 | 0 |  |  | 0 |  | 0 | my $self = shift; | 
| 972 | 0 |  |  |  |  | 0 | my ($xpath, $context) = @_; | 
| 973 | 0 |  |  |  |  | 0 | my $node = $self->_get_node($xpath, $context); | 
| 974 | 0 | 0 |  |  |  | 0 | if ($node) { | 
| 975 | 0 |  |  |  |  | 0 | return $node->toString; | 
| 976 |  |  |  |  |  |  | } else { | 
| 977 | 0 |  |  |  |  | 0 | return ''; | 
| 978 |  |  |  |  |  |  | } | 
| 979 |  |  |  |  |  |  | } | 
| 980 |  |  |  |  |  |  |  | 
| 981 |  |  |  |  |  |  | # TODO remove unused? | 
| 982 |  |  |  |  |  |  | sub _transform_env_sig { | 
| 983 | 0 |  |  | 0 |  | 0 | my $self = shift; | 
| 984 | 0 |  |  |  |  | 0 | my ($str) = @_; | 
| 985 | 0 |  |  |  |  | 0 | my $prefix = ''; | 
| 986 | 0 | 0 | 0 |  |  | 0 | if (defined $self->{dsig_prefix} && length $self->{dsig_prefix}) { | 
| 987 | 0 |  |  |  |  | 0 | $prefix = $self->{dsig_prefix} . ':'; | 
| 988 |  |  |  |  |  |  | } | 
| 989 |  |  |  |  |  |  |  | 
| 990 |  |  |  |  |  |  | # This removes the first Signature tag from the XML - even if there is another XML tree with another Signature inside and that comes first. | 
| 991 |  |  |  |  |  |  | # TODO: Remove the outermost Signature only. | 
| 992 |  |  |  |  |  |  |  | 
| 993 | 0 |  |  |  |  | 0 | $str =~ s/(<${prefix}Signature(.*?)>(.*?)\<\/${prefix}Signature>)//is; | 
| 994 |  |  |  |  |  |  |  | 
| 995 | 0 |  |  |  |  | 0 | return $str; | 
| 996 |  |  |  |  |  |  | } | 
| 997 |  |  |  |  |  |  |  | 
| 998 |  |  |  |  |  |  | ## | 
| 999 |  |  |  |  |  |  | ## _trim($string) | 
| 1000 |  |  |  |  |  |  | ## | 
| 1001 |  |  |  |  |  |  | ## Arguments: | 
| 1002 |  |  |  |  |  |  | ##    $string:      string String to remove whitespace | 
| 1003 |  |  |  |  |  |  | ## | 
| 1004 |  |  |  |  |  |  | ## Returns: string  Trimmed String | 
| 1005 |  |  |  |  |  |  | ## | 
| 1006 |  |  |  |  |  |  | ## Trim the whitespace from the begining and end of the string | 
| 1007 |  |  |  |  |  |  | ## | 
| 1008 |  |  |  |  |  |  | sub _trim { | 
| 1009 | 26 |  |  | 26 |  | 1056 | my $string = shift; | 
| 1010 | 26 |  |  |  |  | 144 | $string =~ s/^\s+//; | 
| 1011 | 26 |  |  |  |  | 258 | $string =~ s/\s+$//; | 
| 1012 | 26 |  |  |  |  | 132 | return $string; | 
| 1013 |  |  |  |  |  |  | } | 
| 1014 |  |  |  |  |  |  |  | 
| 1015 |  |  |  |  |  |  | ## | 
| 1016 |  |  |  |  |  |  | ## _load_ecdsa_key($key_text) | 
| 1017 |  |  |  |  |  |  | ## | 
| 1018 |  |  |  |  |  |  | ## Arguments: | 
| 1019 |  |  |  |  |  |  | ##    $key_text:    string ECDSA Private Key as String | 
| 1020 |  |  |  |  |  |  | ## | 
| 1021 |  |  |  |  |  |  | ## Returns: nothing | 
| 1022 |  |  |  |  |  |  | ## | 
| 1023 |  |  |  |  |  |  | ## Populate: | 
| 1024 |  |  |  |  |  |  | ##   self->{KeyInfo} | 
| 1025 |  |  |  |  |  |  | ##   self->{key_obj} | 
| 1026 |  |  |  |  |  |  | ##   self->{key_type} | 
| 1027 |  |  |  |  |  |  | ## | 
| 1028 |  |  |  |  |  |  | sub _load_ecdsa_key { | 
| 1029 | 0 |  |  | 0 |  | 0 | my $self = shift; | 
| 1030 | 0 |  |  |  |  | 0 | my $key_text = shift; | 
| 1031 |  |  |  |  |  |  |  | 
| 1032 | 0 | 0 |  |  |  | 0 | eval {require Crypt::PK::ECC; CryptX->VERSION('0.036'); 1} | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
| 1033 |  |  |  |  |  |  | or confess "Crypt::PK::ECC 0.036+ needs to be installed so | 
| 1034 |  |  |  |  |  |  | that we can handle ECDSA signatures"; | 
| 1035 |  |  |  |  |  |  |  | 
| 1036 | 0 |  |  |  |  | 0 | my $ecdsa_key = Crypt::PK::ECC->new('t/ecdsa.private.pem'); | 
| 1037 |  |  |  |  |  |  |  | 
| 1038 | 0 | 0 |  |  |  | 0 | if ( $ecdsa_key ) { | 
| 1039 | 0 |  |  |  |  | 0 | $self->{ key_obj } = $ecdsa_key; | 
| 1040 |  |  |  |  |  |  |  | 
| 1041 | 0 |  |  |  |  | 0 | my $key_hash    = $ecdsa_key->key2hash; | 
| 1042 |  |  |  |  |  |  |  | 
| 1043 | 0 |  |  |  |  | 0 | my $oid         = $key_hash->{ curve_oid }; | 
| 1044 | 0 |  |  |  |  | 0 | my $x           = $key_hash->{ pub_x }; | 
| 1045 | 0 |  |  |  |  | 0 | my $y           = $key_hash->{ pub_y }; | 
| 1046 |  |  |  |  |  |  |  | 
| 1047 | 0 |  |  |  |  | 0 | $self->{KeyInfo} = "<dsig:KeyInfo> | 
| 1048 |  |  |  |  |  |  | <dsig:KeyValue> | 
| 1049 |  |  |  |  |  |  | <dsig:ECDSAKeyValue> | 
| 1050 |  |  |  |  |  |  | <dsig:DomainParameters> | 
| 1051 |  |  |  |  |  |  | <dsig:NamedCurve URN=\"urn:oid:$oid\" /> | 
| 1052 |  |  |  |  |  |  | </dsig:DomainParameters> | 
| 1053 |  |  |  |  |  |  | <dsig:PublicKey> | 
| 1054 |  |  |  |  |  |  | <dsig:X Value=\"$x\" /> | 
| 1055 |  |  |  |  |  |  | <dsig:Y Value=\"$y\" /> | 
| 1056 |  |  |  |  |  |  | </dsig:PublicKey> | 
| 1057 |  |  |  |  |  |  | </dsig:ECDSAKeyValue> | 
| 1058 |  |  |  |  |  |  | </dsig:KeyValue> | 
| 1059 |  |  |  |  |  |  | </dsig:KeyInfo>"; | 
| 1060 | 0 |  |  |  |  | 0 | $self->{key_type} = 'ecdsa'; | 
| 1061 |  |  |  |  |  |  | } | 
| 1062 |  |  |  |  |  |  | else { | 
| 1063 | 0 |  |  |  |  | 0 | confess "did not get a new Crypt::PK::ECC object"; | 
| 1064 |  |  |  |  |  |  | } | 
| 1065 |  |  |  |  |  |  | } | 
| 1066 |  |  |  |  |  |  |  | 
| 1067 |  |  |  |  |  |  | ## | 
| 1068 |  |  |  |  |  |  | ## _load_dsa_key($key_text) | 
| 1069 |  |  |  |  |  |  | ## | 
| 1070 |  |  |  |  |  |  | ## Arguments: | 
| 1071 |  |  |  |  |  |  | ##    $key_text:    string DSA Private Key as String | 
| 1072 |  |  |  |  |  |  | ## | 
| 1073 |  |  |  |  |  |  | ## Returns: nothing | 
| 1074 |  |  |  |  |  |  | ## | 
| 1075 |  |  |  |  |  |  | ## Populate: | 
| 1076 |  |  |  |  |  |  | ##   self->{KeyInfo} | 
| 1077 |  |  |  |  |  |  | ##   self->{key_obj} | 
| 1078 |  |  |  |  |  |  | ##   self->{key_type} | 
| 1079 |  |  |  |  |  |  | ## | 
| 1080 |  |  |  |  |  |  | sub _load_dsa_key { | 
| 1081 | 0 |  |  | 0 |  | 0 | my $self = shift; | 
| 1082 | 0 |  |  |  |  | 0 | my $key_text = shift; | 
| 1083 |  |  |  |  |  |  |  | 
| 1084 | 0 |  |  |  |  | 0 | eval { | 
| 1085 | 0 |  |  |  |  | 0 | require Crypt::OpenSSL::DSA; | 
| 1086 |  |  |  |  |  |  | }; | 
| 1087 |  |  |  |  |  |  |  | 
| 1088 | 0 | 0 |  |  |  | 0 | confess "Crypt::OpenSSL::DSA needs to be installed so that we can handle DSA keys." if $@; | 
| 1089 |  |  |  |  |  |  |  | 
| 1090 | 0 |  |  |  |  | 0 | my $dsa_key = Crypt::OpenSSL::DSA->read_priv_key_str( $key_text ); | 
| 1091 |  |  |  |  |  |  |  | 
| 1092 | 0 | 0 |  |  |  | 0 | if ( $dsa_key ) { | 
| 1093 | 0 |  |  |  |  | 0 | $self->{ key_obj } = $dsa_key; | 
| 1094 | 0 |  |  |  |  | 0 | my $g = encode_base64( $dsa_key->get_g(), '' ); | 
| 1095 | 0 |  |  |  |  | 0 | my $p = encode_base64( $dsa_key->get_p(), '' ); | 
| 1096 | 0 |  |  |  |  | 0 | my $q = encode_base64( $dsa_key->get_q(), '' ); | 
| 1097 | 0 |  |  |  |  | 0 | my $y = encode_base64( $dsa_key->get_pub_key(), '' ); | 
| 1098 |  |  |  |  |  |  |  | 
| 1099 | 0 |  |  |  |  | 0 | $self->{KeyInfo} = "<dsig:KeyInfo> | 
| 1100 |  |  |  |  |  |  | <dsig:KeyValue> | 
| 1101 |  |  |  |  |  |  | <dsig:DSAKeyValue> | 
| 1102 |  |  |  |  |  |  | <dsig:P>$p</dsig:P> | 
| 1103 |  |  |  |  |  |  | <dsig:Q>$q</dsig:Q> | 
| 1104 |  |  |  |  |  |  | <dsig:G>$g</dsig:G> | 
| 1105 |  |  |  |  |  |  | <dsig:Y>$y</dsig:Y> | 
| 1106 |  |  |  |  |  |  | </dsig:DSAKeyValue> | 
| 1107 |  |  |  |  |  |  | </dsig:KeyValue> | 
| 1108 |  |  |  |  |  |  | </dsig:KeyInfo>"; | 
| 1109 | 0 |  |  |  |  | 0 | $self->{key_type} = 'dsa'; | 
| 1110 |  |  |  |  |  |  | } | 
| 1111 |  |  |  |  |  |  | else { | 
| 1112 | 0 |  |  |  |  | 0 | confess "did not get a new Crypt::OpenSSL::RSA object"; | 
| 1113 |  |  |  |  |  |  | } | 
| 1114 |  |  |  |  |  |  | } | 
| 1115 |  |  |  |  |  |  |  | 
| 1116 |  |  |  |  |  |  | ## | 
| 1117 |  |  |  |  |  |  | ## _load_rsa_key($key_text) | 
| 1118 |  |  |  |  |  |  | ## | 
| 1119 |  |  |  |  |  |  | ## Arguments: | 
| 1120 |  |  |  |  |  |  | ##    $key_text:    string RSA Private Key as String | 
| 1121 |  |  |  |  |  |  | ## | 
| 1122 |  |  |  |  |  |  | ## Returns: nothing | 
| 1123 |  |  |  |  |  |  | ## | 
| 1124 |  |  |  |  |  |  | ## Populate: | 
| 1125 |  |  |  |  |  |  | ##   self->{KeyInfo} | 
| 1126 |  |  |  |  |  |  | ##   self->{key_obj} | 
| 1127 |  |  |  |  |  |  | ##   self->{key_type} | 
| 1128 |  |  |  |  |  |  | ## | 
| 1129 |  |  |  |  |  |  | sub _load_rsa_key { | 
| 1130 | 4 |  |  | 4 |  | 12 | my $self = shift; | 
| 1131 | 4 |  |  |  |  | 14 | my ($key_text) = @_; | 
| 1132 |  |  |  |  |  |  |  | 
| 1133 | 4 |  |  |  |  | 7 | eval { | 
| 1134 | 4 |  |  |  |  | 37 | require Crypt::OpenSSL::RSA; | 
| 1135 |  |  |  |  |  |  | }; | 
| 1136 | 4 | 50 |  |  |  | 16 | confess "Crypt::OpenSSL::RSA needs to be installed so that we can handle RSA keys." if $@; | 
| 1137 |  |  |  |  |  |  |  | 
| 1138 | 4 |  |  |  |  | 253 | my $rsaKey = Crypt::OpenSSL::RSA->new_private_key( $key_text ); | 
| 1139 |  |  |  |  |  |  |  | 
| 1140 | 4 | 50 |  |  |  | 41 | if ( $rsaKey ) { | 
| 1141 | 4 |  |  |  |  | 27 | $rsaKey->use_pkcs1_padding(); | 
| 1142 | 4 |  |  |  |  | 18 | $self->{ key_obj }  = $rsaKey; | 
| 1143 | 4 |  |  |  |  | 16 | $self->{ key_type } = 'rsa'; | 
| 1144 |  |  |  |  |  |  |  | 
| 1145 | 4 | 100 |  |  |  | 19 | if (!$self->{ x509 }) { | 
| 1146 | 1 |  |  |  |  | 12 | my $bigNum = ( $rsaKey->get_key_parameters() )[1]; | 
| 1147 | 1 |  |  |  |  | 1418 | my $bin = $bigNum->to_bin(); | 
| 1148 | 1 |  |  |  |  | 7 | my $exp = encode_base64( $bin, '' ); | 
| 1149 |  |  |  |  |  |  |  | 
| 1150 | 1 |  |  |  |  | 27 | $bigNum = ( $rsaKey->get_key_parameters() )[0]; | 
| 1151 | 1 |  |  |  |  | 30 | $bin = $bigNum->to_bin(); | 
| 1152 | 1 |  |  |  |  | 5 | my $mod = encode_base64( $bin, '' ); | 
| 1153 | 1 |  |  |  |  | 8 | $self->{KeyInfo} = "<dsig:KeyInfo> | 
| 1154 |  |  |  |  |  |  | <dsig:KeyValue> | 
| 1155 |  |  |  |  |  |  | <dsig:RSAKeyValue> | 
| 1156 |  |  |  |  |  |  | <dsig:Modulus>$mod</dsig:Modulus> | 
| 1157 |  |  |  |  |  |  | <dsig:Exponent>$exp</dsig:Exponent> | 
| 1158 |  |  |  |  |  |  | </dsig:RSAKeyValue> | 
| 1159 |  |  |  |  |  |  | </dsig:KeyValue> | 
| 1160 |  |  |  |  |  |  | </dsig:KeyInfo>"; | 
| 1161 |  |  |  |  |  |  | } | 
| 1162 |  |  |  |  |  |  | } | 
| 1163 |  |  |  |  |  |  | else { | 
| 1164 | 0 |  |  |  |  | 0 | confess "did not get a new Crypt::OpenSSL::RSA object"; | 
| 1165 |  |  |  |  |  |  | } | 
| 1166 |  |  |  |  |  |  | } | 
| 1167 |  |  |  |  |  |  |  | 
| 1168 |  |  |  |  |  |  | ## | 
| 1169 |  |  |  |  |  |  | ## _load_x509_key($key_text) | 
| 1170 |  |  |  |  |  |  | ## | 
| 1171 |  |  |  |  |  |  | ## Arguments: | 
| 1172 |  |  |  |  |  |  | ##    $key_text:    string RSA Private Key as String | 
| 1173 |  |  |  |  |  |  | ## | 
| 1174 |  |  |  |  |  |  | ## Returns: nothing | 
| 1175 |  |  |  |  |  |  | ## | 
| 1176 |  |  |  |  |  |  | ## Populate: | 
| 1177 |  |  |  |  |  |  | ##   self->{key_obj} | 
| 1178 |  |  |  |  |  |  | ##   self->{key_type} | 
| 1179 |  |  |  |  |  |  | ## | 
| 1180 |  |  |  |  |  |  | sub _load_x509_key { | 
| 1181 | 0 |  |  | 0 |  | 0 | my $self = shift; | 
| 1182 | 0 |  |  |  |  | 0 | my $key_text = shift; | 
| 1183 |  |  |  |  |  |  |  | 
| 1184 | 0 |  |  |  |  | 0 | eval { | 
| 1185 | 0 |  |  |  |  | 0 | require Crypt::OpenSSL::X509; | 
| 1186 |  |  |  |  |  |  | }; | 
| 1187 | 0 | 0 |  |  |  | 0 | confess "Crypt::OpenSSL::X509 needs to be installed so that we | 
| 1188 |  |  |  |  |  |  | can handle X509 Certificates." if $@; | 
| 1189 |  |  |  |  |  |  |  | 
| 1190 | 0 |  |  |  |  | 0 | my $x509Key = Crypt::OpenSSL::X509->new_private_key( $key_text ); | 
| 1191 |  |  |  |  |  |  |  | 
| 1192 | 0 | 0 |  |  |  | 0 | if ( $x509Key ) { | 
| 1193 | 0 |  |  |  |  | 0 | $x509Key->use_pkcs1_padding(); | 
| 1194 | 0 |  |  |  |  | 0 | $self->{ key_obj } = $x509Key; | 
| 1195 | 0 |  |  |  |  | 0 | $self->{key_type} = 'x509'; | 
| 1196 |  |  |  |  |  |  | } | 
| 1197 |  |  |  |  |  |  | else { | 
| 1198 | 0 |  |  |  |  | 0 | confess "did not get a new Crypt::OpenSSL::X509 object"; | 
| 1199 |  |  |  |  |  |  | } | 
| 1200 |  |  |  |  |  |  | } | 
| 1201 |  |  |  |  |  |  |  | 
| 1202 |  |  |  |  |  |  | ## | 
| 1203 |  |  |  |  |  |  | ## _load_cert_file() | 
| 1204 |  |  |  |  |  |  | ## | 
| 1205 |  |  |  |  |  |  | ## Arguments: none | 
| 1206 |  |  |  |  |  |  | ## | 
| 1207 |  |  |  |  |  |  | ## Returns: nothing | 
| 1208 |  |  |  |  |  |  | ## | 
| 1209 |  |  |  |  |  |  | ## Read the file name from $self->{ cert } and | 
| 1210 |  |  |  |  |  |  | ## Populate: | 
| 1211 |  |  |  |  |  |  | ##   self->{key_obj} | 
| 1212 |  |  |  |  |  |  | ##   $self->{KeyInfo} | 
| 1213 |  |  |  |  |  |  | ## | 
| 1214 |  |  |  |  |  |  | sub _load_cert_file { | 
| 1215 | 4 |  |  | 4 |  | 10 | my $self = shift; | 
| 1216 |  |  |  |  |  |  |  | 
| 1217 | 4 |  |  |  |  | 8 | eval { | 
| 1218 | 4 |  |  |  |  | 27 | require Crypt::OpenSSL::X509; | 
| 1219 |  |  |  |  |  |  | }; | 
| 1220 |  |  |  |  |  |  |  | 
| 1221 | 4 | 50 |  |  |  | 17 | confess "Crypt::OpenSSL::X509 needs to be installed so that we can handle X509 certs." if $@; | 
| 1222 |  |  |  |  |  |  |  | 
| 1223 | 4 |  |  |  |  | 12 | my $file = $self->{ cert }; | 
| 1224 | 4 | 50 |  |  |  | 184 | if ( open my $CERT, '<', $file ) { | 
| 1225 | 4 |  |  |  |  | 16 | my $text = ''; | 
| 1226 | 4 |  |  |  |  | 20 | local $/ = undef; | 
| 1227 | 4 |  |  |  |  | 112 | $text = <$CERT>; | 
| 1228 | 4 |  |  |  |  | 51 | close $CERT; | 
| 1229 |  |  |  |  |  |  |  | 
| 1230 | 4 |  |  |  |  | 398 | my $cert = Crypt::OpenSSL::X509->new_from_string($text); | 
| 1231 | 4 | 50 |  |  |  | 49 | if ( $cert ) { | 
| 1232 | 4 |  |  |  |  | 15 | $self->{ cert_obj } = $cert; | 
| 1233 | 4 |  |  |  |  | 168 | my $cert_text = $cert->as_string; | 
| 1234 | 4 |  |  |  |  | 60 | $cert_text =~ s/-----[^-]*-----//gm; | 
| 1235 | 4 |  |  |  |  | 26 | $self->{KeyInfo} = "<dsig:KeyInfo><dsig:X509Data><dsig:X509Certificate>\n"._trim($cert_text)."\n</dsig:X509Certificate></dsig:X509Data></dsig:KeyInfo>"; | 
| 1236 |  |  |  |  |  |  | } | 
| 1237 |  |  |  |  |  |  | else { | 
| 1238 | 0 |  |  |  |  | 0 | confess "Could not load certificate from $file"; | 
| 1239 |  |  |  |  |  |  | } | 
| 1240 |  |  |  |  |  |  | } | 
| 1241 |  |  |  |  |  |  | else { | 
| 1242 | 0 |  |  |  |  | 0 | confess "Could not find certificate file $file"; | 
| 1243 |  |  |  |  |  |  | } | 
| 1244 |  |  |  |  |  |  |  | 
| 1245 | 4 |  |  |  |  | 24 | return; | 
| 1246 |  |  |  |  |  |  | } | 
| 1247 |  |  |  |  |  |  |  | 
| 1248 |  |  |  |  |  |  | ## | 
| 1249 |  |  |  |  |  |  | ## _load_cert_text() | 
| 1250 |  |  |  |  |  |  | ## | 
| 1251 |  |  |  |  |  |  | ## Arguments: none | 
| 1252 |  |  |  |  |  |  | ## | 
| 1253 |  |  |  |  |  |  | ## Returns: nothing | 
| 1254 |  |  |  |  |  |  | ## | 
| 1255 |  |  |  |  |  |  | ## Read the certificate from $self->{ cert_text } and | 
| 1256 |  |  |  |  |  |  | ## Populate: | 
| 1257 |  |  |  |  |  |  | ##   self->{key_obj} | 
| 1258 |  |  |  |  |  |  | ##   $self->{KeyInfo} | 
| 1259 |  |  |  |  |  |  | ## | 
| 1260 |  |  |  |  |  |  | sub _load_cert_text { | 
| 1261 | 1 |  |  | 1 |  | 3 | my $self = shift; | 
| 1262 |  |  |  |  |  |  |  | 
| 1263 | 1 |  |  |  |  | 2 | eval { | 
| 1264 | 1 |  |  |  |  | 6 | require Crypt::OpenSSL::X509; | 
| 1265 |  |  |  |  |  |  | }; | 
| 1266 |  |  |  |  |  |  |  | 
| 1267 | 1 | 50 |  |  |  | 3 | confess "Crypt::OpenSSL::X509 needs to be installed so that we can handle X509 certs." if $@; | 
| 1268 |  |  |  |  |  |  |  | 
| 1269 | 1 |  |  |  |  | 3 | my $text = $self->{ cert_text }; | 
| 1270 | 1 |  |  |  |  | 74 | my $cert = Crypt::OpenSSL::X509->new_from_string($text); | 
| 1271 | 1 | 50 |  |  |  | 5 | if ( $cert ) { | 
| 1272 | 1 |  |  |  |  | 3 | $self->{ cert_obj } = $cert; | 
| 1273 | 1 |  |  |  |  | 39 | my $cert_text = $cert->as_string; | 
| 1274 | 1 |  |  |  |  | 13 | $cert_text =~ s/-----[^-]*-----//gm; | 
| 1275 | 1 |  |  |  |  | 5 | $self->{KeyInfo} = "<dsig:KeyInfo><dsig:X509Data><dsig:X509Certificate>\n"._trim($cert_text)."\n</dsig:X509Certificate></dsig:X509Data></dsig:KeyInfo>"; | 
| 1276 |  |  |  |  |  |  | } | 
| 1277 |  |  |  |  |  |  | else { | 
| 1278 | 0 |  |  |  |  | 0 | confess "Could not load certificate from given text."; | 
| 1279 |  |  |  |  |  |  | } | 
| 1280 |  |  |  |  |  |  |  | 
| 1281 | 1 |  |  |  |  | 3 | return; | 
| 1282 |  |  |  |  |  |  | } | 
| 1283 |  |  |  |  |  |  |  | 
| 1284 |  |  |  |  |  |  | ## | 
| 1285 |  |  |  |  |  |  | ## _load_key($file) | 
| 1286 |  |  |  |  |  |  | ## | 
| 1287 |  |  |  |  |  |  | ## Arguments: $self->{ key } | 
| 1288 |  |  |  |  |  |  | ## | 
| 1289 |  |  |  |  |  |  | ## Returns: nothing | 
| 1290 |  |  |  |  |  |  | ## | 
| 1291 |  |  |  |  |  |  | ## Load the key and process it acording to its headers | 
| 1292 |  |  |  |  |  |  | ## | 
| 1293 |  |  |  |  |  |  | sub _load_key { | 
| 1294 | 4 |  |  | 4 |  | 10 | my $self = shift; | 
| 1295 | 4 |  |  |  |  | 11 | my $file = $self->{ key }; | 
| 1296 |  |  |  |  |  |  |  | 
| 1297 | 4 | 50 |  |  |  | 216 | if ( open my $KEY, '<', $file ) { | 
| 1298 | 4 |  |  |  |  | 21 | my $text = ''; | 
| 1299 | 4 |  |  |  |  | 22 | local $/ = undef; | 
| 1300 | 4 |  |  |  |  | 144 | $text = <$KEY>; | 
| 1301 | 4 |  |  |  |  | 56 | close $KEY; | 
| 1302 |  |  |  |  |  |  |  | 
| 1303 | 4 | 50 |  |  |  | 45 | if ( $text =~ m/BEGIN ([DR]SA) PRIVATE KEY/ ) { | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
| 1304 | 4 |  |  |  |  | 18 | my $key_used = $1; | 
| 1305 |  |  |  |  |  |  |  | 
| 1306 | 4 | 50 |  |  |  | 18 | if ( $key_used eq 'RSA' ) { | 
| 1307 | 4 |  |  |  |  | 22 | $self->_load_rsa_key( $text ); | 
| 1308 |  |  |  |  |  |  | } | 
| 1309 |  |  |  |  |  |  | else { | 
| 1310 | 0 |  |  |  |  | 0 | $self->_load_dsa_key( $text ); | 
| 1311 |  |  |  |  |  |  | } | 
| 1312 |  |  |  |  |  |  |  | 
| 1313 | 4 |  |  |  |  | 28 | return 1; | 
| 1314 |  |  |  |  |  |  | } elsif ( $text =~ m/BEGIN EC PRIVATE KEY/ ) { | 
| 1315 | 0 |  |  |  |  | 0 | $self->_load_ecdsa_key( $text ); | 
| 1316 |  |  |  |  |  |  | } elsif ( $text =~ m/BEGIN PRIVATE KEY/ ) { | 
| 1317 | 0 |  |  |  |  | 0 | $self->_load_rsa_key( $text ); | 
| 1318 |  |  |  |  |  |  | } elsif ($text =~ m/BEGIN CERTIFICATE/) { | 
| 1319 | 0 |  |  |  |  | 0 | $self->_load_x509_key( $text ); | 
| 1320 |  |  |  |  |  |  | } | 
| 1321 |  |  |  |  |  |  | else { | 
| 1322 | 0 |  |  |  |  | 0 | confess "Could not detect type of key $file."; | 
| 1323 |  |  |  |  |  |  | } | 
| 1324 |  |  |  |  |  |  | } | 
| 1325 |  |  |  |  |  |  | else { | 
| 1326 | 0 |  |  |  |  | 0 | confess "Could not load key $file: $!"; | 
| 1327 |  |  |  |  |  |  | } | 
| 1328 |  |  |  |  |  |  |  | 
| 1329 | 0 |  |  |  |  | 0 | return; | 
| 1330 |  |  |  |  |  |  | } | 
| 1331 |  |  |  |  |  |  |  | 
| 1332 |  |  |  |  |  |  | ## | 
| 1333 |  |  |  |  |  |  | ## _signature_xml($signed_info,$signature_value) | 
| 1334 |  |  |  |  |  |  | ## | 
| 1335 |  |  |  |  |  |  | ## Arguments: | 
| 1336 |  |  |  |  |  |  | ##   $signed_info:      string XML String Fragment | 
| 1337 |  |  |  |  |  |  | ##   $signature_value   String Base64 Signature Value | 
| 1338 |  |  |  |  |  |  | ## | 
| 1339 |  |  |  |  |  |  | ## Returns: string      XML fragment | 
| 1340 |  |  |  |  |  |  | ## | 
| 1341 |  |  |  |  |  |  | ## Create a XML string of the Signature | 
| 1342 |  |  |  |  |  |  | ## | 
| 1343 |  |  |  |  |  |  | sub _signature_xml { | 
| 1344 | 5 |  |  | 5 |  | 11 | my $self = shift; | 
| 1345 | 5 |  |  |  |  | 15 | my ($signed_info,$signature_value) = @_; | 
| 1346 | 5 |  |  |  |  | 108 | return qq{<dsig:Signature xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"> | 
| 1347 |  |  |  |  |  |  | $signed_info | 
| 1348 |  |  |  |  |  |  | <dsig:SignatureValue>$signature_value</dsig:SignatureValue> | 
| 1349 |  |  |  |  |  |  | $self->{KeyInfo} | 
| 1350 |  |  |  |  |  |  | </dsig:Signature>}; | 
| 1351 |  |  |  |  |  |  | } | 
| 1352 |  |  |  |  |  |  |  | 
| 1353 |  |  |  |  |  |  | ## | 
| 1354 |  |  |  |  |  |  | ## _signedinfo_xml($digest_xml) | 
| 1355 |  |  |  |  |  |  | ## | 
| 1356 |  |  |  |  |  |  | ## Arguments: | 
| 1357 |  |  |  |  |  |  | ##   $digest_xml        string XML String Fragment | 
| 1358 |  |  |  |  |  |  | ## | 
| 1359 |  |  |  |  |  |  | ## Returns: string      XML fragment | 
| 1360 |  |  |  |  |  |  | ## | 
| 1361 |  |  |  |  |  |  | ## Create a XML string of the SignedInfo | 
| 1362 |  |  |  |  |  |  | ## | 
| 1363 |  |  |  |  |  |  | sub _signedinfo_xml { | 
| 1364 | 5 |  |  | 5 |  | 13 | my $self = shift; | 
| 1365 | 5 |  |  |  |  | 13 | my ($digest_xml) = @_; | 
| 1366 |  |  |  |  |  |  |  | 
| 1367 | 5 |  |  |  |  | 10 | my $algorithm; | 
| 1368 | 5 | 100 | 66 |  |  | 40 | if ( $self->{ sig_hash } eq 'sha1' && $self->{key_type} ne 'ecdsa' ) { | 
|  |  | 50 | 33 |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
| 1369 | 4 |  |  |  |  | 15 | $algorithm = "http://www.w3.org/2000/09/xmldsig#$self->{key_type}-$self->{ sig_hash }"; | 
| 1370 |  |  |  |  |  |  | } | 
| 1371 |  |  |  |  |  |  | elsif ( $self->{key_type} eq 'ecdsa' ) { | 
| 1372 | 0 |  |  |  |  | 0 | $algorithm = "http://www.w3.org/2001/04/xmldsig-more#$self->{key_type}-$self->{ sig_hash }"; | 
| 1373 |  |  |  |  |  |  | } | 
| 1374 |  |  |  |  |  |  | elsif ( $self->{ key_type } eq 'dsa' && $self->{ sig_hash } eq 'sha256') { | 
| 1375 | 0 |  |  |  |  | 0 | $algorithm = "http://www.w3.org/2009/xmldsig11#$self->{key_type}-$self->{ sig_hash }"; | 
| 1376 |  |  |  |  |  |  | } | 
| 1377 |  |  |  |  |  |  | else { | 
| 1378 | 1 |  |  |  |  | 5 | $algorithm = "http://www.w3.org/2001/04/xmldsig-more#$self->{key_type}-$self->{ sig_hash }"; | 
| 1379 |  |  |  |  |  |  | } | 
| 1380 |  |  |  |  |  |  |  | 
| 1381 |  |  |  |  |  |  | #return qq{<dsig:SignedInfo xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"> | 
| 1382 | 5 |  |  |  |  | 53 | return qq{<dsig:SignedInfo xmlns:dsig="http://www.w3.org/2000/09/xmldsig#" xmlns:xenc="http://www.w3.org/2001/04/xmlenc#"> | 
| 1383 |  |  |  |  |  |  | <dsig:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" /> | 
| 1384 |  |  |  |  |  |  | <dsig:SignatureMethod Algorithm="$algorithm" /> | 
| 1385 |  |  |  |  |  |  | $digest_xml | 
| 1386 |  |  |  |  |  |  | </dsig:SignedInfo>}; | 
| 1387 |  |  |  |  |  |  | } | 
| 1388 |  |  |  |  |  |  |  | 
| 1389 |  |  |  |  |  |  | ## | 
| 1390 |  |  |  |  |  |  | ## _reference_xml($id) | 
| 1391 |  |  |  |  |  |  | ## | 
| 1392 |  |  |  |  |  |  | ## Arguments: | 
| 1393 |  |  |  |  |  |  | ##   $id        string XML ID related to the URI | 
| 1394 |  |  |  |  |  |  | ##   $digest    string Base64 encoded digest | 
| 1395 |  |  |  |  |  |  | ## | 
| 1396 |  |  |  |  |  |  | ## Returns: string      XML fragment | 
| 1397 |  |  |  |  |  |  | ## | 
| 1398 |  |  |  |  |  |  | ## Create a XML string of the Reference | 
| 1399 |  |  |  |  |  |  | ## | 
| 1400 |  |  |  |  |  |  | sub _reference_xml { | 
| 1401 | 5 |  |  | 5 |  | 14 | my $self = shift; | 
| 1402 | 5 |  |  |  |  | 12 | my $id = shift; | 
| 1403 | 5 |  |  |  |  | 13 | my ($digest) = @_; | 
| 1404 |  |  |  |  |  |  |  | 
| 1405 | 5 |  |  |  |  | 12 | my $algorithm; | 
| 1406 | 5 | 100 | 33 |  |  | 29 | if ( $self->{ digest_hash } eq 'sha1') { | 
|  |  | 50 |  |  |  |  |  | 
| 1407 | 4 |  |  |  |  | 13 | $algorithm = "http://www.w3.org/2000/09/xmldsig#$self->{ digest_hash }"; | 
| 1408 |  |  |  |  |  |  | } | 
| 1409 |  |  |  |  |  |  | elsif (($self->{ digest_hash } eq 'sha224') || ($self->{ digest_hash } eq 'sha384')) { | 
| 1410 | 0 |  |  |  |  | 0 | $algorithm = "http://www.w3.org/2001/04/xmldsig-more#$self->{ digest_hash }"; | 
| 1411 |  |  |  |  |  |  | } | 
| 1412 |  |  |  |  |  |  | else { | 
| 1413 | 1 |  |  |  |  | 4 | $algorithm = "http://www.w3.org/2001/04/xmlenc#$self->{ digest_hash }"; | 
| 1414 |  |  |  |  |  |  | } | 
| 1415 |  |  |  |  |  |  |  | 
| 1416 | 5 |  |  |  |  | 39 | return qq{<dsig:Reference URI="#$id"> | 
| 1417 |  |  |  |  |  |  | <dsig:Transforms> | 
| 1418 |  |  |  |  |  |  | <dsig:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" /> | 
| 1419 |  |  |  |  |  |  | <dsig:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/> | 
| 1420 |  |  |  |  |  |  | </dsig:Transforms> | 
| 1421 |  |  |  |  |  |  | <dsig:DigestMethod Algorithm="$algorithm" /> | 
| 1422 |  |  |  |  |  |  | <dsig:DigestValue>$digest</dsig:DigestValue> | 
| 1423 |  |  |  |  |  |  | </dsig:Reference>}; | 
| 1424 |  |  |  |  |  |  | } | 
| 1425 |  |  |  |  |  |  |  | 
| 1426 |  |  |  |  |  |  |  | 
| 1427 |  |  |  |  |  |  | ## | 
| 1428 |  |  |  |  |  |  | ## _canonicalize_xml($xml, $context) | 
| 1429 |  |  |  |  |  |  | ## | 
| 1430 |  |  |  |  |  |  | ## Arguments: | 
| 1431 |  |  |  |  |  |  | ##    $xml:     string XML NodeSet | 
| 1432 |  |  |  |  |  |  | ##    $context: string XML Context | 
| 1433 |  |  |  |  |  |  | ## | 
| 1434 |  |  |  |  |  |  | ## Returns: string  Canonical XML | 
| 1435 |  |  |  |  |  |  | ## | 
| 1436 |  |  |  |  |  |  | ## Canonicalizes xml based on the CanonicalizationMethod | 
| 1437 |  |  |  |  |  |  | ## from the SignedInfo. | 
| 1438 |  |  |  |  |  |  | ## | 
| 1439 |  |  |  |  |  |  | sub _canonicalize_xml { | 
| 1440 | 11 |  |  | 11 |  | 24 | my $self = shift; | 
| 1441 | 11 |  |  |  |  | 26 | my ($xml, $context) = @_; | 
| 1442 |  |  |  |  |  |  |  | 
| 1443 | 11 | 50 |  |  |  | 33 | print ("_canonicalize_xml:\n") if $DEBUG; | 
| 1444 |  |  |  |  |  |  | my $canon_method = $self->{ parser }->findnodes( | 
| 1445 | 11 |  |  |  |  | 40 | 'dsig:SignedInfo/dsig:CanonicalizationMethod', $context | 
| 1446 |  |  |  |  |  |  | ); | 
| 1447 |  |  |  |  |  |  |  | 
| 1448 | 11 |  |  |  |  | 510 | foreach my $node ($canon_method->get_nodelist) { | 
| 1449 | 11 |  |  |  |  | 100 | my $alg = $node->getAttribute('Algorithm'); | 
| 1450 |  |  |  |  |  |  |  | 
| 1451 | 11 | 50 |  |  |  | 155 | print ("    Canon Method: $alg\n") if $DEBUG; | 
| 1452 | 11 | 50 |  |  |  | 89 | if ($alg eq TRANSFORM_C14N) { | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
| 1453 | 0 | 0 |  |  |  | 0 | print ("        toStringC14N\n") if $DEBUG; | 
| 1454 | 0 |  |  |  |  | 0 | $xml = $xml->toStringC14N(); | 
| 1455 |  |  |  |  |  |  | } | 
| 1456 |  |  |  |  |  |  | elsif ($alg eq TRANSFORM_C14N_COMMENTS) { | 
| 1457 | 0 | 0 |  |  |  | 0 | print ("        toStringC14N_Comments\n") if $DEBUG; | 
| 1458 | 0 |  |  |  |  | 0 | $xml = $xml->toStringC14N(1); | 
| 1459 |  |  |  |  |  |  | } | 
| 1460 |  |  |  |  |  |  | elsif ($alg eq TRANSFORM_C14N_V1_1) { | 
| 1461 | 0 | 0 |  |  |  | 0 | print ("        toStringC14N_v1_1\n") if $DEBUG; | 
| 1462 | 0 |  |  |  |  | 0 | $xml = $xml->toStringC14N_v1_1(); | 
| 1463 |  |  |  |  |  |  | } | 
| 1464 |  |  |  |  |  |  | elsif ($alg eq TRANSFORM_C14N_V1_1_COMMENTS) { | 
| 1465 | 0 | 0 |  |  |  | 0 | print ("        toStringC14N_v1_1_Comments\n") if $DEBUG; | 
| 1466 | 0 |  |  |  |  | 0 | $xml = $xml->toStringC14N_v1_1(1); | 
| 1467 |  |  |  |  |  |  | } | 
| 1468 |  |  |  |  |  |  | elsif ($alg eq TRANSFORM_EXC_C14N) { | 
| 1469 | 11 | 50 |  |  |  | 27 | print ("        toStringEC14N\n") if $DEBUG; | 
| 1470 | 11 |  |  |  |  | 51 | $xml = $xml->toStringEC14N(); | 
| 1471 |  |  |  |  |  |  | } | 
| 1472 |  |  |  |  |  |  | elsif ($alg eq TRANSFORM_EXC_C14N_COMMENTS) { | 
| 1473 | 0 | 0 |  |  |  | 0 | print ("        toStringEC14N_Comments\n") if $DEBUG; | 
| 1474 | 0 |  |  |  |  | 0 | $xml = $xml->toStringEC14N(1); | 
| 1475 |  |  |  |  |  |  | } | 
| 1476 |  |  |  |  |  |  | else { | 
| 1477 | 0 |  |  |  |  | 0 | die "Unsupported transform: $alg"; | 
| 1478 |  |  |  |  |  |  | } | 
| 1479 |  |  |  |  |  |  | } | 
| 1480 | 11 |  |  |  |  | 2366 | return $xml; | 
| 1481 |  |  |  |  |  |  | } | 
| 1482 |  |  |  |  |  |  | 1; | 
| 1483 |  |  |  |  |  |  |  | 
| 1484 |  |  |  |  |  |  | =pod | 
| 1485 |  |  |  |  |  |  |  | 
| 1486 |  |  |  |  |  |  | =encoding UTF-8 | 
| 1487 |  |  |  |  |  |  |  | 
| 1488 |  |  |  |  |  |  | =head1 NAME | 
| 1489 |  |  |  |  |  |  |  | 
| 1490 |  |  |  |  |  |  | Net::SAML2::XML::Sig - Net::SAML2::XML::Sig - A toolkit to help sign and verify XML Digital Signatures | 
| 1491 |  |  |  |  |  |  |  | 
| 1492 |  |  |  |  |  |  | =head1 VERSION | 
| 1493 |  |  |  |  |  |  |  | 
| 1494 |  |  |  |  |  |  | version 0.43 | 
| 1495 |  |  |  |  |  |  |  | 
| 1496 |  |  |  |  |  |  | =head1 SYNOPSIS | 
| 1497 |  |  |  |  |  |  |  | 
| 1498 |  |  |  |  |  |  | my $xml = '<foo ID="abc">123</foo>'; | 
| 1499 |  |  |  |  |  |  | my $signer = Net::SAML2::XML::Sig->new({ | 
| 1500 |  |  |  |  |  |  | key => 'path/to/private.key', | 
| 1501 |  |  |  |  |  |  | }); | 
| 1502 |  |  |  |  |  |  |  | 
| 1503 |  |  |  |  |  |  | # create a signature | 
| 1504 |  |  |  |  |  |  | my $signed = $signer->sign($xml); | 
| 1505 |  |  |  |  |  |  | print "Signed XML: $signed\n"; | 
| 1506 |  |  |  |  |  |  |  | 
| 1507 |  |  |  |  |  |  | # verify a signature | 
| 1508 |  |  |  |  |  |  | $signer->verify($signed) | 
| 1509 |  |  |  |  |  |  | or die "Signature Invalid."; | 
| 1510 |  |  |  |  |  |  | print "Signature valid.\n"; | 
| 1511 |  |  |  |  |  |  |  | 
| 1512 |  |  |  |  |  |  | =head1 DESCRIPTION | 
| 1513 |  |  |  |  |  |  |  | 
| 1514 |  |  |  |  |  |  | This perl module provides two primary capabilities: given an XML string, create | 
| 1515 |  |  |  |  |  |  | and insert digital signatures, or if one is already present in the string verify | 
| 1516 |  |  |  |  |  |  | it -- all in accordance with the W3C standard governing XML signatures. | 
| 1517 |  |  |  |  |  |  |  | 
| 1518 |  |  |  |  |  |  | =head1 NAME | 
| 1519 |  |  |  |  |  |  |  | 
| 1520 |  |  |  |  |  |  | Net::SAML2::XML::Sig - A toolkit to help sign and verify XML Digital Signatures. | 
| 1521 |  |  |  |  |  |  |  | 
| 1522 |  |  |  |  |  |  | =head1 PREREQUISITES | 
| 1523 |  |  |  |  |  |  |  | 
| 1524 |  |  |  |  |  |  | =over | 
| 1525 |  |  |  |  |  |  |  | 
| 1526 |  |  |  |  |  |  | =item * L<Digest::SHA> | 
| 1527 |  |  |  |  |  |  |  | 
| 1528 |  |  |  |  |  |  | =item * L<XML::LibXML> | 
| 1529 |  |  |  |  |  |  |  | 
| 1530 |  |  |  |  |  |  | =item * L<MIME::Base64> | 
| 1531 |  |  |  |  |  |  |  | 
| 1532 |  |  |  |  |  |  | =item * L<Crypt::OpenSSL::X509> | 
| 1533 |  |  |  |  |  |  |  | 
| 1534 |  |  |  |  |  |  | =item * L<Crypt::OpenSSL::Bignum> | 
| 1535 |  |  |  |  |  |  |  | 
| 1536 |  |  |  |  |  |  | =item * L<Crypt::OpenSSL::RSA> | 
| 1537 |  |  |  |  |  |  |  | 
| 1538 |  |  |  |  |  |  | =item * L<Crypt::OpenSSL::DSA> | 
| 1539 |  |  |  |  |  |  |  | 
| 1540 |  |  |  |  |  |  | =item * L<Crypt::PK::ECC> | 
| 1541 |  |  |  |  |  |  |  | 
| 1542 |  |  |  |  |  |  | =back | 
| 1543 |  |  |  |  |  |  |  | 
| 1544 |  |  |  |  |  |  | =head1 USAGE | 
| 1545 |  |  |  |  |  |  |  | 
| 1546 |  |  |  |  |  |  | =head2 SUPPORTED ALGORITHMS & TRANSFORMS | 
| 1547 |  |  |  |  |  |  |  | 
| 1548 |  |  |  |  |  |  | This module supports the following signature methods: | 
| 1549 |  |  |  |  |  |  |  | 
| 1550 |  |  |  |  |  |  | =over | 
| 1551 |  |  |  |  |  |  |  | 
| 1552 |  |  |  |  |  |  | =item * DSA | 
| 1553 |  |  |  |  |  |  |  | 
| 1554 |  |  |  |  |  |  | =item * RSA | 
| 1555 |  |  |  |  |  |  |  | 
| 1556 |  |  |  |  |  |  | =item * RSA encoded as x509 | 
| 1557 |  |  |  |  |  |  |  | 
| 1558 |  |  |  |  |  |  | =item * ECDSA | 
| 1559 |  |  |  |  |  |  |  | 
| 1560 |  |  |  |  |  |  | =item * ECDSA encoded as x509 | 
| 1561 |  |  |  |  |  |  |  | 
| 1562 |  |  |  |  |  |  | =back | 
| 1563 |  |  |  |  |  |  |  | 
| 1564 |  |  |  |  |  |  | This module supports the following canonicalization methods and transforms: | 
| 1565 |  |  |  |  |  |  |  | 
| 1566 |  |  |  |  |  |  | =over | 
| 1567 |  |  |  |  |  |  |  | 
| 1568 |  |  |  |  |  |  | =item * Enveloped Signature | 
| 1569 |  |  |  |  |  |  |  | 
| 1570 |  |  |  |  |  |  | =item * REC-xml-c14n-20010315# | 
| 1571 |  |  |  |  |  |  |  | 
| 1572 |  |  |  |  |  |  | =item * REC-xml-c14n-20010315#WithComments | 
| 1573 |  |  |  |  |  |  |  | 
| 1574 |  |  |  |  |  |  | =item * REC-xml-c14n11-20080502 | 
| 1575 |  |  |  |  |  |  |  | 
| 1576 |  |  |  |  |  |  | =item * REC-xml-c14n11-20080502#WithComments | 
| 1577 |  |  |  |  |  |  |  | 
| 1578 |  |  |  |  |  |  | =item * xml-exc-c14n# | 
| 1579 |  |  |  |  |  |  |  | 
| 1580 |  |  |  |  |  |  | =item * xml-exc-c14n#WithComments | 
| 1581 |  |  |  |  |  |  |  | 
| 1582 |  |  |  |  |  |  | =back | 
| 1583 |  |  |  |  |  |  |  | 
| 1584 |  |  |  |  |  |  | =head2 OPTIONS | 
| 1585 |  |  |  |  |  |  |  | 
| 1586 |  |  |  |  |  |  | Each of the following options are also accessors on the main | 
| 1587 |  |  |  |  |  |  | Net::SAML2::XML::Sig object. TODO Not strictly correct rewrite | 
| 1588 |  |  |  |  |  |  |  | 
| 1589 |  |  |  |  |  |  | =over | 
| 1590 |  |  |  |  |  |  |  | 
| 1591 |  |  |  |  |  |  | =item B<key> | 
| 1592 |  |  |  |  |  |  |  | 
| 1593 |  |  |  |  |  |  | The path to a file containing the contents of a private key. This option | 
| 1594 |  |  |  |  |  |  | is used only when generating signatures. | 
| 1595 |  |  |  |  |  |  |  | 
| 1596 |  |  |  |  |  |  | =item B<cert> | 
| 1597 |  |  |  |  |  |  |  | 
| 1598 |  |  |  |  |  |  | The path to a file containing a PEM-formatted X509 certificate. This | 
| 1599 |  |  |  |  |  |  | option is used only when generating signatures with the "x509" | 
| 1600 |  |  |  |  |  |  | option. This certificate will be embedded in the signed document, and | 
| 1601 |  |  |  |  |  |  | should match the private key used for the signature. | 
| 1602 |  |  |  |  |  |  |  | 
| 1603 |  |  |  |  |  |  | =item B<cert_text> | 
| 1604 |  |  |  |  |  |  |  | 
| 1605 |  |  |  |  |  |  | A string containing a PEM-formatted X509 certificate. This | 
| 1606 |  |  |  |  |  |  | option is used only when generating signatures with the "x509" | 
| 1607 |  |  |  |  |  |  | option. This certificate will be embedded in the signed document, and | 
| 1608 |  |  |  |  |  |  | should match the private key used for the signature. | 
| 1609 |  |  |  |  |  |  |  | 
| 1610 |  |  |  |  |  |  | =item B<x509> | 
| 1611 |  |  |  |  |  |  |  | 
| 1612 |  |  |  |  |  |  | Takes a true (1) or false (0) value and indicates how you want the | 
| 1613 |  |  |  |  |  |  | signature to be encoded. When true, the X509 certificate supplied will | 
| 1614 |  |  |  |  |  |  | be encoded in the signature. Otherwise the native encoding format for | 
| 1615 |  |  |  |  |  |  | RSA, DSA and ECDSA will be used. | 
| 1616 |  |  |  |  |  |  |  | 
| 1617 |  |  |  |  |  |  | =item B<sig_hash> | 
| 1618 |  |  |  |  |  |  |  | 
| 1619 |  |  |  |  |  |  | Passing sig_hash to new allows you to specify the SignatureMethod | 
| 1620 |  |  |  |  |  |  | hashing algorithm used when signing the SignedInfo.  RSA and ECDSA | 
| 1621 |  |  |  |  |  |  | supports the hashes specified sha1, sha224, sha256, sha384 and sha512 | 
| 1622 |  |  |  |  |  |  |  | 
| 1623 |  |  |  |  |  |  | DSA supports only sha1 and sha256 (but you really should not sign | 
| 1624 |  |  |  |  |  |  | anything with DSA anyway). | 
| 1625 |  |  |  |  |  |  |  | 
| 1626 |  |  |  |  |  |  | =item B<digest_hash> | 
| 1627 |  |  |  |  |  |  |  | 
| 1628 |  |  |  |  |  |  | Passing digest_hash to new allows you to specify the DigestMethod | 
| 1629 |  |  |  |  |  |  | hashing algorithm used when calculating the hash of the XML being | 
| 1630 |  |  |  |  |  |  | signed.  Supported hashes can be specified sha1, sha224, sha256, | 
| 1631 |  |  |  |  |  |  | sha384, and sha512 | 
| 1632 |  |  |  |  |  |  |  | 
| 1633 |  |  |  |  |  |  | =item B<no_xml_declaration> | 
| 1634 |  |  |  |  |  |  |  | 
| 1635 |  |  |  |  |  |  | Some applications such as Net::SAML2 expect to sign a fragment of the | 
| 1636 |  |  |  |  |  |  | full XML document so is this is true (1) it will not include the | 
| 1637 |  |  |  |  |  |  | XML Declaration at the beginning of the signed XML.  False (0) or | 
| 1638 |  |  |  |  |  |  | undefined returns an XML document starting with the XML Declaration. | 
| 1639 |  |  |  |  |  |  |  | 
| 1640 |  |  |  |  |  |  | =back | 
| 1641 |  |  |  |  |  |  |  | 
| 1642 |  |  |  |  |  |  | =head2 METHODS | 
| 1643 |  |  |  |  |  |  |  | 
| 1644 |  |  |  |  |  |  | =head3 B<new(...)> | 
| 1645 |  |  |  |  |  |  |  | 
| 1646 |  |  |  |  |  |  | Constructor; see OPTIONS above. | 
| 1647 |  |  |  |  |  |  |  | 
| 1648 |  |  |  |  |  |  | =head3 B<sign($xml)> | 
| 1649 |  |  |  |  |  |  |  | 
| 1650 |  |  |  |  |  |  | When given a string of XML, it will return the same string with a signature | 
| 1651 |  |  |  |  |  |  | generated from the key provided when the Net::SAML2::XML::Sig object was initialized. | 
| 1652 |  |  |  |  |  |  |  | 
| 1653 |  |  |  |  |  |  | This method will sign all elements in your XML with an ID (case sensitive) | 
| 1654 |  |  |  |  |  |  | attribute. Each element with an ID attribute will be the basis for a seperate | 
| 1655 |  |  |  |  |  |  | signature. It will correspond to the URI attribute in the Reference element | 
| 1656 |  |  |  |  |  |  | that will be contained by the signature. If no ID attribute can be found on | 
| 1657 |  |  |  |  |  |  | an element, the signature will not be created. | 
| 1658 |  |  |  |  |  |  |  | 
| 1659 |  |  |  |  |  |  | The elements are signed in reverse order currently assuming (possibly | 
| 1660 |  |  |  |  |  |  | incorrectly) that the lower element in the tree may need to be signed | 
| 1661 |  |  |  |  |  |  | inclusive of its Signature because it is a child of the higher element. | 
| 1662 |  |  |  |  |  |  |  | 
| 1663 |  |  |  |  |  |  | Arguments: | 
| 1664 |  |  |  |  |  |  | $xml:     string XML string | 
| 1665 |  |  |  |  |  |  |  | 
| 1666 |  |  |  |  |  |  | Returns: string  Signed XML | 
| 1667 |  |  |  |  |  |  |  | 
| 1668 |  |  |  |  |  |  | =head3 B<verify($xml)> | 
| 1669 |  |  |  |  |  |  |  | 
| 1670 |  |  |  |  |  |  | Returns true or false based upon whether the signature is valid or not. | 
| 1671 |  |  |  |  |  |  |  | 
| 1672 |  |  |  |  |  |  | When using Net::SAML2::XML::Sig exclusively to verify a signature, no key needs to be | 
| 1673 |  |  |  |  |  |  | specified during initialization given that the public key should be | 
| 1674 |  |  |  |  |  |  | transmitted with the signature. | 
| 1675 |  |  |  |  |  |  |  | 
| 1676 |  |  |  |  |  |  | Net::SAML2::XML::Sig checks all signature in the provided xml and will fail should any | 
| 1677 |  |  |  |  |  |  | signature pointing to an existing ID in the XML fail to verify. | 
| 1678 |  |  |  |  |  |  |  | 
| 1679 |  |  |  |  |  |  | Should there be a Signature included that does not point to an existing node | 
| 1680 |  |  |  |  |  |  | in the XML it is ignored and other Signaures are checked.  If there are no | 
| 1681 |  |  |  |  |  |  | other Signatures it will return false. | 
| 1682 |  |  |  |  |  |  |  | 
| 1683 |  |  |  |  |  |  | Arguments: | 
| 1684 |  |  |  |  |  |  | $xml:     string XML string | 
| 1685 |  |  |  |  |  |  |  | 
| 1686 |  |  |  |  |  |  | Returns: string  Signed XML | 
| 1687 |  |  |  |  |  |  |  | 
| 1688 |  |  |  |  |  |  | =head3 B<signer_cert()> | 
| 1689 |  |  |  |  |  |  |  | 
| 1690 |  |  |  |  |  |  | Following a successful verify with an X509 certificate, returns the | 
| 1691 |  |  |  |  |  |  | signer's certificate as embedded in the XML document for verification | 
| 1692 |  |  |  |  |  |  | against a CA certificate. The certificate is returned as a | 
| 1693 |  |  |  |  |  |  | Crypt::OpenSSL::X509 object. | 
| 1694 |  |  |  |  |  |  |  | 
| 1695 |  |  |  |  |  |  | Arguments: none | 
| 1696 |  |  |  |  |  |  |  | 
| 1697 |  |  |  |  |  |  | Returns: Crypt::OpenSSL::X509: Certificate used to sign the XML | 
| 1698 |  |  |  |  |  |  |  | 
| 1699 |  |  |  |  |  |  | =head1 ABOUT DIGITAL SIGNATURES | 
| 1700 |  |  |  |  |  |  |  | 
| 1701 |  |  |  |  |  |  | Just as one might want to send an email message that is cryptographically signed | 
| 1702 |  |  |  |  |  |  | in order to give the recipient the means to independently verify who sent the email, | 
| 1703 |  |  |  |  |  |  | one might also want to sign an XML document. This is especially true in the | 
| 1704 |  |  |  |  |  |  | scenario where an XML document is received in an otherwise unauthenticated | 
| 1705 |  |  |  |  |  |  | context, e.g. SAML. | 
| 1706 |  |  |  |  |  |  |  | 
| 1707 |  |  |  |  |  |  | However XML provides a challenge that email does not. In XML, two documents can be | 
| 1708 |  |  |  |  |  |  | byte-wise inequivalent, and semanticaly equivalent at the same time. For example: | 
| 1709 |  |  |  |  |  |  |  | 
| 1710 |  |  |  |  |  |  | <?xml version="1.0"?> | 
| 1711 |  |  |  |  |  |  | <foo> | 
| 1712 |  |  |  |  |  |  | <bar /> | 
| 1713 |  |  |  |  |  |  | </foo> | 
| 1714 |  |  |  |  |  |  |  | 
| 1715 |  |  |  |  |  |  | And: | 
| 1716 |  |  |  |  |  |  |  | 
| 1717 |  |  |  |  |  |  | <?xml version="1.0"?> | 
| 1718 |  |  |  |  |  |  | <foo> | 
| 1719 |  |  |  |  |  |  | <bar></bar> | 
| 1720 |  |  |  |  |  |  | </foo> | 
| 1721 |  |  |  |  |  |  |  | 
| 1722 |  |  |  |  |  |  | Each of these document express the same thing, or in other words they "mean" | 
| 1723 |  |  |  |  |  |  | the same thing. However if you were to strictly sign the raw text of these | 
| 1724 |  |  |  |  |  |  | documents, they would each produce different signatures. | 
| 1725 |  |  |  |  |  |  |  | 
| 1726 |  |  |  |  |  |  | XML Signatures on the other hand will produce the same signature for each of | 
| 1727 |  |  |  |  |  |  | the documents above. Therefore an XML document can be written and rewritten by | 
| 1728 |  |  |  |  |  |  | different parties and still be able to have someone at the end of the line | 
| 1729 |  |  |  |  |  |  | verify a signature the document may contain. | 
| 1730 |  |  |  |  |  |  |  | 
| 1731 |  |  |  |  |  |  | There is a specially subscribed methodology for how this process should be | 
| 1732 |  |  |  |  |  |  | executed and involves transforming the XML into its canonical form so a | 
| 1733 |  |  |  |  |  |  | signature can be reliably inserted or extracted for verification. This | 
| 1734 |  |  |  |  |  |  | module implements that process. | 
| 1735 |  |  |  |  |  |  |  | 
| 1736 |  |  |  |  |  |  | =head2 EXAMPLE SIGNATURE | 
| 1737 |  |  |  |  |  |  |  | 
| 1738 |  |  |  |  |  |  | Below is a sample XML signature to give you some sense of what they look like. | 
| 1739 |  |  |  |  |  |  | First let's look at the original XML document, prior to being signed: | 
| 1740 |  |  |  |  |  |  |  | 
| 1741 |  |  |  |  |  |  | <?xml version="1.0"?> | 
| 1742 |  |  |  |  |  |  | <foo ID="abc"> | 
| 1743 |  |  |  |  |  |  | <bar>123</bar> | 
| 1744 |  |  |  |  |  |  | </foo> | 
| 1745 |  |  |  |  |  |  |  | 
| 1746 |  |  |  |  |  |  | Now, let's insert a signature: | 
| 1747 |  |  |  |  |  |  |  | 
| 1748 |  |  |  |  |  |  | <?xml version="1.0"?> | 
| 1749 |  |  |  |  |  |  | <foo ID="abc"> | 
| 1750 |  |  |  |  |  |  | <bar>123</bar> | 
| 1751 |  |  |  |  |  |  | <Signature xmlns="http://www.w3.org/2000/09/xmldsig#"> | 
| 1752 |  |  |  |  |  |  | <SignedInfo xmlns="http://www.w3.org/2000/09/xmldsig#" xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:xenc="http://www.w3.org/2001/04/xmlenc#"> | 
| 1753 |  |  |  |  |  |  | <CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments" /> | 
| 1754 |  |  |  |  |  |  | <SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" /> | 
| 1755 |  |  |  |  |  |  | <Reference URI="#abc"> | 
| 1756 |  |  |  |  |  |  | <Transforms> | 
| 1757 |  |  |  |  |  |  | <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" /> | 
| 1758 |  |  |  |  |  |  | </Transforms> | 
| 1759 |  |  |  |  |  |  | <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" /> | 
| 1760 |  |  |  |  |  |  | <DigestValue>9kpmrvv3peVJpNSTRycrV+jeHVY=</DigestValue> | 
| 1761 |  |  |  |  |  |  | </Reference> | 
| 1762 |  |  |  |  |  |  | </SignedInfo> | 
| 1763 |  |  |  |  |  |  | <SignatureValue> | 
| 1764 |  |  |  |  |  |  | HXUBnMgPJf//j4ihaWnaylNwAR5AzDFY83HljFIlLmTqX1w1C72ZTuRObvYve8TNEbVsQlTQkj4R | 
| 1765 |  |  |  |  |  |  | hiY0pgIMQUb75GLYFtc+f0YmBZf5rCWY3NWzo432D3ogAvpEzYXEQPmicWe2QozQhybaz9/wrYki | 
| 1766 |  |  |  |  |  |  | XiXY+57fqCkf7aT8Bb6G+fn7Aj8gnZFLkmKxwCdyGsIZOIZdQ8MWpeQrifxBR0d8W1Zm6ix21WNv | 
| 1767 |  |  |  |  |  |  | ONt575h7VxLKw8BDhNPS0p8CS3hOnSk29stpiDMCHFPxAwrbKVL1kGDLaLZn1q8nNRmH8oFxG15l | 
| 1768 |  |  |  |  |  |  | UmS3JXDZAss8gZhU7g9T4XllCqjrAvzPLOFdeQ== | 
| 1769 |  |  |  |  |  |  | </SignatureValue> | 
| 1770 |  |  |  |  |  |  | <KeyInfo> | 
| 1771 |  |  |  |  |  |  | <KeyValue> | 
| 1772 |  |  |  |  |  |  | <RSAKeyValue> | 
| 1773 |  |  |  |  |  |  | <Modulus> | 
| 1774 |  |  |  |  |  |  | 1b+m37u3Xyawh2ArV8txLei251p03CXbkVuWaJu9C8eHy1pu87bcthi+T5WdlCPKD7KGtkKn9vq | 
| 1775 |  |  |  |  |  |  | i4BJBZcG/Y10e8KWVlXDLg9gibN5hb0Agae3i1cCJTqqnQ0Ka8w1XABtbxTimS1B0aO1zYW6d+U | 
| 1776 |  |  |  |  |  |  | Yl0xIeAOPsGMfWeu1NgLChZQton1/NrJsKwzMaQy1VI8m4gUleit9Z8mbz9bNMshdgYEZ9oC4bH | 
| 1777 |  |  |  |  |  |  | n/SnA4FvQl1fjWyTpzL/aWF/bEzS6Qd8IBk7yhcWRJAGdXTWtwiX4mXb4h/2sdrSNvyOsd/shCf | 
| 1778 |  |  |  |  |  |  | OSMsf0TX+OdlbH079AsxOwoUjlzjuKdCiFPdU6yAJw== | 
| 1779 |  |  |  |  |  |  | </Modulus> | 
| 1780 |  |  |  |  |  |  | <Exponent>Iw==</Exponent> | 
| 1781 |  |  |  |  |  |  | </RSAKeyValue> | 
| 1782 |  |  |  |  |  |  | </KeyValue> | 
| 1783 |  |  |  |  |  |  | </KeyInfo> | 
| 1784 |  |  |  |  |  |  | </Signature> | 
| 1785 |  |  |  |  |  |  | </foo> | 
| 1786 |  |  |  |  |  |  |  | 
| 1787 |  |  |  |  |  |  | =head1 SEE ALSO | 
| 1788 |  |  |  |  |  |  |  | 
| 1789 |  |  |  |  |  |  | L<http://www.w3.org/TR/xmldsig-core/> | 
| 1790 |  |  |  |  |  |  |  | 
| 1791 |  |  |  |  |  |  | =head1 VERSION CONTROL | 
| 1792 |  |  |  |  |  |  |  | 
| 1793 |  |  |  |  |  |  | L<https://github.com/perl-net-saml2/perl-XML-Sig> | 
| 1794 |  |  |  |  |  |  |  | 
| 1795 |  |  |  |  |  |  | =head1 AUTHORS and CREDITS | 
| 1796 |  |  |  |  |  |  |  | 
| 1797 |  |  |  |  |  |  | Author: Byrne Reese <byrne@majordojo.com> | 
| 1798 |  |  |  |  |  |  |  | 
| 1799 |  |  |  |  |  |  | Thanks to Manni Heumann who wrote Google::SAML::Response from | 
| 1800 |  |  |  |  |  |  | which this module borrows heavily in order to create digital | 
| 1801 |  |  |  |  |  |  | signatures. | 
| 1802 |  |  |  |  |  |  |  | 
| 1803 |  |  |  |  |  |  | Net::SAML2 embedded version amended by Chris Andrews <chris@nodnol.org>. | 
| 1804 |  |  |  |  |  |  |  | 
| 1805 |  |  |  |  |  |  | Maintainer: Timothy Legge <timlegge@cpan.org> | 
| 1806 |  |  |  |  |  |  |  | 
| 1807 |  |  |  |  |  |  | =head1 AUTHOR | 
| 1808 |  |  |  |  |  |  |  | 
| 1809 |  |  |  |  |  |  | Chris Andrews  <chrisa@cpan.org> | 
| 1810 |  |  |  |  |  |  |  | 
| 1811 |  |  |  |  |  |  | =head1 COPYRIGHT AND LICENSE | 
| 1812 |  |  |  |  |  |  |  | 
| 1813 |  |  |  |  |  |  | This software is copyright (c) 2021 by Chris Andrews and Others, see the git log. | 
| 1814 |  |  |  |  |  |  |  | 
| 1815 |  |  |  |  |  |  | This is free software; you can redistribute it and/or modify it under | 
| 1816 |  |  |  |  |  |  | the same terms as the Perl 5 programming language system itself. | 
| 1817 |  |  |  |  |  |  |  | 
| 1818 |  |  |  |  |  |  | =cut | 
| 1819 |  |  |  |  |  |  |  | 
| 1820 |  |  |  |  |  |  | __END__ | 
| 1821 |  |  |  |  |  |  |  | 
| 1822 |  |  |  |  |  |  | } | 
| 1823 |  |  |  |  |  |  |  |