Extend KeyResolver to include certificate resolution, add inline resolver.
[shibboleth/cpp-xmltooling.git] / xmltooling / signature / impl / InlineKeyResolver.cpp
1 /*\r
2  *  Copyright 2001-2005 Internet2\r
3  * \r
4  * Licensed under the Apache License, Version 2.0 (the "License");\r
5  * you may not use this file except in compliance with the License.\r
6  * You may obtain a copy of the License at\r
7  *\r
8  *     http://www.apache.org/licenses/LICENSE-2.0\r
9  *\r
10  * Unless required by applicable law or agreed to in writing, software\r
11  * distributed under the License is distributed on an "AS IS" BASIS,\r
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
13  * See the License for the specific language governing permissions and\r
14  * limitations under the License.\r
15  */\r
16 \r
17 /**\r
18  * InlineKeyResolver.cpp\r
19  * \r
20  * Resolves key information directly from recognized KeyInfo structures.\r
21  */\r
22 \r
23 #include "internal.h"\r
24 #include "signature/KeyResolver.h"\r
25 #include "util/NDC.h"\r
26 #include "util/Threads.h"\r
27 \r
28 #include <algorithm>\r
29 #include <log4cpp/Category.hh>\r
30 #include <xercesc/util/XMLUniDefs.hpp>\r
31 #include <xsec/dsig/DSIGKeyInfoX509.hpp>\r
32 #include <xsec/enc/XSECKeyInfoResolverDefault.hpp>\r
33 #include <xsec/enc/OpenSSL/OpenSSLCryptoX509.hpp>\r
34 #include <xsec/enc/OpenSSL/OpenSSLCryptoKeyRSA.hpp>\r
35 #include <xsec/enc/OpenSSL/OpenSSLCryptoKeyDSA.hpp>\r
36 #include <xsec/enc/XSECCryptoException.hpp>\r
37 #include <xsec/framework/XSECException.hpp>\r
38 \r
39 using namespace xmlsignature;\r
40 using namespace xmltooling;\r
41 using namespace log4cpp;\r
42 using namespace std;\r
43 \r
44 namespace xmlsignature {\r
45     class XMLTOOL_DLLLOCAL InlineKeyResolver : public KeyResolver\r
46     {\r
47     public:\r
48         InlineKeyResolver(const DOMElement* e);\r
49         virtual ~InlineKeyResolver();\r
50 \r
51         XSECCryptoKey* resolveKey(const KeyInfo* keyInfo) const;\r
52         XSECCryptoKey* resolveKey(DSIGKeyInfoList* keyInfo) const;\r
53         vector<XSECCryptoX509*>::size_type resolveCertificates(const KeyInfo* keyInfo, vector<XSECCryptoX509*>& certs) const;\r
54         vector<XSECCryptoX509*>::size_type resolveCertificates(DSIGKeyInfoList* keyInfo, vector<XSECCryptoX509*>& certs) const;\r
55         \r
56     private:\r
57         struct XMLTOOL_DLLLOCAL CacheEntry {\r
58             CacheEntry() : m_key(NULL) {}\r
59             ~CacheEntry() {\r
60                 delete m_key;\r
61                 for_each(m_certs.begin(),m_certs.end(),xmltooling::cleanup<XSECCryptoX509>());\r
62             }\r
63             XSECCryptoKey* m_key;\r
64             vector<XSECCryptoX509*> m_certs;\r
65         };\r
66 \r
67         void _resolve(const KeyInfo* keyInfo, CacheEntry& entry) const;\r
68         XSECCryptoKey* _resolveKey(const KeyInfo* keyInfo) const;\r
69         vector<XSECCryptoX509*>::size_type _resolveCertificates(const KeyInfo* keyInfo, vector<XSECCryptoX509*>& certs) const;\r
70 \r
71         RWLock* m_lock;\r
72         mutable map<const KeyInfo*,CacheEntry> m_cache;\r
73     };\r
74 \r
75     KeyResolver* XMLTOOL_DLLLOCAL InlineKeyResolverFactory(const DOMElement* const & e)\r
76     {\r
77         return new InlineKeyResolver(e);\r
78     }\r
79 };\r
80 \r
81 static const XMLCh cache[] = UNICODE_LITERAL_5(c,a,c,h,e);\r
82 \r
83 InlineKeyResolver::InlineKeyResolver(const DOMElement* e) : m_lock(NULL)\r
84 {\r
85     const XMLCh* flag = e ? e->getAttributeNS(NULL,cache) : NULL;\r
86     if (flag && XMLString::equals(flag,XMLConstants::XML_TRUE) || XMLString::equals(flag,XMLConstants::XML_ONE))\r
87         m_lock=RWLock::create();\r
88 }\r
89 \r
90 InlineKeyResolver::~InlineKeyResolver()\r
91 {\r
92     m_cache.clear();\r
93     delete m_lock;\r
94 }\r
95 \r
96 void InlineKeyResolver::_resolve(const KeyInfo* keyInfo, CacheEntry& entry) const\r
97 {\r
98     if (_resolveCertificates(keyInfo, entry.m_certs)>0)\r
99         entry.m_key = entry.m_certs.front()->clonePublicKey();\r
100     else\r
101         entry.m_key = _resolveKey(keyInfo);\r
102 }\r
103 \r
104 XSECCryptoKey* InlineKeyResolver::_resolveKey(const KeyInfo* keyInfo) const\r
105 {\r
106 #ifdef _DEBUG\r
107     NDC ndc("_resolveKey");\r
108 #endif\r
109     Category& log=Category::getInstance(XMLTOOLING_LOGCAT".KeyResolver");\r
110 \r
111     // Check for ds:X509Data\r
112     const vector<X509Data*>& x509Datas=keyInfo->getX509Datas();\r
113     for (vector<X509Data*>::const_iterator j=x509Datas.begin(); j!=x509Datas.end(); ++j) {\r
114         try {\r
115             const vector<X509Certificate*> x509Certs=const_cast<const X509Data*>(*j)->getX509Certificates();\r
116             if (!x509Certs.empty()) {\r
117                 auto_ptr_char x(x509Certs.front()->getValue());\r
118                 if (!x.get()) {\r
119                     log.warn("skipping empty ds:X509Certificate");\r
120                 }\r
121                 else {\r
122                     log.debug("resolving ds:X509Certificate");\r
123                     auto_ptr<XSECCryptoX509> x509(XSECPlatformUtils::g_cryptoProvider->X509());\r
124                     x509->loadX509Base64Bin(x.get(), strlen(x.get()));\r
125                     return x509->clonePublicKey();\r
126                 }\r
127             }\r
128         }\r
129         catch(XSECException& e) {\r
130             auto_ptr_char temp(e.getMsg());\r
131             log.error("caught XML-Security exception loading certificate: %s", temp.get());\r
132         }\r
133         catch(XSECCryptoException& e) {\r
134             log.error("caught XML-Security exception loading certificate: %s", e.getMsg());\r
135         }\r
136     }\r
137 \r
138     // Check for ds:KeyValue\r
139     const vector<KeyValue*>& keyValues = keyInfo->getKeyValues();\r
140     for (vector<KeyValue*>::const_iterator i=keyValues.begin(); i!=keyValues.end(); ++i) {\r
141         try {\r
142             KeyInfoSchemaValidators.validate(*i);    // see if it's a "valid" key\r
143             RSAKeyValue* rsakv = (*i)->getRSAKeyValue();\r
144             if (rsakv) {\r
145                 log.debug("resolving ds:RSAKeyValue");\r
146                 auto_ptr_char mod(rsakv->getModulus()->getValue());\r
147                 auto_ptr_char exp(rsakv->getExponent()->getValue());\r
148                 auto_ptr<XSECCryptoKeyRSA> rsa(XSECPlatformUtils::g_cryptoProvider->keyRSA());\r
149                 rsa->loadPublicModulusBase64BigNums(mod.get(), strlen(mod.get()));\r
150                 rsa->loadPublicExponentBase64BigNums(exp.get(), strlen(exp.get()));\r
151                 return rsa.release();\r
152             }\r
153             DSAKeyValue* dsakv = (*i)->getDSAKeyValue();\r
154             if (dsakv) {\r
155                 log.debug("resolving ds:DSAKeyValue");\r
156                 auto_ptr<XSECCryptoKeyDSA> dsa(XSECPlatformUtils::g_cryptoProvider->keyDSA());\r
157                 auto_ptr_char y(dsakv->getY()->getValue());\r
158                 dsa->loadYBase64BigNums(y.get(), strlen(y.get()));\r
159                 if (dsakv->getP()) {\r
160                     auto_ptr_char p(dsakv->getP()->getValue());\r
161                     dsa->loadPBase64BigNums(p.get(), strlen(p.get()));\r
162                 }\r
163                 if (dsakv->getQ()) {\r
164                     auto_ptr_char q(dsakv->getQ()->getValue());\r
165                     dsa->loadQBase64BigNums(q.get(), strlen(q.get()));\r
166                 }\r
167                 if (dsakv->getG()) {\r
168                     auto_ptr_char g(dsakv->getG()->getValue());\r
169                     dsa->loadGBase64BigNums(g.get(), strlen(g.get()));\r
170                 }\r
171                 return dsa.release();\r
172             }\r
173         }\r
174         catch (ValidationException& ex) {\r
175             log.warn("skipping invalid ds:KeyValue (%s)", ex.what());\r
176         }\r
177         catch(XSECException& e) {\r
178             auto_ptr_char temp(e.getMsg());\r
179             log.error("caught XML-Security exception loading key: %s", temp.get());\r
180         }\r
181         catch(XSECCryptoException& e) {\r
182             log.error("caught XML-Security exception loading key: %s", e.getMsg());\r
183         }\r
184     }\r
185 \r
186     log.warn("unable to resolve key");\r
187     return NULL;\r
188 }\r
189 \r
190 vector<XSECCryptoX509*>::size_type InlineKeyResolver::_resolveCertificates(\r
191     const KeyInfo* keyInfo, vector<XSECCryptoX509*>& certs\r
192     ) const\r
193 {\r
194 #ifdef _DEBUG\r
195     NDC ndc("_resolveCertificates");\r
196 #endif\r
197     Category& log=Category::getInstance(XMLTOOLING_LOGCAT".KeyResolver");\r
198 \r
199     // Check for ds:X509Data\r
200     const vector<X509Data*>& x509Datas=keyInfo->getX509Datas();\r
201     for (vector<X509Data*>::const_iterator j=x509Datas.begin(); certs.empty() && j!=x509Datas.end(); ++j) {\r
202         const vector<X509Certificate*> x509Certs=const_cast<const X509Data*>(*j)->getX509Certificates();\r
203         for (vector<X509Certificate*>::const_iterator k=x509Certs.begin(); k!=x509Certs.end(); ++k) {\r
204             try {\r
205                 auto_ptr_char x((*k)->getValue());\r
206                 if (!x.get()) {\r
207                     log.warn("skipping empty ds:X509Certificate");\r
208                 }\r
209                 else {\r
210                     log.debug("resolving ds:X509Certificate");\r
211                     auto_ptr<XSECCryptoX509> x509(XSECPlatformUtils::g_cryptoProvider->X509());\r
212                     x509->loadX509Base64Bin(x.get(), strlen(x.get()));\r
213                     certs.push_back(x509.release());\r
214                 }\r
215             }\r
216             catch(XSECException& e) {\r
217                 auto_ptr_char temp(e.getMsg());\r
218                 log.error("caught XML-Security exception loading certificate: %s", temp.get());\r
219             }\r
220             catch(XSECCryptoException& e) {\r
221                 log.error("caught XML-Security exception loading certificate: %s", e.getMsg());\r
222             }\r
223         }\r
224     }\r
225     if (log.isDebugEnabled()) {\r
226         log.debug("resolved %d certificate%s", certs.size(), certs.size()==1 ? "" : "s");\r
227     }\r
228     return certs.size();\r
229 }\r
230 \r
231 XSECCryptoKey* InlineKeyResolver::resolveKey(const KeyInfo* keyInfo) const\r
232 {\r
233     // Caching?\r
234     if (m_lock) {\r
235         // Get read lock.\r
236         m_lock->rdlock();\r
237         map<const KeyInfo*,CacheEntry>::iterator i=m_cache.find(keyInfo);\r
238         if (i != m_cache.end()) {\r
239             // Found in cache, so just return the results.\r
240             SharedLock locker(m_lock,false);\r
241             return i->second.m_key ? i->second.m_key->clone() : NULL;\r
242         }\r
243         else {\r
244             // Elevate lock.\r
245             m_lock->unlock();\r
246             m_lock->wrlock();\r
247             SharedLock locker(m_lock,false);\r
248             // Recheck cache.\r
249             i=m_cache.find(keyInfo);\r
250             if (i == m_cache.end()) {\r
251                 i = m_cache.insert(make_pair(keyInfo,CacheEntry())).first;\r
252                 _resolve(i->first, i->second);\r
253             }\r
254             return i->second.m_key ? i->second.m_key->clone() : NULL;\r
255         }\r
256     }\r
257     return _resolveKey(keyInfo);\r
258 }\r
259 \r
260 vector<XSECCryptoX509*>::size_type InlineKeyResolver::resolveCertificates(\r
261     const KeyInfo* keyInfo, vector<XSECCryptoX509*>& certs\r
262     ) const\r
263 {\r
264     // Caching?\r
265     if (m_lock) {\r
266         // Get read lock.\r
267         m_lock->rdlock();\r
268         map<const KeyInfo*,CacheEntry>::iterator i=m_cache.find(keyInfo);\r
269         if (i != m_cache.end()) {\r
270             // Found in cache, so just return the results.\r
271             SharedLock locker(m_lock,false);\r
272             certs.assign(i->second.m_certs.begin(), i->second.m_certs.end());\r
273             return certs.size();\r
274         }\r
275         else {\r
276             // Elevate lock.\r
277             m_lock->unlock();\r
278             m_lock->wrlock();\r
279             SharedLock locker(m_lock,false);\r
280             // Recheck cache.\r
281             i=m_cache.find(keyInfo);\r
282             if (i == m_cache.end()) {\r
283                 i = m_cache.insert(make_pair(keyInfo,CacheEntry())).first;\r
284                 _resolve(i->first, i->second);\r
285             }\r
286             certs.assign(i->second.m_certs.begin(), i->second.m_certs.end());\r
287             return certs.size();\r
288         }\r
289     }\r
290     return _resolveCertificates(keyInfo, certs);\r
291 }\r
292 \r
293 XSECCryptoKey* InlineKeyResolver::resolveKey(DSIGKeyInfoList* keyInfo) const\r
294 {\r
295 #ifdef _DEBUG\r
296     NDC ndc("_resolveKey");\r
297 #endif\r
298 \r
299     // Default resolver handles RSA/DSAKeyValue and X509Certificate elements.\r
300     try {\r
301         XSECKeyInfoResolverDefault def;\r
302         return def.resolveKey(keyInfo);\r
303     }\r
304     catch(XSECException& e) {\r
305         auto_ptr_char temp(e.getMsg());\r
306         Category::getInstance(XMLTOOLING_LOGCAT".KeyResolver").error("caught XML-Security exception loading certificate: %s", temp.get());\r
307     }\r
308     catch(XSECCryptoException& e) {\r
309         Category::getInstance(XMLTOOLING_LOGCAT".KeyResolver").error("caught XML-Security exception loading certificate: %s", e.getMsg());\r
310     }\r
311     return NULL;\r
312 }\r
313 \r
314 vector<XSECCryptoX509*>::size_type InlineKeyResolver::resolveCertificates(\r
315     DSIGKeyInfoList* keyInfo, vector<XSECCryptoX509*>& certs\r
316     ) const\r
317 {\r
318     certs.clear();\r
319         DSIGKeyInfoList::size_type sz = keyInfo->getSize();\r
320     for (DSIGKeyInfoList::size_type i=0; certs.empty() && i<sz; ++i) {\r
321         if (keyInfo->item(i)->getKeyInfoType()==DSIGKeyInfo::KEYINFO_X509) {\r
322             DSIGKeyInfoX509* x509 = static_cast<DSIGKeyInfoX509*>(keyInfo->item(i));\r
323             int count = x509->getCertificateListSize();\r
324             for (int j=0; j<count; ++j) {\r
325                 certs.push_back(x509->getCertificateCryptoItem(j));\r
326             }\r
327         }\r
328     }\r
329     return certs.size();\r
330 }\r