Add credential context APIs, refactor criteria matching, auto-extract criteria from...
[shibboleth/cpp-xmltooling.git] / xmltooling / security / impl / ExplicitKeyTrustEngine.cpp
1 /*
2  *  Copyright 2001-2007 Internet2
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 /**
18  * ExplicitKeyTrustEngine.cpp
19  * 
20  * TrustEngine based on explicit knowledge of peer key information.
21  */
22
23 #include "internal.h"
24 #include "security/Credential.h"
25 #include "security/CredentialCriteria.h"
26 #include "security/CredentialResolver.h"
27 #include "security/OpenSSLTrustEngine.h"
28 #include "signature/SignatureValidator.h"
29 #include "util/NDC.h"
30
31 #include <log4cpp/Category.hh>
32 #include <xercesc/util/XMLUniDefs.hpp>
33 #include <xsec/enc/OpenSSL/OpenSSLCryptoKeyDSA.hpp>
34 #include <xsec/enc/OpenSSL/OpenSSLCryptoKeyRSA.hpp>
35 #include <xsec/enc/OpenSSL/OpenSSLCryptoX509.hpp>
36
37 using namespace xmlsignature;
38 using namespace xmltooling;
39 using namespace log4cpp;
40 using namespace std;
41
42 namespace xmltooling {
43     class XMLTOOL_DLLLOCAL ExplicitKeyTrustEngine : public OpenSSLTrustEngine
44     {
45     public:
46         ExplicitKeyTrustEngine(const DOMElement* e) : OpenSSLTrustEngine(e) {}
47         virtual ~ExplicitKeyTrustEngine() {}
48
49         virtual bool validate(
50             Signature& sig,
51             const CredentialResolver& credResolver,
52             CredentialCriteria* criteria=NULL
53             ) const;
54         virtual bool validate(
55             const XMLCh* sigAlgorithm,
56             const char* sig,
57             KeyInfo* keyInfo,
58             const char* in,
59             unsigned int in_len,
60             const CredentialResolver& credResolver,
61             CredentialCriteria* criteria=NULL
62             ) const;
63         virtual bool validate(
64             XSECCryptoX509* certEE,
65             const vector<XSECCryptoX509*>& certChain,
66             const CredentialResolver& credResolver,
67             CredentialCriteria* criteria=NULL
68             ) const;
69         virtual bool validate(
70             X509* certEE,
71             STACK_OF(X509)* certChain,
72             const CredentialResolver& credResolver,
73             CredentialCriteria* criteria=NULL
74             ) const;
75     };
76
77     TrustEngine* XMLTOOL_DLLLOCAL ExplicitKeyTrustEngineFactory(const DOMElement* const & e)
78     {
79         return new ExplicitKeyTrustEngine(e);
80     }
81 };
82
83 bool ExplicitKeyTrustEngine::validate(
84     Signature& sig,
85     const CredentialResolver& credResolver,
86     CredentialCriteria* criteria
87     ) const
88 {
89 #ifdef _DEBUG
90     NDC ndc("validate");
91 #endif
92     Category& log=Category::getInstance(XMLTOOLING_LOGCAT".TrustEngine");
93
94     vector<const Credential*> credentials;
95     if (criteria) {
96         criteria->setUsage(CredentialCriteria::SIGNING_CREDENTIAL);
97         criteria->setSignature(sig, CredentialCriteria::KEYINFO_EXTRACTION_KEY);
98         credResolver.resolve(credentials,criteria);
99     }
100     else {
101         CredentialCriteria cc;
102         cc.setUsage(CredentialCriteria::SIGNING_CREDENTIAL);
103         cc.setSignature(sig, CredentialCriteria::KEYINFO_EXTRACTION_KEY);
104         credResolver.resolve(credentials,&cc);
105     }
106     if (credentials.empty()) {
107         log.warn("unable to validate signature, no credentials available from peer");
108         return false;
109     }
110     
111     log.debug("attempting to validate signature with the peer's credentials");
112     SignatureValidator sigValidator;
113     for (vector<const Credential*>::const_iterator c=credentials.begin(); c!=credentials.end(); ++c) {
114         sigValidator.setCredential(*c);
115         try {
116             sigValidator.validate(&sig);
117             log.debug("signature validated with credential");
118             return true;
119         }
120         catch (ValidationException& e) {
121             log.debug("public key did not validate signature: %s", e.what());
122         }
123     }
124
125     log.error("no peer credentials validated the signature");
126     return false;
127 }
128
129 bool ExplicitKeyTrustEngine::validate(
130     const XMLCh* sigAlgorithm,
131     const char* sig,
132     KeyInfo* keyInfo,
133     const char* in,
134     unsigned int in_len,
135     const CredentialResolver& credResolver,
136     CredentialCriteria* criteria
137     ) const
138 {
139 #ifdef _DEBUG
140     NDC ndc("validate");
141 #endif
142     Category& log=Category::getInstance(XMLTOOLING_LOGCAT".TrustEngine");
143     
144     vector<const Credential*> credentials;
145     if (criteria) {
146         criteria->setUsage(CredentialCriteria::SIGNING_CREDENTIAL);
147         criteria->setKeyInfo(keyInfo, CredentialCriteria::KEYINFO_EXTRACTION_KEY);
148         criteria->setXMLAlgorithm(sigAlgorithm);
149         credResolver.resolve(credentials,criteria);
150     }
151     else {
152         CredentialCriteria cc;
153         cc.setUsage(CredentialCriteria::SIGNING_CREDENTIAL);
154         cc.setKeyInfo(keyInfo, CredentialCriteria::KEYINFO_EXTRACTION_KEY);
155         cc.setXMLAlgorithm(sigAlgorithm);
156         credResolver.resolve(credentials,&cc);
157     }
158     if (credentials.empty()) {
159         log.warn("unable to validate signature, no credentials available from peer");
160         return false;
161     }
162     
163     log.debug("attempting to validate signature with the peer's credentials");
164     for (vector<const Credential*>::const_iterator c=credentials.begin(); c!=credentials.end(); ++c) {
165         if ((*c)->getPublicKey()) {
166             try {
167                 if (Signature::verifyRawSignature((*c)->getPublicKey(), sigAlgorithm, sig, in, in_len)) {
168                     log.debug("signature validated with public key");
169                     return true;
170                 }
171             }
172             catch (SignatureException& e) {
173                 if (log.isDebugEnabled()) {
174                     log.debug("public key did not validate signature: %s", e.what());
175                 }
176             }
177         }
178     }
179
180     log.error("no peer credentials validated the signature");
181     return false;
182 }
183
184 bool ExplicitKeyTrustEngine::validate(
185     XSECCryptoX509* certEE,
186     const vector<XSECCryptoX509*>& certChain,
187     const CredentialResolver& credResolver,
188     CredentialCriteria* criteria
189     ) const
190 {
191 #ifdef _DEBUG
192         NDC ndc("validate");
193 #endif
194     if (!certEE) {
195         Category::getInstance(XMLTOOLING_LOGCAT".TrustEngine").error("unable to validate, end-entity certificate was null");
196         return false;
197     }
198     else if (certEE->getProviderName()!=DSIGConstants::s_unicodeStrPROVOpenSSL) {
199         Category::getInstance(XMLTOOLING_LOGCAT".TrustEngine").error("only the OpenSSL XSEC provider is supported");
200         return false;
201     }
202
203     return validate(static_cast<OpenSSLCryptoX509*>(certEE)->getOpenSSLX509(), NULL, credResolver, criteria);
204 }
205
206 bool ExplicitKeyTrustEngine::validate(
207     X509* certEE,
208     STACK_OF(X509)* certChain,
209     const CredentialResolver& credResolver,
210     CredentialCriteria* criteria
211     ) const
212 {
213 #ifdef _DEBUG
214     NDC ndc("validate");
215 #endif
216     Category& log=Category::getInstance(XMLTOOLING_LOGCAT".TrustEngine");
217     
218     if (!certEE) {
219         log.error("unable to validate, end-entity certificate was null");
220         return false;
221     }
222
223     vector<const Credential*> credentials;
224     if (criteria) {
225         if (criteria->getUsage()==CredentialCriteria::UNSPECIFIED_CREDENTIAL)
226             criteria->setUsage(CredentialCriteria::SIGNING_CREDENTIAL);
227         credResolver.resolve(credentials,criteria);
228     }
229     else {
230         CredentialCriteria cc;
231         cc.setUsage(CredentialCriteria::SIGNING_CREDENTIAL);
232         credResolver.resolve(credentials,&cc);
233     }
234     if (credentials.empty()) {
235         log.warn("unable to validate certificate, no credentials available from peer");
236         return false;
237     }
238
239     // The "explicit" trust implementation relies solely on keys living within the
240     // peer resolver to verify the EE certificate.
241
242     log.debug("attempting to match credentials from peer with end-entity certificate");
243     for (vector<const Credential*>::const_iterator c=credentials.begin(); c!=credentials.end(); ++c) {
244         XSECCryptoKey* key = (*c)->getPublicKey();
245         if (key) {
246             if (key->getProviderName()!=DSIGConstants::s_unicodeStrPROVOpenSSL) {
247                 log.error("only the OpenSSL XSEC provider is supported");
248                 continue;
249             }
250             switch (key->getKeyType()) {
251                 case XSECCryptoKey::KEY_RSA_PUBLIC:
252                 {
253                     RSA* rsa = static_cast<OpenSSLCryptoKeyRSA*>(key)->getOpenSSLRSA();
254                     EVP_PKEY* evp = X509_PUBKEY_get(X509_get_X509_PUBKEY(certEE));
255                     if (rsa && evp && evp->type == EVP_PKEY_RSA &&
256                             BN_cmp(rsa->n,evp->pkey.rsa->n) == 0 && BN_cmp(rsa->e,evp->pkey.rsa->e) == 0) {
257                         if (evp)
258                             EVP_PKEY_free(evp);
259                         log.debug("end-entity certificate matches peer RSA key information");
260                         return true;
261                     }
262                     if (evp)
263                         EVP_PKEY_free(evp);
264                     break;
265                 }
266                 
267                 case XSECCryptoKey::KEY_DSA_PUBLIC:
268                 {
269                     DSA* dsa = static_cast<OpenSSLCryptoKeyDSA*>(key)->getOpenSSLDSA();
270                     EVP_PKEY* evp = X509_PUBKEY_get(X509_get_X509_PUBKEY(certEE));
271                     if (dsa && evp && evp->type == EVP_PKEY_DSA && BN_cmp(dsa->pub_key,evp->pkey.dsa->pub_key) == 0) {
272                         if (evp)
273                             EVP_PKEY_free(evp);
274                         log.debug("end-entity certificate matches peer DSA key information");
275                         return true;
276                     }
277                     if (evp)
278                         EVP_PKEY_free(evp);
279                     break;
280                 }
281
282                 default:
283                     log.warn("unknown peer key type, skipping...");
284             }
285         }
286     }
287
288     log.debug("no keys within this peer's key information matched the given end-entity certificate");
289     return false;
290 }