Major revamp of credential and trust handling code, PKIX engine still needs work.
[shibboleth/cpp-xmltooling.git] / xmltooling / signature / impl / XMLSecSignatureImpl.cpp
1 /*
2 *  Copyright 2001-2007 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 "security/Credential.h"
27 #include "signature/KeyInfo.h"
28 #include "signature/Signature.h"
29 #include "util/NDC.h"
30 #include "util/XMLConstants.h"
31 #include "util/XMLHelper.h"
32
33 #include <log4cpp/Category.hh>
34 #include <xercesc/framework/MemBufInputSource.hpp>
35 #include <xercesc/framework/Wrapper4InputSource.hpp>
36 #include <xercesc/util/XMLUniDefs.hpp>
37 #include <xsec/dsig/DSIGKeyInfoX509.hpp>
38 #include <xsec/dsig/DSIGReference.hpp>
39 #include <xsec/enc/XSECCryptoException.hpp>
40 #include <xsec/framework/XSECAlgorithmHandler.hpp>
41 #include <xsec/framework/XSECAlgorithmMapper.hpp>
42 #include <xsec/framework/XSECException.hpp>
43 #include <xsec/transformers/TXFMSB.hpp>
44 #include <xsec/transformers/TXFMChain.hpp>
45 #include <xsec/transformers/TXFMOutputFile.hpp>
46
47 using namespace xmlsignature;
48 using namespace xmltooling;
49 using namespace log4cpp;
50 using namespace std;
51 using xmlconstants::XMLSIG_NS;
52 using xmlconstants::XMLSIG_PREFIX;
53
54 #if defined (_MSC_VER)
55     #pragma warning( push )
56     #pragma warning( disable : 4250 4251 )
57 #endif
58
59 namespace xmlsignature {
60     
61     class XMLTOOL_DLLLOCAL XMLSecSignatureImpl : public UnknownElementImpl, public virtual Signature
62     {
63     public:
64         XMLSecSignatureImpl() : UnknownElementImpl(XMLSIG_NS, Signature::LOCAL_NAME, XMLSIG_PREFIX),
65             m_signature(NULL), m_c14n(NULL), m_sm(NULL), m_key(NULL), m_keyInfo(NULL), m_reference(NULL) {}
66         virtual ~XMLSecSignatureImpl();
67         
68         void releaseDOM() const;
69         void releaseChildrenDOM(bool propagateRelease=true) const {
70             if (m_keyInfo) {
71                 m_keyInfo->releaseDOM();
72                 if (propagateRelease)
73                     m_keyInfo->releaseChildrenDOM();
74             }
75         }
76         XMLObject* clone() const;
77         Signature* cloneSignature() const;
78
79         DOMElement* marshall(DOMDocument* document=NULL, const vector<Signature*>* sigs=NULL, const Credential* credential=NULL) const;
80         DOMElement* marshall(DOMElement* parentElement, const vector<Signature*>* sigs=NULL, const Credential* credential=NULL) const;
81         XMLObject* unmarshall(DOMElement* element, bool bindDocument=false);
82         
83         // Getters
84         const XMLCh* getCanonicalizationMethod() const { return m_c14n ? m_c14n : DSIGConstants::s_unicodeStrURIEXC_C14N_NOC; }
85         const XMLCh* getSignatureAlgorithm() const { return m_sm ? m_sm : DSIGConstants::s_unicodeStrURIRSA_SHA1; }
86         KeyInfo* getKeyInfo() const { return m_keyInfo; }
87         ContentReference* getContentReference() const { return m_reference; }
88         DSIGSignature* getXMLSignature() const { return m_signature; }
89         
90         // Setters
91         void setCanonicalizationMethod(const XMLCh* c14n) { m_c14n = prepareForAssignment(m_c14n,c14n); }
92         void setSignatureAlgorithm(const XMLCh* sm) { m_sm = prepareForAssignment(m_sm,sm); }
93         void setSigningKey(XSECCryptoKey* signingKey) {
94             delete m_key;
95             m_key=signingKey;
96         }
97         void setKeyInfo(KeyInfo* keyInfo) {
98             prepareForAssignment(m_keyInfo, keyInfo);
99             m_keyInfo=keyInfo;
100         }
101         void setContentReference(ContentReference* reference) {
102             delete m_reference;
103             m_reference=reference;
104         }
105         
106         void sign(const Credential* credential=NULL);
107
108     private:
109         mutable DSIGSignature* m_signature;
110         XMLCh* m_c14n;
111         XMLCh* m_sm;
112         XSECCryptoKey* m_key;
113         mutable KeyInfo* m_keyInfo;
114         ContentReference* m_reference;
115     };
116     
117 };
118
119 #if defined (_MSC_VER)
120     #pragma warning( pop )
121 #endif
122
123 XMLSecSignatureImpl::~XMLSecSignatureImpl()
124 {
125     // Release the associated signature.
126     if (m_signature)
127         XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->releaseSignature(m_signature);
128
129     XMLString::release(&m_c14n);
130     XMLString::release(&m_sm);
131     delete m_key;
132     delete m_keyInfo;
133     delete m_reference;
134 }
135
136 void XMLSecSignatureImpl::releaseDOM() const
137 {
138     if (getDOM()) {
139         // This should save off the DOM
140         UnknownElementImpl::releaseDOM();
141         
142         // Release the associated signature.
143         if (m_signature) {
144             XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->releaseSignature(m_signature);
145             m_signature=NULL;
146         }
147     }
148 }
149
150 XMLObject* XMLSecSignatureImpl::clone() const
151 {
152     return cloneSignature();
153 }
154
155 Signature* XMLSecSignatureImpl::cloneSignature() const
156 {
157     XMLSecSignatureImpl* ret=new XMLSecSignatureImpl();
158
159     ret->m_c14n=XMLString::replicate(m_c14n);
160     ret->m_sm=XMLString::replicate(m_sm);
161     if (m_key)
162         ret->m_key=m_key->clone();
163     if (m_keyInfo)
164         ret->m_keyInfo=m_keyInfo->cloneKeyInfo();
165
166     // If there's no XML locally, serialize this object into the new one, otherwise just copy it over.
167     if (m_xml.empty())
168         serialize(ret->m_xml);
169     else
170         ret->m_xml=m_xml;
171
172     return ret;
173 }
174
175 void XMLSecSignatureImpl::sign(const Credential* credential)
176 {
177     Category& log=Category::getInstance(XMLTOOLING_LOGCAT".Signature");
178     log.debug("applying signature");
179
180     if (!m_signature)
181         throw SignatureException("Only a marshalled Signature object can be signed.");
182     else if (!m_reference)
183         throw SignatureException("No ContentReference object set for signature creation.");
184
185     XSECCryptoKey* key = credential ? credential->getPrivateKey() : m_key;
186     if (!key)
187         throw SignatureException("No signing key available for signature creation.");
188
189     try {
190         log.debug("creating signature reference(s)");
191         DSIGReferenceList* refs = m_signature->getReferenceList();
192         while (refs && refs->getSize())
193             delete refs->removeReference(0);
194         m_reference->createReferences(m_signature);
195         
196         log.debug("computing signature");
197         m_signature->setSigningKey(key->clone());
198         m_signature->sign();
199     }
200     catch(XSECException& e) {
201         auto_ptr_char temp(e.getMsg());
202         throw SignatureException(string("Caught an XMLSecurity exception while signing: ") + temp.get());
203     }
204     catch(XSECCryptoException& e) {
205         throw SignatureException(string("Caught an XMLSecurity exception while signing: ") + e.getMsg());
206     }
207 }
208
209 DOMElement* XMLSecSignatureImpl::marshall(DOMDocument* document, const vector<Signature*>* sigs, const Credential* credential) const
210 {
211 #ifdef _DEBUG
212     xmltooling::NDC ndc("marshall");
213 #endif
214     
215     Category& log=Category::getInstance(XMLTOOLING_LOGCAT".XMLObject.Signature");
216     log.debug("marshalling ds:Signature");
217
218     DOMElement* cachedDOM=getDOM();
219     if (cachedDOM) {
220         if (!document || document==cachedDOM->getOwnerDocument()) {
221             log.debug("Signature has a usable cached DOM, reusing it");
222             if (document)
223                 setDocumentElement(cachedDOM->getOwnerDocument(),cachedDOM);
224             releaseParentDOM(true);
225             return cachedDOM;
226         }
227         
228         // We have a DOM but it doesn't match the document we were given. This both sucks and blows.
229         // Without an adoptNode option to maintain the child pointers, we have to either import the
230         // DOM while somehow reassigning all the nested references (which amounts to a complete
231         // *unmarshall* operation), or we just release the existing DOM and hope that we can get
232         // it back. This depends on all objects being able to preserve their DOM at all costs.
233         releaseChildrenDOM(true);
234         releaseDOM();
235     }
236     
237     // If we get here, we didn't have a usable DOM.
238     bool bindDocument=false;
239     if (m_xml.empty()) {
240         // Fresh signature, so we just create an empty one.
241         log.debug("creating empty Signature element");
242         if (!document) {
243             document=DOMImplementationRegistry::getDOMImplementation(NULL)->createDocument();
244             bindDocument=true;
245         }
246         m_signature=XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->newSignature();
247         m_signature->setDSIGNSPrefix(XMLSIG_PREFIX);
248         cachedDOM=m_signature->createBlankSignature(document, getCanonicalizationMethod(), getSignatureAlgorithm());
249     }
250     else {
251         // We need to reparse the XML we saved off into a new DOM.
252         MemBufInputSource src(reinterpret_cast<const XMLByte*>(m_xml.c_str()),m_xml.length(),"XMLSecSignatureImpl");
253         Wrapper4InputSource dsrc(&src,false);
254         log.debug("parsing Signature XML back into DOM tree");
255         DOMDocument* internalDoc=XMLToolingConfig::getConfig().getParser().parse(dsrc);
256         if (document) {
257             // The caller insists on using his own document, so we now have to import the thing
258             // into it. Then we're just dumping the one we built.
259             log.debug("reimporting new DOM into caller-supplied document");
260             cachedDOM=static_cast<DOMElement*>(document->importNode(internalDoc->getDocumentElement(), true));
261             internalDoc->release();
262         }
263         else {
264             // We just bind the document we built to the object as the result.
265             cachedDOM=static_cast<DOMElement*>(internalDoc->getDocumentElement());
266             document=internalDoc;
267             bindDocument=true;
268         }
269
270         // Now reload the signature from the DOM.
271         try {
272             m_signature=XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->newSignatureFromDOM(
273                 document, cachedDOM
274                 );
275             m_signature->load();
276         }
277         catch(XSECException& e) {
278             if (bindDocument)
279                 document->release();
280             auto_ptr_char temp(e.getMsg());
281             throw MarshallingException(string("Caught an XMLSecurity exception while loading signature: ") + temp.get());
282         }
283         catch(XSECCryptoException& e) {
284             if (bindDocument)
285                 document->release();
286             throw MarshallingException(string("Caught an XMLSecurity exception while loading signature: ") + e.getMsg());
287         }
288     }
289     
290     // Marshall KeyInfo data.
291     if (credential) {
292         delete m_keyInfo;
293         m_keyInfo = NULL;
294         const KeyInfo* fromcred = credential->getKeyInfo();
295         if (fromcred)
296             m_keyInfo = fromcred->cloneKeyInfo();
297     }
298     if (m_keyInfo && (!m_signature->getKeyInfoList() || m_signature->getKeyInfoList()->isEmpty())) {
299         m_keyInfo->marshall(cachedDOM);
300     }
301
302     // Recache the DOM and clear the serialized copy.
303     setDocumentElement(document, cachedDOM);
304     log.debug("caching DOM for Signature (document is %sbound)", bindDocument ? "" : "not ");
305     setDOM(cachedDOM, bindDocument);
306     releaseParentDOM(true);
307     m_xml.erase();
308     return cachedDOM;
309 }
310
311 DOMElement* XMLSecSignatureImpl::marshall(DOMElement* parentElement, const vector<Signature*>* sigs, const Credential* credential) const
312 {
313 #ifdef _DEBUG
314     xmltooling::NDC ndc("marshall");
315 #endif
316     
317     Category& log=Category::getInstance(XMLTOOLING_LOGCAT".XMLObject.Signature");
318     log.debug("marshalling ds:Signature");
319
320     DOMElement* cachedDOM=getDOM();
321     if (cachedDOM) {
322         if (parentElement->getOwnerDocument()==cachedDOM->getOwnerDocument()) {
323             log.debug("Signature has a usable cached DOM, reusing it");
324             if (parentElement!=cachedDOM->getParentNode()) {
325                 parentElement->appendChild(cachedDOM);
326                 releaseParentDOM(true);
327             }
328             return cachedDOM;
329         }
330         
331         // We have a DOM but it doesn't match the document we were given. This both sucks and blows.
332         // Without an adoptNode option to maintain the child pointers, we have to either import the
333         // DOM while somehow reassigning all the nested references (which amounts to a complete
334         // *unmarshall* operation), or we just release the existing DOM and hope that we can get
335         // it back. This depends on all objects being able to preserve their DOM at all costs.
336         releaseChildrenDOM(true);
337         releaseDOM();
338     }
339     
340     // If we get here, we didn't have a usable DOM.
341     if (m_xml.empty()) {
342         // Fresh signature, so we just create an empty one.
343         log.debug("creating empty Signature element");
344         m_signature=XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->newSignature();
345         m_signature->setDSIGNSPrefix(XMLSIG_PREFIX);
346         cachedDOM=m_signature->createBlankSignature(
347             parentElement->getOwnerDocument(), getCanonicalizationMethod(), getSignatureAlgorithm()
348             );
349     }
350     else {
351         MemBufInputSource src(reinterpret_cast<const XMLByte*>(m_xml.c_str()),m_xml.length(),"XMLSecSignatureImpl");
352         Wrapper4InputSource dsrc(&src,false);
353         log.debug("parsing XML back into DOM tree");
354         DOMDocument* internalDoc=XMLToolingConfig::getConfig().getParser().parse(dsrc);
355         
356         log.debug("reimporting new DOM into caller-supplied document");
357         cachedDOM=static_cast<DOMElement*>(parentElement->getOwnerDocument()->importNode(internalDoc->getDocumentElement(),true));
358         internalDoc->release();
359
360         // Now reload the signature from the DOM.
361         try {
362             m_signature=XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->newSignatureFromDOM(
363                 parentElement->getOwnerDocument(), cachedDOM
364                 );
365             m_signature->load();
366         }
367         catch(XSECException& e) {
368             auto_ptr_char temp(e.getMsg());
369             throw MarshallingException(string("Caught an XMLSecurity exception while loading signature: ") + temp.get());
370         }
371         catch(XSECCryptoException& e) {
372             throw MarshallingException(string("Caught an XMLSecurity exception while loading signature: ") + e.getMsg());
373         }
374     }
375
376     // Marshall KeyInfo data.
377     if (credential) {
378         delete m_keyInfo;
379         m_keyInfo = NULL;
380         const KeyInfo* fromcred = credential->getKeyInfo();
381         if (fromcred)
382             m_keyInfo = fromcred->cloneKeyInfo();
383     }
384     if (m_keyInfo && (!m_signature->getKeyInfoList() || m_signature->getKeyInfoList()->isEmpty())) {
385         m_keyInfo->marshall(cachedDOM);
386     }
387
388     // Recache the DOM and clear the serialized copy.
389     parentElement->appendChild(cachedDOM);
390     log.debug("caching DOM for Signature");
391     setDOM(cachedDOM, false);
392     releaseParentDOM(true);
393     m_xml.erase();
394     return cachedDOM;
395 }
396
397 XMLObject* XMLSecSignatureImpl::unmarshall(DOMElement* element, bool bindDocument)
398 {
399     Category::getInstance(XMLTOOLING_LOGCAT".XMLObject.Signature").debug("unmarshalling ds:Signature");
400
401     try {
402         m_signature=XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->newSignatureFromDOM(
403             element->getOwnerDocument(), element
404             );
405         m_signature->load();
406     }
407     catch(XSECException& e) {
408         auto_ptr_char temp(e.getMsg());
409         throw UnmarshallingException(string("Caught an XMLSecurity exception while loading signature: ") + temp.get());
410     }
411     catch(XSECCryptoException& e) {
412         throw UnmarshallingException(string("Caught an XMLSecurity exception while loading signature: ") + e.getMsg());
413     }
414
415     setDOM(element, bindDocument);
416     return this;
417 }
418
419 #ifdef HAVE_COVARIANT_RETURNS
420 Signature*
421 #else
422 XMLObject*
423 #endif
424 SignatureBuilder::buildObject(
425     const XMLCh* nsURI, const XMLCh* localName, const XMLCh* prefix, const QName* schemaType
426     ) const
427 {
428     if (!XMLString::equals(nsURI,XMLSIG_NS) || !XMLString::equals(localName,Signature::LOCAL_NAME))
429         throw XMLObjectException("XMLSecSignatureBuilder requires standard Signature element name.");
430     return buildObject();
431 }
432
433 #ifdef HAVE_COVARIANT_RETURNS
434 Signature*
435 #else
436 XMLObject*
437 #endif
438 SignatureBuilder::buildObject() const
439 {
440     return new XMLSecSignatureImpl();
441 }
442
443 const XMLCh Signature::LOCAL_NAME[] = UNICODE_LITERAL_9(S,i,g,n,a,t,u,r,e);
444
445 // Raw signature methods.
446
447 unsigned int Signature::createRawSignature(
448     XSECCryptoKey* key, const XMLCh* sigAlgorithm, const char* in, unsigned int in_len, char* out, unsigned int out_len
449     )
450 {
451     try {
452         XSECAlgorithmHandler* handler = XSECPlatformUtils::g_algorithmMapper->mapURIToHandler(sigAlgorithm);
453         if (!handler) {
454             auto_ptr_char alg(sigAlgorithm);
455             throw SignatureException("Unsupported signature algorithm ($1).", params(1,alg.get()));
456         }
457         
458         // Move input into a safeBuffer to source the transform chain.
459         safeBuffer sb,sbout;
460         sb.sbStrncpyIn(in,in_len);
461         TXFMSB* sbt = new TXFMSB(NULL);
462         sbt->setInput(sb, in_len);
463         TXFMChain tx(sbt);
464         
465         // Sign the chain.
466         unsigned int siglen = handler->signToSafeBuffer(&tx, sigAlgorithm, key, out_len-1, sbout);
467         if (siglen >= out_len)
468             throw SignatureException("Signature size exceeded output buffer size.");
469         
470         // Push all non-whitespace into buffer.
471         unsigned int ret_len = 0;
472         const char* source = sbout.rawCharBuffer();
473         while (siglen--) {
474             if (isspace(*source))
475                 ++source;
476             else {
477                 *out++ = *source++;
478                 ++ret_len;
479             }
480         }
481         *out = 0;
482         return ret_len;
483     }
484     catch(XSECException& e) {
485         auto_ptr_char temp(e.getMsg());
486         throw SignatureException(string("Caught an XMLSecurity exception while creating raw signature: ") + temp.get());
487     }
488     catch(XSECCryptoException& e) {
489         throw SignatureException(string("Caught an XMLSecurity exception while creating raw signature: ") + e.getMsg());
490     }
491 }
492
493 bool Signature::verifyRawSignature(
494     XSECCryptoKey* key, const XMLCh* sigAlgorithm, const char* signature, const char* in, unsigned int in_len
495     )
496 {
497     try {
498         XSECAlgorithmHandler* handler = XSECPlatformUtils::g_algorithmMapper->mapURIToHandler(sigAlgorithm);
499         if (!handler) {
500             auto_ptr_char alg(sigAlgorithm);
501             throw SignatureException("Unsupported signature algorithm ($1).", params(1,alg.get()));
502         }
503         
504         // Move input into a safeBuffer to source the transform chain.
505         safeBuffer sb;
506         sb.sbStrncpyIn(in,in_len);
507         TXFMSB* sbt = new TXFMSB(NULL);
508         sbt->setInput(sb, in_len);
509         TXFMChain tx(sbt);
510         
511         // Verify the chain.
512         return handler->verifyBase64Signature(&tx, sigAlgorithm, signature, 0, key);
513     }
514     catch(XSECException& e) {
515         auto_ptr_char temp(e.getMsg());
516         throw SignatureException(string("Caught an XMLSecurity exception while verifying raw signature: ") + temp.get());
517     }
518     catch(XSECCryptoException& e) {
519         throw SignatureException(string("Caught an XMLSecurity exception while verifying raw signature: ") + e.getMsg());
520     }
521 }