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