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