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 org.opensaml.xml.security.CriteriaSet;
20 import org.opensaml.xml.security.SecurityException;
21 import org.opensaml.xml.security.credential.Credential;
22 import org.opensaml.xml.security.keyinfo.KeyInfoCredentialResolver;
23 import org.opensaml.xml.security.keyinfo.KeyInfoCriteria;
24 import org.opensaml.xml.signature.Signature;
25 import org.opensaml.xml.signature.SignatureTrustEngine;
26 import org.opensaml.xml.signature.SignatureValidator;
27 import org.opensaml.xml.util.DatatypeHelper;
28 import org.opensaml.xml.validation.ValidationException;
29 import org.slf4j.Logger;
30 import org.slf4j.LoggerFactory;
31
32 /**
33 * A base implementation of {@link SignatureTrustEngine} which evaluates the validity and trustworthiness of XML and raw
34 * signatures.
35 *
36 * <p>
37 * When processing XML signatures, the supplied KeyInfoCredentialResolver will be used to resolve credential(s)
38 * containing the (advisory) signing key from the KeyInfo element of the Signature, if present. If any of these
39 * credentials do contain the valid signing key, they will be evaluated for trustworthiness against trusted information,
40 * which will be resolved in an implementation-specific manner.
41 *
42 * <p>
43 * Subclasses are required to implement {@link #evaluateTrust(Credential, Object)} using an implementation-specific
44 * trust model.
45 * </p>
46 *
47 * @param <TrustBasisType> the type of trusted information which has been resolved and which will serve as the basis for
48 * trust evaluation
49 *
50 */
51 public abstract class BaseSignatureTrustEngine<TrustBasisType> implements SignatureTrustEngine {
52
53 /** Class logger. */
54 private final Logger log = LoggerFactory.getLogger(BaseSignatureTrustEngine.class);
55
56 /** KeyInfo credential resolver used to obtain the signing credential from a Signature's KeyInfo. */
57 private KeyInfoCredentialResolver keyInfoCredentialResolver;
58
59 /**
60 * Constructor.
61 *
62 * @param keyInfoResolver KeyInfo credential resolver used to obtain the (advisory) signing credential from a
63 * Signature's KeyInfo element.
64 */
65 public BaseSignatureTrustEngine(KeyInfoCredentialResolver keyInfoResolver) {
66 if (keyInfoResolver == null) {
67 throw new IllegalArgumentException("KeyInfo credential resolver may not be null");
68 }
69
70 keyInfoCredentialResolver = keyInfoResolver;
71 }
72
73 /** {@inheritDoc} */
74 public KeyInfoCredentialResolver getKeyInfoResolver() {
75 return keyInfoCredentialResolver;
76 }
77
78 /**
79 * Attempt to establish trust by resolving signature verification credentials from the Signature's KeyInfo. If any
80 * credentials so resolved correctly verify the signature, attempt to establish trust using subclass-specific trust
81 * logic against trusted information as implemented in {@link #evaluateTrust(Credential, Object)}.
82 *
83 * @param signature the Signature to evaluate
84 * @param trustBasis the information which serves as the basis for trust evaluation
85 * @return true if the signature is verified by any KeyInfo-derived credential which can be established as trusted,
86 * otherwise false
87 * @throws SecurityException if an error occurs during signature verification or trust processing
88 */
89 protected boolean validate(Signature signature, TrustBasisType trustBasis) throws SecurityException {
90
91 log.debug("Attempting to verify signature and establish trust using KeyInfo-derived credentials");
92
93 if (signature.getKeyInfo() != null) {
94
95 KeyInfoCriteria keyInfoCriteria = new KeyInfoCriteria(signature.getKeyInfo());
96 CriteriaSet keyInfoCriteriaSet = new CriteriaSet(keyInfoCriteria);
97
98 for (Credential kiCred : getKeyInfoResolver().resolve(keyInfoCriteriaSet)) {
99 if (verifySignature(signature, kiCred)) {
100 log.debug("Successfully verified signature using KeyInfo-derived credential");
101 log.debug("Attempting to establish trust of KeyInfo-derived credential");
102 if (evaluateTrust(kiCred, trustBasis)) {
103 log.debug("Successfully established trust of KeyInfo-derived credential");
104 return true;
105 } else {
106 log.debug("Failed to establish trust of KeyInfo-derived credential");
107 }
108 }
109 }
110 } else {
111 log.debug("Signature contained no KeyInfo element, could not resolve verification credentials");
112 }
113
114 log.debug("Failed to verify signature and/or establish trust using any KeyInfo-derived credentials");
115 return false;
116 }
117
118 /**
119 * Evaluate the untrusted KeyInfo-derived credential with respect to the specified trusted information.
120 *
121 * @param untrustedCredential the untrusted credential being evaluated
122 * @param trustBasis the information which serves as the basis for trust evaluation
123 *
124 * @return true if the trust can be established for the untrusted credential, otherwise false
125 *
126 * @throws SecurityException if an error occurs during trust processing
127 */
128 protected abstract boolean evaluateTrust(Credential untrustedCredential, TrustBasisType trustBasis)
129 throws SecurityException;
130
131 /**
132 * Attempt to verify a signature using the key from the supplied credential.
133 *
134 * @param signature the signature on which to attempt verification
135 * @param credential the credential containing the candidate validation key
136 * @return true if the signature can be verified using the key from the credential, otherwise false
137 */
138 protected boolean verifySignature(Signature signature, Credential credential) {
139 SignatureValidator validator = new SignatureValidator(credential);
140 try {
141 validator.validate(signature);
142 } catch (ValidationException e) {
143 log.debug("Signature validation using candidate validation credential failed", e);
144 return false;
145 }
146
147 log.debug("Signature validation using candidate credential was successful");
148 return true;
149 }
150
151 /**
152 * Check the signature and credential criteria for required values.
153 *
154 * @param signature the signature to be evaluated
155 * @param trustBasisCriteria the set of trusted credential criteria
156 * @throws SecurityException thrown if required values are absent or otherwise invalid
157 */
158 protected void checkParams(Signature signature, CriteriaSet trustBasisCriteria) throws SecurityException {
159
160 if (signature == null) {
161 throw new SecurityException("Signature was null");
162 }
163 if (trustBasisCriteria == null) {
164 throw new SecurityException("Trust basis criteria set was null");
165 }
166 if (trustBasisCriteria.isEmpty()) {
167 throw new SecurityException("Trust basis criteria set was empty");
168 }
169 }
170
171 /**
172 * Check the signature and credential criteria for required values.
173 *
174 * @param signature the signature to be evaluated
175 * @param content the data over which the signature was computed
176 * @param algorithmURI the signing algorithm URI which was used
177 * @param trustBasisCriteria the set of trusted credential criteria
178 * @throws SecurityException thrown if required values are absent or otherwise invalid
179 */
180 protected void checkParamsRaw(byte[] signature, byte[] content, String algorithmURI, CriteriaSet trustBasisCriteria)
181 throws SecurityException {
182
183 if (signature == null || signature.length == 0) {
184 throw new SecurityException("Signature byte array was null or empty");
185 }
186 if (content == null || content.length == 0) {
187 throw new SecurityException("Content byte array was null or empty");
188 }
189 if (DatatypeHelper.isEmpty(algorithmURI)) {
190 throw new SecurityException("Signature algorithm was null or empty");
191 }
192 if (trustBasisCriteria == null) {
193 throw new SecurityException("Trust basis criteria set was null");
194 }
195 if (trustBasisCriteria.isEmpty()) {
196 throw new SecurityException("Trust basis criteria set was empty");
197 }
198 }
199
200 }