8a4ffcf08764e77665b1004f655414d97a37c46e
[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/CachingKeyResolver.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 CachingKeyResolver\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         void clearCache() {\r
57             if (m_lock)\r
58                 m_lock->wrlock();\r
59             m_cache.clear();\r
60             if (m_lock)\r
61                 m_lock->unlock();\r
62         }\r
63         \r
64     private:\r
65         struct XMLTOOL_DLLLOCAL CacheEntry {\r
66             CacheEntry() : m_key(NULL) {}\r
67             ~CacheEntry() {\r
68                 delete m_key;\r
69                 for_each(m_certs.begin(),m_certs.end(),xmltooling::cleanup<XSECCryptoX509>());\r
70             }\r
71             XSECCryptoKey* m_key;\r
72             vector<XSECCryptoX509*> m_certs;\r
73         };\r
74 \r
75         void _resolve(const KeyInfo* keyInfo, CacheEntry& entry) const;\r
76         XSECCryptoKey* _resolveKey(const KeyInfo* keyInfo) const;\r
77         vector<XSECCryptoX509*>::size_type _resolveCertificates(const KeyInfo* keyInfo, vector<XSECCryptoX509*>& certs) const;\r
78 \r
79         RWLock* m_lock;\r
80         mutable map<const KeyInfo*,CacheEntry> m_cache;\r
81     };\r
82 \r
83     KeyResolver* XMLTOOL_DLLLOCAL InlineKeyResolverFactory(const DOMElement* const & e)\r
84     {\r
85         return new InlineKeyResolver(e);\r
86     }\r
87 };\r
88 \r
89 static const XMLCh cache[] = UNICODE_LITERAL_5(c,a,c,h,e);\r
90 \r
91 InlineKeyResolver::InlineKeyResolver(const DOMElement* e) : m_lock(NULL)\r
92 {\r
93     const XMLCh* flag = e ? e->getAttributeNS(NULL,cache) : NULL;\r
94     if (flag && XMLString::equals(flag,XMLConstants::XML_TRUE) || XMLString::equals(flag,XMLConstants::XML_ONE))\r
95         m_lock=RWLock::create();\r
96 }\r
97 \r
98 InlineKeyResolver::~InlineKeyResolver()\r
99 {\r
100     clearCache();\r
101     delete m_lock;\r
102 }\r
103 \r
104 void InlineKeyResolver::_resolve(const KeyInfo* keyInfo, CacheEntry& entry) const\r
105 {\r
106     if (_resolveCertificates(keyInfo, entry.m_certs)>0)\r
107         entry.m_key = entry.m_certs.front()->clonePublicKey();\r
108     else\r
109         entry.m_key = _resolveKey(keyInfo);\r
110 }\r
111 \r
112 XSECCryptoKey* InlineKeyResolver::_resolveKey(const KeyInfo* keyInfo) const\r
113 {\r
114 #ifdef _DEBUG\r
115     NDC ndc("_resolveKey");\r
116 #endif\r
117     Category& log=Category::getInstance(XMLTOOLING_LOGCAT".KeyResolver");\r
118 \r
119     // Check for ds:X509Data\r
120     const vector<X509Data*>& x509Datas=keyInfo->getX509Datas();\r
121     for (vector<X509Data*>::const_iterator j=x509Datas.begin(); j!=x509Datas.end(); ++j) {\r
122         try {\r
123             const vector<X509Certificate*> x509Certs=const_cast<const X509Data*>(*j)->getX509Certificates();\r
124             if (!x509Certs.empty()) {\r
125                 auto_ptr_char x(x509Certs.front()->getValue());\r
126                 if (!x.get()) {\r
127                     log.warn("skipping empty ds:X509Certificate");\r
128                 }\r
129                 else {\r
130                     log.debug("resolving ds:X509Certificate");\r
131                     auto_ptr<XSECCryptoX509> x509(XSECPlatformUtils::g_cryptoProvider->X509());\r
132                     x509->loadX509Base64Bin(x.get(), strlen(x.get()));\r
133                     return x509->clonePublicKey();\r
134                 }\r
135             }\r
136         }\r
137         catch(XSECException& e) {\r
138             auto_ptr_char temp(e.getMsg());\r
139             log.error("caught XML-Security exception loading certificate: %s", temp.get());\r
140         }\r
141         catch(XSECCryptoException& e) {\r
142             log.error("caught XML-Security exception loading certificate: %s", e.getMsg());\r
143         }\r
144     }\r
145 \r
146     // Check for ds:KeyValue\r
147     const vector<KeyValue*>& keyValues = keyInfo->getKeyValues();\r
148     for (vector<KeyValue*>::const_iterator i=keyValues.begin(); i!=keyValues.end(); ++i) {\r
149         try {\r
150             KeyInfoSchemaValidators.validate(*i);    // see if it's a "valid" key\r
151             RSAKeyValue* rsakv = (*i)->getRSAKeyValue();\r
152             if (rsakv) {\r
153                 log.debug("resolving ds:RSAKeyValue");\r
154                 auto_ptr_char mod(rsakv->getModulus()->getValue());\r
155                 auto_ptr_char exp(rsakv->getExponent()->getValue());\r
156                 auto_ptr<XSECCryptoKeyRSA> rsa(XSECPlatformUtils::g_cryptoProvider->keyRSA());\r
157                 rsa->loadPublicModulusBase64BigNums(mod.get(), strlen(mod.get()));\r
158                 rsa->loadPublicExponentBase64BigNums(exp.get(), strlen(exp.get()));\r
159                 return rsa.release();\r
160             }\r
161             DSAKeyValue* dsakv = (*i)->getDSAKeyValue();\r
162             if (dsakv) {\r
163                 log.debug("resolving ds:DSAKeyValue");\r
164                 auto_ptr<XSECCryptoKeyDSA> dsa(XSECPlatformUtils::g_cryptoProvider->keyDSA());\r
165                 auto_ptr_char y(dsakv->getY()->getValue());\r
166                 dsa->loadYBase64BigNums(y.get(), strlen(y.get()));\r
167                 if (dsakv->getP()) {\r
168                     auto_ptr_char p(dsakv->getP()->getValue());\r
169                     dsa->loadPBase64BigNums(p.get(), strlen(p.get()));\r
170                 }\r
171                 if (dsakv->getQ()) {\r
172                     auto_ptr_char q(dsakv->getQ()->getValue());\r
173                     dsa->loadQBase64BigNums(q.get(), strlen(q.get()));\r
174                 }\r
175                 if (dsakv->getG()) {\r
176                     auto_ptr_char g(dsakv->getG()->getValue());\r
177                     dsa->loadGBase64BigNums(g.get(), strlen(g.get()));\r
178                 }\r
179                 return dsa.release();\r
180             }\r
181         }\r
182         catch (ValidationException& ex) {\r
183             log.warn("skipping invalid ds:KeyValue (%s)", ex.what());\r
184         }\r
185         catch(XSECException& e) {\r
186             auto_ptr_char temp(e.getMsg());\r
187             log.error("caught XML-Security exception loading key: %s", temp.get());\r
188         }\r
189         catch(XSECCryptoException& e) {\r
190             log.error("caught XML-Security exception loading key: %s", e.getMsg());\r
191         }\r
192     }\r
193 \r
194     log.warn("unable to resolve key");\r
195     return NULL;\r
196 }\r
197 \r
198 vector<XSECCryptoX509*>::size_type InlineKeyResolver::_resolveCertificates(\r
199     const KeyInfo* keyInfo, vector<XSECCryptoX509*>& certs\r
200     ) const\r
201 {\r
202 #ifdef _DEBUG\r
203     NDC ndc("_resolveCertificates");\r
204 #endif\r
205     Category& log=Category::getInstance(XMLTOOLING_LOGCAT".KeyResolver");\r
206 \r
207     // Check for ds:X509Data\r
208     const vector<X509Data*>& x509Datas=keyInfo->getX509Datas();\r
209     for (vector<X509Data*>::const_iterator j=x509Datas.begin(); certs.empty() && j!=x509Datas.end(); ++j) {\r
210         const vector<X509Certificate*> x509Certs=const_cast<const X509Data*>(*j)->getX509Certificates();\r
211         for (vector<X509Certificate*>::const_iterator k=x509Certs.begin(); k!=x509Certs.end(); ++k) {\r
212             try {\r
213                 auto_ptr_char x((*k)->getValue());\r
214                 if (!x.get()) {\r
215                     log.warn("skipping empty ds:X509Certificate");\r
216                 }\r
217                 else {\r
218                     log.debug("resolving ds:X509Certificate");\r
219                     auto_ptr<XSECCryptoX509> x509(XSECPlatformUtils::g_cryptoProvider->X509());\r
220                     x509->loadX509Base64Bin(x.get(), strlen(x.get()));\r
221                     certs.push_back(x509.release());\r
222                 }\r
223             }\r
224             catch(XSECException& e) {\r
225                 auto_ptr_char temp(e.getMsg());\r
226                 log.error("caught XML-Security exception loading certificate: %s", temp.get());\r
227             }\r
228             catch(XSECCryptoException& e) {\r
229                 log.error("caught XML-Security exception loading certificate: %s", e.getMsg());\r
230             }\r
231         }\r
232     }\r
233     if (log.isDebugEnabled()) {\r
234         log.debug("resolved %d certificate%s", certs.size(), certs.size()==1 ? "" : "s");\r
235     }\r
236     return certs.size();\r
237 }\r
238 \r
239 XSECCryptoKey* InlineKeyResolver::resolveKey(const KeyInfo* keyInfo) const\r
240 {\r
241     // Caching?\r
242     if (m_lock) {\r
243         // Get read lock.\r
244         m_lock->rdlock();\r
245         map<const KeyInfo*,CacheEntry>::iterator i=m_cache.find(keyInfo);\r
246         if (i != m_cache.end()) {\r
247             // Found in cache, so just return the results.\r
248             SharedLock locker(m_lock,false);\r
249             return i->second.m_key ? i->second.m_key->clone() : NULL;\r
250         }\r
251         else {\r
252             // Elevate lock.\r
253             m_lock->unlock();\r
254             m_lock->wrlock();\r
255             SharedLock locker(m_lock,false);\r
256             // Recheck cache.\r
257             i=m_cache.find(keyInfo);\r
258             if (i == m_cache.end()) {\r
259                 i = m_cache.insert(make_pair(keyInfo,CacheEntry())).first;\r
260                 _resolve(i->first, i->second);\r
261             }\r
262             return i->second.m_key ? i->second.m_key->clone() : NULL;\r
263         }\r
264     }\r
265     return _resolveKey(keyInfo);\r
266 }\r
267 \r
268 vector<XSECCryptoX509*>::size_type InlineKeyResolver::resolveCertificates(\r
269     const KeyInfo* keyInfo, vector<XSECCryptoX509*>& certs\r
270     ) const\r
271 {\r
272     // Caching?\r
273     if (m_lock) {\r
274         // Get read lock.\r
275         m_lock->rdlock();\r
276         map<const KeyInfo*,CacheEntry>::iterator i=m_cache.find(keyInfo);\r
277         if (i != m_cache.end()) {\r
278             // Found in cache, so just return the results.\r
279             SharedLock locker(m_lock,false);\r
280             certs.assign(i->second.m_certs.begin(), i->second.m_certs.end());\r
281             return certs.size();\r
282         }\r
283         else {\r
284             // Elevate lock.\r
285             m_lock->unlock();\r
286             m_lock->wrlock();\r
287             SharedLock locker(m_lock,false);\r
288             // Recheck cache.\r
289             i=m_cache.find(keyInfo);\r
290             if (i == m_cache.end()) {\r
291                 i = m_cache.insert(make_pair(keyInfo,CacheEntry())).first;\r
292                 _resolve(i->first, i->second);\r
293             }\r
294             certs.assign(i->second.m_certs.begin(), i->second.m_certs.end());\r
295             return certs.size();\r
296         }\r
297     }\r
298     return _resolveCertificates(keyInfo, certs);\r
299 }\r
300 \r
301 XSECCryptoKey* InlineKeyResolver::resolveKey(DSIGKeyInfoList* keyInfo) const\r
302 {\r
303 #ifdef _DEBUG\r
304     NDC ndc("_resolveKey");\r
305 #endif\r
306 \r
307     // Default resolver handles RSA/DSAKeyValue and X509Certificate elements.\r
308     try {\r
309         XSECKeyInfoResolverDefault def;\r
310         return def.resolveKey(keyInfo);\r
311     }\r
312     catch(XSECException& e) {\r
313         auto_ptr_char temp(e.getMsg());\r
314         Category::getInstance(XMLTOOLING_LOGCAT".KeyResolver").error("caught XML-Security exception loading certificate: %s", temp.get());\r
315     }\r
316     catch(XSECCryptoException& e) {\r
317         Category::getInstance(XMLTOOLING_LOGCAT".KeyResolver").error("caught XML-Security exception loading certificate: %s", e.getMsg());\r
318     }\r
319     return NULL;\r
320 }\r
321 \r
322 vector<XSECCryptoX509*>::size_type InlineKeyResolver::resolveCertificates(\r
323     DSIGKeyInfoList* keyInfo, vector<XSECCryptoX509*>& certs\r
324     ) const\r
325 {\r
326     certs.clear();\r
327         DSIGKeyInfoList::size_type sz = keyInfo->getSize();\r
328     for (DSIGKeyInfoList::size_type i=0; certs.empty() && i<sz; ++i) {\r
329         if (keyInfo->item(i)->getKeyInfoType()==DSIGKeyInfo::KEYINFO_X509) {\r
330             DSIGKeyInfoX509* x509 = static_cast<DSIGKeyInfoX509*>(keyInfo->item(i));\r
331             int count = x509->getCertificateListSize();\r
332             for (int j=0; j<count; ++j) {\r
333                 certs.push_back(x509->getCertificateCryptoItem(j));\r
334             }\r
335         }\r
336     }\r
337     return certs.size();\r
338 }\r