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