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