Adjustments to trust interface and providers for token handling.
authorcantor <cantor@ab3bd59b-922f-494d-bb5f-6f0a3c29deca>
Mon, 11 Apr 2005 22:28:30 +0000 (22:28 +0000)
committercantor <cantor@ab3bd59b-922f-494d-bb5f-6f0a3c29deca>
Mon, 11 Apr 2005 22:28:30 +0000 (22:28 +0000)
git-svn-id: https://subversion.switch.ch/svn/shibboleth/java-idp/trunk@1396 ab3bd59b-922f-494d-bb5f-6f0a3c29deca

src/edu/internet2/middleware/shibboleth/common/Trust.java
src/edu/internet2/middleware/shibboleth/common/provider/BasicTrust.java
src/edu/internet2/middleware/shibboleth/common/provider/ShibbolethTrust.java
src/edu/internet2/middleware/shibboleth/idp/provider/SAMLv1_1ArtifactQueryHandler.java
src/edu/internet2/middleware/shibboleth/idp/provider/SAMLv1_AttributeQueryHandler.java
tests/edu/internet2/middleware/shibboleth/common/TrustTests.java

index 3ac72e4..426a4f0 100644 (file)
@@ -27,6 +27,8 @@ package edu.internet2.middleware.shibboleth.common;
 
 import java.security.cert.X509Certificate;
 
+import org.opensaml.SAMLSignedObject;
+
 import edu.internet2.middleware.shibboleth.metadata.RoleDescriptor;
 
 /**
@@ -36,18 +38,46 @@ import edu.internet2.middleware.shibboleth.metadata.RoleDescriptor;
  */
 public interface Trust {
 
-       /**
+    /**
+     * Verifies that a certificate or ordered chain of certificates represents a valid credential set for a specific
+     * action by a specific entity.
+     * 
+     * @param certificateEE
+     *            the end-entity certificate being validated
+     * @param certificateChain
+     *            additional certificates supplied by the entity (may also contain the end-entity certificate)
+     * @param descriptor
+     *            the SAML 2 role descriptor of the entity purported to be performing the action
+     * @return true if the validation was successful and false if it was not successful
+     */
+    public boolean validate(X509Certificate certificateEE, X509Certificate[] certificateChain, RoleDescriptor descriptor);
+
+    /**
         * Verifies that a certificate or ordered chain of certificates represents a valid credential set for a specific
         * action by a specific entity.
         * 
-        * @param descriptor
-        *            the SAML 2 role descriptor of the entity purported to be performing the action
+     * @param certificateEE
+     *            the end-entity certificate being validated
         * @param certificateChain
-        *            the credentials supplied by the entity (if this contains a certificate chain, it should be ordered
-        *            with the end-entity certificate first
-        * @param keyUse
-        *            the action being performed (must be valid <code>KeyDescriptor</code> usage type
+        *            additional certificates supplied by the entity (may also contain the end-entity certificate)
+     * @param descriptor
+     *            the SAML 2 role descriptor of the entity purported to be performing the action
+     * @param checkName
+     *            whether the check the name of the certificate during the validation process, normally true unless
+     *  the name has already been checked as part of other processing (for example, TLS)
         * @return true if the validation was successful and false if it was not successful
         */
-       public boolean validate(RoleDescriptor descriptor, X509Certificate[] certificateChain, int keyUse);
+       public boolean validate(X509Certificate certificateEE, X509Certificate[] certificateChain, RoleDescriptor descriptor, boolean checkName);
+
+    /**
+     * Verifies that a certificate or ordered chain of certificates represents a valid credential set for a specific
+     * action by a specific entity.
+     * 
+     * @param token
+     *            the signed object to validate
+     * @param descriptor
+     *            the SAML 2 role descriptor of the entity purported to be performing the action
+     * @return true if the validation was successful and false if it was not successful
+     */
+    public boolean validate(SAMLSignedObject token, RoleDescriptor descriptor);
 }
\ No newline at end of file
index 17be741..e383026 100644 (file)
@@ -33,6 +33,7 @@ import java.util.Iterator;
 import org.apache.log4j.Logger;
 import org.apache.xml.security.keys.KeyInfo;
 import org.apache.xml.security.keys.keyresolver.KeyResolverException;
+import org.opensaml.SAMLSignedObject;
 
 import edu.internet2.middleware.shibboleth.common.Trust;
 import edu.internet2.middleware.shibboleth.metadata.KeyDescriptor;
@@ -48,12 +49,12 @@ public class BasicTrust implements Trust {
        private static Logger log = Logger.getLogger(BasicTrust.class.getName());
 
        /*
-        * @see edu.internet2.middleware.shibboleth.common.Trust#validate(edu.internet2.middleware.shibboleth.metadata.RoleDescriptor,
-        *      java.security.cert.X509Certificate[], int)
+        * @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(RoleDescriptor descriptor, X509Certificate[] certificateChain, int keyUse) {
+       public boolean validate(X509Certificate certificateEE, X509Certificate[] certificateChain, RoleDescriptor descriptor, boolean checkName) {
 
-               if (descriptor == null || certificateChain == null || certificateChain.length < 1) {
+               if (descriptor == null || certificateEE == null) {
                        log.error("Appropriate data was not supplied for trust evaluation.");
                        return false;
                }
@@ -63,8 +64,8 @@ public class BasicTrust implements Trust {
                while (keyDescriptors.hasNext()) {
                        // Look for a key descriptor with the right usage bits
                        KeyDescriptor keyDescriptor = (KeyDescriptor) keyDescriptors.next();
-                       if (keyDescriptor.getUse() != KeyDescriptor.UNSPECIFIED && keyDescriptor.getUse() != keyUse) {
-                               log.debug("Role contains a key descriptor, but the usage specification is not valid for this action.");
+                       if (keyDescriptor.getUse() == KeyDescriptor.ENCRYPTION) {
+                               log.debug("Skipping key descriptor with inappropriate usage indicator.");
                                continue;
                        }
 
@@ -75,8 +76,7 @@ public class BasicTrust implements Trust {
                                log.debug("Attempting to match X509 certificate.");
                                try {
                                        X509Certificate metaCert = keyInfo.getX509Certificate();
-                                       if (certificateChain != null && certificateChain.length > 0
-                                                       && Arrays.equals(metaCert.getEncoded(), certificateChain[0].getEncoded())) {
+                                       if (Arrays.equals(metaCert.getEncoded(), certificateEE.getEncoded())) {
                                                log.debug("Match successful.");
                                                return true;
                                        } else {
@@ -92,4 +92,25 @@ public class BasicTrust implements Trust {
                }
                return false;
        }
+
+    /*
+     * @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) {
+        // TODO Auto-generated method stub
+        
+        /*
+         * Proposed algorithm for this is just to walk each KeyDescriptor with keyUse of signing
+         * and try and extract a public key to use and try and verify the token. If it works, we're
+         * done.
+         */ 
+        return false;
+    }
 }
\ No newline at end of file
index 67d3216..63da40b 100644 (file)
@@ -59,6 +59,7 @@ import org.apache.xml.security.keys.content.KeyName;
 import org.apache.xml.security.keys.content.X509Data;
 import org.apache.xml.security.keys.content.x509.XMLX509CRL;
 import org.apache.xml.security.keys.content.x509.XMLX509Certificate;
+import org.opensaml.SAMLSignedObject;
 
 import edu.internet2.middleware.shibboleth.common.Trust;
 import edu.internet2.middleware.shibboleth.metadata.EntitiesDescriptor;
@@ -80,18 +81,41 @@ 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(edu.internet2.middleware.shibboleth.metadata.RoleDescriptor,
-        *      java.security.cert.X509Certificate[], int)
-        */
-       public boolean validate(RoleDescriptor descriptor, X509Certificate[] certificateChain, int keyUse) {
+    /*
+     * @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) {
+        // TODO Auto-generated method stub
+        
+        /*
+         * Proposed algorithm for this (will modify C++ to match:
+         * 
+         * - get the certificates from the token
+         * - iterate over them in order, until one verifies the signature
+         * - pass that as the EE cert to the other validate method, with the full set as a chain
+         */ 
+        return false;
+    }
+
+    /*
+     * @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(descriptor, certificateChain, keyUse);
+               boolean defaultValidation = super.validate(certificateEE, certificateChain, descriptor, checkName);
                if (defaultValidation == true) { return true; }
 
                // Make sure we have the data we need
-               if (descriptor == null || certificateChain == null || certificateChain.length < 1) {
+               if (descriptor == null || certificateEE == null) {
                        log.error("Appropriate data was not supplied for trust evaluation.");
                        return false;
                }
@@ -100,42 +124,50 @@ 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
-               Iterator keyDescriptors = descriptor.getKeyDescriptors();
-               while (keyDescriptors.hasNext()) {
-                       // Look for a key descriptor with the right usage bits
-                       KeyDescriptor keyDescriptor = (KeyDescriptor) keyDescriptors.next();
-                       if (keyDescriptor.getUse() != KeyDescriptor.UNSPECIFIED && keyDescriptor.getUse() != keyUse) {
-                               log.debug("Role contains a key descriptor, but the usage specification is not valid for this action.");
-                               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))) {
-                                                       // If we find a match, try to do path validation against any key authorities we might have
-                                                       // in the metadata
-                                                       if (pkixValidate(certificateChain, descriptor.getEntityDescriptor())) { return true; }
-                                               }
-                                       } catch (XMLSecurityException e) {
-                                               log.error("Problem retrieving key name from metadata: " + e);
-                                       }
-                               }
-                       }
-               }
-               return false;
+        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;    
        }
 
-       private boolean pkixValidate(X509Certificate[] certChain, EntityDescriptor entity) {
+       private boolean pkixValidate(X509Certificate certEE, X509Certificate[] certChain, EntityDescriptor entity) {
 
                if (entity instanceof ExtendedEntityDescriptor) {
                        Iterator keyAuthorities = ((ExtendedEntityDescriptor) entity).getKeyAuthorities();
                        // if we have any key authorities, construct a flat list of trust anchors representing each and attempt to
                        // validate against them in turn
                        while (keyAuthorities.hasNext()) {
-                               if (pkixValidate(certChain, (KeyAuthority) keyAuthorities.next())) { return true; }
+                               if (pkixValidate(certEE, certChain, (KeyAuthority) keyAuthorities.next())) { return true; }
                        }
                }
 
@@ -143,14 +175,14 @@ public class ShibbolethTrust extends BasicTrust implements Trust {
                // nested entities and attempt to validate at each group level
                EntitiesDescriptor group = entity.getEntitiesDescriptor();
                if (group != null) {
-                       if (pkixValidate(certChain, group)) { return true; }
+                       if (pkixValidate(certEE, certChain, group)) { return true; }
                }
 
                // We've walked the entire metadata chain with no success, so fail
                return false;
        }
 
-       private boolean pkixValidate(X509Certificate[] certChain, EntitiesDescriptor group) {
+       private boolean pkixValidate(X509Certificate certEE, X509Certificate[] certChain, EntitiesDescriptor group) {
 
                log.debug("Attemping to validate against parent group.");
                if (group instanceof ExtendedEntitiesDescriptor) {
@@ -158,20 +190,20 @@ public class ShibbolethTrust extends BasicTrust implements Trust {
                        // if we have any key authorities, construct a flat list of trust anchors representing each and attempt to
                        // validate against them in turn
                        while (keyAuthorities.hasNext()) {
-                               if (pkixValidate(certChain, (KeyAuthority) keyAuthorities.next())) { return true; }
+                               if (pkixValidate(certEE, certChain, (KeyAuthority) keyAuthorities.next())) { return true; }
                        }
                }
 
                // If not, attempt to walk up the chain for validation
                EntitiesDescriptor parent = group.getEntitiesDescriptor();
                if (parent != null) {
-                       if (pkixValidate(certChain, parent)) { return true; }
+                       if (pkixValidate(certEE, certChain, parent)) { return true; }
                }
 
                return false;
        }
 
-       private boolean pkixValidate(X509Certificate[] certChain, KeyAuthority authority) {
+       private boolean pkixValidate(X509Certificate certEE, X509Certificate[] certChain, KeyAuthority authority) {
 
                Set anchors = new HashSet();
                Set crls = new HashSet();
@@ -216,7 +248,7 @@ public class ShibbolethTrust extends BasicTrust implements Trust {
                                CertPathValidator validator = CertPathValidator.getInstance("PKIX");
 
                                X509CertSelector selector = new X509CertSelector();
-                               selector.setCertificate(certChain[0]);
+                               selector.setCertificate(certEE);
                                PKIXBuilderParameters params = new PKIXBuilderParameters(anchors, selector);
                                params.setMaxPathLength(authority.getVerifyDepth());
                                List storeMaterial = new ArrayList(crls);
index 17e44c1..407d555 100644 (file)
@@ -135,9 +135,8 @@ public class SAMLv1_1ArtifactQueryHandler extends BaseServiceHandler implements
                                }
 
                                // Make sure that the suppplied credential is valid for the provider to which the artifact was issued
-                               if (!support.getTrust().validate(role,
-                                               (X509Certificate[]) request.getAttribute("javax.servlet.request.X509Certificate"),
-                                               KeyDescriptor.ENCRYPTION)) {
+                X509Certificate[] chain = (X509Certificate[])request.getAttribute("javax.servlet.request.X509Certificate");
+                               if (!support.getTrust().validate((chain != null && chain.length > 0) ? chain[0] : null, chain, role)) {
                                        log.error("Supplied credential ("
                                                        + credential.getSubjectX500Principal().getName(X500Principal.RFC2253)
                                                        + ") is NOT valid for provider (" + mapping.getServiceProviderId()
index 158b2b4..5f413b7 100644 (file)
@@ -131,9 +131,8 @@ public class SAMLv1_AttributeQueryHandler extends BaseServiceHandler implements
 
                                // Make sure that the suppplied credential is valid for the
                                // selected relying party
-                               if (support.getTrust().validate(role,
-                                               (X509Certificate[]) req.getAttribute("javax.servlet.request.X509Certificate"),
-                                               KeyDescriptor.ENCRYPTION)) {
+                X509Certificate[] chain = (X509Certificate[])req.getAttribute("javax.servlet.request.X509Certificate");
+                if (support.getTrust().validate((chain != null && chain.length > 0) ? chain[0] : null, chain, role)) {
                                        log.info("Supplied credential validated for this provider.");
                                        log.info("Request from service provider: (" + relyingParty.getProviderId() + ").");
                                        return relyingParty.getProviderId();
index 786b930..1a73566 100644 (file)
@@ -96,7 +96,7 @@ public class TrustTests extends TestCase {
 
                        // Try to validate against the metadata
                        Trust validator = new BasicTrust();
-                       boolean successful = validator.validate(role, new X509Certificate[]{cert}, KeyDescriptor.ENCRYPTION);
+                       boolean successful = validator.validate(cert, new X509Certificate[]{cert}, role);
                        if (!successful) {
                                fail("Validation should have succeeded.");
                        }
@@ -133,7 +133,7 @@ public class TrustTests extends TestCase {
 
                        // Try to validate against the metadata
                        Trust validator = new BasicTrust();
-                       boolean successful = validator.validate(role, new X509Certificate[]{cert}, KeyDescriptor.ENCRYPTION);
+                       boolean successful = validator.validate(cert, new X509Certificate[]{cert}, role);
                        if (successful) {
                                fail("Validation should have failed.");
                        }
@@ -170,7 +170,7 @@ public class TrustTests extends TestCase {
 
                        // Try to validate against the metadata
                        Trust validator = new ShibbolethTrust();
-                       boolean successful = validator.validate(role, new X509Certificate[]{cert}, KeyDescriptor.ENCRYPTION);
+                       boolean successful = validator.validate(cert, new X509Certificate[]{cert}, role);
                        if (!successful) {
                                fail("Validation should have succeeded.");
                        }
@@ -207,7 +207,7 @@ public class TrustTests extends TestCase {
 
                        // Try to validate against the metadata
                        Trust validator = new ShibbolethTrust();
-                       boolean successful = validator.validate(role, new X509Certificate[]{cert}, KeyDescriptor.ENCRYPTION);
+                       boolean successful = validator.validate(cert, new X509Certificate[]{cert}, role);
                        if (!successful) {
                                fail("Validation should have succeeded.");
                        }
@@ -244,7 +244,7 @@ public class TrustTests extends TestCase {
 
                        // Try to validate against the metadata
                        Trust validator = new ShibbolethTrust();
-                       boolean successful = validator.validate(role, new X509Certificate[]{cert}, KeyDescriptor.ENCRYPTION);
+                       boolean successful = validator.validate(cert, new X509Certificate[]{cert}, role);
                        if (!successful) {
                                fail("Validation should have succeeded.");
                        }
@@ -282,8 +282,7 @@ public class TrustTests extends TestCase {
 
                        // Try to validate against the metadata
                        Trust validator = new ShibbolethTrust();
-                       boolean successful = validator.validate(role, new X509Certificate[]{endEntity, intermediate},
-                                       KeyDescriptor.ENCRYPTION);
+                       boolean successful = validator.validate(endEntity, new X509Certificate[]{endEntity, intermediate}, role);
                        if (successful) {
                                fail("Validation should not have succeeded.");
                        }
@@ -321,8 +320,7 @@ public class TrustTests extends TestCase {
 
                        // Try to validate against the metadata
                        Trust validator = new ShibbolethTrust();
-                       boolean successful = validator.validate(role, new X509Certificate[]{endEntity, intermediate},
-                                       KeyDescriptor.ENCRYPTION);
+                       boolean successful = validator.validate(endEntity, new X509Certificate[]{endEntity, intermediate}, role);
                        if (!successful) {
                                fail("Validation should have succeeded.");
                        }
@@ -359,7 +357,7 @@ public class TrustTests extends TestCase {
 
                        // Try to validate against the metadata
                        Trust validator = new ShibbolethTrust();
-                       boolean successful = validator.validate(role, new X509Certificate[]{cert}, KeyDescriptor.ENCRYPTION);
+                       boolean successful = validator.validate(cert, new X509Certificate[]{cert}, role);
                        if (successful) {
                                fail("Validation should not have succeeded.");
                        }
@@ -396,7 +394,7 @@ public class TrustTests extends TestCase {
 
                        // Try to validate against the metadata
                        Trust validator = new ShibbolethTrust();
-                       boolean successful = validator.validate(role, new X509Certificate[]{cert}, KeyDescriptor.ENCRYPTION);
+                       boolean successful = validator.validate(cert,  new X509Certificate[]{cert}, role);
                        if (!successful) {
                                fail("Validation should have succeeded.");
                        }