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