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