Skip to content

Commit 6b648a8

Browse files
authored
Merge pull request #178 from waterkip/GL-decrypt-assertion_XML
Decrypt an assertion which has encrypted nodes via XML::Enc
2 parents 800470f + 9d389f1 commit 6b648a8

File tree

5 files changed

+51
-39
lines changed

5 files changed

+51
-39
lines changed

.github/workflows/linux.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
---
12
name: linux
23

34
on:

Makefile.PL

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ my %WriteMakefileArgs = (
4949
"URI::Escape" => 0,
5050
"URI::QueryParam" => 0,
5151
"URN::OASIS::SAML2" => "0.003",
52-
"XML::Enc" => "0.05",
52+
"XML::Enc" => "0.12",
5353
"XML::Generator" => "1.13",
5454
"XML::LibXML" => 0,
5555
"XML::LibXML::XPathContext" => 0,
@@ -131,7 +131,7 @@ my %FallbackPrereqs = (
131131
"URI::QueryParam" => 0,
132132
"URI::URL" => 0,
133133
"URN::OASIS::SAML2" => "0.003",
134-
"XML::Enc" => "0.05",
134+
"XML::Enc" => "0.12",
135135
"XML::Generator" => "1.13",
136136
"XML::LibXML" => 0,
137137
"XML::LibXML::XPathContext" => 0,

cpanfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ requires "URI::Encode" => "0";
3333
requires "URI::Escape" => "0";
3434
requires "URI::QueryParam" => "0";
3535
requires "URN::OASIS::SAML2" => "0.003";
36-
requires "XML::Enc" => "0.05";
36+
requires "XML::Enc" => "0.12";
3737
requires "XML::Generator" => "1.13";
3838
requires "XML::LibXML" => "0";
3939
requires "XML::LibXML::XPathContext" => "0";

dist.ini

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ copy = cpanfile, Makefile.PL, README, LICENSE, CONTRIBUTORS
4848
skip = Saml2Test
4949

5050
[Prereqs / RuntimeRequires]
51-
XML::Enc = 0.05
51+
XML::Enc = 0.12
5252
XML::Sig = 0.64
5353
XML::Writer = 0.625
5454
; Here because otherwise only on test you get to pull in this dependency

lib/Net/SAML2/Protocol/Assertion.pm

Lines changed: 46 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -89,60 +89,54 @@ EncryptedAssertion is properly validated.
8989
sub new_from_xml {
9090
my($class, %args) = @_;
9191

92-
my $dom = no_comments($args{xml});
9392
my $key_file = $args{key_file};
94-
my $cacert = $args{cacert};
93+
my $cacert = delete $args{cacert};
9594

96-
my $xpath = XML::LibXML::XPathContext->new($dom);
95+
my $xpath = XML::LibXML::XPathContext->new();
9796
$xpath->registerNs('saml', 'urn:oasis:names:tc:SAML:2.0:assertion');
9897
$xpath->registerNs('samlp', 'urn:oasis:names:tc:SAML:2.0:protocol');
99-
$xpath->registerNs('xenc', 'http://www.w3.org/2001/04/xmlenc#');
98+
$xpath->registerNs('dsig', 'http://www.w3.org/2000/09/xmldsig#');
99+
$xpath->registerNs('xenc', 'http://www.w3.org/2001/04/xmlenc#');
100100

101-
my $attributes = {};
101+
my $xml = no_comments($args{xml});
102+
$xpath->setContextNode($xml);
102103

103-
if ($xpath->findnodes('//saml:EncryptedAssertion')) {
104-
if ( ! defined $key_file) {
105-
die "Encrypted Assertions require key_file";
106-
}
107-
my $decrypted;
108-
my $enc = XML::Enc->new(
109-
{ key => $key_file , no_xml_declaration => 1 }, );
110-
$decrypted = $enc->decrypt($dom->toString());
111-
$dom = XML::LibXML->load_xml(string => $decrypted);
112-
$xpath = XML::LibXML::XPathContext->new($dom);
113-
$xpath->registerNs('saml', 'urn:oasis:names:tc:SAML:2.0:assertion');
114-
$xpath->registerNs('samlp', 'urn:oasis:names:tc:SAML:2.0:protocol');
115-
$xpath->registerNs('dsig', 'http://www.w3.org/2000/09/xmldsig#');
116-
$xpath->registerNs('xenc', 'http://www.w3.org/2001/04/xmlenc#');
117-
118-
my $xml_opts->{ no_xml_declaration } = 1;
104+
my $dom = $xml;
105+
106+
if ($xpath->exists('//saml:EncryptedAssertion')) {
107+
108+
croak "Encrypted Assertions require key_file" if !defined $key_file;
119109

120110
my $assert = $xpath->findnodes('//saml:Assertion')->[0];
121111
my @signedinfo = $xpath->findnodes('dsig:Signature', $assert);
122112

123113
if (defined $assert && (scalar @signedinfo ne 0)) {
114+
my $xml_opts->{ no_xml_declaration } = 1;
124115
my $x = Net::SAML2::XML::Sig->new($xml_opts);
125116
my $ret = $x->verify($assert->serialize);
126117
die "Decrypted Assertion signature check failed" unless $ret;
127118

128-
if ($cacert) {
129-
my $cert = $x->signer_cert
130-
or die "Certificate not provided and not in SAML Response, cannot validate";
119+
return unless $cacert;
120+
my $cert = $x->signer_cert
121+
or die "Certificate not provided and not in SAML Response, cannot validate";
131122

132-
my $ca = Crypt::OpenSSL::Verify->new($cacert, { strict_certs => 0, });
133-
if (! $ca->verify($cert)) {
134-
die "Decrypted Assertion - Unable to verify signer cert with cacert: $cert->subject";
135-
}
136-
}
123+
my $ca = Crypt::OpenSSL::Verify->new($cacert, { strict_certs => 0 });
124+
die "Unable to verify signer cert with cacert: " . $cert->subject
125+
unless $ca->verify($cert);
137126
}
138127
}
139128

140-
for my $node (
141-
$xpath->findnodes('//saml:Assertion/saml:AttributeStatement/saml:Attribute'))
129+
my $dec = $class->_decrypt(
130+
$xml,
131+
key_file => $key_file,
132+
key_name => $args{key_name}
133+
);
134+
$xpath->setContextNode($dec);
135+
136+
my $attributes = {};
137+
for my $node ($xpath->findnodes('//saml:Assertion/saml:AttributeStatement/saml:Attribute/saml:AttributeValue/..'))
142138
{
143-
# We can't select by saml:AttributeValue
144-
# because of https://rt.cpan.org/Public/Bug/Display.html?id=8784
145-
my @values = $node->findnodes("*[local-name()='AttributeValue']");
139+
my @values = $xpath->findnodes("saml:AttributeValue", $node);
146140
$attributes->{$node->getAttribute('Name')} = [map $_->string_value, @values];
147141
}
148142

@@ -171,7 +165,7 @@ sub new_from_xml {
171165
}
172166

173167
my $nameid;
174-
if (my $node = $xpath->findnodes('//samlp:Response/saml:Assertion/saml:Subject/ saml:NameID')) {
168+
if (my $node = $xpath->findnodes('/samlp:Response/saml:Assertion/saml:Subject/saml:NameID')) {
175169
$nameid = $node->get_node(1);
176170
}
177171
elsif (my $global = $xpath->findnodes('//saml:Subject/saml:NameID')) {
@@ -209,6 +203,7 @@ sub new_from_xml {
209203
return $self;
210204
}
211205

206+
212207
=head2 response_status
213208
214209
Returns the response status
@@ -335,4 +330,20 @@ sub success {
335330
return 0;
336331
}
337332

333+
sub _decrypt {
334+
my $self = shift;
335+
my $xml = shift;
336+
my %options = @_;
337+
338+
return $xml unless $options{key_file};
339+
340+
my $enc = XML::Enc->new(
341+
{
342+
no_xml_declaration => 1,
343+
key => $options{key_file},
344+
}
345+
);
346+
return XML::LibXML->load_xml(string => $enc->decrypt($xml, %options));
347+
}
348+
338349
1;

0 commit comments

Comments
 (0)