eb013455d8a67cceded9c84ac4482db867fc6d39
[shibboleth/cpp-xmltooling.git] / xmltooling / signature / impl / XMLSecSignature.cpp
1 /*\r
2 *  Copyright 2001-2006 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  * XMLSecSignature.cpp\r
19  * \r
20  * Signature classes for XMLSec-based signature-handling\r
21  */\r
22 \r
23 #include "internal.h"\r
24 #include "exceptions.h"\r
25 #include "signature/impl/XMLSecSignature.h"\r
26 #include "util/NDC.h"\r
27 #include "util/XMLHelper.h"\r
28 \r
29 #include <log4cpp/Category.hh>\r
30 #include <xercesc/framework/MemBufInputSource.hpp>\r
31 #include <xercesc/framework/Wrapper4InputSource.hpp>\r
32 #include <xercesc/util/XMLUniDefs.hpp>\r
33 #include <xsec/dsig/DSIGKeyInfoX509.hpp>\r
34 #include <xsec/enc/XSECCryptoException.hpp>\r
35 #include <xsec/framework/XSECException.hpp>\r
36 \r
37 using namespace xmltooling;\r
38 using namespace log4cpp;\r
39 using namespace std;\r
40 \r
41 const XMLCh xmltooling::Signature::LOCAL_NAME[] = {\r
42     chLatin_S, chLatin_i, chLatin_g, chLatin_n, chLatin_a, chLatin_t, chLatin_u, chLatin_r, chLatin_e, chNull\r
43 }; \r
44 \r
45 const XMLCh xmltooling::Signature::PREFIX[] = {\r
46     chLatin_d, chLatin_s, chNull\r
47 }; \r
48 \r
49 XMLSecSignatureImpl::~XMLSecSignatureImpl()\r
50 {\r
51     // Release the associated signature.\r
52     if (m_signature)\r
53         XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->releaseSignature(m_signature);\r
54 \r
55     XMLString::release(&m_c14n);\r
56     XMLString::release(&m_sm);\r
57 }\r
58 \r
59 void XMLSecSignatureImpl::releaseDOM()\r
60 {\r
61     // This should save off the DOM\r
62     UnknownElementImpl::releaseDOM();\r
63     \r
64     // Release the associated signature.\r
65     if (m_signature) {\r
66         XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->releaseSignature(m_signature);\r
67         m_signature=NULL;\r
68     }\r
69 }\r
70 \r
71 XMLObject* XMLSecSignatureImpl::clone() const\r
72 {\r
73     XMLSecSignatureImpl* ret=new XMLSecSignatureImpl();\r
74 \r
75     ret->m_c14n=XMLString::replicate(m_c14n);\r
76     ret->m_sm=XMLString::replicate(m_sm);\r
77 \r
78     // If there's no XML locally, serialize this object into the new one, otherwise just copy it over.\r
79     if (m_xml.empty())\r
80         serialize(ret->m_xml);\r
81     else\r
82         ret->m_xml=m_xml;\r
83 \r
84     return ret;\r
85 }\r
86 \r
87 DSIGKeyInfoList* XMLSecSignatureImpl::getKeyInfo() const\r
88 {\r
89     return m_signature ? m_signature->getKeyInfoList() : NULL;\r
90 }\r
91 \r
92 class _addcert : public std::binary_function<DSIGKeyInfoX509*,XSECCryptoX509*,void> {\r
93 public:\r
94     void operator()(DSIGKeyInfoX509* bag, XSECCryptoX509* cert) const {\r
95         safeBuffer& buf=cert->getDEREncodingSB();\r
96         bag->appendX509Certificate(buf.sbStrToXMLCh());\r
97     }\r
98 };\r
99 \r
100 void XMLSecSignatureImpl::sign(const SigningContext& ctx)\r
101 {\r
102     Category& log=Category::getInstance(XMLTOOLING_LOGCAT".Signature");\r
103     log.debug("applying signature");\r
104 \r
105     if (!m_signature)\r
106         throw SignatureException("Only a marshalled Signature object can be signed.");\r
107 \r
108     try {\r
109         log.debug("creating signature content");\r
110         ctx.createSignature(m_signature);\r
111         const std::vector<XSECCryptoX509*>& certs=ctx.getX509Certificates();\r
112         if (!certs.empty()) {\r
113             DSIGKeyInfoX509* x509Data=m_signature->appendX509Data();\r
114             for_each(certs.begin(),certs.end(),bind1st(_addcert(),x509Data));\r
115         }\r
116         \r
117         log.debug("computing signature");\r
118         m_signature->setSigningKey(ctx.getSigningKey());\r
119         m_signature->sign();\r
120     }\r
121     catch(XSECException& e) {\r
122         auto_ptr_char temp(e.getMsg());\r
123         throw SignatureException(string("Caught an XMLSecurity exception while signing: ") + temp.get());\r
124     }\r
125     catch(XSECCryptoException& e) {\r
126         throw SignatureException(string("Caught an XMLSecurity exception while signing: ") + e.getMsg());\r
127     }\r
128 }\r
129 \r
130 void XMLSecSignatureImpl::verify(const VerifyingContext& ctx) const\r
131 {\r
132     Category& log=Category::getInstance(XMLTOOLING_LOGCAT".Signature");\r
133     log.debug("verifying signature");\r
134 \r
135     if (!m_signature)\r
136         throw SignatureException("Only a marshalled Signature object can be verified.");\r
137 \r
138     try {\r
139         ctx.verifySignature(m_signature);\r
140     }\r
141     catch(XSECException& e) {\r
142         auto_ptr_char temp(e.getMsg());\r
143         throw SignatureException(string("Caught an XMLSecurity exception verifying signature: ") + temp.get());\r
144     }\r
145     catch(XSECCryptoException& e) {\r
146         throw SignatureException(string("Caught an XMLSecurity exception verifying signature: ") + e.getMsg());\r
147     }\r
148 }\r
149 \r
150 DOMElement* XMLSecSignatureImpl::marshall(DOMDocument* document, MarshallingContext* ctx) const\r
151 {\r
152 #ifdef _DEBUG\r
153     xmltooling::NDC ndc("marshall");\r
154 #endif\r
155     \r
156     Category& log=Category::getInstance(XMLTOOLING_LOGCAT".Signature");\r
157     log.debug("marshalling ds:Signature");\r
158 \r
159     DOMElement* cachedDOM=getDOM();\r
160     if (cachedDOM) {\r
161         if (!document || document==cachedDOM->getOwnerDocument()) {\r
162             log.debug("Signature has a usable cached DOM, reusing it");\r
163             if (document)\r
164                 setDocumentElement(cachedDOM->getOwnerDocument(),cachedDOM);\r
165             releaseParentDOM(true);\r
166             return cachedDOM;\r
167         }\r
168         \r
169         // We have a DOM but it doesn't match the document we were given, so we import\r
170         // it into the new document.\r
171         cachedDOM=static_cast<DOMElement*>(document->importNode(cachedDOM, true));\r
172 \r
173         try {\r
174             XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->releaseSignature(m_signature);\r
175             m_signature=NULL;\r
176             m_signature=XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->newSignatureFromDOM(\r
177                 document, cachedDOM\r
178                 );\r
179             m_signature->load();\r
180         }\r
181         catch(XSECException& e) {\r
182             auto_ptr_char temp(e.getMsg());\r
183             throw MarshallingException(string("Caught an XMLSecurity exception while loading signature: ") + temp.get());\r
184         }\r
185         catch(XSECCryptoException& e) {\r
186             throw MarshallingException(string("Caught an XMLSecurity exception while loading signature: ") + e.getMsg());\r
187         }\r
188 \r
189         // Recache the DOM.\r
190         setDocumentElement(document, cachedDOM);\r
191         log.debug("caching imported DOM for Signature");\r
192         setDOM(cachedDOM, false);\r
193         releaseParentDOM(true);\r
194         return cachedDOM;\r
195     }\r
196     \r
197     // If we get here, we didn't have a usable DOM.\r
198     bool bindDocument=false;\r
199     if (m_xml.empty()) {\r
200         // Fresh signature, so we just create an empty one.\r
201         log.debug("creating empty Signature element");\r
202         if (!document) {\r
203             document=DOMImplementationRegistry::getDOMImplementation(NULL)->createDocument();\r
204             bindDocument=true;\r
205         }\r
206         m_signature=XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->newSignature();\r
207         m_signature->setDSIGNSPrefix(Signature::PREFIX);\r
208         cachedDOM=m_signature->createBlankSignature(document, getCanonicalizationMethod(), getSignatureAlgorithm());\r
209     }\r
210     else {\r
211         // We need to reparse the XML we saved off into a new DOM.\r
212         MemBufInputSource src(reinterpret_cast<const XMLByte*>(m_xml.c_str()),m_xml.length(),"XMLSecSignatureImpl");\r
213         Wrapper4InputSource dsrc(&src,false);\r
214         log.debug("parsing Signature XML back into DOM tree");\r
215         DOMDocument* internalDoc=XMLToolingInternalConfig::getInternalConfig().m_parserPool->parse(dsrc);\r
216         if (document) {\r
217             // The caller insists on using his own document, so we now have to import the thing\r
218             // into it. Then we're just dumping the one we built.\r
219             log.debug("reimporting new DOM into caller-supplied document");\r
220             cachedDOM=static_cast<DOMElement*>(document->importNode(internalDoc->getDocumentElement(), true));\r
221             internalDoc->release();\r
222         }\r
223         else {\r
224             // We just bind the document we built to the object as the result.\r
225             cachedDOM=static_cast<DOMElement*>(internalDoc->getDocumentElement());\r
226             document=internalDoc;\r
227             bindDocument=true;\r
228         }\r
229 \r
230         // Now reload the signature from the DOM.\r
231         try {\r
232             m_signature=XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->newSignatureFromDOM(\r
233                 document, cachedDOM\r
234                 );\r
235             m_signature->load();\r
236         }\r
237         catch(XSECException& e) {\r
238             if (bindDocument)\r
239                 document->release();\r
240             auto_ptr_char temp(e.getMsg());\r
241             throw MarshallingException(string("Caught an XMLSecurity exception while loading signature: ") + temp.get());\r
242         }\r
243         catch(XSECCryptoException& e) {\r
244             if (bindDocument)\r
245                 document->release();\r
246             throw MarshallingException(string("Caught an XMLSecurity exception while loading signature: ") + e.getMsg());\r
247         }\r
248     }\r
249     \r
250     // Recache the DOM and clear the serialized copy.\r
251     setDocumentElement(document, cachedDOM);\r
252     log.debug("caching DOM for Signature (document is %sbound)", bindDocument ? "" : "not ");\r
253     setDOM(cachedDOM, bindDocument);\r
254     releaseParentDOM(true);\r
255     m_xml.erase();\r
256     return cachedDOM;\r
257 }\r
258 \r
259 DOMElement* XMLSecSignatureImpl::marshall(DOMElement* parentElement, MarshallingContext* ctx) const\r
260 {\r
261 #ifdef _DEBUG\r
262     xmltooling::NDC ndc("marshall");\r
263 #endif\r
264     \r
265     Category& log=Category::getInstance(XMLTOOLING_LOGCAT".Signature");\r
266     log.debug("marshalling ds:Signature");\r
267 \r
268     DOMElement* cachedDOM=getDOM();\r
269     if (cachedDOM) {\r
270         if (parentElement->getOwnerDocument()==cachedDOM->getOwnerDocument()) {\r
271             log.debug("Signature has a usable cached DOM, reusing it");\r
272             parentElement->appendChild(cachedDOM);\r
273             releaseParentDOM(true);\r
274             return cachedDOM;\r
275         }\r
276         \r
277         // We have a DOM but it doesn't match the document we were given, so we import\r
278         // it into the new document.\r
279         cachedDOM=static_cast<DOMElement*>(parentElement->getOwnerDocument()->importNode(cachedDOM, true));\r
280 \r
281         try {\r
282             XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->releaseSignature(m_signature);\r
283             m_signature=NULL;\r
284             m_signature=XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->newSignatureFromDOM(\r
285                 parentElement->getOwnerDocument(), cachedDOM\r
286                 );\r
287             m_signature->load();\r
288         }\r
289         catch(XSECException& e) {\r
290             auto_ptr_char temp(e.getMsg());\r
291             throw MarshallingException(string("Caught an XMLSecurity exception while loading signature: ") + temp.get());\r
292         }\r
293         catch(XSECCryptoException& e) {\r
294             throw MarshallingException(string("Caught an XMLSecurity exception while loading signature: ") + e.getMsg());\r
295         }\r
296 \r
297         // Recache the DOM.\r
298         parentElement->appendChild(cachedDOM);\r
299         log.debug("caching imported DOM for Signature");\r
300         setDOM(cachedDOM, false);\r
301         releaseParentDOM(true);\r
302         return cachedDOM;\r
303     }\r
304     \r
305     // If we get here, we didn't have a usable DOM.\r
306     if (m_xml.empty()) {\r
307         // Fresh signature, so we just create an empty one.\r
308         log.debug("creating empty Signature element");\r
309         m_signature=XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->newSignature();\r
310         m_signature->setDSIGNSPrefix(Signature::PREFIX);\r
311         cachedDOM=m_signature->createBlankSignature(\r
312             parentElement->getOwnerDocument(), getCanonicalizationMethod(), getSignatureAlgorithm()\r
313             );\r
314     }\r
315     else {\r
316         MemBufInputSource src(reinterpret_cast<const XMLByte*>(m_xml.c_str()),m_xml.length(),"XMLSecSignatureImpl");\r
317         Wrapper4InputSource dsrc(&src,false);\r
318         log.debug("parsing XML back into DOM tree");\r
319         DOMDocument* internalDoc=XMLToolingInternalConfig::getInternalConfig().m_parserPool->parse(dsrc);\r
320         \r
321         log.debug("reimporting new DOM into caller-supplied document");\r
322         cachedDOM=static_cast<DOMElement*>(parentElement->getOwnerDocument()->importNode(internalDoc->getDocumentElement(),true));\r
323         internalDoc->release();\r
324 \r
325         // Now reload the signature from the DOM.\r
326         try {\r
327             m_signature=XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->newSignatureFromDOM(\r
328                 parentElement->getOwnerDocument(), cachedDOM\r
329                 );\r
330             m_signature->load();\r
331         }\r
332         catch(XSECException& e) {\r
333             auto_ptr_char temp(e.getMsg());\r
334             throw MarshallingException(string("Caught an XMLSecurity exception while loading signature: ") + temp.get());\r
335         }\r
336         catch(XSECCryptoException& e) {\r
337             throw MarshallingException(string("Caught an XMLSecurity exception while loading signature: ") + e.getMsg());\r
338         }\r
339     }\r
340 \r
341     // Recache the DOM and clear the serialized copy.\r
342     parentElement->appendChild(cachedDOM);\r
343     log.debug("caching DOM for Signature");\r
344     setDOM(cachedDOM, false);\r
345     releaseParentDOM(true);\r
346     m_xml.erase();\r
347     return cachedDOM;\r
348 }\r
349 \r
350 XMLObject* XMLSecSignatureImpl::unmarshall(DOMElement* element, bool bindDocument)\r
351 {\r
352     Category::getInstance(XMLTOOLING_LOGCAT".Signature").debug("unmarshalling ds:Signature");\r
353 \r
354     try {\r
355         m_signature=XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->newSignatureFromDOM(\r
356             element->getOwnerDocument(), element\r
357             );\r
358         m_signature->load();\r
359     }\r
360     catch(XSECException& e) {\r
361         auto_ptr_char temp(e.getMsg());\r
362         throw UnmarshallingException(string("Caught an XMLSecurity exception while loading signature: ") + temp.get());\r
363     }\r
364     catch(XSECCryptoException& e) {\r
365         throw UnmarshallingException(string("Caught an XMLSecurity exception while loading signature: ") + e.getMsg());\r
366     }\r
367 \r
368     setDOM(element, bindDocument);\r
369     return this;\r
370 }\r