ruby-saml 0.5.3 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of ruby-saml might be problematic. Click here for more details.

@@ -26,7 +26,7 @@ require 'rubygems'
26
26
  require "rexml/document"
27
27
  require "rexml/xpath"
28
28
  require "openssl"
29
- require "xmlcanonicalizer"
29
+ require 'nokogiri'
30
30
  require "digest/sha1"
31
31
  require "digest/sha2"
32
32
  require "onelogin/ruby-saml/validation_error"
@@ -34,9 +34,10 @@ require "onelogin/ruby-saml/validation_error"
34
34
  module XMLSecurity
35
35
 
36
36
  class SignedDocument < REXML::Document
37
+ C14N = "http://www.w3.org/2001/10/xml-exc-c14n#"
37
38
  DSIG = "http://www.w3.org/2000/09/xmldsig#"
38
39
 
39
- attr_accessor :signed_element_id
40
+ attr_accessor :signed_element_id, :sig_element, :noko_sig_element
40
41
 
41
42
  def initialize(response)
42
43
  super(response)
@@ -45,9 +46,10 @@ module XMLSecurity
45
46
 
46
47
  def validate(idp_cert_fingerprint, soft = true)
47
48
  # get cert from response
48
- base64_cert = REXML::XPath.first(self, "//ds:X509Certificate").text
49
- cert_text = Base64.decode64(base64_cert)
50
- cert = OpenSSL::X509::Certificate.new(cert_text)
49
+ cert_element = REXML::XPath.first(self, "//ds:X509Certificate", { "ds"=>DSIG })
50
+ base64_cert = cert_element.text
51
+ cert_text = Base64.decode64(base64_cert)
52
+ cert = OpenSSL::X509::Certificate.new(cert_text)
51
53
 
52
54
  # check cert matches registered idp cert
53
55
  fingerprint = Digest::SHA1.hexdigest(cert.to_der)
@@ -63,40 +65,43 @@ module XMLSecurity
63
65
  # validate references
64
66
 
65
67
  # check for inclusive namespaces
68
+ inclusive_namespaces = extract_inclusive_namespaces
66
69
 
67
- inclusive_namespaces = []
68
- inclusive_namespace_element = REXML::XPath.first(self, "//ec:InclusiveNamespaces")
70
+ document = Nokogiri.parse(self.to_s)
69
71
 
70
- if inclusive_namespace_element
71
- prefix_list = inclusive_namespace_element.attributes.get_attribute('PrefixList').value
72
- inclusive_namespaces = prefix_list.split(" ")
72
+ # store and remove signature node
73
+ self.sig_element ||= begin
74
+ element = REXML::XPath.first(self, "//ds:Signature", {"ds"=>DSIG})
75
+ element.remove
73
76
  end
74
77
 
75
- # remove signature node
76
- sig_element = REXML::XPath.first(self, "//ds:Signature", {"ds"=>DSIG})
77
- sig_element.remove
78
+
79
+ # verify signature
80
+ signed_info_element = REXML::XPath.first(sig_element, "//ds:SignedInfo", {"ds"=>DSIG})
81
+ self.noko_sig_element ||= document.at_xpath('//ds:Signature', 'ds' => DSIG)
82
+ noko_signed_info_element = noko_sig_element.at_xpath('./ds:SignedInfo', 'ds' => DSIG)
83
+ canon_algorithm = canon_algorithm REXML::XPath.first(sig_element, '//ds:CanonicalizationMethod')
84
+ canon_string = noko_signed_info_element.canonicalize(canon_algorithm)
85
+ noko_sig_element.remove
78
86
 
79
87
  # check digests
80
88
  REXML::XPath.each(sig_element, "//ds:Reference", {"ds"=>DSIG}) do |ref|
81
89
  uri = ref.attributes.get_attribute("URI").value
82
- hashed_element = REXML::XPath.first(self, "//[@ID='#{uri[1..-1]}']")
83
- canoner = XML::Util::XmlCanonicalizer.new(false, true)
84
- canoner.inclusive_namespaces = inclusive_namespaces if canoner.respond_to?(:inclusive_namespaces) && !inclusive_namespaces.empty?
85
- canon_hashed_element = canoner.canonicalize(hashed_element).gsub('&','&amp;')
86
- algorithm = digest_algorithm(REXML::XPath.first(ref, "//ds:DigestMethod"))
87
- hash = Base64.encode64(algorithm.digest(canon_hashed_element)).chomp
88
- digest_value = REXML::XPath.first(ref, "//ds:DigestValue", {"ds"=>DSIG}).text
90
+
91
+ hashed_element = document.at_xpath("//*[@ID='#{uri[1..-1]}']")
92
+ canon_algorithm = canon_algorithm REXML::XPath.first(ref, '//ds:CanonicalizationMethod')
93
+ canon_hashed_element = hashed_element.canonicalize(canon_algorithm, inclusive_namespaces).gsub('&','&amp;')
94
+
95
+ digest_algorithm = algorithm(REXML::XPath.first(ref, "//ds:DigestMethod"))
96
+
97
+ hash = digest_algorithm.digest(canon_hashed_element)
98
+ digest_value = Base64.decode64(REXML::XPath.first(ref, "//ds:DigestValue", {"ds"=>DSIG}).text)
89
99
 
90
100
  unless digests_match?(hash, digest_value)
91
101
  return soft ? false : (raise Onelogin::Saml::ValidationError.new("Digest mismatch"))
92
102
  end
93
103
  end
94
104
 
95
- # verify signature
96
- canoner = XML::Util::XmlCanonicalizer.new(false, true)
97
- signed_info_element = REXML::XPath.first(sig_element, "//ds:SignedInfo", {"ds"=>DSIG})
98
- canon_string = canoner.canonicalize(signed_info_element)
99
-
100
105
  base64_signature = REXML::XPath.first(sig_element, "//ds:SignatureValue", {"ds"=>DSIG}).text
101
106
  signature = Base64.decode64(base64_signature)
102
107
 
@@ -105,10 +110,10 @@ module XMLSecurity
105
110
  cert = OpenSSL::X509::Certificate.new(cert_text)
106
111
 
107
112
  # signature method
108
- algorithm = signature_algorithm(REXML::XPath.first(signed_info_element, "//ds:SignatureMethod"))
113
+ signature_algorithm = algorithm(REXML::XPath.first(signed_info_element, "//ds:SignatureMethod", {"ds"=>DSIG}))
109
114
 
110
- if !cert.public_key.verify(algorithm.new, signature, canon_string)
111
- return soft ? false : (raise ValidationError.new("Key validation error"))
115
+ unless cert.public_key.verify(signature_algorithm.new, signature, canon_string)
116
+ return soft ? false : (raise Onelogin::Saml::ValidationError.new("Key validation error"))
112
117
  end
113
118
 
114
119
  return true
@@ -125,17 +130,19 @@ module XMLSecurity
125
130
  self.signed_element_id = reference_element.attribute("URI").value[1..-1] unless reference_element.nil?
126
131
  end
127
132
 
128
- def digest_algorithm(element)
129
- algorithm = element.attribute("Algorithm").value if element
130
- algorithm && algorithm =~ /sha(256|384|512)$/ ? Digest::SHA2 : Digest::SHA1
133
+ def canon_algorithm(element)
134
+ algorithm = element.attribute('Algorithm').value if element
135
+ case algorithm
136
+ when "http://www.w3.org/2001/10/xml-exc-c14n#" then Nokogiri::XML::XML_C14N_EXCLUSIVE_1_0
137
+ when "http://www.w3.org/TR/2001/REC-xml-c14n-20010315" then Nokogiri::XML::XML_C14N_1_0
138
+ when "http://www.w3.org/2006/12/xml-c14n11" then Nokogiri::XML::XML_C14N_1_1
139
+ else Nokogiri::XML::XML_C14N_EXCLUSIVE_1_0
140
+ end
131
141
  end
132
142
 
133
- def signature_algorithm(element)
143
+ def algorithm(element)
134
144
  algorithm = element.attribute("Algorithm").value if element
135
- if algorithm
136
- algorithm =~ /sha(.*?)$/i
137
- algorithm = $1.to_i
138
- end
145
+ algorithm = algorithm && algorithm =~ /sha(.*?)$/i && $1.to_i
139
146
  case algorithm
140
147
  when 256 then OpenSSL::Digest::SHA256
141
148
  when 384 then OpenSSL::Digest::SHA384
@@ -145,5 +152,14 @@ module XMLSecurity
145
152
  end
146
153
  end
147
154
 
155
+ def extract_inclusive_namespaces
156
+ if element = REXML::XPath.first(self, "//ec:InclusiveNamespaces", { "ec" => C14N })
157
+ prefix_list = element.attributes.get_attribute("PrefixList").value
158
+ prefix_list.split(" ")
159
+ else
160
+ []
161
+ end
162
+ end
163
+
148
164
  end
149
165
  end
@@ -12,7 +12,7 @@ Gem::Specification.new do |s|
12
12
  s.email = %q{support@onelogin.com}
13
13
  s.extra_rdoc_files = [
14
14
  "LICENSE",
15
- "README.rdoc"
15
+ "README.md"
16
16
  ]
17
17
  s.files = `git ls-files`.split("\n")
18
18
  s.homepage = %q{http://github.com/onelogin/ruby-saml}
@@ -23,29 +23,7 @@ Gem::Specification.new do |s|
23
23
  s.summary = %q{SAML Ruby Tookit}
24
24
  s.test_files = `git ls-files test/*`.split("\n")
25
25
 
26
- if s.respond_to? :specification_version then
27
- current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
28
- s.specification_version = 3
29
-
30
- if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
31
- s.add_runtime_dependency(%q<canonix>, ["~> 0.1"])
32
- s.add_runtime_dependency(%q<uuid>, ["~> 2.3"])
33
- s.add_development_dependency(%q<shoulda>, [">= 0"])
34
- s.add_development_dependency(%q<ruby-debug>, [">= 0"])
35
- s.add_development_dependency(%q<mocha>, [">= 0"])
36
- else
37
- s.add_dependency(%q<canonix>, ["~> 0.1"])
38
- s.add_dependency(%q<uuid>, ["~> 2.3"])
39
- s.add_dependency(%q<shoulda>, [">= 0"])
40
- s.add_dependency(%q<ruby-debug>, [">= 0"])
41
- s.add_dependency(%q<mocha>, [">= 0"])
42
- end
43
- else
44
- s.add_dependency(%q<canonix>, ["~> 0.1"])
45
- s.add_dependency(%q<uuid>, ["~> 2.3"])
46
- s.add_dependency(%q<shoulda>, [">= 0"])
47
- s.add_dependency(%q<ruby-debug>, [">= 0"])
48
- s.add_dependency(%q<mocha>, [">= 0"])
49
- end
26
+ s.add_runtime_dependency("canonix", ["0.1.1"])
27
+ s.add_runtime_dependency("uuid", ["~> 2.3"])
28
+ s.add_runtime_dependency("nokogiri")
50
29
  end
51
-
@@ -0,0 +1,98 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), "test_helper"))
2
+
3
+ class RequestTest < Test::Unit::TestCase
4
+
5
+ context "Logoutrequest" do
6
+ settings = Onelogin::Saml::Settings.new
7
+
8
+ should "create the deflated SAMLRequest URL parameter" do
9
+ settings.idp_slo_target_url = "http://unauth.com/logout"
10
+ unauth_url = Onelogin::Saml::Logoutrequest.new.create(settings)
11
+ assert unauth_url =~ /^http:\/\/unauth\.com\/logout\?SAMLRequest=/
12
+
13
+ inflated = decode_saml_request_payload(unauth_url)
14
+
15
+ assert_match /^<samlp:LogoutRequest/, inflated
16
+ end
17
+
18
+ should "support additional params" do
19
+
20
+ unauth_url = Onelogin::Saml::Logoutrequest.new.create(settings, { :hello => nil })
21
+ assert unauth_url =~ /&hello=$/
22
+
23
+ unauth_url = Onelogin::Saml::Logoutrequest.new.create(settings, { :foo => "bar" })
24
+ assert unauth_url =~ /&foo=bar$/
25
+ end
26
+
27
+ should "set sessionindex" do
28
+ settings.idp_slo_target_url = "http://example.com"
29
+ sessionidx = UUID.new.generate
30
+ settings.sessionindex = sessionidx
31
+
32
+ unauth_url = Onelogin::Saml::Logoutrequest.new.create(settings, { :name_id => "there" })
33
+ inflated = decode_saml_request_payload(unauth_url)
34
+
35
+ assert_match /<samlp:SessionIndex/, inflated
36
+ assert_match %r(#{sessionidx}</samlp:SessionIndex>), inflated
37
+ end
38
+
39
+ should "set name_identifier_value" do
40
+ settings = Onelogin::Saml::Settings.new
41
+ settings.idp_slo_target_url = "http://example.com"
42
+ settings.name_identifier_format = "transient"
43
+ name_identifier_value = "abc123"
44
+ settings.name_identifier_value = name_identifier_value
45
+
46
+ unauth_url = Onelogin::Saml::Logoutrequest.new.create(settings, { :name_id => "there" })
47
+ inflated = decode_saml_request_payload(unauth_url)
48
+
49
+ assert_match /<saml:NameID/, inflated
50
+ assert_match %r(#{name_identifier_value}</saml:NameID>), inflated
51
+ end
52
+
53
+ context "when the target url doesn't contain a query string" do
54
+ should "create the SAMLRequest parameter correctly" do
55
+ settings = Onelogin::Saml::Settings.new
56
+ settings.idp_slo_target_url = "http://example.com"
57
+
58
+ unauth_url = Onelogin::Saml::Logoutrequest.new.create(settings)
59
+ assert unauth_url =~ /^http:\/\/example.com\?SAMLRequest/
60
+ end
61
+ end
62
+
63
+ context "when the target url contains a query string" do
64
+ should "create the SAMLRequest parameter correctly" do
65
+ settings = Onelogin::Saml::Settings.new
66
+ settings.idp_slo_target_url = "http://example.com?field=value"
67
+
68
+ unauth_url = Onelogin::Saml::Logoutrequest.new.create(settings)
69
+ assert unauth_url =~ /^http:\/\/example.com\?field=value&SAMLRequest/
70
+ end
71
+ end
72
+
73
+ context "consumation of logout may need to track the transaction" do
74
+ should "have access to the request uuid" do
75
+ settings = Onelogin::Saml::Settings.new
76
+ settings.idp_slo_target_url = "http://example.com?field=value"
77
+
78
+ unauth_req = Onelogin::Saml::Logoutrequest.new
79
+ unauth_url = unauth_req.create(settings)
80
+
81
+ inflated = decode_saml_request_payload(unauth_url)
82
+ assert_match %r[ID='#{unauth_req.uuid}'], inflated
83
+ end
84
+ end
85
+ end
86
+
87
+ def decode_saml_request_payload(unauth_url)
88
+ payload = CGI.unescape(unauth_url.split("SAMLRequest=").last)
89
+ decoded = Base64.decode64(payload)
90
+
91
+ zstream = Zlib::Inflate.new(-Zlib::MAX_WBITS)
92
+ inflated = zstream.inflate(decoded)
93
+ zstream.finish
94
+ zstream.close
95
+ inflated
96
+ end
97
+
98
+ end
@@ -27,6 +27,12 @@ class RubySamlTest < Test::Unit::TestCase
27
27
  assert !response.name_id.nil?
28
28
  end
29
29
 
30
+ should "default to raw input when a response is not Base64 encoded" do
31
+ decoded = Base64.decode64(response_document_2)
32
+ response = Onelogin::Saml::Response.new(decoded)
33
+ assert response.document
34
+ end
35
+
30
36
  context "Assertion" do
31
37
  should "only retreive an assertion with an ID that matches the signature's reference URI" do
32
38
  response = Onelogin::Saml::Response.new(wrapped_response_2)
@@ -89,14 +95,34 @@ class RubySamlTest < Test::Unit::TestCase
89
95
  assert response.name_id == "test@onelogin.com"
90
96
  end
91
97
 
98
+ should "support dynamic namespace resolution on signature elements" do
99
+ response = Onelogin::Saml::Response.new(fixture("no_signature_ns.xml"))
100
+ response.stubs(:conditions).returns(nil)
101
+ settings = Onelogin::Saml::Settings.new
102
+ response.settings = settings
103
+ settings.idp_cert_fingerprint = "28:74:9B:E8:1F:E8:10:9C:A8:7C:A9:C3:E3:C5:01:6C:92:1C:B4:BA"
104
+ XMLSecurity::SignedDocument.any_instance.expects(:validate_doc).returns(true)
105
+ assert response.validate!
106
+ end
107
+
92
108
  should "validate ADFS assertions" do
93
- response = Onelogin::Saml::Response.new(fixture(:adfs_response))
109
+ response = Onelogin::Saml::Response.new(fixture(:adfs_response_sha256))
94
110
  response.stubs(:conditions).returns(nil)
95
111
  settings = Onelogin::Saml::Settings.new
96
112
  settings.idp_cert_fingerprint = "28:74:9B:E8:1F:E8:10:9C:A8:7C:A9:C3:E3:C5:01:6C:92:1C:B4:BA"
97
113
  response.settings = settings
98
114
  assert response.validate!
99
115
  end
116
+
117
+ should "validate SAML 2.0 XML structure" do
118
+ resp_xml = Base64.decode64(response_document_4).gsub(/emailAddress/,'test')
119
+ response = Onelogin::Saml::Response.new(Base64.encode64(resp_xml))
120
+ response.stubs(:conditions).returns(nil)
121
+ settings = Onelogin::Saml::Settings.new
122
+ settings.idp_cert_fingerprint = signature_fingerprint_1
123
+ response.settings = settings
124
+ assert_raises(Onelogin::Saml::ValidationError, 'Digest mismatch'){ response.validate! }
125
+ end
100
126
  end
101
127
 
102
128
  context "#name_id" do
@@ -171,11 +197,23 @@ class RubySamlTest < Test::Unit::TestCase
171
197
  end
172
198
 
173
199
  context "#issuer" do
174
- should "return the issuer of the assertion" do
200
+ should "return the issuer inside the response assertion" do
201
+ response = Onelogin::Saml::Response.new(response_document)
202
+ assert_equal "https://app.onelogin.com/saml/metadata/13590", response.issuer
203
+ end
204
+
205
+ should "return the issuer inside the response" do
175
206
  response = Onelogin::Saml::Response.new(response_document_2)
176
207
  assert_equal "wibble", response.issuer
177
208
  end
178
209
  end
210
+
211
+ context "#success" do
212
+ should "find a status code that says success" do
213
+ response = Onelogin::Saml::Response.new(response_document)
214
+ response.success?
215
+ end
216
+ end
179
217
 
180
218
  end
181
219
  end
@@ -0,0 +1,46 @@
1
+ <?xml version="1.0"?>
2
+ <samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" ID="_0263a07b-205f-479c-90fc-7495715ecbbf" Version="2.0" IssueInstant="2011-06-22T12:49:30.348Z" Destination="https://someone.example.com/endpoint" Consent="urn:oasis:names:tc:SAML:2.0:consent:unspecified" InResponseTo="_fc4a34b0-7efb-012e-caae-782bcb13bb38">
3
+ <Issuer xmlns="urn:oasis:names:tc:SAML:2.0:assertion">http://login.example.com/issuer</Issuer>
4
+ <samlp:Status>
5
+ <samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/>
6
+ </samlp:Status>
7
+ <Assertion xmlns="urn:oasis:names:tc:SAML:2.0:assertion" ID="_721b4a5a-d7e1-4861-9754-a9b197b6f9ab" IssueInstant="2011-06-22T12:49:30.348Z" Version="2.0">
8
+ <Issuer>http://login.example.com/issuer</Issuer>
9
+ <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
10
+ <ds:SignedInfo>
11
+ <ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
12
+ <ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha1"/>
13
+ <ds:Reference URI="#_721b4a5a-d7e1-4861-9754-a9b197b6f9ab">
14
+ <ds:Transforms>
15
+ <ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
16
+ <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
17
+ </ds:Transforms>
18
+ <ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha1"/>
19
+ <ds:DigestValue>tGpkynNC34A5SFqDSfXmPSiIGpU=</ds:DigestValue>
20
+ </ds:Reference>
21
+ </ds:SignedInfo>
22
+ <ds:SignatureValue>WXtmslqh2npLtwhvU8yVx0pvH7E1s8ASksv7VtWirQDFrRRO9k+sNnQcGzA75QNyd6nP+T2e+ofIWyj8G70Rd6gEU4ZmV1vlGVq49Ilc7r/oxauitIuasOvrmpyHCXRbttYeWz4T5xoTCDx9RZQvI4fdrFugrymFT2OREFx1lSk=</ds:SignatureValue>
23
+ <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
24
+ <ds:X509Data>
25
+ <ds:X509Certificate>LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURxekNDQXhTZ0F3SUJBZ0lCQVRBTkJna3Foa2lHOXcwQkFRc0ZBRENCaGpFTE1Ba0dBMVVFQmhNQ1FWVXgKRERBS0JnTlZCQWdUQTA1VFZ6RVBNQTBHQTFVRUJ4TUdVM2xrYm1WNU1Rd3dDZ1lEVlFRS0RBTlFTVlF4Q1RBSApCZ05WQkFzTUFERVlNQllHQTFVRUF3d1BiR0YzY21WdVkyVndhWFF1WTI5dE1TVXdJd1lKS29aSWh2Y05BUWtCCkRCWnNZWGR5Wlc1alpTNXdhWFJBWjIxaGFXd3VZMjl0TUI0WERURXlNRFF4T1RJeU5UUXhPRm9YRFRNeU1EUXgKTkRJeU5UUXhPRm93Z1lZeEN6QUpCZ05WQkFZVEFrRlZNUXd3Q2dZRFZRUUlFd05PVTFjeER6QU5CZ05WQkFjVApCbE41Wkc1bGVURU1NQW9HQTFVRUNnd0RVRWxVTVFrd0J3WURWUVFMREFBeEdEQVdCZ05WQkFNTUQyeGhkM0psCmJtTmxjR2wwTG1OdmJURWxNQ01HQ1NxR1NJYjNEUUVKQVF3V2JHRjNjbVZ1WTJVdWNHbDBRR2R0WVdsc0xtTnYKYlRDQm56QU5CZ2txaGtpRzl3MEJBUUVGQUFPQmpRQXdnWWtDZ1lFQXFqaWUzUjJvaStwRGFldndJeXMvbWJVVApubkdsa3h0ZGlrcnExMXZleHd4SmlQTmhtaHFSVzNtVXVKRXpsbElkVkw2RW14R1lUcXBxZjkzSGxoa3NhZUowCjhVZ2pQOVVtTVlyaFZKdTFqY0ZXVjdmei9yKzIxL2F3VG5EVjlzTVlRcXVJUllZeTdiRzByMU9iaXdkb3ZudGsKN2dGSTA2WjB2WmFjREU1Ym9xVUNBd0VBQWFPQ0FTVXdnZ0VoTUFrR0ExVWRFd1FDTUFBd0N3WURWUjBQQkFRRApBZ1VnTUIwR0ExVWREZ1FXQkJTUk9OOEdKOG8rOGpnRnRqa3R3WmRxeDZCUnlUQVRCZ05WSFNVRUREQUtCZ2dyCkJnRUZCUWNEQVRBZEJnbGdoa2dCaHZoQ0FRMEVFQllPVkdWemRDQllOVEE1SUdObGNuUXdnYk1HQTFVZEl3U0IKcXpDQnFJQVVrVGpmQmlmS1B2STRCYlk1TGNHWGFzZWdVY21oZ1l5a2dZa3dnWVl4Q3pBSkJnTlZCQVlUQWtGVgpNUXd3Q2dZRFZRUUlFd05PVTFjeER6QU5CZ05WQkFjVEJsTjVaRzVsZVRFTU1Bb0dBMVVFQ2d3RFVFbFVNUWt3CkJ3WURWUVFMREFBeEdEQVdCZ05WQkFNTUQyeGhkM0psYm1ObGNHbDBMbU52YlRFbE1DTUdDU3FHU0liM0RRRUoKQVF3V2JHRjNjbVZ1WTJVdWNHbDBRR2R0WVdsc0xtTnZiWUlCQVRBTkJna3Foa2lHOXcwQkFRc0ZBQU9CZ1FDRQpUQWVKVERTQVc2ejFVRlRWN1FyZWg0VUxGT1JhajkrZUN1RjNLV0RIYyswSVFDajlyZG5ERzRRL3dmNy9yYVEwCkpuUFFDU0NkclBMSmV5b1BIN1FhVHdvYUY3ZHpWdzRMQ3N5TkpURld4NGNNNTBWdzZSNWZET2dpQzhic2ZmUzgKQkptb3VscnJaRE5OVmpHOG1XNmNMeHJZdlZRT3JSVmVjQ0ZJZ3NzQ2JBPT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=</ds:X509Certificate>
26
+ </ds:X509Data>
27
+ </KeyInfo>
28
+ </ds:Signature>
29
+ <Subject>
30
+ <NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress">hello@example.com</NameID>
31
+ <SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
32
+ <SubjectConfirmationData InResponseTo="_fc4a34b0-7efb-012e-caae-782bcb13bb38" NotOnOrAfter="2011-06-22T12:54:30.348Z" Recipient="https://someone.example.com/endpoint"/>
33
+ </SubjectConfirmation>
34
+ </Subject>
35
+ <Conditions NotBefore="2011-06-22T12:49:30.332Z" NotOnOrAfter="2011-06-22T13:49:30.332Z">
36
+ <AudienceRestriction>
37
+ <Audience>example.com</Audience>
38
+ </AudienceRestriction>
39
+ </Conditions>
40
+ <AuthnStatement AuthnInstant="2011-06-22T12:49:30.112Z" SessionIndex="_721b4a5a-d7e1-4861-9754-a9b197b6f9ab">
41
+ <AuthnContext>
42
+ <AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</AuthnContextClassRef>
43
+ </AuthnContext>
44
+ </AuthnStatement>
45
+ </Assertion>
46
+ </samlp:Response>
@@ -0,0 +1,46 @@
1
+ <?xml version="1.0"?>
2
+ <samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" ID="_0263a07b-205f-479c-90fc-7495715ecbbf" Version="2.0" IssueInstant="2011-06-22T12:49:30.348Z" Destination="https://someone.example.com/endpoint" Consent="urn:oasis:names:tc:SAML:2.0:consent:unspecified" InResponseTo="_fc4a34b0-7efb-012e-caae-782bcb13bb38">
3
+ <Issuer xmlns="urn:oasis:names:tc:SAML:2.0:assertion">http://login.example.com/issuer</Issuer>
4
+ <samlp:Status>
5
+ <samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/>
6
+ </samlp:Status>
7
+ <Assertion xmlns="urn:oasis:names:tc:SAML:2.0:assertion" ID="_721b4a5a-d7e1-4861-9754-a9b197b6f9ab" IssueInstant="2011-06-22T12:49:30.348Z" Version="2.0">
8
+ <Issuer>http://login.example.com/issuer</Issuer>
9
+ <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
10
+ <ds:SignedInfo>
11
+ <ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
12
+ <ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha384"/>
13
+ <ds:Reference URI="#_721b4a5a-d7e1-4861-9754-a9b197b6f9ab">
14
+ <ds:Transforms>
15
+ <ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
16
+ <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
17
+ </ds:Transforms>
18
+ <ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha384"/>
19
+ <ds:DigestValue>XU0mb78TVA+VwcA71jxe5osjiOzOP/OwDcJ8t/mn2d9+/V2zxejEo9+fkSY2ZR0Z</ds:DigestValue>
20
+ </ds:Reference>
21
+ </ds:SignedInfo>
22
+ <ds:SignatureValue>bq1zDllmAFzx0O3HAAoedSqQIl/n2+mK2Vx1pK0/yEpuc84ovwmau/ZfHk3MFNQjuxL+JmlO7I3c6CEmOGeAupFTpnFGkRfJGSu6ilvcL4yasPq80LNEcCYhApiEW2pJXs5t3sfOdG2MJHTuMvz4MtnrLd9Cuf/EQK2a27HDrB4=</ds:SignatureValue>
23
+ <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
24
+ <ds:X509Data>
25
+ <ds:X509Certificate>LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURxekNDQXhTZ0F3SUJBZ0lCQVRBTkJna3Foa2lHOXcwQkFRc0ZBRENCaGpFTE1Ba0dBMVVFQmhNQ1FWVXgKRERBS0JnTlZCQWdUQTA1VFZ6RVBNQTBHQTFVRUJ4TUdVM2xrYm1WNU1Rd3dDZ1lEVlFRS0RBTlFTVlF4Q1RBSApCZ05WQkFzTUFERVlNQllHQTFVRUF3d1BiR0YzY21WdVkyVndhWFF1WTI5dE1TVXdJd1lKS29aSWh2Y05BUWtCCkRCWnNZWGR5Wlc1alpTNXdhWFJBWjIxaGFXd3VZMjl0TUI0WERURXlNRFF4T1RJeU5UUXhPRm9YRFRNeU1EUXgKTkRJeU5UUXhPRm93Z1lZeEN6QUpCZ05WQkFZVEFrRlZNUXd3Q2dZRFZRUUlFd05PVTFjeER6QU5CZ05WQkFjVApCbE41Wkc1bGVURU1NQW9HQTFVRUNnd0RVRWxVTVFrd0J3WURWUVFMREFBeEdEQVdCZ05WQkFNTUQyeGhkM0psCmJtTmxjR2wwTG1OdmJURWxNQ01HQ1NxR1NJYjNEUUVKQVF3V2JHRjNjbVZ1WTJVdWNHbDBRR2R0WVdsc0xtTnYKYlRDQm56QU5CZ2txaGtpRzl3MEJBUUVGQUFPQmpRQXdnWWtDZ1lFQXFqaWUzUjJvaStwRGFldndJeXMvbWJVVApubkdsa3h0ZGlrcnExMXZleHd4SmlQTmhtaHFSVzNtVXVKRXpsbElkVkw2RW14R1lUcXBxZjkzSGxoa3NhZUowCjhVZ2pQOVVtTVlyaFZKdTFqY0ZXVjdmei9yKzIxL2F3VG5EVjlzTVlRcXVJUllZeTdiRzByMU9iaXdkb3ZudGsKN2dGSTA2WjB2WmFjREU1Ym9xVUNBd0VBQWFPQ0FTVXdnZ0VoTUFrR0ExVWRFd1FDTUFBd0N3WURWUjBQQkFRRApBZ1VnTUIwR0ExVWREZ1FXQkJTUk9OOEdKOG8rOGpnRnRqa3R3WmRxeDZCUnlUQVRCZ05WSFNVRUREQUtCZ2dyCkJnRUZCUWNEQVRBZEJnbGdoa2dCaHZoQ0FRMEVFQllPVkdWemRDQllOVEE1SUdObGNuUXdnYk1HQTFVZEl3U0IKcXpDQnFJQVVrVGpmQmlmS1B2STRCYlk1TGNHWGFzZWdVY21oZ1l5a2dZa3dnWVl4Q3pBSkJnTlZCQVlUQWtGVgpNUXd3Q2dZRFZRUUlFd05PVTFjeER6QU5CZ05WQkFjVEJsTjVaRzVsZVRFTU1Bb0dBMVVFQ2d3RFVFbFVNUWt3CkJ3WURWUVFMREFBeEdEQVdCZ05WQkFNTUQyeGhkM0psYm1ObGNHbDBMbU52YlRFbE1DTUdDU3FHU0liM0RRRUoKQVF3V2JHRjNjbVZ1WTJVdWNHbDBRR2R0WVdsc0xtTnZiWUlCQVRBTkJna3Foa2lHOXcwQkFRc0ZBQU9CZ1FDRQpUQWVKVERTQVc2ejFVRlRWN1FyZWg0VUxGT1JhajkrZUN1RjNLV0RIYyswSVFDajlyZG5ERzRRL3dmNy9yYVEwCkpuUFFDU0NkclBMSmV5b1BIN1FhVHdvYUY3ZHpWdzRMQ3N5TkpURld4NGNNNTBWdzZSNWZET2dpQzhic2ZmUzgKQkptb3VscnJaRE5OVmpHOG1XNmNMeHJZdlZRT3JSVmVjQ0ZJZ3NzQ2JBPT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=</ds:X509Certificate>
26
+ </ds:X509Data>
27
+ </KeyInfo>
28
+ </ds:Signature>
29
+ <Subject>
30
+ <NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress">hello@example.com</NameID>
31
+ <SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
32
+ <SubjectConfirmationData InResponseTo="_fc4a34b0-7efb-012e-caae-782bcb13bb38" NotOnOrAfter="2011-06-22T12:54:30.348Z" Recipient="https://someone.example.com/endpoint"/>
33
+ </SubjectConfirmation>
34
+ </Subject>
35
+ <Conditions NotBefore="2011-06-22T12:49:30.332Z" NotOnOrAfter="2011-06-22T13:49:30.332Z">
36
+ <AudienceRestriction>
37
+ <Audience>example.com</Audience>
38
+ </AudienceRestriction>
39
+ </Conditions>
40
+ <AuthnStatement AuthnInstant="2011-06-22T12:49:30.112Z" SessionIndex="_721b4a5a-d7e1-4861-9754-a9b197b6f9ab">
41
+ <AuthnContext>
42
+ <AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</AuthnContextClassRef>
43
+ </AuthnContext>
44
+ </AuthnStatement>
45
+ </Assertion>
46
+ </samlp:Response>