Fixed a bug in trust processing that would cause validation to fail when empty CRLs...
authorwassa <wassa@ab3bd59b-922f-494d-bb5f-6f0a3c29deca>
Wed, 11 May 2005 23:50:41 +0000 (23:50 +0000)
committerwassa <wassa@ab3bd59b-922f-494d-bb5f-6f0a3c29deca>
Wed, 11 May 2005 23:50:41 +0000 (23:50 +0000)
Certificates can now be match based on entity IDs in addition to key names.  Bugzilla #282.

git-svn-id: https://subversion.switch.ch/svn/shibboleth/java-idp/trunk@1470 ab3bd59b-922f-494d-bb5f-6f0a3c29deca

data/metadata9.xml [new file with mode: 0644]
src/edu/internet2/middleware/shibboleth/common/provider/ShibbolethTrust.java
tests/edu/internet2/middleware/shibboleth/common/TrustTests.java

diff --git a/data/metadata9.xml b/data/metadata9.xml
new file mode 100644 (file)
index 0000000..3c54edf
--- /dev/null
@@ -0,0 +1,39 @@
+<?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
index b70e435..8a97602 100644 (file)
@@ -38,6 +38,7 @@ import java.security.cert.PKIXBuilderParameters;
 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;
@@ -82,24 +83,27 @@ public class ShibbolethTrust extends BasicTrust implements Trust {
        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 {
@@ -118,24 +122,24 @@ public class ShibbolethTrust extends BasicTrust implements Trust {
                                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);
@@ -151,40 +155,47 @@ public class ShibbolethTrust extends BasicTrust implements Trust {
 
                // 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) {
@@ -239,7 +250,7 @@ public class ShibbolethTrust extends BasicTrust implements Trust {
                        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()) {
@@ -253,8 +264,11 @@ public class ShibbolethTrust extends BasicTrust implements Trust {
                                                        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);
                                                                }
@@ -289,7 +303,7 @@ public class ShibbolethTrust extends BasicTrust implements Trust {
                                } else {
                                        params.setRevocationEnabled(false);
                                }
-
+                               // System.err.println(params.toString());
                                CertPathBuilder builder = CertPathBuilder.getInstance("PKIX");
                                PKIXCertPathBuilderResult buildResult = (PKIXCertPathBuilderResult) builder.build(params);
 
@@ -352,6 +366,37 @@ public class ShibbolethTrust extends BasicTrust implements Trust {
                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));
index 637e8e3..0e184d4 100644 (file)
@@ -412,4 +412,41 @@ public class TrustTests extends TestCase {
                        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