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