Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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.
*
* <p>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 <xenc11:MGF>} in exactly that position, and
* without this schema the whole response is refused before it is decrypted.</p>
*/
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.
*
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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()) });
}
}
107 changes: 107 additions & 0 deletions core/src/main/resources/schemas/xenc-schema-11.xsd
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
<?xml version="1.0" encoding="utf-8"?>

<!--
#
# Copyright ©[2011] World Wide Web Consortium
# (Massachusetts Institute of Technology,
# European Research Consortium for Informatics and Mathematics,
# Keio University). All Rights Reserved.
# This work is distributed under the W3C® Software License [1] in the
# hope that it will be useful, but WITHOUT ANY WARRANTY; without even
# the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
# PURPOSE.
# [1] http://www.w3.org/Consortium/Legal/2002/copyright-software-20021231
#
-->

<schema xmlns='http://www.w3.org/2001/XMLSchema' version='1.0'
xmlns:xenc='http://www.w3.org/2001/04/xmlenc#'
xmlns:xenc11='http://www.w3.org/2009/xmlenc11#'
xmlns:ds='http://www.w3.org/2000/09/xmldsig#'
targetNamespace='http://www.w3.org/2009/xmlenc11#'
elementFormDefault='qualified'>

<import namespace='http://www.w3.org/2000/09/xmldsig#'
schemaLocation='./xmldsig-core-schema.xsd'/>

<import namespace='http://www.w3.org/2001/04/xmlenc#'
schemaLocation='./xenc-schema.xsd'/>

<element name="ConcatKDFParams" type="xenc11:ConcatKDFParamsType"/>
<complexType name="ConcatKDFParamsType">
<sequence>
<element ref="ds:DigestMethod"/>
</sequence>
<attribute name="AlgorithmID" type="hexBinary"/>
<attribute name="PartyUInfo" type="hexBinary"/>
<attribute name="PartyVInfo" type="hexBinary"/>
<attribute name="SuppPubInfo" type="hexBinary"/>
<attribute name="SuppPrivInfo" type="hexBinary"/>
</complexType>

<element name="DerivedKey" type="xenc11:DerivedKeyType"/>
<complexType name="DerivedKeyType">
<sequence>
<element ref="xenc11:KeyDerivationMethod" minOccurs="0"/>
<element ref="xenc:ReferenceList" minOccurs="0"/>
<element name="DerivedKeyName" type="string" minOccurs="0"/>
<element name="MasterKeyName" type="string" minOccurs="0"/>
</sequence>
<attribute name="Recipient" type="string" use="optional"/>
<attribute name="Id" type="ID" use="optional"/>
<attribute name="Type" type="anyURI" use="optional"/>
</complexType>

<element name="KeyDerivationMethod" type="xenc11:KeyDerivationMethodType"/>
<complexType name="KeyDerivationMethodType">
<sequence>
<any namespace="##any" minOccurs="0" maxOccurs="unbounded"/>
</sequence>
<attribute name="Algorithm" type="anyURI" use="required"/>
</complexType>

<element name="PBKDF2-params" type="xenc11:PBKDF2ParameterType"/>

<complexType name="AlgorithmIdentifierType">
<sequence>
<element name="Parameters" minOccurs="0"/>
</sequence>
<attribute name="Algorithm" type="anyURI" use="required" />
</complexType>

<complexType name="PRFAlgorithmIdentifierType">
<complexContent>
<restriction base="xenc11:AlgorithmIdentifierType">
<attribute name="Algorithm" type="anyURI" use="required" />
</restriction>
</complexContent>
</complexType>

<complexType name="PBKDF2ParameterType">
<sequence>
<element name="Salt">
<complexType>
<choice>
<element name="Specified" type="base64Binary"/>
<element name="OtherSource" type="xenc11:AlgorithmIdentifierType"/>
</choice>
</complexType>
</element>
<element name="IterationCount" type="positiveInteger"/>
<element name="KeyLength" type="positiveInteger"/>
<element name="PRF" type="xenc11:PRFAlgorithmIdentifierType"/>
</sequence>
</complexType>

<element name="MGF" type="xenc11:MGFType"/>
<complexType name="MGFType">
<complexContent>
<restriction base="xenc11:AlgorithmIdentifierType">
<attribute name="Algorithm" type="anyURI" use="required" />
</restriction>
</complexContent>
</complexType>

</schema>


Original file line number Diff line number Diff line change
Expand Up @@ -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 <xenc11:MGF> inside
// <xenc:EncryptionMethod>, 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 = "<xml><EntityDescriptor>";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
PHNhbWxwOlJlc3BvbnNlIHhtbG5zOnNhbWxwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiIHhtbG5zOnNhbWw9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iIElEPSJfNWY0NjgyNDk2MDkwNDBjNmEzNTFhYzFiZTBlOWZjNjA1MzNmZjA5ZDNkIiBWZXJzaW9uPSIyLjAiIElzc3VlSW5zdGFudD0iMjAxNC0wMy0yOVQxMjowMTo1N1oiIERlc3RpbmF0aW9uPSJodHRwOi8vbG9jYWxob3N0OjgwODAvamF2YS1zYW1sLWpzcHNhbXBsZS9hY3MuanNwIiBJblJlc3BvbnNlVG89Ik9ORUxPR0lOX2JlNjBiOGNhZjhlOWQxOWI3YTM1NTFiMjQ0ZjExNmM5NDdmZjI0N2QiPg0KICAgIDxzYW1sOklzc3Vlcj5odHRwOi8vaWRwLmV4YW1wbGUuY29tLzwvc2FtbDpJc3N1ZXI+DQogICAgPHNhbWxwOlN0YXR1cz4NCiAgICAgICAgPHNhbWxwOlN0YXR1c0NvZGUgVmFsdWU9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpzdGF0dXM6U3VjY2VzcyIvPg0KICAgIDwvc2FtbHA6U3RhdHVzPg0KICAgIA0KPHNhbWw6RW5jcnlwdGVkQXNzZXJ0aW9uPjx4ZW5jOkVuY3J5cHRlZERhdGEgeG1sbnM6eGVuYz0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8wNC94bWxlbmMjIiB4bWxuczpkc2lnPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjIiBUeXBlPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGVuYyNFbGVtZW50Ij48eGVuYzpFbmNyeXB0aW9uTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwOS94bWxlbmMxMSNhZXMyNTYtZ2NtIi8+PGRzaWc6S2V5SW5mbyB4bWxuczpkc2lnPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjIj48eGVuYzpFbmNyeXB0ZWRLZXk+PHhlbmM6RW5jcnlwdGlvbk1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDkveG1sZW5jMTEjcnNhLW9hZXAiPjxkc2lnOkRpZ2VzdE1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMDQveG1sZW5jI3NoYTI1NiIvPjx4ZW5jMTE6TUdGIHhtbG5zOnhlbmMxMT0iaHR0cDovL3d3dy53My5vcmcvMjAwOS94bWxlbmMxMSMiIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwOS94bWxlbmMxMSNtZ2Yxc2hhMjU2Ii8+PC94ZW5jOkVuY3J5cHRpb25NZXRob2Q+PHhlbmM6Q2lwaGVyRGF0YT48eGVuYzpDaXBoZXJWYWx1ZT5lWURRZ1cwK2IrNUp3Z3lwUmZRSWVKa0JxcXpZSDBvSjQ5eDBhQ0ttem91UlEzK1IrelRTMEZxdDM1N0tqRkUrcmNjM0ViMWJ6Y3NEM3doTlVXQmVrMzJlbDVLWit0eklxMUl1RWtQNG0wOTNjOUhlWE1GSVY1VzZkQjl2Z1JTbXVXeUZRemp3d0k2VXY3ME1PQ2lnUGVUZmp4Qk1MVlBLYjRLSzFLS0hkOUE9PC94ZW5jOkNpcGhlclZhbHVlPjwveGVuYzpDaXBoZXJEYXRhPjwveGVuYzpFbmNyeXB0ZWRLZXk+PC9kc2lnOktleUluZm8+DQogICA8eGVuYzpDaXBoZXJEYXRhPg0KICAgICAgPHhlbmM6Q2lwaGVyVmFsdWU+KytvZDArZzhDV2tCOEE3R3NTb004aVQ2a0xkK2JBTE1adnNhWnRVTFM4cXdKdGNCV29ycTF1VjFvR093dzVnb2s3NEdnQzFpbUhXd2ZYRTdDUklBZkxFU1F3N2VrZkZBZ09ESGtVcTVaM3d0OFIzSUgxMHVDaUg0MHVDQjJiT2hoeFpqSUtUR0oxMlEreFZYRUZXYXZEdlp4alhRSkM1a2RPMTFDVUhicnNiUTE5MG0xdkROUUpjTDRBV3FtWFloa1JLeGhKS3k3V3Rpc2dCR2NWZWdyWlY4dmlvZGN2SHRXVXBrZ0JGa2ZVSlVwZ0hIVlJhK0xMMnJLRGNNTFNGSmVtdjhGc1A4ME8waEZ1NlRWa2t5S3dTL3lja3dTQ2ZpTGFnRUsvdHFtd0M0cU0xd2l4OEdTQWRmaWZQODNuT1JMbEdZNkowd2RZdGVuYi9pWStnd213UDdib3Q1b0JpZm10ZTYySjcvNk9CbHlua3c3QkR4dU1rMFVyZ1h5Nk5JVnFaUHFNTzY1RzZtV2haS0E1ZTF3K3c0MFprZGkyQ2E1cTg4Tm9FOEJCVVBnNysycjZxczM0VlVpQ0Q0QWRvNEJqOWNaOTd4QVVzYTlPMHVEbmR5UWV1RFVNM2dkaGk5RzRXNmIwL3VNOUJKZm10VmN5VUZBb0xRMU10QmtZamNic2JwS0FGbUlQVXNta1JsNlkvSWl6eGNXQmhYOUQrR3U3bmFvTWdEOVcrL09yNU5Fczlob0Fnb3N5MGR0K1kxZCtLUHhFd0ppS0VJcTdqT211cDZWbVMxektqZ25PUmwvRDRJNWkwQjFJY1VRbWdTS3JrL25EWG1panZNVUpZOFN5MEhhMk96NVNkMFhDRFdUamN4cnAzRkQwVmxNTnV1UTBOQXprMmI4QVhXejY5VllESUV6SmZ4OVZFaVlOd3NOdWNlVGtzUVk5K1c3dThGbk5HQjdkL05sZ0NPQkxid3JJRHNXZ2tkN083NW4rdmJKWmsza2FHd1dpbWcrNHpCckt2TFh1UkIrdjBGUGl2bUk0c1Z0WENOZFhQekVXb1RpTE5ic29ZWkkvNE12SnlxdnA5cTZVanF1R0hIcWVCVUtqa0lHM3NWWmpiVCtNckZWOFArRXFiSVMyZkJOOEF3dDI5Ri9paHFQdUVtL1REWTRvcmFIMm5vajlPWnlQRktWNzNGM0VQV3JxaXFzZkdmcEVLRjQrZFJRUXhXK01ERkFZQlZMZmlPSDFEYzZYblY4SVlUNHhsbjY3SmV4SGZEdUVtK3hUcHVGTi9ENGpQZDFWWTQydlovVGZCQURkQmU0QTQyeHdWMTF5YnZDcVBJbHJuNWlhenNiVFN5OFJUaUkvSi9KbVFmNVg1eVlJMVpnUWZoQnB0UzlIUk9JWnFZVm5KWjlsL2xxTUFGQzhuWkMyV3hiM0pyT1Y3NjZINE1aUkFGWTdlaDBqSkJrMFRsemdaMncxdXE4TkVhUVlsNnZjYVAwUVc1TEEzTDdvR0tSTHB6clQzbEZiOHdEUmxxeTNnL3hDdFpYQ1hQWm5qTTJ4di8zRy8zV2R0NlFkZ0xPQmRQSU5uRlljb1VRQlJjOE9Lc3NGWEl3eTdBTmVkZ3NTbGhtWnljZks2WUNRaXBGRDRRcmRYSklXTmwzRVRXR3BJdnlaN1hmcXFlcUxINnZ3OWRjWDEvRi9KVUJjWWpHelpMRldvbVhXeDBLZDBUTTNIMlVubWhJdW0zU3kwY2ZXcSszTVBwamJUQUQ5Y3FSMnQ2OGhOcU9sazYxelJRS3J2N1Y0WXF1S1dVdUVBYVVXS1cydVFwTlBKcjJqWE5mNWdvSGhXTzZvK014ZUFNVkoyQ0J6T0VSd2tqVmI4NkNMallUUVQ0bU1uaW52eUhER3BhSkxvbk04YmpueHBjOFRRTXczaXVXT0UrZEkwemZrbDRRRzFieWFHMDQ0ampBaFFWVEpUOTRhZXF1VCtVYkZXNkZxRDRYckd1Nkp1SGZkK2xBZHdOSjdBeHROaUk1ZmtWUlVtQ0lEWWhXVHVxQTRMOHF6QVlZdmVEUk5rRCtsL0xGem9ZZm5nYnplVU9vVGVWRE5EbDVUYi8wdXlVM1BPWVlzMVFVN3B2VnlKUnZ3OEUweHNtaEx2R3NYalJER2E1NzZmektnMXlha25CMlhONVFJUk5yZldmOWt5WmdJcFp2cnFwQXRjNU4reEZHS1g2M2pYV2pzU0Q1WDduNE0zaEhvekpXbjU4OEluOVp2OUl6akNuTWN2c0FDKzhoYWFIVko5Si8wWXIzTk9JTGY2VHhpcUVLNDNSS2JHWGRtY3cyUUpwUGlUa2RPQXRHWTA1bTlOSi9lQ1hyQ3dpVTNONDRCa2sxRFV6WDdrQStaSjF4RnlrbzhGa1hJcFhkWEphK05aM1VsTEdYUlRIdUY3dG8wSlR0VE4xRGVIWDE3MlhUbGhjSlJCbHMrUXUxTytVUjV0WTZmNDY4aEJ5V1hQUE83bkJXVVBxT2dDWWhZVlhaU3JEUXYzVmpDcnIyU2h6dEZkMlQwN0ltSm5UNGRWMlhrREk0VUE5bitzcDN4UmIrOWZEMHhzd0VFZWI0M0tBdnpjeG9xNWRTN1h1dVBxeVVpVXE3TC95VWFSSlY0MmFXOTBMN3NHcUM2dFI0ZS9yR3VsSm9GbDJpRTA1dnlPai9hKzB1QnlMOTNZTkErT3lEaE5QQnVIVjFkcngvK3RVLzVtQnFCdCs4VHVsMmVTZUpZVWVSTjNNTnI2b2wvM1BBakUrT0h3Vk96ZHU4OGd5MFkrMTM1YzJXTWIyZmN3dVdYZXVMVzZIZTVlaUNDbUNoWWpkUkhlZ3R5Wi80S3Q3dHNUV2dveWJNc2kvR3h3bjIxSjlLOVFVTExaMmlmV3kzaFNJbGpUZjVOZXF4NlNQWE1UWmNQenZxT2xvMFBZUUJIdExObmRRQStKbzJCVzVvdENtQmt2WWRRQWUwMDc4SlhpNW9saHhRVHlkQkI0WGlFbjl5YWJnZmRMa1FpY2pydUpTZTBISzZhUGpjS1RTWXVNcHVCZzJmbHlDTXV6YzJBY1p0UjhxTHZzUEl0WXFFWG00dXFRaTZxbjExVzZlcjUyYm9HM0liODJOZzV0bVZUUjZxak5hdEM3Zk9tK0RrQ3BlcWVWT01uUmYvbmtTSGxmQW9mK2hEWDV0a2ZvRUlHdngvRjRheVJmc0tJWnR0MHBLd3VicGFSUHZFOXZIS3JxeElLQVFDL3BTaXQ3QTJtcEtXcUN6YmJyY1UyeUVHcXYzbSs5dzdoSlhGNlcrZElmMU92Q2xjaXBtMTJDY2Jua1ZkQUlJdkcvTTF2aWFFZE9ZeGEzdVpVK0JYNHBZRjNZbUNsaUE1VUJlVHpabmNsdTJqQ2lzMHBTM0J5K3lHd0Jtc2pYSGhyMTh6SXlJZVBwTGZiNlgxVHB4MTRhUWxoa2NocEhiMHUreUQxa3pFeXY2U29Mdm03TUFZcHcxcXNZbmd4bnExbzdDN0hCUTNobEw4UEQ4M3dxMnV4SlVJU2IrS292Y0JQbmhWRHhLNTZ4N1BzUFl3YWU4RmkyVDcvenZTVTlFUy9wNHhTdTM2d2dLSGF1TE9MWERKeGlPNG13QmJmTlh2cGFkOXJrZnNLWUNqbmgwZFFXNjNubG1RMDhvWkFVT3NUWXRBZHc5MUhObHROMG1tMEdaODdSSC9xSTJPMFFMdk8yWWRIV1R2cG1QbTNnM1FxK29TSnF2czI4WGRLaWFicGtXVyt5c28rZ3JSYWNmb1VPU2xYSTBUdjFVSnpYekhEUjJxbkp4cmtSM25rV1FFZ3RWK28zbDlYOVladUo4aTRwMWZGTVA2TTU0SHRodWV3bFBBSDNiVDJaeGV1cUM3bmFyRndnZnVtZ0hOL1hob2J4ZHZlRjJGTW9jVklWcUt5ZmN3dnM5Y1JlRWp6clR5VzQ3RVpoaUZEL1V0Qy9wOFlLdTdkb1FPSENCeFd3b2VzcUJNbWQ1NHByZDJicklnOWxhRzFuNzZJbkdSQ05wL2VaZENPQ05nZ0w0RkxHRjAxMXlVbVBLejJOQTNVMTNRTFRyQXhLVzZkeXF1V0s4bk5RVW9BOG9WKy9WVjFja1BhTGZ2V0FnNnMzekJUUGdJZzB4RE01SHNNSzVlcFY1WGltUDwveGVuYzpDaXBoZXJWYWx1ZT4NCiAgIDwveGVuYzpDaXBoZXJEYXRhPg0KPC94ZW5jOkVuY3J5cHRlZERhdGE+PC9zYW1sOkVuY3J5cHRlZEFzc2VydGlvbj48L3NhbWxwOlJlc3BvbnNlPg==
Loading