View Javadoc

1   /*
2    * Copyright [2007] [University Corporation for Advanced Internet Development, Inc.]
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.opensaml.xml.signature.impl;
18  
19  import java.util.Set;
20  
21  import org.opensaml.xml.security.CriteriaSet;
22  import org.opensaml.xml.security.SecurityException;
23  import org.opensaml.xml.security.SecurityHelper;
24  import org.opensaml.xml.security.SigningUtil;
25  import org.opensaml.xml.security.credential.Credential;
26  import org.opensaml.xml.security.keyinfo.KeyInfoCredentialResolver;
27  import org.opensaml.xml.security.x509.BasicX509CredentialNameEvaluator;
28  import org.opensaml.xml.security.x509.PKIXTrustEngine;
29  import org.opensaml.xml.security.x509.CertPathPKIXTrustEvaluator;
30  import org.opensaml.xml.security.x509.PKIXTrustEvaluator;
31  import org.opensaml.xml.security.x509.PKIXValidationInformation;
32  import org.opensaml.xml.security.x509.PKIXValidationInformationResolver;
33  import org.opensaml.xml.security.x509.X509Credential;
34  import org.opensaml.xml.security.x509.X509CredentialNameEvaluator;
35  import org.opensaml.xml.signature.Signature;
36  import org.opensaml.xml.signature.SignatureTrustEngine;
37  import org.opensaml.xml.util.Pair;
38  import org.slf4j.Logger;
39  import org.slf4j.LoggerFactory;
40  
41  /**
42   * An implementation of {@link SignatureTrustEngine} which evaluates the validity and trustworthiness of XML and raw
43   * signatures.
44   * 
45   * <p>
46   * Processing is performed as described in {@link BaseSignatureTrustEngine}. If based on this processing, it is
47   * determined that the Signature's KeyInfo is not present or does not contain a valid (and trusted) signing key, then
48   * trust engine validation fails. Since the PKIX engine is based on the assumption that trusted signing keys are not
49   * known in advance, the signing key must be present in, or derivable from, the information in the Signature's KeyInfo
50   * element.
51   * </p>
52   */
53  public class PKIXSignatureTrustEngine extends
54          BaseSignatureTrustEngine<Pair<Set<String>, Iterable<PKIXValidationInformation>>> implements
55          PKIXTrustEngine<Signature> {
56  
57      /** Class logger. */
58      private final Logger log = LoggerFactory.getLogger(PKIXSignatureTrustEngine.class);
59  
60      /** Resolver used for resolving trusted credentials. */
61      private PKIXValidationInformationResolver pkixResolver;
62  
63      /** The external PKIX trust evaluator used to establish trust. */
64      private PKIXTrustEvaluator pkixTrustEvaluator;
65      
66      /** The external credential name evaluator used to establish trusted name compliance. */
67      private X509CredentialNameEvaluator credNameEvaluator;
68  
69      /**
70       * Constructor.
71       * 
72       * <p>The PKIX trust evaluator used defaults to {@link CertPathPKIXTrustEvaluator}.</p>
73       * 
74       * <p>The X.509 credential name evaluator used defaults to {@link BasicX509CredentialNameEvaluator}.</p>
75       * 
76       * @param resolver credential resolver used to resolve trusted credentials.
77       * @param keyInfoResolver KeyInfo credential resolver used to obtain the (advisory) signing credential from a
78       *            Signature's KeyInfo element.
79       */
80      public PKIXSignatureTrustEngine(PKIXValidationInformationResolver resolver,
81              KeyInfoCredentialResolver keyInfoResolver) {
82  
83          super(keyInfoResolver);
84          if (resolver == null) {
85              throw new IllegalArgumentException("PKIX trust information resolver may not be null");
86          }
87          pkixResolver = resolver;
88  
89          pkixTrustEvaluator = new CertPathPKIXTrustEvaluator();
90          credNameEvaluator = new BasicX509CredentialNameEvaluator();
91      }
92  
93      /**
94       * Constructor.
95       * 
96       * @param resolver credential resolver used to resolve trusted credentials.
97       * @param keyInfoResolver KeyInfo credential resolver used to obtain the (advisory) signing credential from a
98       *            Signature's KeyInfo element.
99       * * @param pkixEvaluator the PKIX trust evaluator to use
100      * @param nameEvaluator the X.509 credential name evaluator to use (may be null)
101      */
102     public PKIXSignatureTrustEngine(PKIXValidationInformationResolver resolver,
103             KeyInfoCredentialResolver keyInfoResolver, PKIXTrustEvaluator pkixEvaluator, 
104             X509CredentialNameEvaluator nameEvaluator) {
105 
106         super(keyInfoResolver);
107         if (resolver == null) {
108             throw new IllegalArgumentException("PKIX trust information resolver may not be null");
109         }
110         pkixResolver = resolver;
111 
112         if (pkixEvaluator == null) {
113             throw new IllegalArgumentException("PKIX trust evaluator may not be null");
114         }
115         pkixTrustEvaluator = pkixEvaluator;
116         credNameEvaluator = nameEvaluator;
117     }
118     
119     /**
120      * Get the PKIXTrustEvaluator instance used to evalute trust.
121      * 
122      * <p>The parameters of this evaluator may be modified to
123      * adjust trust evaluation processing.</p>
124      * 
125      * @return the PKIX trust evaluator instance that will be used
126      */
127     public PKIXTrustEvaluator getPKIXTrustEvaluator() {
128         return pkixTrustEvaluator;
129     }
130     
131     /**
132      * Get the X509CredentialNameEvaluator instance used to evalute a credential 
133      * against trusted names.
134      * 
135      * <p>The parameters of this evaluator may be modified to
136      * adjust trust evaluation processing.</p>
137      * 
138      * @return the PKIX trust evaluator instance that will be used
139      */
140     public X509CredentialNameEvaluator getX509CredentialNameEvaluator() {
141         return credNameEvaluator;
142     }
143 
144     /** {@inheritDoc} */
145     public PKIXValidationInformationResolver getPKIXResolver() {
146         return pkixResolver;
147     }
148 
149     /** {@inheritDoc} */
150     public boolean validate(Signature signature, CriteriaSet trustBasisCriteria) throws SecurityException {
151 
152         checkParams(signature, trustBasisCriteria);
153 
154         Pair<Set<String>, Iterable<PKIXValidationInformation>> validationPair  = 
155             resolveValidationInfo(trustBasisCriteria);
156 
157         if (validate(signature, validationPair)) {
158             return true;
159         }
160 
161         log.error("PKIX validation of signature failed, unable to resolve valid and trusted signing key");
162         return false;
163     }
164 
165     /** {@inheritDoc} */
166     public boolean validate(byte[] signature, byte[] content, String algorithmURI, CriteriaSet trustBasisCriteria,
167             Credential candidateCredential) throws SecurityException {
168 
169         if (candidateCredential == null || SecurityHelper.extractVerificationKey(candidateCredential) == null) {
170             log.debug("Candidate credential was either not supplied or did not contain verification key");
171             log.debug("PKIX trust engine requires supplied key, skipping PKIX trust evaluation");
172             return false;
173         }
174 
175         checkParamsRaw(signature, content, algorithmURI, trustBasisCriteria);
176 
177         Pair<Set<String>, Iterable<PKIXValidationInformation>> validationPair = 
178             resolveValidationInfo(trustBasisCriteria);
179 
180         if (SigningUtil.verifyWithURI(candidateCredential, algorithmURI, signature, content)) {
181             log.debug("Successfully verified raw signature using supplied candidate credential");
182             log.debug("Attempting to establish trust of supplied candidate credential");
183             if (evaluateTrust(candidateCredential, validationPair)) {
184                 log.debug("Successfully established trust of supplied candidate credential");
185                 return true;
186             } else {
187                 log.debug("Failed to establish trust of supplied candidate credential");
188             }
189         } else {
190             log.error("Cryptographic verification of raw signature failed with candidate credential");
191         }
192 
193         log.error("PKIX validation of raw signature failed, "
194                 + "unable to establish trust of supplied verification credential");
195         return false;
196     }
197 
198     /** {@inheritDoc} */
199     protected boolean evaluateTrust(Credential untrustedCredential,
200             Pair<Set<String>, Iterable<PKIXValidationInformation>> validationPair) throws SecurityException {
201 
202         if (!(untrustedCredential instanceof X509Credential)) {
203             log.info("Can not evaluate trust of non-X509Credential");
204             return false;
205         }
206         X509Credential untrustedX509Credential = (X509Credential) untrustedCredential;
207 
208         Set<String> trustedNames = validationPair.getFirst();
209         Iterable<PKIXValidationInformation> validationInfoSet = validationPair.getSecond();
210         
211         if (!checkNames(trustedNames, untrustedX509Credential)) {
212             log.error("Evaluation of credential against trusted names failed. Aborting PKIX validation");
213             return false;
214         }
215 
216         for (PKIXValidationInformation validationInfo : validationInfoSet) {
217             try {
218                 if (pkixTrustEvaluator.validate(validationInfo, untrustedX509Credential)) {
219                     log.debug("Signature trust established via PKIX validation of signing credential");
220                     return true;
221                 }
222             } catch (SecurityException e) {
223                 // log the operational error, but allow other validation info sets to be tried
224                 log.error("Error performing PKIX validation on untrusted credential", e);
225             }
226         }
227 
228         log.debug("Signature trust could not be established via PKIX validation of signing credential");
229         return false;
230     }
231 
232     /**
233      * Resolve and return a set of trusted validation information.
234      * 
235      * @param trustBasisCriteria criteria used to describe and/or resolve the information which serves as the basis for
236      *            trust evaluation
237      * @return a pair consisting of an optional set of trusted names, and an iterable of trusted
238      *         PKIXValidationInformation
239      * @throws SecurityException thrown if there is an error resolving the information from the trusted resolver
240      */
241     protected Pair<Set<String>, Iterable<PKIXValidationInformation>> resolveValidationInfo(
242             CriteriaSet trustBasisCriteria) throws SecurityException {
243 
244         Set<String> trustedNames = null;
245         if (pkixResolver.supportsTrustedNameResolution()) {
246             trustedNames = pkixResolver.resolveTrustedNames(trustBasisCriteria);
247         } else {
248             log.debug("PKIX resolver does not support resolution of trusted names, skipping name checking");
249         }
250         Iterable<PKIXValidationInformation> validationInfoSet = pkixResolver.resolve(trustBasisCriteria);
251 
252         Pair<Set<String>, Iterable<PKIXValidationInformation>> validationPair = 
253             new Pair<Set<String>, Iterable<PKIXValidationInformation>>(trustedNames, validationInfoSet);
254 
255         return validationPair;
256     }
257     
258     /**
259      * Evaluate the credential against the set of trusted names.
260      * 
261      * <p>Evaluates to true if no intsance of {@link X509CredentialNameEvaluator} is configured.</p>
262      * 
263      * @param trustedNames set of trusted names
264      * @param untrustedCredential the credential being evaluated
265      * @return true if evaluation is successful, false otherwise
266      * @throws SecurityException thrown if there is an error evaluation the credential
267      */
268     protected boolean checkNames(Set<String> trustedNames, X509Credential untrustedCredential) 
269             throws SecurityException {
270         
271         if (credNameEvaluator == null) {
272             log.debug("No credential name evaluator was available, skipping trusted name evaluation");
273            return true; 
274         } else {
275             return credNameEvaluator.evaluate(untrustedCredential, trustedNames);
276         }
277 
278     }
279 
280 }