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