7034ca31aefaa4ea0332a469ae52b753d01b279e
[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 class _addcert : public std::binary_function<DSIGKeyInfoX509*,XSECCryptoX509*,void> {\r
88 public:\r
89     void operator()(DSIGKeyInfoX509* bag, XSECCryptoX509* cert) const {\r
90         safeBuffer& buf=cert->getDEREncodingSB();\r
91         bag->appendX509Certificate(buf.sbStrToXMLCh());\r
92     }\r
93 };\r
94 \r
95 void XMLSecSignatureImpl::sign(const SigningContext& ctx)\r
96 {\r
97     Category& log=Category::getInstance(XMLTOOLING_LOGCAT".Signature");\r
98     log.debug("applying signature");\r
99 \r
100     if (!m_signature)\r
101         throw SignatureException("Only a marshalled Signature object can be signed.");\r
102 \r
103     try {\r
104         log.debug("creating signature content");\r
105         ctx.createSignature(m_signature);\r
106         const std::vector<XSECCryptoX509*>& certs=ctx.getX509Certificates();\r
107         if (!certs.empty()) {\r
108             DSIGKeyInfoX509* x509Data=m_signature->appendX509Data();\r
109             for_each(certs.begin(),certs.end(),bind1st(_addcert(),x509Data));\r
110         }\r
111         \r
112         log.debug("computing signature");\r
113         m_signature->setSigningKey(ctx.getSigningKey());\r
114         m_signature->sign();\r
115     }\r
116     catch(XSECException& e) {\r
117         auto_ptr_char temp(e.getMsg());\r
118         throw SignatureException(string("Caught an XMLSecurity exception while signing: ") + temp.get());\r
119     }\r
120     catch(XSECCryptoException& e) {\r
121         throw SignatureException(string("Caught an XMLSecurity exception while signing: ") + e.getMsg());\r
122     }\r
123 }\r
124 \r
125 void XMLSecSignatureImpl::verify(const VerifyingContext& ctx) const\r
126 {\r
127     Category& log=Category::getInstance(XMLTOOLING_LOGCAT".Signature");\r
128     log.debug("verifying signature");\r
129 \r
130     if (!m_signature)\r
131         throw SignatureException("Only a marshalled Signature object can be verified.");\r
132 \r
133     try {\r
134         ctx.verifySignature(m_signature);\r
135     }\r
136     catch(XSECException& e) {\r
137         auto_ptr_char temp(e.getMsg());\r
138         throw SignatureException(string("Caught an XMLSecurity exception verifying signature: ") + temp.get());\r
139     }\r
140     catch(XSECCryptoException& e) {\r
141         throw SignatureException(string("Caught an XMLSecurity exception verifying signature: ") + e.getMsg());\r
142     }\r
143 }\r
144 \r
145 DOMElement* XMLSecSignatureImpl::marshall(DOMDocument* document, MarshallingContext* ctx) const\r
146 {\r
147 #ifdef _DEBUG\r
148     xmltooling::NDC ndc("marshall");\r
149 #endif\r
150     \r
151     Category& log=Category::getInstance(XMLTOOLING_LOGCAT".Signature");\r
152     log.debug("marshalling ds:Signature");\r
153 \r
154     DOMElement* cachedDOM=getDOM();\r
155     if (cachedDOM) {\r
156         if (!document || document==cachedDOM->getOwnerDocument()) {\r
157             log.debug("Signature has a usable cached DOM, reusing it");\r
158             if (document)\r
159                 setDocumentElement(cachedDOM->getOwnerDocument(),cachedDOM);\r
160             releaseParentDOM(true);\r
161             return cachedDOM;\r
162         }\r
163         \r
164         // We have a DOM but it doesn't match the document we were given, so we import\r
165         // it into the new document.\r
166         cachedDOM=static_cast<DOMElement*>(document->importNode(cachedDOM, true));\r
167 \r
168         try {\r
169             XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->releaseSignature(m_signature);\r
170             m_signature=NULL;\r
171             m_signature=XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->newSignatureFromDOM(\r
172                 document, cachedDOM\r
173                 );\r
174             m_signature->load();\r
175         }\r
176         catch(XSECException& e) {\r
177             auto_ptr_char temp(e.getMsg());\r
178             throw MarshallingException(string("Caught an XMLSecurity exception while loading signature: ") + temp.get());\r
179         }\r
180         catch(XSECCryptoException& e) {\r
181             throw MarshallingException(string("Caught an XMLSecurity exception while loading signature: ") + e.getMsg());\r
182         }\r
183 \r
184         // Recache the DOM.\r
185         setDocumentElement(document, cachedDOM);\r
186         log.debug("caching imported DOM for Signature");\r
187         setDOM(cachedDOM, false);\r
188         releaseParentDOM(true);\r
189         return cachedDOM;\r
190     }\r
191     \r
192     // If we get here, we didn't have a usable DOM.\r
193     bool bindDocument=false;\r
194     if (m_xml.empty()) {\r
195         // Fresh signature, so we just create an empty one.\r
196         log.debug("creating empty Signature element");\r
197         if (!document) {\r
198             document=DOMImplementationRegistry::getDOMImplementation(NULL)->createDocument();\r
199             bindDocument=true;\r
200         }\r
201         m_signature=XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->newSignature();\r
202         m_signature->setDSIGNSPrefix(Signature::PREFIX);\r
203         cachedDOM=m_signature->createBlankSignature(document, getCanonicalizationMethod(), getSignatureAlgorithm());\r
204     }\r
205     else {\r
206         // We need to reparse the XML we saved off into a new DOM.\r
207         MemBufInputSource src(reinterpret_cast<const XMLByte*>(m_xml.c_str()),m_xml.length(),"XMLSecSignatureImpl");\r
208         Wrapper4InputSource dsrc(&src,false);\r
209         log.debug("parsing Signature XML back into DOM tree");\r
210         DOMDocument* internalDoc=XMLToolingInternalConfig::getInternalConfig().m_parserPool->parse(dsrc);\r
211         if (document) {\r
212             // The caller insists on using his own document, so we now have to import the thing\r
213             // into it. Then we're just dumping the one we built.\r
214             log.debug("reimporting new DOM into caller-supplied document");\r
215             cachedDOM=static_cast<DOMElement*>(document->importNode(internalDoc->getDocumentElement(), true));\r
216             internalDoc->release();\r
217         }\r
218         else {\r
219             // We just bind the document we built to the object as the result.\r
220             cachedDOM=static_cast<DOMElement*>(internalDoc->getDocumentElement());\r
221             document=internalDoc;\r
222             bindDocument=true;\r
223         }\r
224 \r
225         // Now reload the signature from the DOM.\r
226         try {\r
227             m_signature=XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->newSignatureFromDOM(\r
228                 document, cachedDOM\r
229                 );\r
230             m_signature->load();\r
231         }\r
232         catch(XSECException& e) {\r
233             if (bindDocument)\r
234                 document->release();\r
235             auto_ptr_char temp(e.getMsg());\r
236             throw MarshallingException(string("Caught an XMLSecurity exception while loading signature: ") + temp.get());\r
237         }\r
238         catch(XSECCryptoException& e) {\r
239             if (bindDocument)\r
240                 document->release();\r
241             throw MarshallingException(string("Caught an XMLSecurity exception while loading signature: ") + e.getMsg());\r
242         }\r
243     }\r
244     \r
245     // Recache the DOM and clear the serialized copy.\r
246     setDocumentElement(document, cachedDOM);\r
247     log.debug("caching DOM for Signature (document is %sbound)", bindDocument ? "" : "not ");\r
248     setDOM(cachedDOM, bindDocument);\r
249     releaseParentDOM(true);\r
250     m_xml.erase();\r
251     return cachedDOM;\r
252 }\r
253 \r
254 DOMElement* XMLSecSignatureImpl::marshall(DOMElement* parentElement, MarshallingContext* ctx) const\r
255 {\r
256 #ifdef _DEBUG\r
257     xmltooling::NDC ndc("marshall");\r
258 #endif\r
259     \r
260     Category& log=Category::getInstance(XMLTOOLING_LOGCAT".Signature");\r
261     log.debug("marshalling ds:Signature");\r
262 \r
263     DOMElement* cachedDOM=getDOM();\r
264     if (cachedDOM) {\r
265         if (parentElement->getOwnerDocument()==cachedDOM->getOwnerDocument()) {\r
266             log.debug("Signature has a usable cached DOM, reusing it");\r
267             parentElement->appendChild(cachedDOM);\r
268             releaseParentDOM(true);\r
269             return cachedDOM;\r
270         }\r
271         \r
272         // We have a DOM but it doesn't match the document we were given, so we import\r
273         // it into the new document.\r
274         cachedDOM=static_cast<DOMElement*>(parentElement->getOwnerDocument()->importNode(cachedDOM, true));\r
275 \r
276         try {\r
277             XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->releaseSignature(m_signature);\r
278             m_signature=NULL;\r
279             m_signature=XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->newSignatureFromDOM(\r
280                 parentElement->getOwnerDocument(), cachedDOM\r
281                 );\r
282             m_signature->load();\r
283         }\r
284         catch(XSECException& e) {\r
285             auto_ptr_char temp(e.getMsg());\r
286             throw MarshallingException(string("Caught an XMLSecurity exception while loading signature: ") + temp.get());\r
287         }\r
288         catch(XSECCryptoException& e) {\r
289             throw MarshallingException(string("Caught an XMLSecurity exception while loading signature: ") + e.getMsg());\r
290         }\r
291 \r
292         // Recache the DOM.\r
293         parentElement->appendChild(cachedDOM);\r
294         log.debug("caching imported DOM for Signature");\r
295         setDOM(cachedDOM, false);\r
296         releaseParentDOM(true);\r
297         return cachedDOM;\r
298     }\r
299     \r
300     // If we get here, we didn't have a usable DOM.\r
301     if (m_xml.empty()) {\r
302         // Fresh signature, so we just create an empty one.\r
303         log.debug("creating empty Signature element");\r
304         m_signature=XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->newSignature();\r
305         m_signature->setDSIGNSPrefix(Signature::PREFIX);\r
306         cachedDOM=m_signature->createBlankSignature(\r
307             parentElement->getOwnerDocument(), getCanonicalizationMethod(), getSignatureAlgorithm()\r
308             );\r
309     }\r
310     else {\r
311         MemBufInputSource src(reinterpret_cast<const XMLByte*>(m_xml.c_str()),m_xml.length(),"XMLSecSignatureImpl");\r
312         Wrapper4InputSource dsrc(&src,false);\r
313         log.debug("parsing XML back into DOM tree");\r
314         DOMDocument* internalDoc=XMLToolingInternalConfig::getInternalConfig().m_parserPool->parse(dsrc);\r
315         \r
316         log.debug("reimporting new DOM into caller-supplied document");\r
317         cachedDOM=static_cast<DOMElement*>(parentElement->getOwnerDocument()->importNode(internalDoc->getDocumentElement(),true));\r
318         internalDoc->release();\r
319 \r
320         // Now reload the signature from the DOM.\r
321         try {\r
322             m_signature=XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->newSignatureFromDOM(\r
323                 parentElement->getOwnerDocument(), cachedDOM\r
324                 );\r
325             m_signature->load();\r
326         }\r
327         catch(XSECException& e) {\r
328             auto_ptr_char temp(e.getMsg());\r
329             throw MarshallingException(string("Caught an XMLSecurity exception while loading signature: ") + temp.get());\r
330         }\r
331         catch(XSECCryptoException& e) {\r
332             throw MarshallingException(string("Caught an XMLSecurity exception while loading signature: ") + e.getMsg());\r
333         }\r
334     }\r
335 \r
336     // Recache the DOM and clear the serialized copy.\r
337     parentElement->appendChild(cachedDOM);\r
338     log.debug("caching DOM for Signature");\r
339     setDOM(cachedDOM, false);\r
340     releaseParentDOM(true);\r
341     m_xml.erase();\r
342     return cachedDOM;\r
343 }\r
344 \r
345 XMLObject* XMLSecSignatureImpl::unmarshall(DOMElement* element, bool bindDocument)\r
346 {\r
347     Category::getInstance(XMLTOOLING_LOGCAT".Signature").debug("unmarshalling ds:Signature");\r
348 \r
349     try {\r
350         m_signature=XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->newSignatureFromDOM(\r
351             element->getOwnerDocument(), element\r
352             );\r
353         m_signature->load();\r
354     }\r
355     catch(XSECException& e) {\r
356         auto_ptr_char temp(e.getMsg());\r
357         throw UnmarshallingException(string("Caught an XMLSecurity exception while loading signature: ") + temp.get());\r
358     }\r
359     catch(XSECCryptoException& e) {\r
360         throw UnmarshallingException(string("Caught an XMLSecurity exception while loading signature: ") + e.getMsg());\r
361     }\r
362 \r
363     setDOM(element, bindDocument);\r
364     return this;\r
365 }\r