2 * Licensed to the University Corporation for Advanced Internet
3 * Development, Inc. (UCAID) under one or more contributor license
4 * agreements. See the NOTICE file distributed with this work for
5 * additional information regarding copyright ownership.
7 * UCAID licenses this file to you under the Apache License,
8 * Version 2.0 (the "License"); you may not use this file except
9 * in compliance with the License. You may obtain a copy of the
12 * http://www.apache.org/licenses/LICENSE-2.0
14 * Unless required by applicable law or agreed to in writing,
15 * software distributed under the License is distributed on an
16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
17 * either express or implied. See the License for the specific
18 * language governing permissions and limitations under the License.
22 * XMLSecSignatureImpl.cpp
24 * Signature class for XMLSec-based signature-handling.
28 #include "exceptions.h"
30 #include "impl/UnknownElement.h"
31 #include "security/Credential.h"
32 #include "signature/ContentReference.h"
33 #include "signature/KeyInfo.h"
34 #include "signature/Signature.h"
36 #include "util/XMLConstants.h"
37 #include "util/XMLHelper.h"
39 #include <xercesc/framework/MemBufInputSource.hpp>
40 #include <xercesc/framework/Wrapper4InputSource.hpp>
41 #include <xercesc/util/XMLUniDefs.hpp>
42 #include <xsec/dsig/DSIGKeyInfoX509.hpp>
43 #include <xsec/dsig/DSIGReference.hpp>
44 #include <xsec/enc/XSECCryptoException.hpp>
45 #include <xsec/framework/XSECAlgorithmHandler.hpp>
46 #include <xsec/framework/XSECAlgorithmMapper.hpp>
47 #include <xsec/framework/XSECException.hpp>
48 #include <xsec/transformers/TXFMSB.hpp>
49 #include <xsec/transformers/TXFMChain.hpp>
50 #include <xsec/transformers/TXFMOutputFile.hpp>
52 using namespace xmlsignature;
53 using namespace xmltooling::logging;
54 using namespace xmltooling;
55 using namespace xercesc;
57 using xmlconstants::XMLSIG_NS;
58 using xmlconstants::XMLSIG_PREFIX;
60 namespace xmlsignature {
62 #if defined (_MSC_VER)
63 #pragma warning( push )
64 #pragma warning( disable : 4250 4251 )
67 class XMLTOOL_DLLLOCAL XMLSecSignatureImpl : public UnknownElementImpl, public virtual Signature
70 XMLSecSignatureImpl() : AbstractXMLObject(XMLSIG_NS, Signature::LOCAL_NAME, XMLSIG_PREFIX),
71 UnknownElementImpl(XMLSIG_NS, Signature::LOCAL_NAME, XMLSIG_PREFIX),
72 m_signature(nullptr), m_c14n(nullptr), m_sm(nullptr), m_key(nullptr), m_keyInfo(nullptr), m_reference(nullptr) {}
73 virtual ~XMLSecSignatureImpl();
75 void releaseDOM() const;
76 void releaseChildrenDOM(bool propagateRelease=true) const {
78 m_keyInfo->releaseDOM();
80 m_keyInfo->releaseChildrenDOM();
83 XMLObject* clone() const;
84 Signature* cloneSignature() const;
86 DOMElement* marshall(DOMDocument* document=nullptr, const vector<Signature*>* sigs=nullptr, const Credential* credential=nullptr) const;
87 DOMElement* marshall(DOMElement* parentElement, const vector<Signature*>* sigs=nullptr, const Credential* credential=nullptr) const;
88 XMLObject* unmarshall(DOMElement* element, bool bindDocument=false);
91 const XMLCh* getCanonicalizationMethod() const {
93 return canonicalizationMethod2UNICODEURI(m_signature->getCanonicalizationMethod());
94 return m_c14n ? m_c14n : DSIGConstants::s_unicodeStrURIEXC_C14N_NOC;
96 const XMLCh* getSignatureAlgorithm() const {
97 if (!m_sm && m_signature) {
98 #ifdef XMLTOOLING_XMLSEC_SIGALGORITHM
99 m_sm = XMLString::replicate(m_signature->getAlgorithmURI());
102 if (signatureHashMethod2URI(sURI, m_signature->getSignatureMethod(), m_signature->getHashMethod()))
103 m_sm = XMLString::replicate(sURI.sbStrToXMLCh());
109 KeyInfo* getKeyInfo() const { return m_keyInfo; }
110 ContentReference* getContentReference() const { return m_reference; }
111 DSIGSignature* getXMLSignature() const { return m_signature; }
114 void setCanonicalizationMethod(const XMLCh* c14n) { m_c14n = prepareForAssignment(m_c14n,c14n); }
115 void setSignatureAlgorithm(const XMLCh* sm) { m_sm = prepareForAssignment(m_sm,sm); }
116 void setSigningKey(XSECCryptoKey* signingKey) {
120 void setKeyInfo(KeyInfo* keyInfo) {
121 prepareForAssignment(m_keyInfo, keyInfo);
124 void setContentReference(ContentReference* reference) {
126 m_reference=reference;
129 void sign(const Credential* credential=nullptr);
132 mutable DSIGSignature* m_signature;
135 XSECCryptoKey* m_key;
136 mutable KeyInfo* m_keyInfo;
137 ContentReference* m_reference;
140 #if defined (_MSC_VER)
141 #pragma warning( pop )
145 ContentReference::ContentReference()
149 ContentReference::~ContentReference()
153 Signature::Signature()
157 Signature::~Signature()
161 XMLSecSignatureImpl::~XMLSecSignatureImpl()
163 // Release the associated signature.
165 XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->releaseSignature(m_signature);
167 XMLString::release(&m_c14n);
168 XMLString::release(&m_sm);
174 void XMLSecSignatureImpl::releaseDOM() const
177 // This should save off the DOM
178 UnknownElementImpl::releaseDOM();
180 // Release the associated signature.
182 XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->releaseSignature(m_signature);
188 XMLObject* XMLSecSignatureImpl::clone() const
190 return cloneSignature();
193 Signature* XMLSecSignatureImpl::cloneSignature() const
195 XMLSecSignatureImpl* ret=new XMLSecSignatureImpl();
197 ret->m_c14n=XMLString::replicate(m_c14n);
198 ret->m_sm=XMLString::replicate(m_sm);
200 ret->m_key=m_key->clone();
202 ret->m_keyInfo=m_keyInfo->cloneKeyInfo();
204 // If there's no XML locally, serialize this object into the new one, otherwise just copy it over.
206 serialize(ret->m_xml);
213 void XMLSecSignatureImpl::sign(const Credential* credential)
215 Category& log=Category::getInstance(XMLTOOLING_LOGCAT".Signature");
216 log.debug("applying signature");
219 throw SignatureException("Only a marshalled Signature object can be signed.");
220 else if (!m_reference)
221 throw SignatureException("No ContentReference object set for signature creation.");
223 XSECCryptoKey* key = credential ? credential->getPrivateKey() : m_key;
225 throw SignatureException("No signing key available for signature creation.");
228 log.debug("creating signature reference(s)");
229 DSIGReferenceList* refs = m_signature->getReferenceList();
230 while (refs && refs->getSize())
231 delete refs->removeReference(0);
232 m_reference->createReferences(m_signature);
234 log.debug("computing signature");
235 m_signature->setSigningKey(key->clone());
238 catch(XSECException& e) {
239 auto_ptr_char temp(e.getMsg());
240 throw SignatureException(string("Caught an XMLSecurity exception while signing: ") + temp.get());
242 catch(XSECCryptoException& e) {
243 throw SignatureException(string("Caught an XMLSecurity exception while signing: ") + e.getMsg());
247 DOMElement* XMLSecSignatureImpl::marshall(DOMDocument* document, const vector<Signature*>* sigs, const Credential* credential) const
250 xmltooling::NDC ndc("marshall");
253 Category& log=Category::getInstance(XMLTOOLING_LOGCAT".XMLObject.Signature");
254 log.debug("marshalling ds:Signature");
256 DOMElement* cachedDOM=getDOM();
258 if (!document || document==cachedDOM->getOwnerDocument()) {
259 log.debug("Signature has a usable cached DOM, reusing it");
261 setDocumentElement(cachedDOM->getOwnerDocument(),cachedDOM);
262 releaseParentDOM(true);
266 // We have a DOM but it doesn't match the document we were given. This both sucks and blows.
267 // Without an adoptNode option to maintain the child pointers, we have to either import the
268 // DOM while somehow reassigning all the nested references (which amounts to a complete
269 // *unmarshall* operation), or we just release the existing DOM and hope that we can get
270 // it back. This depends on all objects being able to preserve their DOM at all costs.
271 releaseChildrenDOM(true);
275 // If we get here, we didn't have a usable DOM.
276 bool bindDocument=false;
278 // Fresh signature, so we just create an empty one.
279 log.debug("creating empty Signature element");
281 document=DOMImplementationRegistry::getDOMImplementation(nullptr)->createDocument();
284 DSIGSignature* temp=XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->newSignature();
285 temp->setDSIGNSPrefix(XMLSIG_PREFIX);
286 const XMLCh* alg = getSignatureAlgorithm();
288 alg = DSIGConstants::s_unicodeStrURIRSA_SHA1;
289 cachedDOM=temp->createBlankSignature(document, getCanonicalizationMethod(), alg);
293 // We need to reparse the XML we saved off into a new DOM.
294 MemBufInputSource src(reinterpret_cast<const XMLByte*>(m_xml.c_str()),m_xml.length(),"XMLSecSignatureImpl");
295 Wrapper4InputSource dsrc(&src,false);
296 log.debug("parsing Signature XML back into DOM tree");
297 DOMDocument* internalDoc=XMLToolingConfig::getConfig().getParser().parse(dsrc);
299 // The caller insists on using his own document, so we now have to import the thing
300 // into it. Then we're just dumping the one we built.
301 log.debug("reimporting new DOM into caller-supplied document");
303 cachedDOM=static_cast<DOMElement*>(document->importNode(internalDoc->getDocumentElement(), true));
305 catch (XMLException& ex) {
306 internalDoc->release();
307 auto_ptr_char temp(ex.getMessage());
308 throw XMLParserException(
309 string("Error importing DOM into caller-supplied document: ") + (temp.get() ? temp.get() : "no message")
312 internalDoc->release();
315 // We just bind the document we built to the object as the result.
316 cachedDOM=static_cast<DOMElement*>(internalDoc->getDocumentElement());
317 document=internalDoc;
321 // Now reload the signature from the DOM.
323 m_signature=XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->newSignatureFromDOM(
328 catch(XSECException& e) {
331 auto_ptr_char temp(e.getMsg());
332 throw MarshallingException(string("Caught an XMLSecurity exception while loading signature: ") + temp.get());
334 catch(XSECCryptoException& e) {
337 throw MarshallingException(string("Caught an XMLSecurity exception while loading signature: ") + e.getMsg());
341 // Marshall KeyInfo data.
345 m_keyInfo = credential->getKeyInfo();
347 if (m_keyInfo && (!m_signature->getKeyInfoList() || m_signature->getKeyInfoList()->isEmpty())) {
348 m_keyInfo->marshall(cachedDOM);
351 // Recache the DOM and clear the serialized copy.
352 setDocumentElement(document, cachedDOM);
353 log.debug("caching DOM for Signature (document is %sbound)", bindDocument ? "" : "not ");
354 setDOM(cachedDOM, bindDocument);
355 releaseParentDOM(true);
360 DOMElement* XMLSecSignatureImpl::marshall(DOMElement* parentElement, const vector<Signature*>* sigs, const Credential* credential) const
363 xmltooling::NDC ndc("marshall");
366 Category& log=Category::getInstance(XMLTOOLING_LOGCAT".XMLObject.Signature");
367 log.debug("marshalling ds:Signature");
369 DOMElement* cachedDOM=getDOM();
371 if (parentElement->getOwnerDocument()==cachedDOM->getOwnerDocument()) {
372 log.debug("Signature has a usable cached DOM, reusing it");
373 if (parentElement!=cachedDOM->getParentNode()) {
374 parentElement->appendChild(cachedDOM);
375 releaseParentDOM(true);
380 // We have a DOM but it doesn't match the document we were given. This both sucks and blows.
381 // Without an adoptNode option to maintain the child pointers, we have to either import the
382 // DOM while somehow reassigning all the nested references (which amounts to a complete
383 // *unmarshall* operation), or we just release the existing DOM and hope that we can get
384 // it back. This depends on all objects being able to preserve their DOM at all costs.
385 releaseChildrenDOM(true);
389 // If we get here, we didn't have a usable DOM.
391 // Fresh signature, so we just create an empty one.
392 log.debug("creating empty Signature element");
393 DSIGSignature* temp=XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->newSignature();
394 temp->setDSIGNSPrefix(XMLSIG_PREFIX);
395 const XMLCh* alg = getSignatureAlgorithm();
397 alg = DSIGConstants::s_unicodeStrURIRSA_SHA1;
398 cachedDOM=temp->createBlankSignature(parentElement->getOwnerDocument(), getCanonicalizationMethod(), alg);
402 MemBufInputSource src(reinterpret_cast<const XMLByte*>(m_xml.c_str()),m_xml.length(),"XMLSecSignatureImpl");
403 Wrapper4InputSource dsrc(&src,false);
404 log.debug("parsing XML back into DOM tree");
405 DOMDocument* internalDoc=XMLToolingConfig::getConfig().getParser().parse(dsrc);
407 log.debug("reimporting new DOM into caller-supplied document");
409 cachedDOM=static_cast<DOMElement*>(parentElement->getOwnerDocument()->importNode(internalDoc->getDocumentElement(),true));
411 catch (XMLException& ex) {
412 internalDoc->release();
413 auto_ptr_char temp(ex.getMessage());
414 throw XMLParserException(
415 string("Error importing DOM into caller-supplied document: ") + (temp.get() ? temp.get() : "no message")
418 internalDoc->release();
420 // Now reload the signature from the DOM.
422 m_signature=XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->newSignatureFromDOM(
423 parentElement->getOwnerDocument(), cachedDOM
427 catch(XSECException& e) {
428 auto_ptr_char temp(e.getMsg());
429 throw MarshallingException(string("Caught an XMLSecurity exception while loading signature: ") + temp.get());
431 catch(XSECCryptoException& e) {
432 throw MarshallingException(string("Caught an XMLSecurity exception while loading signature: ") + e.getMsg());
436 // Marshall KeyInfo data.
440 m_keyInfo = credential->getKeyInfo();
442 if (m_keyInfo && (!m_signature->getKeyInfoList() || m_signature->getKeyInfoList()->isEmpty())) {
443 m_keyInfo->marshall(cachedDOM);
446 // Recache the DOM and clear the serialized copy.
447 parentElement->appendChild(cachedDOM);
448 log.debug("caching DOM for Signature");
449 setDOM(cachedDOM, false);
450 releaseParentDOM(true);
455 XMLObject* XMLSecSignatureImpl::unmarshall(DOMElement* element, bool bindDocument)
457 Category::getInstance(XMLTOOLING_LOGCAT".XMLObject.Signature").debug("unmarshalling ds:Signature");
460 m_signature=XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->newSignatureFromDOM(
461 element->getOwnerDocument(), element
465 catch(XSECException& e) {
466 auto_ptr_char temp(e.getMsg());
467 throw UnmarshallingException(string("Caught an XMLSecurity exception while loading signature: ") + temp.get());
469 catch(XSECCryptoException& e) {
470 throw UnmarshallingException(string("Caught an XMLSecurity exception while loading signature: ") + e.getMsg());
473 setDOM(element, bindDocument);
477 #ifdef HAVE_COVARIANT_RETURNS
482 SignatureBuilder::buildObject(
483 const XMLCh* nsURI, const XMLCh* localName, const XMLCh* prefix, const xmltooling::QName* schemaType
486 if (!XMLString::equals(nsURI,XMLSIG_NS) || !XMLString::equals(localName,Signature::LOCAL_NAME))
487 throw XMLObjectException("XMLSecSignatureBuilder requires standard Signature element name.");
488 return buildObject();
491 #ifdef HAVE_COVARIANT_RETURNS
496 SignatureBuilder::buildObject() const
498 return new XMLSecSignatureImpl();
501 Signature* SignatureBuilder::buildSignature() {
502 const SignatureBuilder* b = dynamic_cast<const SignatureBuilder*>(
503 XMLObjectBuilder::getBuilder(xmltooling::QName(xmlconstants::XMLSIG_NS,Signature::LOCAL_NAME))
506 #ifdef HAVE_COVARIANT_RETURNS
507 return b->buildObject();
509 return dynamic_cast<Signature*>(b->buildObject());
512 throw XMLObjectException("Unable to obtain typed builder for Signature.");
515 const XMLCh Signature::LOCAL_NAME[] = UNICODE_LITERAL_9(S,i,g,n,a,t,u,r,e);
517 // Raw signature methods.
519 unsigned int Signature::createRawSignature(
520 XSECCryptoKey* key, const XMLCh* sigAlgorithm, const char* in, unsigned int in_len, char* out, unsigned int out_len
524 XSECAlgorithmHandler* handler = XSECPlatformUtils::g_algorithmMapper->mapURIToHandler(sigAlgorithm);
526 auto_ptr_char alg(sigAlgorithm);
527 throw SignatureException("Unsupported signature algorithm ($1).", params(1,alg.get()));
530 // Move input into a safeBuffer to source the transform chain.
532 sb.sbStrncpyIn(in,in_len);
533 TXFMSB* sbt = new TXFMSB(nullptr);
534 sbt->setInput(sb, in_len);
538 unsigned int siglen = handler->signToSafeBuffer(&tx, sigAlgorithm, key, out_len-1, sbout);
539 if (siglen >= out_len)
540 throw SignatureException("Signature size exceeded output buffer size.");
542 // Push all non-whitespace into buffer.
543 unsigned int ret_len = 0;
544 const char* source = sbout.rawCharBuffer();
546 if (isspace(*source))
556 catch(XSECException& e) {
557 auto_ptr_char temp(e.getMsg());
558 throw SignatureException(string("Caught an XMLSecurity exception while creating raw signature: ") + temp.get());
560 catch(XSECCryptoException& e) {
561 throw SignatureException(string("Caught an XMLSecurity exception while creating raw signature: ") + e.getMsg());
565 bool Signature::verifyRawSignature(
566 XSECCryptoKey* key, const XMLCh* sigAlgorithm, const char* signature, const char* in, unsigned int in_len
570 XSECAlgorithmHandler* handler = XSECPlatformUtils::g_algorithmMapper->mapURIToHandler(sigAlgorithm);
572 auto_ptr_char alg(sigAlgorithm);
573 throw SignatureException("Unsupported signature algorithm ($1).", params(1,alg.get()));
576 // Move input into a safeBuffer to source the transform chain.
578 sb.sbStrncpyIn(in,in_len);
579 TXFMSB* sbt = new TXFMSB(nullptr);
580 sbt->setInput(sb, in_len);
584 return handler->verifyBase64Signature(&tx, sigAlgorithm, signature, 0, key);
586 catch(XSECException& e) {
587 auto_ptr_char temp(e.getMsg());
588 throw SignatureException(string("Caught an XMLSecurity exception while verifying raw signature: ") + temp.get());
590 catch(XSECCryptoException& e) {
591 throw SignatureException(string("Caught an XMLSecurity exception while verifying raw signature: ") + e.getMsg());