2082939f72d3b23d4488e7d8d386843fe739b0c8
[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), m_key(NULL), m_keyInfo(NULL), m_reference(NULL) {}\r
56         virtual ~XMLSecSignatureImpl();\r
57         \r
58         void releaseDOM() const;\r
59         void releaseChildrenDOM(bool propagateRelease=true) const {\r
60             if (m_keyInfo) {\r
61                 m_keyInfo->releaseDOM();\r
62                 if (propagateRelease)\r
63                     m_keyInfo->releaseChildrenDOM();\r
64             }\r
65         }\r
66         XMLObject* clone() const;\r
67         Signature* cloneSignature() const;\r
68 \r
69         DOMElement* marshall(DOMDocument* document=NULL, const vector<Signature*>* sigs=NULL) const;\r
70         DOMElement* marshall(DOMElement* parentElement, const vector<Signature*>* sigs=NULL) const;\r
71         XMLObject* unmarshall(DOMElement* element, bool bindDocument=false);\r
72         \r
73         // Getters\r
74         const XMLCh* getCanonicalizationMethod() const { return m_c14n ? m_c14n : DSIGConstants::s_unicodeStrURIEXC_C14N_NOC; }\r
75         const XMLCh* getSignatureAlgorithm() const { return m_sm ? m_sm : DSIGConstants::s_unicodeStrURIRSA_SHA1; }\r
76         KeyInfo* getKeyInfo() const { return m_keyInfo; }\r
77         ContentReference* getContentReference() const { return m_reference; }\r
78         DSIGSignature* getXMLSignature() const { return m_signature; }\r
79         \r
80         // Setters\r
81         void setCanonicalizationMethod(const XMLCh* c14n) { m_c14n = prepareForAssignment(m_c14n,c14n); }\r
82         void setSignatureAlgorithm(const XMLCh* sm) { m_sm = prepareForAssignment(m_sm,sm); }\r
83         void setSigningKey(XSECCryptoKey* signingKey) {\r
84             delete m_key;\r
85             m_key=signingKey;\r
86             if (m_key)\r
87                 releaseThisandParentDOM();\r
88         }\r
89         void setKeyInfo(KeyInfo* keyInfo) {\r
90             prepareForAssignment(m_keyInfo, keyInfo);\r
91             m_keyInfo=keyInfo;\r
92         }\r
93         void setContentReference(ContentReference* reference) {\r
94             delete m_reference;\r
95             m_reference=reference;\r
96             releaseThisandParentDOM();\r
97         }\r
98         \r
99         void sign();\r
100 \r
101     private:\r
102         mutable DSIGSignature* m_signature;\r
103         XMLCh* m_c14n;\r
104         XMLCh* m_sm;\r
105         XSECCryptoKey* m_key;\r
106         KeyInfo* m_keyInfo;\r
107         ContentReference* m_reference;\r
108     };\r
109     \r
110 };\r
111 \r
112 #if defined (_MSC_VER)\r
113     #pragma warning( pop )\r
114 #endif\r
115 \r
116 XMLSecSignatureImpl::~XMLSecSignatureImpl()\r
117 {\r
118     // Release the associated signature.\r
119     if (m_signature)\r
120         XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->releaseSignature(m_signature);\r
121 \r
122     XMLString::release(&m_c14n);\r
123     XMLString::release(&m_sm);\r
124     delete m_key;\r
125     delete m_keyInfo;\r
126     delete m_reference;\r
127 }\r
128 \r
129 void XMLSecSignatureImpl::releaseDOM() const\r
130 {\r
131     if (getDOM()) {\r
132         // This should save off the DOM\r
133         UnknownElementImpl::releaseDOM();\r
134         \r
135         // Release the associated signature.\r
136         if (m_signature) {\r
137             XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->releaseSignature(m_signature);\r
138             m_signature=NULL;\r
139         }\r
140     }\r
141 }\r
142 \r
143 XMLObject* XMLSecSignatureImpl::clone() const\r
144 {\r
145     return cloneSignature();\r
146 }\r
147 \r
148 Signature* XMLSecSignatureImpl::cloneSignature() const\r
149 {\r
150     XMLSecSignatureImpl* ret=new XMLSecSignatureImpl();\r
151 \r
152     ret->m_c14n=XMLString::replicate(m_c14n);\r
153     ret->m_sm=XMLString::replicate(m_sm);\r
154     if (m_key)\r
155         ret->m_key=m_key->clone();\r
156     if (m_keyInfo)\r
157         ret->m_keyInfo=m_keyInfo->cloneKeyInfo();\r
158 \r
159     // If there's no XML locally, serialize this object into the new one, otherwise just copy it over.\r
160     if (m_xml.empty())\r
161         serialize(ret->m_xml);\r
162     else\r
163         ret->m_xml=m_xml;\r
164 \r
165     return ret;\r
166 }\r
167 \r
168 void XMLSecSignatureImpl::sign()\r
169 {\r
170     Category& log=Category::getInstance(XMLTOOLING_LOGCAT".Signature");\r
171     log.debug("applying signature");\r
172 \r
173     if (!m_signature)\r
174         throw SignatureException("Only a marshalled Signature object can be signed.");\r
175     else if (!m_key)\r
176         throw SignatureException("No signing key available for signature creation.");\r
177     else if (!m_reference)\r
178         throw SignatureException("No ContentReference object set for signature creation.");\r
179 \r
180     try {\r
181         log.debug("creating signature reference(s)");\r
182         m_reference->createReferences(m_signature);\r
183         \r
184         log.debug("computing signature");\r
185         m_signature->setSigningKey(m_key->clone());\r
186         m_signature->sign();\r
187     }\r
188     catch(XSECException& e) {\r
189         auto_ptr_char temp(e.getMsg());\r
190         throw SignatureException(string("Caught an XMLSecurity exception while signing: ") + temp.get());\r
191     }\r
192     catch(XSECCryptoException& e) {\r
193         throw SignatureException(string("Caught an XMLSecurity exception while signing: ") + e.getMsg());\r
194     }\r
195 }\r
196 \r
197 DOMElement* XMLSecSignatureImpl::marshall(DOMDocument* document, const vector<Signature*>* sigs) const\r
198 {\r
199 #ifdef _DEBUG\r
200     xmltooling::NDC ndc("marshall");\r
201 #endif\r
202     \r
203     Category& log=Category::getInstance(XMLTOOLING_LOGCAT".Signature");\r
204     log.debug("marshalling ds:Signature");\r
205 \r
206     DOMElement* cachedDOM=getDOM();\r
207     if (cachedDOM) {\r
208         if (!document || document==cachedDOM->getOwnerDocument()) {\r
209             log.debug("Signature has a usable cached DOM, reusing it");\r
210             if (document)\r
211                 setDocumentElement(cachedDOM->getOwnerDocument(),cachedDOM);\r
212             releaseParentDOM(true);\r
213             return cachedDOM;\r
214         }\r
215         \r
216         // We have a DOM but it doesn't match the document we were given. This both sucks and blows.\r
217         // Without an adoptNode option to maintain the child pointers, we have to either import the\r
218         // DOM while somehow reassigning all the nested references (which amounts to a complete\r
219         // *unmarshall* operation), or we just release the existing DOM and hope that we can get\r
220         // it back. This depends on all objects being able to preserve their DOM at all costs.\r
221         releaseChildrenDOM(true);\r
222         releaseDOM();\r
223     }\r
224     \r
225     // If we get here, we didn't have a usable DOM.\r
226     bool bindDocument=false;\r
227     if (m_xml.empty()) {\r
228         // Fresh signature, so we just create an empty one.\r
229         log.debug("creating empty Signature element");\r
230         if (!document) {\r
231             document=DOMImplementationRegistry::getDOMImplementation(NULL)->createDocument();\r
232             bindDocument=true;\r
233         }\r
234         m_signature=XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->newSignature();\r
235         m_signature->setDSIGNSPrefix(XMLConstants::XMLSIG_PREFIX);\r
236         cachedDOM=m_signature->createBlankSignature(document, getCanonicalizationMethod(), getSignatureAlgorithm());\r
237     }\r
238     else {\r
239         // We need to reparse the XML we saved off into a new DOM.\r
240         MemBufInputSource src(reinterpret_cast<const XMLByte*>(m_xml.c_str()),m_xml.length(),"XMLSecSignatureImpl");\r
241         Wrapper4InputSource dsrc(&src,false);\r
242         log.debug("parsing Signature XML back into DOM tree");\r
243         DOMDocument* internalDoc=XMLToolingConfig::getConfig().getParser().parse(dsrc);\r
244         if (document) {\r
245             // The caller insists on using his own document, so we now have to import the thing\r
246             // into it. Then we're just dumping the one we built.\r
247             log.debug("reimporting new DOM into caller-supplied document");\r
248             cachedDOM=static_cast<DOMElement*>(document->importNode(internalDoc->getDocumentElement(), true));\r
249             internalDoc->release();\r
250         }\r
251         else {\r
252             // We just bind the document we built to the object as the result.\r
253             cachedDOM=static_cast<DOMElement*>(internalDoc->getDocumentElement());\r
254             document=internalDoc;\r
255             bindDocument=true;\r
256         }\r
257 \r
258         // Now reload the signature from the DOM.\r
259         try {\r
260             m_signature=XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->newSignatureFromDOM(\r
261                 document, cachedDOM\r
262                 );\r
263             m_signature->load();\r
264         }\r
265         catch(XSECException& e) {\r
266             if (bindDocument)\r
267                 document->release();\r
268             auto_ptr_char temp(e.getMsg());\r
269             throw MarshallingException(string("Caught an XMLSecurity exception while loading signature: ") + temp.get());\r
270         }\r
271         catch(XSECCryptoException& e) {\r
272             if (bindDocument)\r
273                 document->release();\r
274             throw MarshallingException(string("Caught an XMLSecurity exception while loading signature: ") + e.getMsg());\r
275         }\r
276     }\r
277     \r
278     // Marshall KeyInfo data.\r
279     if (m_keyInfo && (!m_signature->getKeyInfoList() || m_signature->getKeyInfoList()->isEmpty())) {\r
280         m_keyInfo->marshall(cachedDOM);\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, const vector<Signature*>* sigs) 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             if (parentElement!=cachedDOM->getParentNode()) {\r
306                 parentElement->appendChild(cachedDOM);\r
307                 releaseParentDOM(true);\r
308             }\r
309             return cachedDOM;\r
310         }\r
311         \r
312         // We have a DOM but it doesn't match the document we were given. This both sucks and blows.\r
313         // Without an adoptNode option to maintain the child pointers, we have to either import the\r
314         // DOM while somehow reassigning all the nested references (which amounts to a complete\r
315         // *unmarshall* operation), or we just release the existing DOM and hope that we can get\r
316         // it back. This depends on all objects being able to preserve their DOM at all costs.\r
317         releaseChildrenDOM(true);\r
318         releaseDOM();\r
319     }\r
320     \r
321     // If we get here, we didn't have a usable DOM.\r
322     if (m_xml.empty()) {\r
323         // Fresh signature, so we just create an empty one.\r
324         log.debug("creating empty Signature element");\r
325         m_signature=XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->newSignature();\r
326         m_signature->setDSIGNSPrefix(XMLConstants::XMLSIG_PREFIX);\r
327         cachedDOM=m_signature->createBlankSignature(\r
328             parentElement->getOwnerDocument(), getCanonicalizationMethod(), getSignatureAlgorithm()\r
329             );\r
330     }\r
331     else {\r
332         MemBufInputSource src(reinterpret_cast<const XMLByte*>(m_xml.c_str()),m_xml.length(),"XMLSecSignatureImpl");\r
333         Wrapper4InputSource dsrc(&src,false);\r
334         log.debug("parsing XML back into DOM tree");\r
335         DOMDocument* internalDoc=XMLToolingConfig::getConfig().getParser().parse(dsrc);\r
336         \r
337         log.debug("reimporting new DOM into caller-supplied document");\r
338         cachedDOM=static_cast<DOMElement*>(parentElement->getOwnerDocument()->importNode(internalDoc->getDocumentElement(),true));\r
339         internalDoc->release();\r
340 \r
341         // Now reload the signature from the DOM.\r
342         try {\r
343             m_signature=XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->newSignatureFromDOM(\r
344                 parentElement->getOwnerDocument(), cachedDOM\r
345                 );\r
346             m_signature->load();\r
347         }\r
348         catch(XSECException& e) {\r
349             auto_ptr_char temp(e.getMsg());\r
350             throw MarshallingException(string("Caught an XMLSecurity exception while loading signature: ") + temp.get());\r
351         }\r
352         catch(XSECCryptoException& e) {\r
353             throw MarshallingException(string("Caught an XMLSecurity exception while loading signature: ") + e.getMsg());\r
354         }\r
355     }\r
356 \r
357     // Marshall KeyInfo data.\r
358     if (m_keyInfo && (!m_signature->getKeyInfoList() || m_signature->getKeyInfoList()->isEmpty())) {\r
359         m_keyInfo->marshall(cachedDOM);\r
360     }\r
361 \r
362     // Recache the DOM and clear the serialized copy.\r
363     parentElement->appendChild(cachedDOM);\r
364     log.debug("caching DOM for Signature");\r
365     setDOM(cachedDOM, false);\r
366     releaseParentDOM(true);\r
367     m_xml.erase();\r
368     return cachedDOM;\r
369 }\r
370 \r
371 XMLObject* XMLSecSignatureImpl::unmarshall(DOMElement* element, bool bindDocument)\r
372 {\r
373     Category::getInstance(XMLTOOLING_LOGCAT".Signature").debug("unmarshalling ds:Signature");\r
374 \r
375     try {\r
376         m_signature=XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->newSignatureFromDOM(\r
377             element->getOwnerDocument(), element\r
378             );\r
379         m_signature->load();\r
380     }\r
381     catch(XSECException& e) {\r
382         auto_ptr_char temp(e.getMsg());\r
383         throw UnmarshallingException(string("Caught an XMLSecurity exception while loading signature: ") + temp.get());\r
384     }\r
385     catch(XSECCryptoException& e) {\r
386         throw UnmarshallingException(string("Caught an XMLSecurity exception while loading signature: ") + e.getMsg());\r
387     }\r
388 \r
389     setDOM(element, bindDocument);\r
390     return this;\r
391 }\r
392 \r
393 Signature* SignatureBuilder::buildObject(\r
394     const XMLCh* nsURI, const XMLCh* localName, const XMLCh* prefix, const QName* schemaType\r
395     ) const\r
396 {\r
397     if (!XMLString::equals(nsURI,XMLConstants::XMLSIG_NS) || !XMLString::equals(localName,Signature::LOCAL_NAME))\r
398         throw XMLObjectException("XMLSecSignatureBuilder requires standard Signature element name.");\r
399     return buildObject();\r
400 }\r
401 \r
402 Signature* SignatureBuilder::buildObject() const\r
403 {\r
404     return new XMLSecSignatureImpl();\r
405 }\r
406 \r
407 const XMLCh Signature::LOCAL_NAME[] = UNICODE_LITERAL_9(S,i,g,n,a,t,u,r,e);\r