diff --git a/core/src/main/java/org/codelibs/saml2/core/util/SchemaFactory.java b/core/src/main/java/org/codelibs/saml2/core/util/SchemaFactory.java
index b95b755..8497f83 100644
--- a/core/src/main/java/org/codelibs/saml2/core/util/SchemaFactory.java
+++ b/core/src/main/java/org/codelibs/saml2/core/util/SchemaFactory.java
@@ -5,6 +5,8 @@
import java.util.Locale;
import javax.xml.XMLConstants;
+import javax.xml.transform.Source;
+import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import org.slf4j.Logger;
@@ -36,6 +38,17 @@ private SchemaFactory() {
/** URL of the SAML 2.0 protocol schema. */
public static final URL SAML_SCHEMA_PROTOCOL_2_0 = SchemaFactory.class.getResource("/schemas/saml-schema-protocol-2.0.xsd");
+ /**
+ * URL of the XML Encryption 1.1 schema, which is added to every schema set built here.
+ *
+ *
Nothing in the SAML schemas imports this namespace, but {@code xenc:EncryptionMethod}
+ * ends in a wildcard whose {@code processContents} is {@code strict}, so any element carried
+ * there has to be declared somewhere in the schema set or the document is invalid. An IdP
+ * encrypting with XML Encryption 1.1 puts {@code } in exactly that position, and
+ * without this schema the whole response is refused before it is decrypted.
+ */
+ private static final URL XML_ENCRYPTION_SCHEMA_1_1 = SchemaFactory.class.getResource("/schemas/xenc-schema-11.xsd");
+
/**
* Loads a {@link Schema} from the given URL, resolving referenced schemas and DTDs locally.
*
@@ -66,6 +79,7 @@ public LSInput resolveResource(final String type, final String namespaceURI, fin
case "urn:oasis:names:tc:SAML:metadata:algsupport" -> getLocalResource("sstc-saml-metadata-algsupport-v1.0.xsd");
case "urn:oasis:names:tc:SAML:metadata:ui" -> getLocalResource("sstc-saml-metadata-ui-v1.0.xsd");
case "http://www.w3.org/2001/04/xmlenc#" -> getLocalResource("xenc-schema.xsd");
+ case "http://www.w3.org/2009/xmlenc11#" -> getLocalResource("xenc-schema-11.xsd");
case "http://www.w3.org/XML/1998/namespace" -> getLocalResource("xml.xsd");
case "http://www.w3.org/2000/09/xmldsig#" -> getLocalResource("xmldsig-core-schema.xsd");
default -> null;
@@ -108,6 +122,12 @@ public LSInput getLocalResource(final String name) throws Exception {
return lsInput;
}
});
- return factory.newSchema(schemaUrl);
+ if (XML_ENCRYPTION_SCHEMA_1_1 == null) {
+ // the schema is packaged next to the others; carry on without it rather than fail
+ LOGGER.warn("xenc-schema-11.xsd is missing, so XML Encryption 1.1 elements will not validate");
+ return factory.newSchema(schemaUrl);
+ }
+ return factory.newSchema(new Source[] { new StreamSource(schemaUrl.toExternalForm()),
+ new StreamSource(XML_ENCRYPTION_SCHEMA_1_1.toExternalForm()) });
}
}
diff --git a/core/src/main/resources/schemas/xenc-schema-11.xsd b/core/src/main/resources/schemas/xenc-schema-11.xsd
new file mode 100644
index 0000000..0550c3b
--- /dev/null
+++ b/core/src/main/resources/schemas/xenc-schema-11.xsd
@@ -0,0 +1,107 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/core/src/test/java/org/codelibs/saml2/core/test/util/UtilsTest.java b/core/src/test/java/org/codelibs/saml2/core/test/util/UtilsTest.java
index ac7d2b4..967d23d 100644
--- a/core/src/test/java/org/codelibs/saml2/core/test/util/UtilsTest.java
+++ b/core/src/test/java/org/codelibs/saml2/core/test/util/UtilsTest.java
@@ -156,6 +156,31 @@ public void testLoadXML() throws URISyntaxException, IOException {
*
* @see org.codelibs.saml2.core.core.util.Util#validateXML
*/
+ /**
+ * Tests the ValidateXML method
+ * Case: Validates a response encrypted with XML Encryption 1.1
+ *
+ * @throws Exception
+ *
+ * @see org.codelibs.saml2.core.core.util.Util#validateXML
+ */
+ @Test
+ public void testValidateXMLEncryptionMethodFromXmlEnc11() throws Exception {
+ // An IdP encrypting with XML Encryption 1.1 puts inside
+ // , whose wildcard is strict: unless the 1.1 schema is part of the
+ // schema set, the whole response is invalid and is refused before it is ever decrypted.
+ String response =
+ new String(Base64.decodeBase64(Util.getFileAsString("data/responses/valid_encrypted_assertion_xmlenc11.xml.base64")));
+ assertThat(response, containsString("xenc11:MGF"));
+
+ assertTrue(Util.validateXML(Util.loadXML(response), SchemaFactory.SAML_SCHEMA_PROTOCOL_2_0));
+
+ // and the namespace is known rather than waved through: an element that does not exist in
+ // it is still refused, which is what tells this apart from switching validation off
+ String bogus = response.replace("xenc11:MGF", "xenc11:NoSuchElement");
+ assertFalse(Util.validateXML(Util.loadXML(bogus), SchemaFactory.SAML_SCHEMA_PROTOCOL_2_0));
+ }
+
@Test
public void testValidateXMLBadFormat() throws Exception {
String metadataUnloaded = "";
diff --git a/core/src/test/resources/data/responses/valid_encrypted_assertion_xmlenc11.xml.base64 b/core/src/test/resources/data/responses/valid_encrypted_assertion_xmlenc11.xml.base64
new file mode 100644
index 0000000..fb3cd82
--- /dev/null
+++ b/core/src/test/resources/data/responses/valid_encrypted_assertion_xmlenc11.xml.base64
@@ -0,0 +1 @@
+PHNhbWxwOlJlc3BvbnNlIHhtbG5zOnNhbWxwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiIHhtbG5zOnNhbWw9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iIElEPSJfNWY0NjgyNDk2MDkwNDBjNmEzNTFhYzFiZTBlOWZjNjA1MzNmZjA5ZDNkIiBWZXJzaW9uPSIyLjAiIElzc3VlSW5zdGFudD0iMjAxNC0wMy0yOVQxMjowMTo1N1oiIERlc3RpbmF0aW9uPSJodHRwOi8vbG9jYWxob3N0OjgwODAvamF2YS1zYW1sLWpzcHNhbXBsZS9hY3MuanNwIiBJblJlc3BvbnNlVG89Ik9ORUxPR0lOX2JlNjBiOGNhZjhlOWQxOWI3YTM1NTFiMjQ0ZjExNmM5NDdmZjI0N2QiPg0KICAgIDxzYW1sOklzc3Vlcj5odHRwOi8vaWRwLmV4YW1wbGUuY29tLzwvc2FtbDpJc3N1ZXI+DQogICAgPHNhbWxwOlN0YXR1cz4NCiAgICAgICAgPHNhbWxwOlN0YXR1c0NvZGUgVmFsdWU9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpzdGF0dXM6U3VjY2VzcyIvPg0KICAgIDwvc2FtbHA6U3RhdHVzPg0KICAgIA0KPHNhbWw6RW5jcnlwdGVkQXNzZXJ0aW9uPjx4ZW5jOkVuY3J5cHRlZERhdGEgeG1sbnM6eGVuYz0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8wNC94bWxlbmMjIiB4bWxuczpkc2lnPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjIiBUeXBlPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGVuYyNFbGVtZW50Ij48eGVuYzpFbmNyeXB0aW9uTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwOS94bWxlbmMxMSNhZXMyNTYtZ2NtIi8+PGRzaWc6S2V5SW5mbyB4bWxuczpkc2lnPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjIj48eGVuYzpFbmNyeXB0ZWRLZXk+PHhlbmM6RW5jcnlwdGlvbk1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDkveG1sZW5jMTEjcnNhLW9hZXAiPjxkc2lnOkRpZ2VzdE1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMDQveG1sZW5jI3NoYTI1NiIvPjx4ZW5jMTE6TUdGIHhtbG5zOnhlbmMxMT0iaHR0cDovL3d3dy53My5vcmcvMjAwOS94bWxlbmMxMSMiIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwOS94bWxlbmMxMSNtZ2Yxc2hhMjU2Ii8+PC94ZW5jOkVuY3J5cHRpb25NZXRob2Q+PHhlbmM6Q2lwaGVyRGF0YT48eGVuYzpDaXBoZXJWYWx1ZT5lWURRZ1cwK2IrNUp3Z3lwUmZRSWVKa0JxcXpZSDBvSjQ5eDBhQ0ttem91UlEzK1IrelRTMEZxdDM1N0tqRkUrcmNjM0ViMWJ6Y3NEM3doTlVXQmVrMzJlbDVLWit0eklxMUl1RWtQNG0wOTNjOUhlWE1GSVY1VzZkQjl2Z1JTbXVXeUZRemp3d0k2VXY3ME1PQ2lnUGVUZmp4Qk1MVlBLYjRLSzFLS0hkOUE9PC94ZW5jOkNpcGhlclZhbHVlPjwveGVuYzpDaXBoZXJEYXRhPjwveGVuYzpFbmNyeXB0ZWRLZXk+PC9kc2lnOktleUluZm8+DQogICA8eGVuYzpDaXBoZXJEYXRhPg0KICAgICAgPHhlbmM6Q2lwaGVyVmFsdWU+KytvZDArZzhDV2tCOEE3R3NTb004aVQ2a0xkK2JBTE1adnNhWnRVTFM4cXdKdGNCV29ycTF1VjFvR093dzVnb2s3NEdnQzFpbUhXd2ZYRTdDUklBZkxFU1F3N2VrZkZBZ09ESGtVcTVaM3d0OFIzSUgxMHVDaUg0MHVDQjJiT2hoeFpqSUtUR0oxMlEreFZYRUZXYXZEdlp4alhRSkM1a2RPMTFDVUhicnNiUTE5MG0xdkROUUpjTDRBV3FtWFloa1JLeGhKS3k3V3Rpc2dCR2NWZWdyWlY4dmlvZGN2SHRXVXBrZ0JGa2ZVSlVwZ0hIVlJhK0xMMnJLRGNNTFNGSmVtdjhGc1A4ME8waEZ1NlRWa2t5S3dTL3lja3dTQ2ZpTGFnRUsvdHFtd0M0cU0xd2l4OEdTQWRmaWZQODNuT1JMbEdZNkowd2RZdGVuYi9pWStnd213UDdib3Q1b0JpZm10ZTYySjcvNk9CbHlua3c3QkR4dU1rMFVyZ1h5Nk5JVnFaUHFNTzY1RzZtV2haS0E1ZTF3K3c0MFprZGkyQ2E1cTg4Tm9FOEJCVVBnNysycjZxczM0VlVpQ0Q0QWRvNEJqOWNaOTd4QVVzYTlPMHVEbmR5UWV1RFVNM2dkaGk5RzRXNmIwL3VNOUJKZm10VmN5VUZBb0xRMU10QmtZamNic2JwS0FGbUlQVXNta1JsNlkvSWl6eGNXQmhYOUQrR3U3bmFvTWdEOVcrL09yNU5Fczlob0Fnb3N5MGR0K1kxZCtLUHhFd0ppS0VJcTdqT211cDZWbVMxektqZ25PUmwvRDRJNWkwQjFJY1VRbWdTS3JrL25EWG1panZNVUpZOFN5MEhhMk96NVNkMFhDRFdUamN4cnAzRkQwVmxNTnV1UTBOQXprMmI4QVhXejY5VllESUV6SmZ4OVZFaVlOd3NOdWNlVGtzUVk5K1c3dThGbk5HQjdkL05sZ0NPQkxid3JJRHNXZ2tkN083NW4rdmJKWmsza2FHd1dpbWcrNHpCckt2TFh1UkIrdjBGUGl2bUk0c1Z0WENOZFhQekVXb1RpTE5ic29ZWkkvNE12SnlxdnA5cTZVanF1R0hIcWVCVUtqa0lHM3NWWmpiVCtNckZWOFArRXFiSVMyZkJOOEF3dDI5Ri9paHFQdUVtL1REWTRvcmFIMm5vajlPWnlQRktWNzNGM0VQV3JxaXFzZkdmcEVLRjQrZFJRUXhXK01ERkFZQlZMZmlPSDFEYzZYblY4SVlUNHhsbjY3SmV4SGZEdUVtK3hUcHVGTi9ENGpQZDFWWTQydlovVGZCQURkQmU0QTQyeHdWMTF5YnZDcVBJbHJuNWlhenNiVFN5OFJUaUkvSi9KbVFmNVg1eVlJMVpnUWZoQnB0UzlIUk9JWnFZVm5KWjlsL2xxTUFGQzhuWkMyV3hiM0pyT1Y3NjZINE1aUkFGWTdlaDBqSkJrMFRsemdaMncxdXE4TkVhUVlsNnZjYVAwUVc1TEEzTDdvR0tSTHB6clQzbEZiOHdEUmxxeTNnL3hDdFpYQ1hQWm5qTTJ4di8zRy8zV2R0NlFkZ0xPQmRQSU5uRlljb1VRQlJjOE9Lc3NGWEl3eTdBTmVkZ3NTbGhtWnljZks2WUNRaXBGRDRRcmRYSklXTmwzRVRXR3BJdnlaN1hmcXFlcUxINnZ3OWRjWDEvRi9KVUJjWWpHelpMRldvbVhXeDBLZDBUTTNIMlVubWhJdW0zU3kwY2ZXcSszTVBwamJUQUQ5Y3FSMnQ2OGhOcU9sazYxelJRS3J2N1Y0WXF1S1dVdUVBYVVXS1cydVFwTlBKcjJqWE5mNWdvSGhXTzZvK014ZUFNVkoyQ0J6T0VSd2tqVmI4NkNMallUUVQ0bU1uaW52eUhER3BhSkxvbk04YmpueHBjOFRRTXczaXVXT0UrZEkwemZrbDRRRzFieWFHMDQ0ampBaFFWVEpUOTRhZXF1VCtVYkZXNkZxRDRYckd1Nkp1SGZkK2xBZHdOSjdBeHROaUk1ZmtWUlVtQ0lEWWhXVHVxQTRMOHF6QVlZdmVEUk5rRCtsL0xGem9ZZm5nYnplVU9vVGVWRE5EbDVUYi8wdXlVM1BPWVlzMVFVN3B2VnlKUnZ3OEUweHNtaEx2R3NYalJER2E1NzZmektnMXlha25CMlhONVFJUk5yZldmOWt5WmdJcFp2cnFwQXRjNU4reEZHS1g2M2pYV2pzU0Q1WDduNE0zaEhvekpXbjU4OEluOVp2OUl6akNuTWN2c0FDKzhoYWFIVko5Si8wWXIzTk9JTGY2VHhpcUVLNDNSS2JHWGRtY3cyUUpwUGlUa2RPQXRHWTA1bTlOSi9lQ1hyQ3dpVTNONDRCa2sxRFV6WDdrQStaSjF4RnlrbzhGa1hJcFhkWEphK05aM1VsTEdYUlRIdUY3dG8wSlR0VE4xRGVIWDE3MlhUbGhjSlJCbHMrUXUxTytVUjV0WTZmNDY4aEJ5V1hQUE83bkJXVVBxT2dDWWhZVlhaU3JEUXYzVmpDcnIyU2h6dEZkMlQwN0ltSm5UNGRWMlhrREk0VUE5bitzcDN4UmIrOWZEMHhzd0VFZWI0M0tBdnpjeG9xNWRTN1h1dVBxeVVpVXE3TC95VWFSSlY0MmFXOTBMN3NHcUM2dFI0ZS9yR3VsSm9GbDJpRTA1dnlPai9hKzB1QnlMOTNZTkErT3lEaE5QQnVIVjFkcngvK3RVLzVtQnFCdCs4VHVsMmVTZUpZVWVSTjNNTnI2b2wvM1BBakUrT0h3Vk96ZHU4OGd5MFkrMTM1YzJXTWIyZmN3dVdYZXVMVzZIZTVlaUNDbUNoWWpkUkhlZ3R5Wi80S3Q3dHNUV2dveWJNc2kvR3h3bjIxSjlLOVFVTExaMmlmV3kzaFNJbGpUZjVOZXF4NlNQWE1UWmNQenZxT2xvMFBZUUJIdExObmRRQStKbzJCVzVvdENtQmt2WWRRQWUwMDc4SlhpNW9saHhRVHlkQkI0WGlFbjl5YWJnZmRMa1FpY2pydUpTZTBISzZhUGpjS1RTWXVNcHVCZzJmbHlDTXV6YzJBY1p0UjhxTHZzUEl0WXFFWG00dXFRaTZxbjExVzZlcjUyYm9HM0liODJOZzV0bVZUUjZxak5hdEM3Zk9tK0RrQ3BlcWVWT01uUmYvbmtTSGxmQW9mK2hEWDV0a2ZvRUlHdngvRjRheVJmc0tJWnR0MHBLd3VicGFSUHZFOXZIS3JxeElLQVFDL3BTaXQ3QTJtcEtXcUN6YmJyY1UyeUVHcXYzbSs5dzdoSlhGNlcrZElmMU92Q2xjaXBtMTJDY2Jua1ZkQUlJdkcvTTF2aWFFZE9ZeGEzdVpVK0JYNHBZRjNZbUNsaUE1VUJlVHpabmNsdTJqQ2lzMHBTM0J5K3lHd0Jtc2pYSGhyMTh6SXlJZVBwTGZiNlgxVHB4MTRhUWxoa2NocEhiMHUreUQxa3pFeXY2U29Mdm03TUFZcHcxcXNZbmd4bnExbzdDN0hCUTNobEw4UEQ4M3dxMnV4SlVJU2IrS292Y0JQbmhWRHhLNTZ4N1BzUFl3YWU4RmkyVDcvenZTVTlFUy9wNHhTdTM2d2dLSGF1TE9MWERKeGlPNG13QmJmTlh2cGFkOXJrZnNLWUNqbmgwZFFXNjNubG1RMDhvWkFVT3NUWXRBZHc5MUhObHROMG1tMEdaODdSSC9xSTJPMFFMdk8yWWRIV1R2cG1QbTNnM1FxK29TSnF2czI4WGRLaWFicGtXVyt5c28rZ3JSYWNmb1VPU2xYSTBUdjFVSnpYekhEUjJxbkp4cmtSM25rV1FFZ3RWK28zbDlYOVladUo4aTRwMWZGTVA2TTU0SHRodWV3bFBBSDNiVDJaeGV1cUM3bmFyRndnZnVtZ0hOL1hob2J4ZHZlRjJGTW9jVklWcUt5ZmN3dnM5Y1JlRWp6clR5VzQ3RVpoaUZEL1V0Qy9wOFlLdTdkb1FPSENCeFd3b2VzcUJNbWQ1NHByZDJicklnOWxhRzFuNzZJbkdSQ05wL2VaZENPQ05nZ0w0RkxHRjAxMXlVbVBLejJOQTNVMTNRTFRyQXhLVzZkeXF1V0s4bk5RVW9BOG9WKy9WVjFja1BhTGZ2V0FnNnMzekJUUGdJZzB4RE01SHNNSzVlcFY1WGltUDwveGVuYzpDaXBoZXJWYWx1ZT4NCiAgIDwveGVuYzpDaXBoZXJEYXRhPg0KPC94ZW5jOkVuY3J5cHRlZERhdGE+PC9zYW1sOkVuY3J5cHRlZEFzc2VydGlvbj48L3NhbWxwOlJlc3BvbnNlPg==
\ No newline at end of file