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