/* * Copyright [2007] [University Corporation for Advanced Internet Development, Inc.] * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.opensaml.security; import java.security.cert.X509Certificate; import java.security.interfaces.RSAPublicKey; import java.util.ArrayList; import java.util.List; import javax.xml.namespace.QName; import org.opensaml.Configuration; import org.opensaml.common.BaseTestCase; import org.opensaml.saml2.metadata.EntitiesDescriptor; import org.opensaml.saml2.metadata.EntityDescriptor; import org.opensaml.saml2.metadata.IDPSSODescriptor; import org.opensaml.saml2.metadata.RoleDescriptor; import org.opensaml.saml2.metadata.provider.DOMMetadataProvider; import org.opensaml.xml.security.BasicSecurityConfiguration; import org.opensaml.xml.security.CriteriaSet; import org.opensaml.xml.security.SecurityConfiguration; import org.opensaml.xml.security.SecurityException; import org.opensaml.xml.security.SecurityTestHelper; import org.opensaml.xml.security.credential.Credential; import org.opensaml.xml.security.credential.UsageType; import org.opensaml.xml.security.criteria.EntityIDCriteria; import org.opensaml.xml.security.criteria.UsageCriteria; import org.opensaml.xml.security.keyinfo.KeyInfoCredentialResolver; import org.opensaml.xml.security.x509.X509Credential; import org.w3c.dom.Document; /** * Testing the metadata credential resolver. */ public class MetadataCredentialResolverTest extends BaseTestCase { private String idpRSAPubKeyName = "IDP-SSO-RSA-Key"; private RSAPublicKey idpRSAPubKey; private String idpRSAPubKeyBase64 = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDfCVgF2Lvhu0Q35FvmAVGMXc3i" + "1MojcqybcfVbfn0Tg/Aj5FvuAiDFg9KpGvMHDKdLOY+1xsKZqyIm58SFhW+5z51Y" + "pnblHGjuDtPtPbtspQ7pAOsknnvbKZrx7RGNOJyQZE3Qn88Y5ZBNzABusqNXjrWl" + "U9m4a+XNIFqM4YbJLwIDAQAB"; private X509Certificate idpDSACert; private String idpDSACertBase64 = "MIIECTCCAvGgAwIBAgIBMzANBgkqhkiG9w0BAQUFADAtMRIwEAYDVQQKEwlJbnRl" + "cm5ldDIxFzAVBgNVBAMTDmNhLmV4YW1wbGUub3JnMB4XDTA3MDUyNTIwMTYxMVoX" + "DTE3MDUyMjIwMTYxMVowGjEYMBYGA1UEAxMPaWRwLmV4YW1wbGUub3JnMIIBtjCC" + "ASsGByqGSM44BAEwggEeAoGBAI+ktw7R9m7TxjaCrT2MHwWNQUAyXPrqbFCcu+DC" + "irr861U6R6W/GyqWdcy8/D1Hh/I1U94POQn5yfqVPpVH2ZRS4OMFndHWaoo9V5LJ" + "oXTXHiDYB3W4t9tn0fm7It0n7VoUI5C4y9LG32Hq+UIGF/ktNTmo//mEqLS6aJNd" + "bMFpAhUArmKGh0hcpmjukYArWcMRvipB4CMCgYBuCiCrUaHBRRtqrk0P/Luq0l2M" + "2718GwSGeLPZip06gACDG7IctMrgH1J+ZIjsx6vffi977wnMDiktqacmaobV+SCR" + "W9ijJRdkYpUHmlLvuJGnDPjkvewpbGWJsCabpWEvWdYw3ma8RuHOPj4Jkrdd4VcR" + "aFwox/fPJ7cG6kBydgOBhAACgYBxQIPv9DCsmiMHG1FAxSARX0GcRiELJPJ+MtaS" + "tdTrVobNa2jebwc3npLiTvUR4U/CDo1mSZb+Sp/wian8kNZHmGcR6KbtJs9UDsa3" + "V0pbbgpUar4HcxV+NQJBbhn9RGu85g3PDILUrINiUAf26mhPN5Y0paM+HbM68nUf" + "1OLv16OBsjCBrzAJBgNVHRMEAjAAMCwGCWCGSAGG+EIBDQQfFh1PcGVuU1NMIEdl" + "bmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQUIHFAEB/3jIIZzJEJ/qdsuI8v" + "N3kwVQYDVR0jBE4wTIAU1e5lU95R2oetQupBbvKv1u5GlAuhMaQvMC0xEjAQBgNV" + "BAoTCUludGVybmV0MjEXMBUGA1UEAxMOY2EuZXhhbXBsZS5vcmeCAQEwDQYJKoZI" + "hvcNAQEFBQADggEBAJt4Q34+pqjW5tHHhkdzTITSBjOOf8EvYMgxTMRzhagLSHTt" + "9RgO5i/G7ELvnwe1j6187m1XD9iEAWKeKbB//ljeOpgnwzkLR9Er5tr1RI3cbil0" + "AX+oX0c1jfRaQnR50Rfb5YoNX6G963iphlxp9C8VLB6eOk/S270XoWoQIkO1ioQ8" + "JY4HE6AyDsOpJaOmHpBaxjgsiko52ZWZeZyaCyL98BXwVxeml7pYnHlXWWidB0N/" + "Zy+LbvWg3urUkiDjMcB6nGImmEfDSxRdybitcMwbwL26z2WOpwL3llm3mcCydKXg" + "Xt8IQhfDhOZOHWckeD2tStnJRP/cqBgO62/qirw="; private X509Certificate idpRSACert; private String idpRSACertBase64 = "MIIC8TCCAdmgAwIBAgIBMjANBgkqhkiG9w0BAQUFADAtMRIwEAYDVQQKEwlJbnRl" + "cm5ldDIxFzAVBgNVBAMTDmNhLmV4YW1wbGUub3JnMB4XDTA3MDUyNTIwMDk1MVoX" + "DTE3MDUyMjIwMDk1MVowGjEYMBYGA1UEAxMPaWRwLmV4YW1wbGUub3JnMIGfMA0G" + "CSqGSIb3DQEBAQUAA4GNADCBiQKBgQDfCVgF2Lvhu0Q35FvmAVGMXc3i1Mojcqyb" + "cfVbfn0Tg/Aj5FvuAiDFg9KpGvMHDKdLOY+1xsKZqyIm58SFhW+5z51YpnblHGju" + "DtPtPbtspQ7pAOsknnvbKZrx7RGNOJyQZE3Qn88Y5ZBNzABusqNXjrWlU9m4a+XN" + "IFqM4YbJLwIDAQABo4GyMIGvMAkGA1UdEwQCMAAwLAYJYIZIAYb4QgENBB8WHU9w" + "ZW5TU0wgR2VuZXJhdGVkIENlcnRpZmljYXRlMB0GA1UdDgQWBBT2qDRFTzawttBG" + "jN6wxni/12tQQjBVBgNVHSMETjBMgBTV7mVT3lHah61C6kFu8q/W7kaUC6ExpC8w" + "LTESMBAGA1UEChMJSW50ZXJuZXQyMRcwFQYDVQQDEw5jYS5leGFtcGxlLm9yZ4IB" + "ATANBgkqhkiG9w0BAQUFAAOCAQEAlJYAou5ko3ujHVhOc4OB2AOOqdXAjThiXg6z" + "Tjezs7/F53b9IRt4in/k92y1tKZ87F/JcnH6MrzKfb8m5XtcYwtUSvmFTCp5rrFp" + "z1JhXlgnaWVJJ2G2vKLDGuPQvLV9zsWhnkbTPuzocvOotxl7w7LJvO3D/tzTAnnU" + "bgg1AfP+CTDs3F/ceHzWGVWTMUAmNGX8gMS2/xh66QoEzl7LBG8Xzpo0j+gSxe7h" + "Scb5iS4U/XUEbZylMUbbK57h9Bez8VVeO1jfwAniIBT0Ur9ksiYsAdyXYoXssGiF" + "bKW1K3QG1GA9wwGy5GvjyALuuXL4lEzFB0kMsGucNMfyyojX9A=="; private X509Certificate keyAuthorityCert; private String keyAuthorityCertBase64 = "MIIDXTCCAkWgAwIBAgIBATANBgkqhkiG9w0BAQUFADAtMRIwEAYDVQQKEwlJbnRl" + "cm5ldDIxFzAVBgNVBAMTDmNhLmV4YW1wbGUub3JnMB4XDTA3MDQwOTA1NDcxMloX" + "DTE3MDQwNjA1NDcxMlowLTESMBAGA1UEChMJSW50ZXJuZXQyMRcwFQYDVQQDEw5j" + "YS5leGFtcGxlLm9yZzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANxM" + "5/6mBCcX+S7HApcKtfqdFRZzi6Ra91nkEzXOUcO+BPUdYqSxKGnCCso25ZOZP3gn" + "JVkY8Pi7VWrCM6wRgIMyQDvNYqCpNjkZGFkrMoa6fm8BSaDHJ1fz6l/eEl0CVU3U" + "uUAf0mXQLGm6Jannq8aMolRujlhE5iRaOJ2qp6wqsvyatK+vTgDngnwYVa4Cqu0j" + "UeNF28quST5D3gIuZ0OeFHSM2Z1WUKkwwsHqVkxBBcH1QE1JOGIoSnrxxl/o4VlL" + "WGEI8zq5qixE8VYtBBmijBwIL5ETy2fwiqcsvimQaQAtAfbtpO3kBSs8n7nnzMUH" + "fRlcebGkwwcNfYcD5hcCAwEAAaOBhzCBhDAdBgNVHQ4EFgQU1e5lU95R2oetQupB" + "bvKv1u5GlAswVQYDVR0jBE4wTIAU1e5lU95R2oetQupBbvKv1u5GlAuhMaQvMC0x" + "EjAQBgNVBAoTCUludGVybmV0MjEXMBUGA1UEAxMOY2EuZXhhbXBsZS5vcmeCAQEw" + "DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAbqrozetM/iBVIUir9k14" + "HbLEP0lZ6jOPWFIUFXMOn0t8+Ul7PMp9Rdn+7OGQIoJw97/mam3kpQ/HmjJMFYv6" + "MGsDQ4vAOiQiaTKzgMhrnCdVpVH9uz4ARHiysCujnjH9sehTWgybY8pXzzSG5BAj" + "EGowHq01nXxq2K4yAJSdAUBYLfuSKW1uRU6cmEa9uzl9EvoZfAF3BLnGlPqu4Zaj" + "H2NC9ZY0y19LX4yeJLHL1sY4fyxb3x8QhcCXiI16awBTr/VnUpJjSe9vh+OudWGe" + "yCL/KhjlrDkjJ3hIxBF5mP/Y27cFpRnC2gECkieURvh52OyuqkzpbOrTN5rD9fNi" + "nA=="; // On IDPSSODescriptor, has RSAKeyValue (usage = encryption) and DSA cert (usage = signing) private String protocolFoo = "PROTOCOL_FOO"; // On IDPSSODescriptor, has RSA cert (no usage) private String protocolBar = "PROTOCOL_BAR"; private QName idpRole = IDPSSODescriptor.DEFAULT_ELEMENT_NAME; private String idpEntityID = "http://idp.example.org/shibboleth"; private String mdFileName = "/data/org/opensaml/security/test1-metadata.xml"; private DOMMetadataProvider mdProvider; private MetadataCredentialResolver mdResolver; private KeyInfoCredentialResolver keyInfoResolver; private EntityIDCriteria entityCriteria; private MetadataCriteria mdCriteria; private CriteriaSet criteriaSet; private SecurityConfiguration origGlobalSecurityConfig; /** {@inheritDoc} */ protected void setUp() throws Exception { super.setUp(); idpRSAPubKey = SecurityTestHelper.buildJavaRSAPublicKey(idpRSAPubKeyBase64); idpDSACert = SecurityTestHelper.buildJavaX509Cert(idpDSACertBase64); idpRSACert = SecurityTestHelper.buildJavaX509Cert(idpRSACertBase64); keyAuthorityCert = SecurityTestHelper.buildJavaX509Cert(keyAuthorityCertBase64); Document mdDoc = parser.parse(MetadataCredentialResolverTest.class.getResourceAsStream(mdFileName)); mdProvider = new DOMMetadataProvider(mdDoc.getDocumentElement()); mdProvider.initialize(); //For testing, use default KeyInfo resolver from global security config, per metadata resolver constructor origGlobalSecurityConfig = Configuration.getGlobalSecurityConfiguration(); BasicSecurityConfiguration newSecConfig = new BasicSecurityConfiguration(); newSecConfig.setDefaultKeyInfoCredentialResolver( SecurityTestHelper.buildBasicInlineKeyInfoResolver() ); Configuration.setGlobalSecurityConfiguration(newSecConfig); mdResolver = new MetadataCredentialResolver(mdProvider); entityCriteria = new EntityIDCriteria(idpEntityID); // by default set protocol to null mdCriteria = new MetadataCriteria(idpRole, null); criteriaSet = new CriteriaSet(); criteriaSet.add(entityCriteria); criteriaSet.add(mdCriteria); } /** {@inheritDoc} */ protected void tearDown() throws Exception { super.tearDown(); Configuration.setGlobalSecurityConfiguration(origGlobalSecurityConfig); } /** * Test protocol null, and no usage. * Should get 3 credentials, 2 from protocolFoo and 1 from protocolBar. * * @throws SecurityException */ public void testNoProtocolNoUsage() throws SecurityException { List<Credential> resolved = new ArrayList<Credential>(); for (Credential credential : mdResolver.resolve(criteriaSet)) { resolved.add(credential); checkContextAndID(credential, idpEntityID, idpRole); } assertEquals("Incorrect number of credentials resolved", 3, resolved.size()); for (Credential credential : resolved) { X509Credential x509Cred; switch(credential.getUsageType()) { case SIGNING: x509Cred = (X509Credential) credential; assertEquals("Unexpected value for certificate", idpDSACert, x509Cred.getEntityCertificate()); break; case ENCRYPTION: assertTrue("Expected value for key name not found", credential.getKeyNames().contains(idpRSAPubKeyName)); assertEquals("Unexpected value for key", idpRSAPubKey, credential.getPublicKey()); break; case UNSPECIFIED: x509Cred = (X509Credential) credential; assertEquals("Unexpected value for certificate", idpRSACert, x509Cred.getEntityCertificate()); break; default: } } } /** * Test protocol null, and usage = encryption. * Should get 2 credentials, 1 from protocolFoo and 1 from protocolBar. * * @throws SecurityException */ public void testNoProtocolUsageEncryption() throws SecurityException { criteriaSet.add( new UsageCriteria(UsageType.ENCRYPTION) ); List<Credential> resolved = new ArrayList<Credential>(); for (Credential credential : mdResolver.resolve(criteriaSet)) { resolved.add(credential); checkContextAndID(credential, idpEntityID, idpRole); } assertEquals("Incorrect number of credentials resolved", 2, resolved.size()); for (Credential credential : resolved) { X509Credential x509Cred; switch(credential.getUsageType()) { case SIGNING: fail("Credential with invalid usage was resolved"); break; case ENCRYPTION: assertTrue("Expected value for key name not found", credential.getKeyNames().contains(idpRSAPubKeyName)); assertEquals("Unexpected value for key", idpRSAPubKey, credential.getPublicKey()); break; case UNSPECIFIED: x509Cred = (X509Credential) credential; assertEquals("Unexpected value for certificate", idpRSACert, x509Cred.getEntityCertificate()); break; default: } } } /** * Test protocol null, and usage = signing. * Should get 2 credentials, 1 from protocolFoo and 1 from protocolBar. * * @throws SecurityException */ public void testNoProtocolUsageSigning() throws SecurityException { criteriaSet.add( new UsageCriteria(UsageType.SIGNING) ); List<Credential> resolved = new ArrayList<Credential>(); for (Credential credential : mdResolver.resolve(criteriaSet)) { resolved.add(credential); checkContextAndID(credential, idpEntityID, idpRole); } assertEquals("Incorrect number of credentials resolved", 2, resolved.size()); for (Credential credential : resolved) { X509Credential x509Cred; switch(credential.getUsageType()) { case SIGNING: x509Cred = (X509Credential) credential; assertEquals("Unexpected value for certificate", idpDSACert, x509Cred.getEntityCertificate()); break; case ENCRYPTION: fail("Credential with invalid usage was resolved"); break; case UNSPECIFIED: x509Cred = (X509Credential) credential; assertEquals("Unexpected value for certificate", idpRSACert, x509Cred.getEntityCertificate()); break; default: } } } /** * Test 1 protocol (FOO), and no usage . * Should get 2 credentials. * * @throws SecurityException */ public void testProtocolFOONoUsage() throws SecurityException { mdCriteria.setProtocol(protocolFoo); List<Credential> resolved = new ArrayList<Credential>(); for (Credential credential : mdResolver.resolve(criteriaSet)) { resolved.add(credential); checkContextAndID(credential, idpEntityID, idpRole); } assertEquals("Incorrect number of credentials resolved", 2, resolved.size()); for (Credential credential : resolved) { X509Credential x509Cred; switch(credential.getUsageType()) { case SIGNING: x509Cred = (X509Credential) credential; assertEquals("Unexpected value for certificate", idpDSACert, x509Cred.getEntityCertificate()); break; case ENCRYPTION: assertTrue("Expected value for key name not found", credential.getKeyNames().contains(idpRSAPubKeyName)); assertEquals("Unexpected value for key", idpRSAPubKey, credential.getPublicKey()); break; case UNSPECIFIED: fail("Credential was resolved from invalid protocol"); break; default: } } } /** * Test 1 protocol (FOO), and usage = signing. * Should get 1 credentials. * * @throws SecurityException */ public void testProtocolFOOUsageSigning() throws SecurityException { mdCriteria.setProtocol(protocolFoo); criteriaSet.add( new UsageCriteria(UsageType.SIGNING) ); List<Credential> resolved = new ArrayList<Credential>(); for (Credential credential : mdResolver.resolve(criteriaSet)) { resolved.add(credential); checkContextAndID(credential, idpEntityID, idpRole); } assertEquals("Incorrect number of credentials resolved", 1, resolved.size()); for (Credential credential : resolved) { X509Credential x509Cred; switch(credential.getUsageType()) { case SIGNING: x509Cred = (X509Credential) credential; assertEquals("Unexpected value for certificate", idpDSACert, x509Cred.getEntityCertificate()); break; case ENCRYPTION: fail("Credential was resolved from invalid protocol or usage"); break; case UNSPECIFIED: fail("Credential was resolved from invalid protocol or usage"); break; default: } } } /** * Test 1 protocol (FOO), and usage encryption. * Should get 1 credentials. * * @throws SecurityException */ public void testProtocolFOOUsageEncryption() throws SecurityException { mdCriteria.setProtocol(protocolFoo); criteriaSet.add( new UsageCriteria(UsageType.ENCRYPTION) ); List<Credential> resolved = new ArrayList<Credential>(); for (Credential credential : mdResolver.resolve(criteriaSet)) { resolved.add(credential); checkContextAndID(credential, idpEntityID, idpRole); } assertEquals("Incorrect number of credentials resolved", 1, resolved.size()); for (Credential credential : resolved) { X509Credential x509Cred; switch(credential.getUsageType()) { case SIGNING: fail("Credential was resolved from invalid protocol or usage"); break; case ENCRYPTION: assertTrue("Expected value for key name not found", credential.getKeyNames().contains(idpRSAPubKeyName)); assertEquals("Unexpected value for key", idpRSAPubKey, credential.getPublicKey()); break; case UNSPECIFIED: fail("Credential was resolved from invalid protocol or usage"); break; default: } } } /** * Test 1 protocol (BAR), and no usage. * Should get 1 credentials. * * @throws SecurityException */ public void testProtocolBARNoUsage() throws SecurityException { mdCriteria.setProtocol(protocolBar); List<Credential> resolved = new ArrayList<Credential>(); for (Credential credential : mdResolver.resolve(criteriaSet)) { resolved.add(credential); checkContextAndID(credential, idpEntityID, idpRole); } assertEquals("Incorrect number of credentials resolved", 1, resolved.size()); for (Credential credential : resolved) { X509Credential x509Cred; switch(credential.getUsageType()) { case SIGNING: fail("Credential was resolved from invalid protocol"); break; case ENCRYPTION: fail("Credential was resolved from invalid protocol"); break; case UNSPECIFIED: x509Cred = (X509Credential) credential; assertEquals("Unexpected value for certificate", idpRSACert, x509Cred.getEntityCertificate()); break; default: } } } /** * Test 1 protocol (BAR), and usage = signing. * Should get 1 credentials. * * @throws SecurityException */ public void testProtocolBARUsageSigning() throws SecurityException { mdCriteria.setProtocol(protocolBar); criteriaSet.add( new UsageCriteria(UsageType.SIGNING) ); List<Credential> resolved = new ArrayList<Credential>(); for (Credential credential : mdResolver.resolve(criteriaSet)) { resolved.add(credential); checkContextAndID(credential, idpEntityID, idpRole); } assertEquals("Incorrect number of credentials resolved", 1, resolved.size()); for (Credential credential : resolved) { X509Credential x509Cred; switch(credential.getUsageType()) { case SIGNING: fail("Credential was resolved from invalid protocol or usage"); break; case ENCRYPTION: fail("Credential was resolved from invalid protocol or usage"); break; case UNSPECIFIED: x509Cred = (X509Credential) credential; assertEquals("Unexpected value for certificate", idpRSACert, x509Cred.getEntityCertificate()); break; default: } } } /** * Test 1 protocol (BAR), and usage = encryption. * Should get 1 credentials. * * @throws SecurityException */ public void testProtocolBARUsageEncryption() throws SecurityException { mdCriteria.setProtocol(protocolBar); criteriaSet.add( new UsageCriteria(UsageType.ENCRYPTION) ); List<Credential> resolved = new ArrayList<Credential>(); for (Credential credential : mdResolver.resolve(criteriaSet)) { resolved.add(credential); checkContextAndID(credential, idpEntityID, idpRole); } assertEquals("Incorrect number of credentials resolved", 1, resolved.size()); for (Credential credential : resolved) { X509Credential x509Cred; switch(credential.getUsageType()) { case SIGNING: fail("Credential was resolved from invalid protocol or usage"); break; case ENCRYPTION: fail("Credential was resolved from invalid protocol or usage"); break; case UNSPECIFIED: x509Cred = (X509Credential) credential; assertEquals("Unexpected value for certificate", idpRSACert, x509Cred.getEntityCertificate()); break; default: } } } /** * Check expected entity ID and also that expected data is available from the metadata context. * * @param credential the credential to evaluate * @param entityID the expected entity ID value * @param role the expected type of role from the context role descriptor data */ private void checkContextAndID(Credential credential, String entityID, QName role) { assertEquals("Unexpected value found for credential entityID", entityID, credential.getEntityId()); SAMLMDCredentialContext mdContext = credential.getCredentalContextSet().get(SAMLMDCredentialContext.class); assertNotNull("SAMLMDCredentialContext was not available", mdContext); assertNotNull(mdContext.getRoleDescriptor()); RoleDescriptor contextRole = mdContext.getRoleDescriptor(); assertEquals("Unexpected value for context role descriptor", role, contextRole.getElementQName()); assertTrue(contextRole.getParent() instanceof EntityDescriptor); EntityDescriptor entityDescriptor = (EntityDescriptor) mdContext.getRoleDescriptor().getParent(); assertEquals("Unexpected value for entity descriptor entity ID", entityID, entityDescriptor.getEntityID()); assertTrue(entityDescriptor.getParent() instanceof EntitiesDescriptor); EntitiesDescriptor entitiesDescriptor = (EntitiesDescriptor) entityDescriptor.getParent(); assertNotNull(entitiesDescriptor.getExtensions()); assertNotNull(entitiesDescriptor.getExtensions().getUnknownXMLObjects().get(0)); } }