--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<EntitiesDescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="urn:oasis:names:tc:SAML:2.0:metadata ../schemas/sstc-saml-schema-metadata-2.0.xsd urn:mace:shibboleth:metadata:1.0 ../schemas/shibboleth-metadata-1.0.xsd"
+ Name="urn-x:testFed1" validUntil="3010-01-01T00:00:00Z">
+ <EntityDescriptor entityID="Walter Hoehn">
+ <Extensions>
+ <KeyAuthority xmlns="urn:mace:shibboleth:metadata:1.0">
+ <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
+ <ds:X509Data>
+ <ds:X509Certificate>MIIC9zCCArQCBEJMcbswCwYHKoZIzjgEAwUAMGExCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJUTjEQ
+MA4GA1UEBxMHTWVtcGhpczENMAsGA1UEChMEVGVzdDENMAsGA1UECxMEVGVzdDEVMBMGA1UEAxMM
+V2FsdGVyIEhvZWhuMB4XDTA1MDMzMTIxNTUwN1oXDTA1MDYyOTIxNTUwN1owYTELMAkGA1UEBhMC
+VVMxCzAJBgNVBAgTAlROMRAwDgYDVQQHEwdNZW1waGlzMQ0wCwYDVQQKEwRUZXN0MQ0wCwYDVQQL
+EwRUZXN0MRUwEwYDVQQDEwxXYWx0ZXIgSG9laG4wggG3MIIBLAYHKoZIzjgEATCCAR8CgYEA/X9T
+gR11EilS30qcLuzk5/YRt1I870QAwx4/gLZRJmlFXUAiUftZPY1Y+r/F9bow9subVWzXgTuAHTRv
+8mZgt2uZUKWkn5/oBHsQIsJPu6nX/rfGG/g7V+fGqKYVDwT7g/bTxR7DAjVUE1oWkTL2dfOuK2HX
+Ku/yIgMZndFIAccCFQCXYFCPFSMLzLKSuYKi64QL8Fgc9QKBgQD34aCF1ps93su8q1w2uFe5eZSv
+u/o66oL5V0wLPQeCZ1FZV4661FlP5nEHEIGAtEkWcSPoTCgWE7fPCTKMyKbhPBZ6i1R8jSjgo64e
+K7OmdZFuo38L+iE1YvH7YnoBJDvMpPG+qFGQiaiD3+Fa5Z8GkotmXoB7VSVkAUw7/s9JKgOBhAAC
+gYBGtD+qJdBIzaA/a0oeO/LhW06r9dsPz0LnBD7DLZAFaWpMbfaItwXWANCFleNPzou/mU8+bhOe
+FJ+fkGdW4zbg8lzLOOeRduELoO8srzOdyccmRIeRIkDVj0ckienw0skgKgAvilUWkDQcdpLvtfjl
+W7vFedSYzRQiLso+dFHanDALBgcqhkjOOAQDBQADMAAwLQIUU5z1Ppc7gbjkl3PhkiQghUoTm5YC
+FQCVg3Ej3Sdb8FpJwUXoqO4J6FU/UQ==
+</ds:X509Certificate>
+ </ds:X509Data>
+ </ds:KeyInfo>
+ </KeyAuthority>
+ </Extensions>
+ <SPSSODescriptor
+ protocolSupportEnumeration="urn:oasis:names:tc:SAML:1.1:protocol">
+ <NameIDFormat>urn:mace:shibboleth:1.0:nameIdentifier</NameIDFormat>
+ <AssertionConsumerService index="1"
+ Binding="urn:oasis:names:tc:SAML:1.0:profiles:artifact-01"
+ Location="https://www.example.org/Shibboleth.shire"/>
+ </SPSSODescriptor>
+
+ </EntityDescriptor>
+</EntitiesDescriptor>
\ No newline at end of file
import java.security.cert.PKIXCertPathBuilderResult;
import java.security.cert.PKIXCertPathValidatorResult;
import java.security.cert.TrustAnchor;
+import java.security.cert.X509CRL;
import java.security.cert.X509CertSelector;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
private static Logger log = Logger.getLogger(ShibbolethTrust.class.getName());
private static Pattern regex = Pattern.compile(".*?CN=([^,/]+).*");
- /*
- * @see edu.internet2.middleware.shibboleth.common.Trust#validate(java.security.cert.X509Certificate, java.security.cert.X509Certificate[], edu.internet2.middleware.shibboleth.metadata.RoleDescriptor)
- */
- public boolean validate(X509Certificate certificateEE, X509Certificate[] certificateChain, RoleDescriptor descriptor) {
- return validate(certificateEE, certificateChain, descriptor, true);
- }
-
- /*
- * @see edu.internet2.middleware.shibboleth.common.Trust#validate(org.opensaml.SAMLSignedObject, edu.internet2.middleware.shibboleth.metadata.RoleDescriptor)
- */
- public boolean validate(SAMLSignedObject token, RoleDescriptor descriptor) {
- if (super.validate(token,descriptor))
- return true;
-
+ /*
+ * @see edu.internet2.middleware.shibboleth.common.Trust#validate(java.security.cert.X509Certificate,
+ * java.security.cert.X509Certificate[], edu.internet2.middleware.shibboleth.metadata.RoleDescriptor)
+ */
+ public boolean validate(X509Certificate certificateEE, X509Certificate[] certificateChain, RoleDescriptor descriptor) {
+
+ return validate(certificateEE, certificateChain, descriptor, true);
+ }
+
+ /*
+ * @see edu.internet2.middleware.shibboleth.common.Trust#validate(org.opensaml.SAMLSignedObject,
+ * edu.internet2.middleware.shibboleth.metadata.RoleDescriptor)
+ */
+ public boolean validate(SAMLSignedObject token, RoleDescriptor descriptor) {
+
+ if (super.validate(token, descriptor)) return true;
+
/* Certificates supplied with the signed object */
- ArrayList/*<X509Certificate>*/ certificates = new ArrayList/*<X509Certificate>*/();
+ ArrayList/* <X509Certificate> */certificates = new ArrayList/* <X509Certificate> */();
X509Certificate certificateEE = null;
-
+
/* Iterate to count the certificates, and look for the signer */
Iterator icertificates;
try {
certificates.add(certificate);
}
}
-
- if (certificateEE==null)
- return false; // No key validates the signature
-
+
+ if (certificateEE == null) return false; // No key validates the signature
+
// With a count we can now build a typed array
X509Certificate[] certificateChain = new X509Certificate[certificates.size()];
- int i=0;
- for (icertificates = certificates.iterator();icertificates.hasNext();) {
- certificateChain[i++]=(X509Certificate) icertificates.next();
+ int i = 0;
+ for (icertificates = certificates.iterator(); icertificates.hasNext();) {
+ certificateChain[i++] = (X509Certificate) icertificates.next();
}
- return validate(certificateEE, certificateChain, descriptor);
- }
+ return validate(certificateEE, certificateChain, descriptor);
+ }
- /*
- * @see edu.internet2.middleware.shibboleth.common.Trust#validate(java.security.cert.X509Certificate,
- * java.security.cert.X509Certificate[], edu.internet2.middleware.shibboleth.metadata.RoleDescriptor, boolean)
- */
- public boolean validate(X509Certificate certificateEE, X509Certificate[] certificateChain, RoleDescriptor descriptor, boolean checkName) {
+ /*
+ * @see edu.internet2.middleware.shibboleth.common.Trust#validate(java.security.cert.X509Certificate,
+ * java.security.cert.X509Certificate[], edu.internet2.middleware.shibboleth.metadata.RoleDescriptor, boolean)
+ */
+ public boolean validate(X509Certificate certificateEE, X509Certificate[] certificateChain,
+ RoleDescriptor descriptor, boolean checkName) {
// If we can successfully validate with an inline key, that's fine
boolean defaultValidation = super.validate(certificateEE, certificateChain, descriptor, checkName);
// First, we want to see if we can match a keyName from the metadata against the cert
// Iterator through all the keys in the metadata
- if (checkName) {
- Iterator keyDescriptors = descriptor.getKeyDescriptors();
- while (checkName && keyDescriptors.hasNext()) {
- // Look for a key descriptor with the right usage bits
- KeyDescriptor keyDescriptor = (KeyDescriptor) keyDescriptors.next();
- if (keyDescriptor.getUse() == KeyDescriptor.ENCRYPTION) {
- log.debug("Skipping key descriptor with inappropriate usage indicator.");
- continue;
- }
-
- // We found one, see if we can match the metadata's keyName against the cert
- KeyInfo keyInfo = keyDescriptor.getKeyInfo();
- if (keyInfo.containsKeyName()) {
- for (int i = 0; i < keyInfo.lengthKeyName(); i++) {
- try {
- if (matchKeyName(certificateChain[0], keyInfo.itemKeyName(i))) {
- checkName = false;
- break;
- }
- } catch (XMLSecurityException e) {
- log.error("Problem retrieving key name from metadata: " + e);
- }
- }
- }
- }
- }
-
- if (checkName) {
- log.error("cannot match certificate subject against acceptable key names based on KeyDescriptors");
- return false;
- }
-
- if (pkixValidate(certificateEE, certificateChain, descriptor.getEntityDescriptor())) { return true; }
- return false;
+ if (checkName) {
+
+ if (matchProviderId(certificateChain[0], descriptor.getEntityDescriptor().getId())) {
+ checkName = false;
+ } else {
+
+ Iterator keyDescriptors = descriptor.getKeyDescriptors();
+ while (checkName && keyDescriptors.hasNext()) {
+ // Look for a key descriptor with the right usage bits
+ KeyDescriptor keyDescriptor = (KeyDescriptor) keyDescriptors.next();
+ if (keyDescriptor.getUse() == KeyDescriptor.ENCRYPTION) {
+ log.debug("Skipping key descriptor with inappropriate usage indicator.");
+ continue;
+ }
+
+ // We found one, see if we can match the metadata's keyName against the cert
+ KeyInfo keyInfo = keyDescriptor.getKeyInfo();
+ if (keyInfo.containsKeyName()) {
+ for (int i = 0; i < keyInfo.lengthKeyName(); i++) {
+ try {
+ if (matchKeyName(certificateChain[0], keyInfo.itemKeyName(i))) {
+ checkName = false;
+ break;
+ }
+ } catch (XMLSecurityException e) {
+ log.error("Problem retrieving key name from metadata: " + e);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (checkName) {
+ log.error("cannot match certificate subject against acceptable key names based on the "
+ + "metadata entityId or KeyDescriptors");
+ return false;
+ }
+
+ if (pkixValidate(certificateEE, certificateChain, descriptor.getEntityDescriptor())) { return true; }
+ return false;
}
private boolean pkixValidate(X509Certificate certEE, X509Certificate[] certChain, EntityDescriptor entity) {
KeyInfo keyInfo = (KeyInfo) keyInfos.next();
if (keyInfo.containsX509Data()) {
try {
- //Add all certificates in the authority as trust anchors
+ // Add all certificates in the authority as trust anchors
for (int i = 0; i < keyInfo.lengthX509Data(); i++) {
X509Data data = keyInfo.itemX509Data(i);
if (data.containsCertificate()) {
for (int j = 0; j < data.lengthCRL(); j++) {
XMLX509CRL xmlCrl = data.itemCRL(j);
try {
- crls.add(CertificateFactory.getInstance("X.509").generateCRL(
- new ByteArrayInputStream(xmlCrl.getCRLBytes())));
+ X509CRL crl = (X509CRL) CertificateFactory.getInstance("X.509").generateCRL(
+ new ByteArrayInputStream(xmlCrl.getCRLBytes()));
+ if (crl.getRevokedCertificates() != null && crl.getRevokedCertificates().size() > 0) {
+ crls.add(crl);
+ }
} catch (GeneralSecurityException e) {
log.error("Encountered an error parsing CRL from shibboleth metadata: " + e);
}
} else {
params.setRevocationEnabled(false);
}
-
+ // System.err.println(params.toString());
CertPathBuilder builder = CertPathBuilder.getInstance("PKIX");
PKIXCertPathBuilderResult buildResult = (PKIXCertPathBuilderResult) builder.build(params);
return false;
}
+ private static boolean matchProviderId(X509Certificate certificate, String id) {
+
+ // Try matching against URI Subject Alt Names
+ try {
+ Collection altNames = certificate.getSubjectAlternativeNames();
+ if (altNames != null) {
+ for (Iterator nameIterator = altNames.iterator(); nameIterator.hasNext();) {
+ List altName = (List) nameIterator.next();
+ if (altName.get(0).equals(new Integer(6))) { // 6 is URI
+ if (altName.get(0).equals(id)) {
+ log.debug("Entity ID matched against SubjectAltName.");
+ return true;
+ }
+ }
+ }
+ }
+ } catch (CertificateParsingException e1) {
+ log.error("Encountered an problem trying to extract Subject Alternate "
+ + "Name from supplied certificate: " + e1);
+ }
+
+ // If that doesn't work, try to match using
+ // SSL-style hostname matching
+ if (getHostNameFromDN(certificate.getSubjectX500Principal()).equals(id)) {
+ log.debug("Entity ID matched against hostname.");
+ return true;
+ }
+
+ return false;
+ }
+
private static String getHostNameFromDN(X500Principal dn) {
Matcher matches = regex.matcher(dn.getName(X500Principal.RFC2253));
fail("Error in test specification: " + e);
}
}
+
+ public void testPkixX509CertValidateWithExactProviderIdMatch() {
+
+ try {
+ // Pull the role descriptor from example metadata
+ Metadata metadata = new XMLMetadata(new File("data/metadata9.xml").toURL().toString());
+ EntityDescriptor entity = metadata.lookup("Walter Hoehn");
+ SPSSODescriptor role = (SPSSODescriptor) entity.getRoleByType(SPSSODescriptor.class,
+ "urn:oasis:names:tc:SAML:1.1:protocol");
+
+ // Use a pre-defined cert
+ KeyStore keyStore = KeyStore.getInstance("JKS");
+ keyStore.load(new ShibResource(new File("data/trusttest.jks").toURL().toString()).getInputStream(),
+ new char[]{'t', 'e', 's', 't', '1', '2', '3'});
+ X509Certificate cert = (X509Certificate) keyStore.getCertificate("inliine1");
+
+ // Try to validate against the metadata
+ Trust validator = new ShibbolethTrust();
+ boolean successful = validator.validate(cert, new X509Certificate[]{cert}, role);
+ if (!successful) {
+ fail("Validation should have succeeded.");
+ }
+
+ } catch (MetadataException e) {
+ fail("Error in test specification: " + e);
+ } catch (ResourceNotAvailableException e) {
+ fail("Error in test specification: " + e);
+ } catch (IOException e) {
+ fail("Error in test specification: " + e);
+ } catch (NoSuchAlgorithmException e) {
+ fail("Error in test specification: " + e);
+ } catch (CertificateException e) {
+ fail("Error in test specification: " + e);
+ } catch (KeyStoreException e) {
+ fail("Error in test specification: " + e);
+ }
+ }
}
\ No newline at end of file