kl-ruby-saml 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (137) hide show
  1. checksums.yaml +7 -0
  2. data/.document +5 -0
  3. data/.gitignore +14 -0
  4. data/.travis.yml +17 -0
  5. data/Gemfile +9 -0
  6. data/LICENSE +19 -0
  7. data/README.md +575 -0
  8. data/Rakefile +41 -0
  9. data/changelog.md +75 -0
  10. data/gemfiles/nokogiri-1.5.gemfile +5 -0
  11. data/lib/onelogin/ruby-saml.rb +17 -0
  12. data/lib/onelogin/ruby-saml/attribute_service.rb +57 -0
  13. data/lib/onelogin/ruby-saml/attributes.rb +128 -0
  14. data/lib/onelogin/ruby-saml/authrequest.rb +156 -0
  15. data/lib/onelogin/ruby-saml/http_error.rb +7 -0
  16. data/lib/onelogin/ruby-saml/idp_metadata_parser.rb +161 -0
  17. data/lib/onelogin/ruby-saml/logging.rb +30 -0
  18. data/lib/onelogin/ruby-saml/logoutrequest.rb +131 -0
  19. data/lib/onelogin/ruby-saml/logoutresponse.rb +241 -0
  20. data/lib/onelogin/ruby-saml/metadata.rb +123 -0
  21. data/lib/onelogin/ruby-saml/response.rb +722 -0
  22. data/lib/onelogin/ruby-saml/saml_message.rb +158 -0
  23. data/lib/onelogin/ruby-saml/settings.rb +165 -0
  24. data/lib/onelogin/ruby-saml/slo_logoutrequest.rb +258 -0
  25. data/lib/onelogin/ruby-saml/slo_logoutresponse.rb +136 -0
  26. data/lib/onelogin/ruby-saml/utils.rb +172 -0
  27. data/lib/onelogin/ruby-saml/validation_error.rb +7 -0
  28. data/lib/onelogin/ruby-saml/version.rb +5 -0
  29. data/lib/ruby-saml.rb +1 -0
  30. data/lib/schemas/saml-schema-assertion-2.0.xsd +283 -0
  31. data/lib/schemas/saml-schema-authn-context-2.0.xsd +23 -0
  32. data/lib/schemas/saml-schema-authn-context-types-2.0.xsd +821 -0
  33. data/lib/schemas/saml-schema-metadata-2.0.xsd +337 -0
  34. data/lib/schemas/saml-schema-protocol-2.0.xsd +302 -0
  35. data/lib/schemas/sstc-metadata-attr.xsd +35 -0
  36. data/lib/schemas/sstc-saml-attribute-ext.xsd +25 -0
  37. data/lib/schemas/sstc-saml-metadata-algsupport-v1.0.xsd +41 -0
  38. data/lib/schemas/sstc-saml-metadata-ui-v1.0.xsd +89 -0
  39. data/lib/schemas/xenc-schema.xsd +136 -0
  40. data/lib/schemas/xml.xsd +287 -0
  41. data/lib/schemas/xmldsig-core-schema.xsd +309 -0
  42. data/lib/xml_security.rb +358 -0
  43. data/ruby-saml.gemspec +57 -0
  44. data/test/certificates/certificate1 +12 -0
  45. data/test/certificates/certificate_without_head_foot +1 -0
  46. data/test/certificates/formatted_certificate +14 -0
  47. data/test/certificates/formatted_private_key +12 -0
  48. data/test/certificates/formatted_rsa_private_key +12 -0
  49. data/test/certificates/invalid_certificate1 +1 -0
  50. data/test/certificates/invalid_certificate2 +1 -0
  51. data/test/certificates/invalid_certificate3 +12 -0
  52. data/test/certificates/invalid_private_key1 +1 -0
  53. data/test/certificates/invalid_private_key2 +1 -0
  54. data/test/certificates/invalid_private_key3 +10 -0
  55. data/test/certificates/invalid_rsa_private_key1 +1 -0
  56. data/test/certificates/invalid_rsa_private_key2 +1 -0
  57. data/test/certificates/invalid_rsa_private_key3 +10 -0
  58. data/test/certificates/ruby-saml.crt +14 -0
  59. data/test/certificates/ruby-saml.key +15 -0
  60. data/test/idp_metadata_parser_test.rb +95 -0
  61. data/test/logging_test.rb +62 -0
  62. data/test/logout_requests/invalid_slo_request.xml +6 -0
  63. data/test/logout_requests/slo_request.xml +4 -0
  64. data/test/logout_requests/slo_request.xml.base64 +1 -0
  65. data/test/logout_requests/slo_request_deflated.xml.base64 +1 -0
  66. data/test/logout_requests/slo_request_with_session_index.xml +5 -0
  67. data/test/logout_responses/logoutresponse_fixtures.rb +67 -0
  68. data/test/logoutrequest_test.rb +211 -0
  69. data/test/logoutresponse_test.rb +258 -0
  70. data/test/metadata_test.rb +203 -0
  71. data/test/request_test.rb +282 -0
  72. data/test/response_test.rb +1094 -0
  73. data/test/responses/adfs_response_sha1.xml +46 -0
  74. data/test/responses/adfs_response_sha256.xml +46 -0
  75. data/test/responses/adfs_response_sha384.xml +46 -0
  76. data/test/responses/adfs_response_sha512.xml +46 -0
  77. data/test/responses/adfs_response_xmlns.xml +45 -0
  78. data/test/responses/attackxee.xml +13 -0
  79. data/test/responses/idp_descriptor.xml +3 -0
  80. data/test/responses/invalids/invalid_audience.xml.base64 +1 -0
  81. data/test/responses/invalids/invalid_issuer_assertion.xml.base64 +1 -0
  82. data/test/responses/invalids/invalid_issuer_message.xml.base64 +1 -0
  83. data/test/responses/invalids/invalid_signature_position.xml.base64 +1 -0
  84. data/test/responses/invalids/invalid_subjectconfirmation_inresponse.xml.base64 +1 -0
  85. data/test/responses/invalids/invalid_subjectconfirmation_nb.xml.base64 +1 -0
  86. data/test/responses/invalids/invalid_subjectconfirmation_noa.xml.base64 +1 -0
  87. data/test/responses/invalids/invalid_subjectconfirmation_recipient.xml.base64 +1 -0
  88. data/test/responses/invalids/multiple_assertions.xml.base64 +2 -0
  89. data/test/responses/invalids/multiple_signed.xml.base64 +1 -0
  90. data/test/responses/invalids/no_id.xml.base64 +1 -0
  91. data/test/responses/invalids/no_saml2.xml.base64 +1 -0
  92. data/test/responses/invalids/no_signature.xml.base64 +1 -0
  93. data/test/responses/invalids/no_status.xml.base64 +1 -0
  94. data/test/responses/invalids/no_status_code.xml.base64 +1 -0
  95. data/test/responses/invalids/no_subjectconfirmation_data.xml.base64 +1 -0
  96. data/test/responses/invalids/no_subjectconfirmation_method.xml.base64 +1 -0
  97. data/test/responses/invalids/response_encrypted_attrs.xml.base64 +1 -0
  98. data/test/responses/invalids/response_invalid_signed_element.xml.base64 +1 -0
  99. data/test/responses/invalids/status_code_responder.xml.base64 +1 -0
  100. data/test/responses/invalids/status_code_responer_and_msg.xml.base64 +1 -0
  101. data/test/responses/no_signature_ns.xml +48 -0
  102. data/test/responses/open_saml_response.xml +56 -0
  103. data/test/responses/response_assertion_wrapped.xml.base64 +93 -0
  104. data/test/responses/response_encrypted_nameid.xml.base64 +1 -0
  105. data/test/responses/response_eval.xml +7 -0
  106. data/test/responses/response_no_cert_and_encrypted_attrs.xml +29 -0
  107. data/test/responses/response_unsigned_xml_base64 +1 -0
  108. data/test/responses/response_with_ampersands.xml +139 -0
  109. data/test/responses/response_with_ampersands.xml.base64 +93 -0
  110. data/test/responses/response_with_multiple_attribute_values.xml +67 -0
  111. data/test/responses/response_with_saml2_namespace.xml.base64 +102 -0
  112. data/test/responses/response_with_signed_assertion.xml.base64 +66 -0
  113. data/test/responses/response_with_signed_assertion_2.xml.base64 +1 -0
  114. data/test/responses/response_with_undefined_recipient.xml.base64 +1 -0
  115. data/test/responses/response_without_attributes.xml.base64 +79 -0
  116. data/test/responses/response_wrapped.xml.base64 +150 -0
  117. data/test/responses/signed_message_encrypted_signed_assertion.xml.base64 +1 -0
  118. data/test/responses/signed_message_encrypted_unsigned_assertion.xml.base64 +1 -0
  119. data/test/responses/simple_saml_php.xml +71 -0
  120. data/test/responses/starfield_response.xml.base64 +1 -0
  121. data/test/responses/test_sign.xml +43 -0
  122. data/test/responses/unsigned_message_aes128_encrypted_signed_assertion.xml.base64 +1 -0
  123. data/test/responses/unsigned_message_aes192_encrypted_signed_assertion.xml.base64 +1 -0
  124. data/test/responses/unsigned_message_aes256_encrypted_signed_assertion.xml.base64 +1 -0
  125. data/test/responses/unsigned_message_des192_encrypted_signed_assertion.xml.base64 +1 -0
  126. data/test/responses/unsigned_message_encrypted_assertion_without_saml_namespace.xml.base64 +1 -0
  127. data/test/responses/unsigned_message_encrypted_signed_assertion.xml.base64 +1 -0
  128. data/test/responses/unsigned_message_encrypted_unsigned_assertion.xml.base64 +1 -0
  129. data/test/responses/valid_response.xml.base64 +1 -0
  130. data/test/saml_message_test.rb +56 -0
  131. data/test/settings_test.rb +218 -0
  132. data/test/slo_logoutrequest_test.rb +275 -0
  133. data/test/slo_logoutresponse_test.rb +185 -0
  134. data/test/test_helper.rb +252 -0
  135. data/test/utils_test.rb +145 -0
  136. data/test/xml_security_test.rb +329 -0
  137. metadata +415 -0
@@ -0,0 +1,309 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+
3
+ <!-- Schema for XML Signatures
4
+ http://www.w3.org/2000/09/xmldsig#
5
+ $Revision: 1.1 $ on $Date: 2002/02/08 20:32:26 $ by $Author: reagle $
6
+
7
+ Copyright 2001 The Internet Society and W3C (Massachusetts Institute
8
+ of Technology, Institut National de Recherche en Informatique et en
9
+ Automatique, Keio University). All Rights Reserved.
10
+ http://www.w3.org/Consortium/Legal/
11
+
12
+ This document is governed by the W3C Software License [1] as described
13
+ in the FAQ [2].
14
+
15
+ [1] http://www.w3.org/Consortium/Legal/copyright-software-19980720
16
+ [2] http://www.w3.org/Consortium/Legal/IPR-FAQ-20000620.html#DTD
17
+ -->
18
+
19
+
20
+ <schema xmlns="http://www.w3.org/2001/XMLSchema"
21
+ xmlns:ds="http://www.w3.org/2000/09/xmldsig#"
22
+ targetNamespace="http://www.w3.org/2000/09/xmldsig#"
23
+ version="0.1" elementFormDefault="qualified">
24
+
25
+ <!-- Basic Types Defined for Signatures -->
26
+
27
+ <simpleType name="CryptoBinary">
28
+ <restriction base="base64Binary">
29
+ </restriction>
30
+ </simpleType>
31
+
32
+ <!-- Start Signature -->
33
+
34
+ <element name="Signature" type="ds:SignatureType"/>
35
+ <complexType name="SignatureType">
36
+ <sequence>
37
+ <element ref="ds:SignedInfo"/>
38
+ <element ref="ds:SignatureValue"/>
39
+ <element ref="ds:KeyInfo" minOccurs="0"/>
40
+ <element ref="ds:Object" minOccurs="0" maxOccurs="unbounded"/>
41
+ </sequence>
42
+ <attribute name="Id" type="ID" use="optional"/>
43
+ </complexType>
44
+
45
+ <element name="SignatureValue" type="ds:SignatureValueType"/>
46
+ <complexType name="SignatureValueType">
47
+ <simpleContent>
48
+ <extension base="base64Binary">
49
+ <attribute name="Id" type="ID" use="optional"/>
50
+ </extension>
51
+ </simpleContent>
52
+ </complexType>
53
+
54
+ <!-- Start SignedInfo -->
55
+
56
+ <element name="SignedInfo" type="ds:SignedInfoType"/>
57
+ <complexType name="SignedInfoType">
58
+ <sequence>
59
+ <element ref="ds:CanonicalizationMethod"/>
60
+ <element ref="ds:SignatureMethod"/>
61
+ <element ref="ds:Reference" maxOccurs="unbounded"/>
62
+ </sequence>
63
+ <attribute name="Id" type="ID" use="optional"/>
64
+ </complexType>
65
+
66
+ <element name="CanonicalizationMethod" type="ds:CanonicalizationMethodType"/>
67
+ <complexType name="CanonicalizationMethodType" mixed="true">
68
+ <sequence>
69
+ <any namespace="##any" minOccurs="0" maxOccurs="unbounded"/>
70
+ <!-- (0,unbounded) elements from (1,1) namespace -->
71
+ </sequence>
72
+ <attribute name="Algorithm" type="anyURI" use="required"/>
73
+ </complexType>
74
+
75
+ <element name="SignatureMethod" type="ds:SignatureMethodType"/>
76
+ <complexType name="SignatureMethodType" mixed="true">
77
+ <sequence>
78
+ <element name="HMACOutputLength" minOccurs="0" type="ds:HMACOutputLengthType"/>
79
+ <any namespace="##other" minOccurs="0" maxOccurs="unbounded"/>
80
+ <!-- (0,unbounded) elements from (1,1) external namespace -->
81
+ </sequence>
82
+ <attribute name="Algorithm" type="anyURI" use="required"/>
83
+ </complexType>
84
+
85
+ <!-- Start Reference -->
86
+
87
+ <element name="Reference" type="ds:ReferenceType"/>
88
+ <complexType name="ReferenceType">
89
+ <sequence>
90
+ <element ref="ds:Transforms" minOccurs="0"/>
91
+ <element ref="ds:DigestMethod"/>
92
+ <element ref="ds:DigestValue"/>
93
+ </sequence>
94
+ <attribute name="Id" type="ID" use="optional"/>
95
+ <attribute name="URI" type="anyURI" use="optional"/>
96
+ <attribute name="Type" type="anyURI" use="optional"/>
97
+ </complexType>
98
+
99
+ <element name="Transforms" type="ds:TransformsType"/>
100
+ <complexType name="TransformsType">
101
+ <sequence>
102
+ <element ref="ds:Transform" maxOccurs="unbounded"/>
103
+ </sequence>
104
+ </complexType>
105
+
106
+ <element name="Transform" type="ds:TransformType"/>
107
+ <complexType name="TransformType" mixed="true">
108
+ <choice minOccurs="0" maxOccurs="unbounded">
109
+ <any namespace="##other" processContents="lax"/>
110
+ <!-- (1,1) elements from (0,unbounded) namespaces -->
111
+ <element name="XPath" type="string"/>
112
+ </choice>
113
+ <attribute name="Algorithm" type="anyURI" use="required"/>
114
+ </complexType>
115
+
116
+ <!-- End Reference -->
117
+
118
+ <element name="DigestMethod" type="ds:DigestMethodType"/>
119
+ <complexType name="DigestMethodType" mixed="true">
120
+ <sequence>
121
+ <any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
122
+ </sequence>
123
+ <attribute name="Algorithm" type="anyURI" use="required"/>
124
+ </complexType>
125
+
126
+ <element name="DigestValue" type="ds:DigestValueType"/>
127
+ <simpleType name="DigestValueType">
128
+ <restriction base="base64Binary"/>
129
+ </simpleType>
130
+
131
+ <!-- End SignedInfo -->
132
+
133
+ <!-- Start KeyInfo -->
134
+
135
+ <element name="KeyInfo" type="ds:KeyInfoType"/>
136
+ <complexType name="KeyInfoType" mixed="true">
137
+ <choice maxOccurs="unbounded">
138
+ <element ref="ds:KeyName"/>
139
+ <element ref="ds:KeyValue"/>
140
+ <element ref="ds:RetrievalMethod"/>
141
+ <element ref="ds:X509Data"/>
142
+ <element ref="ds:PGPData"/>
143
+ <element ref="ds:SPKIData"/>
144
+ <element ref="ds:MgmtData"/>
145
+ <any processContents="lax" namespace="##other"/>
146
+ <!-- (1,1) elements from (0,unbounded) namespaces -->
147
+ </choice>
148
+ <attribute name="Id" type="ID" use="optional"/>
149
+ </complexType>
150
+
151
+ <element name="KeyName" type="string"/>
152
+ <element name="MgmtData" type="string"/>
153
+
154
+ <element name="KeyValue" type="ds:KeyValueType"/>
155
+ <complexType name="KeyValueType" mixed="true">
156
+ <choice>
157
+ <element ref="ds:DSAKeyValue"/>
158
+ <element ref="ds:RSAKeyValue"/>
159
+ <any namespace="##other" processContents="lax"/>
160
+ </choice>
161
+ </complexType>
162
+
163
+ <element name="RetrievalMethod" type="ds:RetrievalMethodType"/>
164
+ <complexType name="RetrievalMethodType">
165
+ <sequence>
166
+ <element ref="ds:Transforms" minOccurs="0"/>
167
+ </sequence>
168
+ <attribute name="URI" type="anyURI"/>
169
+ <attribute name="Type" type="anyURI" use="optional"/>
170
+ </complexType>
171
+
172
+ <!-- Start X509Data -->
173
+
174
+ <element name="X509Data" type="ds:X509DataType"/>
175
+ <complexType name="X509DataType">
176
+ <sequence maxOccurs="unbounded">
177
+ <choice>
178
+ <element name="X509IssuerSerial" type="ds:X509IssuerSerialType"/>
179
+ <element name="X509SKI" type="base64Binary"/>
180
+ <element name="X509SubjectName" type="string"/>
181
+ <element name="X509Certificate" type="base64Binary"/>
182
+ <element name="X509CRL" type="base64Binary"/>
183
+ <any namespace="##other" processContents="lax"/>
184
+ </choice>
185
+ </sequence>
186
+ </complexType>
187
+
188
+ <complexType name="X509IssuerSerialType">
189
+ <sequence>
190
+ <element name="X509IssuerName" type="string"/>
191
+ <element name="X509SerialNumber" type="integer"/>
192
+ </sequence>
193
+ </complexType>
194
+
195
+ <!-- End X509Data -->
196
+
197
+ <!-- Begin PGPData -->
198
+
199
+ <element name="PGPData" type="ds:PGPDataType"/>
200
+ <complexType name="PGPDataType">
201
+ <choice>
202
+ <sequence>
203
+ <element name="PGPKeyID" type="base64Binary"/>
204
+ <element name="PGPKeyPacket" type="base64Binary" minOccurs="0"/>
205
+ <any namespace="##other" processContents="lax" minOccurs="0"
206
+ maxOccurs="unbounded"/>
207
+ </sequence>
208
+ <sequence>
209
+ <element name="PGPKeyPacket" type="base64Binary"/>
210
+ <any namespace="##other" processContents="lax" minOccurs="0"
211
+ maxOccurs="unbounded"/>
212
+ </sequence>
213
+ </choice>
214
+ </complexType>
215
+
216
+ <!-- End PGPData -->
217
+
218
+ <!-- Begin SPKIData -->
219
+
220
+ <element name="SPKIData" type="ds:SPKIDataType"/>
221
+ <complexType name="SPKIDataType">
222
+ <sequence maxOccurs="unbounded">
223
+ <element name="SPKISexp" type="base64Binary"/>
224
+ <any namespace="##other" processContents="lax" minOccurs="0"/>
225
+ </sequence>
226
+ </complexType>
227
+
228
+ <!-- End SPKIData -->
229
+
230
+ <!-- End KeyInfo -->
231
+
232
+ <!-- Start Object (Manifest, SignatureProperty) -->
233
+
234
+ <element name="Object" type="ds:ObjectType"/>
235
+ <complexType name="ObjectType" mixed="true">
236
+ <sequence minOccurs="0" maxOccurs="unbounded">
237
+ <any namespace="##any" processContents="lax"/>
238
+ </sequence>
239
+ <attribute name="Id" type="ID" use="optional"/>
240
+ <attribute name="MimeType" type="string" use="optional"/> <!-- add a grep facet -->
241
+ <attribute name="Encoding" type="anyURI" use="optional"/>
242
+ </complexType>
243
+
244
+ <element name="Manifest" type="ds:ManifestType"/>
245
+ <complexType name="ManifestType">
246
+ <sequence>
247
+ <element ref="ds:Reference" maxOccurs="unbounded"/>
248
+ </sequence>
249
+ <attribute name="Id" type="ID" use="optional"/>
250
+ </complexType>
251
+
252
+ <element name="SignatureProperties" type="ds:SignaturePropertiesType"/>
253
+ <complexType name="SignaturePropertiesType">
254
+ <sequence>
255
+ <element ref="ds:SignatureProperty" maxOccurs="unbounded"/>
256
+ </sequence>
257
+ <attribute name="Id" type="ID" use="optional"/>
258
+ </complexType>
259
+
260
+ <element name="SignatureProperty" type="ds:SignaturePropertyType"/>
261
+ <complexType name="SignaturePropertyType" mixed="true">
262
+ <choice maxOccurs="unbounded">
263
+ <any namespace="##other" processContents="lax"/>
264
+ <!-- (1,1) elements from (1,unbounded) namespaces -->
265
+ </choice>
266
+ <attribute name="Target" type="anyURI" use="required"/>
267
+ <attribute name="Id" type="ID" use="optional"/>
268
+ </complexType>
269
+
270
+ <!-- End Object (Manifest, SignatureProperty) -->
271
+
272
+ <!-- Start Algorithm Parameters -->
273
+
274
+ <simpleType name="HMACOutputLengthType">
275
+ <restriction base="integer"/>
276
+ </simpleType>
277
+
278
+ <!-- Start KeyValue Element-types -->
279
+
280
+ <element name="DSAKeyValue" type="ds:DSAKeyValueType"/>
281
+ <complexType name="DSAKeyValueType">
282
+ <sequence>
283
+ <sequence minOccurs="0">
284
+ <element name="P" type="ds:CryptoBinary"/>
285
+ <element name="Q" type="ds:CryptoBinary"/>
286
+ </sequence>
287
+ <element name="G" type="ds:CryptoBinary" minOccurs="0"/>
288
+ <element name="Y" type="ds:CryptoBinary"/>
289
+ <element name="J" type="ds:CryptoBinary" minOccurs="0"/>
290
+ <sequence minOccurs="0">
291
+ <element name="Seed" type="ds:CryptoBinary"/>
292
+ <element name="PgenCounter" type="ds:CryptoBinary"/>
293
+ </sequence>
294
+ </sequence>
295
+ </complexType>
296
+
297
+ <element name="RSAKeyValue" type="ds:RSAKeyValueType"/>
298
+ <complexType name="RSAKeyValueType">
299
+ <sequence>
300
+ <element name="Modulus" type="ds:CryptoBinary"/>
301
+ <element name="Exponent" type="ds:CryptoBinary"/>
302
+ </sequence>
303
+ </complexType>
304
+
305
+ <!-- End KeyValue Element-types -->
306
+
307
+ <!-- End Signature -->
308
+
309
+ </schema>
@@ -0,0 +1,358 @@
1
+ # The contents of this file are subject to the terms
2
+ # of the Common Development and Distribution License
3
+ # (the License). You may not use this file except in
4
+ # compliance with the License.
5
+ #
6
+ # You can obtain a copy of the License at
7
+ # https://opensso.dev.java.net/public/CDDLv1.0.html or
8
+ # opensso/legal/CDDLv1.0.txt
9
+ # See the License for the specific language governing
10
+ # permission and limitations under the License.
11
+ #
12
+ # When distributing Covered Code, include this CDDL
13
+ # Header Notice in each file and include the License file
14
+ # at opensso/legal/CDDLv1.0.txt.
15
+ # If applicable, add the following below the CDDL Header,
16
+ # with the fields enclosed by brackets [] replaced by
17
+ # your own identifying information:
18
+ # "Portions Copyrighted [year] [name of copyright owner]"
19
+ #
20
+ # $Id: xml_sec.rb,v 1.6 2007/10/24 00:28:41 todddd Exp $
21
+ #
22
+ # Copyright 2007 Sun Microsystems Inc. All Rights Reserved
23
+ # Portions Copyrighted 2007 Todd W Saxton.
24
+
25
+ require 'rubygems'
26
+ require "rexml/document"
27
+ require "rexml/xpath"
28
+ require "openssl"
29
+ require 'nokogiri'
30
+ require "digest/sha1"
31
+ require "digest/sha2"
32
+ require "onelogin/ruby-saml/validation_error"
33
+
34
+ module XMLSecurity
35
+
36
+ class BaseDocument < REXML::Document
37
+ REXML::Document::entity_expansion_limit = 0
38
+
39
+ C14N = "http://www.w3.org/2001/10/xml-exc-c14n#"
40
+ DSIG = "http://www.w3.org/2000/09/xmldsig#"
41
+ NOKOGIRI_OPTIONS = Nokogiri::XML::ParseOptions::STRICT |
42
+ Nokogiri::XML::ParseOptions::NONET
43
+
44
+ def canon_algorithm(element)
45
+ algorithm = element
46
+ if algorithm.is_a?(REXML::Element)
47
+ algorithm = element.attribute('Algorithm').value
48
+ end
49
+
50
+ case algorithm
51
+ when "http://www.w3.org/TR/2001/REC-xml-c14n-20010315" then Nokogiri::XML::XML_C14N_1_0
52
+ when "http://www.w3.org/2006/12/xml-c14n11" then Nokogiri::XML::XML_C14N_1_1
53
+ else Nokogiri::XML::XML_C14N_EXCLUSIVE_1_0
54
+ end
55
+ end
56
+
57
+ def algorithm(element)
58
+ algorithm = element
59
+ if algorithm.is_a?(REXML::Element)
60
+ algorithm = element.attribute("Algorithm").value
61
+ end
62
+
63
+ algorithm = algorithm && algorithm =~ /(rsa-)?sha(.*?)$/i && $2.to_i
64
+
65
+ case algorithm
66
+ when 256 then OpenSSL::Digest::SHA256
67
+ when 384 then OpenSSL::Digest::SHA384
68
+ when 512 then OpenSSL::Digest::SHA512
69
+ else
70
+ OpenSSL::Digest::SHA1
71
+ end
72
+ end
73
+
74
+ end
75
+
76
+ class Document < BaseDocument
77
+ RSA_SHA1 = "http://www.w3.org/2000/09/xmldsig#rsa-sha1"
78
+ RSA_SHA256 = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"
79
+ RSA_SHA384 = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha384"
80
+ RSA_SHA512 = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha512"
81
+ SHA1 = "http://www.w3.org/2000/09/xmldsig#sha1"
82
+ SHA256 = "http://www.w3.org/2001/04/xmldsig-more#sha256"
83
+ SHA384 = "http://www.w3.org/2001/04/xmldsig-more#sha384"
84
+ SHA512 = "http://www.w3.org/2001/04/xmldsig-more#sha512"
85
+ ENVELOPED_SIG = "http://www.w3.org/2000/09/xmldsig#enveloped-signature"
86
+ INC_PREFIX_LIST = "#default samlp saml ds xs xsi md"
87
+
88
+ attr_accessor :uuid
89
+
90
+ def uuid
91
+ @uuid ||= begin
92
+ document.root.nil? ? nil : document.root.attributes['ID']
93
+ end
94
+ end
95
+
96
+ #<Signature>
97
+ #<SignedInfo>
98
+ #<CanonicalizationMethod />
99
+ #<SignatureMethod />
100
+ #<Reference>
101
+ #<Transforms>
102
+ #<DigestMethod>
103
+ #<DigestValue>
104
+ #</Reference>
105
+ #<Reference /> etc.
106
+ #</SignedInfo>
107
+ #<SignatureValue />
108
+ #<KeyInfo />
109
+ #<Object />
110
+ #</Signature>
111
+ def sign_document(private_key, certificate, signature_method = RSA_SHA1, digest_method = SHA1)
112
+ noko = Nokogiri.parse(self.to_s) do |options|
113
+ options = XMLSecurity::BaseDocument::NOKOGIRI_OPTIONS
114
+ end
115
+
116
+ signature_element = REXML::Element.new("ds:Signature").add_namespace('ds', DSIG)
117
+ signed_info_element = signature_element.add_element("ds:SignedInfo")
118
+ signed_info_element.add_element("ds:CanonicalizationMethod", {"Algorithm" => C14N})
119
+ signed_info_element.add_element("ds:SignatureMethod", {"Algorithm"=>signature_method})
120
+
121
+ # Add Reference
122
+ reference_element = signed_info_element.add_element("ds:Reference", {"URI" => "##{uuid}"})
123
+
124
+ # Add Transforms
125
+ transforms_element = reference_element.add_element("ds:Transforms")
126
+ transforms_element.add_element("ds:Transform", {"Algorithm" => ENVELOPED_SIG})
127
+ c14element = transforms_element.add_element("ds:Transform", {"Algorithm" => C14N})
128
+ c14element.add_element("ec:InclusiveNamespaces", {"xmlns:ec" => C14N, "PrefixList" => INC_PREFIX_LIST})
129
+
130
+ digest_method_element = reference_element.add_element("ds:DigestMethod", {"Algorithm" => digest_method})
131
+ inclusive_namespaces = INC_PREFIX_LIST.split(" ")
132
+ canon_doc = noko.canonicalize(canon_algorithm(C14N), inclusive_namespaces)
133
+ reference_element.add_element("ds:DigestValue").text = compute_digest(canon_doc, algorithm(digest_method_element))
134
+
135
+ # add SignatureValue
136
+ noko_sig_element = Nokogiri.parse(signature_element.to_s) do |options|
137
+ options = XMLSecurity::BaseDocument::NOKOGIRI_OPTIONS
138
+ end
139
+
140
+ noko_signed_info_element = noko_sig_element.at_xpath('//ds:Signature/ds:SignedInfo', 'ds' => DSIG)
141
+ canon_string = noko_signed_info_element.canonicalize(canon_algorithm(C14N))
142
+
143
+ signature = compute_signature(private_key, algorithm(signature_method).new, canon_string)
144
+ signature_element.add_element("ds:SignatureValue").text = signature
145
+
146
+ # add KeyInfo
147
+ key_info_element = signature_element.add_element("ds:KeyInfo")
148
+ x509_element = key_info_element.add_element("ds:X509Data")
149
+ x509_cert_element = x509_element.add_element("ds:X509Certificate")
150
+ if certificate.is_a?(String)
151
+ certificate = OpenSSL::X509::Certificate.new(certificate)
152
+ end
153
+ x509_cert_element.text = Base64.encode64(certificate.to_der).gsub(/\n/, "")
154
+
155
+ # add the signature
156
+ issuer_element = self.elements["//saml:Issuer"]
157
+ if issuer_element
158
+ self.root.insert_after issuer_element, signature_element
159
+ else
160
+ if sp_sso_descriptor = self.elements["/md:EntityDescriptor"]
161
+ self.root.insert_before sp_sso_descriptor, signature_element
162
+ else
163
+ self.root.add_element(signature_element)
164
+ end
165
+ end
166
+ end
167
+
168
+ protected
169
+
170
+ def compute_signature(private_key, signature_algorithm, document)
171
+ Base64.encode64(private_key.sign(signature_algorithm, document)).gsub(/\n/, "")
172
+ end
173
+
174
+ def compute_digest(document, digest_algorithm)
175
+ digest = digest_algorithm.digest(document)
176
+ Base64.encode64(digest).strip!
177
+ end
178
+
179
+ end
180
+
181
+ class SignedDocument < BaseDocument
182
+
183
+ attr_accessor :signed_element_id
184
+ attr_accessor :errors
185
+
186
+ def initialize(response, errors = [])
187
+ super(response)
188
+ @errors = errors
189
+ end
190
+
191
+ def signed_element_id
192
+ @signed_element_id ||= extract_signed_element_id
193
+ end
194
+
195
+ def validate_document(idp_cert_fingerprint, soft = true, options = {})
196
+ # get cert from response
197
+ cert_element = REXML::XPath.first(
198
+ self,
199
+ "//ds:X509Certificate",
200
+ { "ds"=>DSIG }
201
+ )
202
+ unless cert_element
203
+ if soft
204
+ return false
205
+ else
206
+ raise OneLogin::RubySaml::ValidationError.new("Certificate element missing in response (ds:X509Certificate)")
207
+ end
208
+ end
209
+ base64_cert = cert_element.text
210
+ cert_text = Base64.decode64(base64_cert)
211
+ cert = OpenSSL::X509::Certificate.new(cert_text)
212
+
213
+ if options[:fingerprint_alg]
214
+ fingerprint_alg = XMLSecurity::BaseDocument.new.algorithm(options[:fingerprint_alg]).new
215
+ else
216
+ fingerprint_alg = OpenSSL::Digest::SHA1.new
217
+ end
218
+ fingerprint = fingerprint_alg.hexdigest(cert.to_der)
219
+
220
+ # check cert matches registered idp cert
221
+ if fingerprint != idp_cert_fingerprint.gsub(/[^a-zA-Z0-9]/,"").downcase
222
+ @errors << "Fingerprint mismatch"
223
+ return soft ? false : (raise OneLogin::RubySaml::ValidationError.new("Fingerprint mismatch"))
224
+ end
225
+
226
+ validate_signature(base64_cert, soft)
227
+ end
228
+
229
+ def validate_signature(base64_cert, soft = true)
230
+ # validate references
231
+
232
+ # check for inclusive namespaces
233
+ inclusive_namespaces = extract_inclusive_namespaces
234
+
235
+ document = Nokogiri.parse(self.to_s) do |options|
236
+ options = XMLSecurity::BaseDocument::NOKOGIRI_OPTIONS
237
+ end
238
+
239
+ # create a working copy so we don't modify the original
240
+ @working_copy ||= REXML::Document.new(self.to_s).root
241
+
242
+ # store and remove signature node
243
+ @sig_element ||= begin
244
+ element = REXML::XPath.first(
245
+ @working_copy,
246
+ "//ds:Signature",
247
+ {"ds"=>DSIG}
248
+ )
249
+ element.remove
250
+ end
251
+
252
+ # verify signature
253
+ signed_info_element = REXML::XPath.first(
254
+ @sig_element,
255
+ "//ds:SignedInfo",
256
+ {"ds"=>DSIG}
257
+ )
258
+ noko_sig_element = document.at_xpath('//ds:Signature', 'ds' => DSIG)
259
+ noko_signed_info_element = noko_sig_element.at_xpath('./ds:SignedInfo', 'ds' => DSIG)
260
+ canon_algorithm = canon_algorithm REXML::XPath.first(
261
+ @sig_element,
262
+ '//ds:CanonicalizationMethod',
263
+ 'ds' => DSIG
264
+ )
265
+ canon_string = noko_signed_info_element.canonicalize(canon_algorithm)
266
+ noko_sig_element.remove
267
+
268
+ # check digests
269
+ REXML::XPath.each(@sig_element, "//ds:Reference", {"ds"=>DSIG}) do |ref|
270
+ uri = ref.attributes.get_attribute("URI").value
271
+
272
+ # Handle enveloped signatures, not just enveloping signatures: http://www.di-mgt.com.au/xmldsig2.html
273
+ hashed_element = uri.empty? ? @working_copy : document.at_xpath("//*[@ID=$uri]", nil, { 'uri' => uri[1..-1] })
274
+ canon_algorithm = canon_algorithm REXML::XPath.first(
275
+ ref,
276
+ '//ds:CanonicalizationMethod',
277
+ { "ds" => DSIG }
278
+ )
279
+ canon_hashed_element = hashed_element.canonicalize(canon_algorithm, inclusive_namespaces)
280
+
281
+ digest_algorithm = algorithm(REXML::XPath.first(
282
+ ref,
283
+ "//ds:DigestMethod",
284
+ { "ds" => DSIG }
285
+ ))
286
+ hash = digest_algorithm.digest(canon_hashed_element)
287
+ encoded_digest_value = REXML::XPath.first(
288
+ ref,
289
+ "//ds:DigestValue",
290
+ { "ds" => DSIG }
291
+ ).text
292
+ digest_value = Base64.decode64(encoded_digest_value)
293
+
294
+ unless digests_match?(hash, digest_value)
295
+ @errors << "Digest mismatch"
296
+ return soft ? false : (raise OneLogin::RubySaml::ValidationError.new("Digest mismatch"))
297
+ end
298
+ end
299
+
300
+ base64_signature = REXML::XPath.first(
301
+ @sig_element,
302
+ "//ds:SignatureValue",
303
+ {"ds" => DSIG}
304
+ ).text
305
+
306
+ signature = Base64.decode64(base64_signature)
307
+
308
+ # get certificate object
309
+ cert_text = Base64.decode64(base64_cert)
310
+ cert = OpenSSL::X509::Certificate.new(cert_text)
311
+
312
+ # signature method
313
+ sig_alg_value = REXML::XPath.first(
314
+ signed_info_element,
315
+ "//ds:SignatureMethod",
316
+ {"ds"=>DSIG}
317
+ )
318
+ signature_algorithm = algorithm(sig_alg_value)
319
+
320
+ unless cert.public_key.verify(signature_algorithm.new, signature, canon_string)
321
+ @errors << "Key validation error"
322
+ return soft ? false : (raise OneLogin::RubySaml::ValidationError.new("Key validation error"))
323
+ end
324
+
325
+ return true
326
+ end
327
+
328
+ private
329
+
330
+ def digests_match?(hash, digest_value)
331
+ hash == digest_value
332
+ end
333
+
334
+ def extract_signed_element_id
335
+ reference_element = REXML::XPath.first(
336
+ self,
337
+ "//ds:Signature/ds:SignedInfo/ds:Reference",
338
+ {"ds"=>DSIG}
339
+ )
340
+ self.signed_element_id = reference_element.attribute("URI").value[1..-1] unless reference_element.nil?
341
+ end
342
+
343
+ def extract_inclusive_namespaces
344
+ element = REXML::XPath.first(
345
+ self,
346
+ "//ec:InclusiveNamespaces",
347
+ { "ec" => C14N }
348
+ )
349
+ if element
350
+ prefix_list = element.attributes.get_attribute("PrefixList").value
351
+ prefix_list.split(" ")
352
+ else
353
+ []
354
+ end
355
+ end
356
+
357
+ end
358
+ end