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