From 7068408b1c6000ac3d207c782ba4f8c4e731b178 Mon Sep 17 00:00:00 2001 From: Scott Cantor Date: Sun, 12 Mar 2006 00:00:30 +0000 Subject: [PATCH] First cut at signing support. --- .cdtproject | 4 +- xmltooling/AbstractDOMCachingXMLObject.cpp | 27 ++ xmltooling/AbstractDOMCachingXMLObject.h | 33 ++- xmltooling/Makefile.am | 11 +- xmltooling/XMLObject.h | 4 +- xmltooling/XMLObjectBuilder.h | 11 + xmltooling/XMLToolingConfig.cpp | 29 +- xmltooling/exceptions.h | 1 + xmltooling/impl/UnknownElement.cpp | 34 ++- xmltooling/impl/UnknownElement.h | 17 +- xmltooling/internal.h | 7 +- xmltooling/io/AbstractXMLObjectMarshaller.cpp | 37 ++- xmltooling/io/AbstractXMLObjectMarshaller.h | 14 +- xmltooling/io/AbstractXMLObjectUnmarshaller.cpp | 2 +- xmltooling/io/AbstractXMLObjectUnmarshaller.h | 7 +- xmltooling/io/Marshaller.h | 37 ++- xmltooling/signature/KeyInfo.h | 62 +++++ xmltooling/signature/Signature.h | 97 +++++++ xmltooling/signature/SigningContext.h | 79 ++++++ xmltooling/signature/impl/XMLSecSignature.cpp | 356 ++++++++++++++++++++++++ xmltooling/signature/impl/XMLSecSignature.h | 132 +++++++++ xmltooling/util/ParserPool.cpp | 2 +- xmltooling/util/XMLHelper.h | 10 +- xmltooling/util/XMLObjectChildrenList.h | 4 + xmltooling/xmltooling.vcproj | 36 +++ xmltoolingtest/ComplexXMLObjectTest.h | 6 +- xmltoolingtest/Makefile.am | 1 + xmltoolingtest/SignatureTest.h | 133 +++++++++ xmltoolingtest/XMLObjectBaseTestCase.h | 46 ++- xmltoolingtest/data/cert.pem | 16 ++ xmltoolingtest/data/key.pem | 15 + xmltoolingtest/xmltoolingtest.vcproj | 38 ++- 32 files changed, 1211 insertions(+), 97 deletions(-) create mode 100644 xmltooling/signature/KeyInfo.h create mode 100644 xmltooling/signature/Signature.h create mode 100644 xmltooling/signature/SigningContext.h create mode 100644 xmltooling/signature/impl/XMLSecSignature.cpp create mode 100644 xmltooling/signature/impl/XMLSecSignature.h create mode 100644 xmltoolingtest/SignatureTest.h create mode 100644 xmltoolingtest/data/cert.pem create mode 100644 xmltoolingtest/data/key.pem diff --git a/.cdtproject b/.cdtproject index 3db5ea8..3d9715b 100644 --- a/.cdtproject +++ b/.cdtproject @@ -60,12 +60,14 @@ - + + + diff --git a/xmltooling/AbstractDOMCachingXMLObject.cpp b/xmltooling/AbstractDOMCachingXMLObject.cpp index 655f22d..7ffc58c 100644 --- a/xmltooling/AbstractDOMCachingXMLObject.cpp +++ b/xmltooling/AbstractDOMCachingXMLObject.cpp @@ -123,6 +123,8 @@ XMLObject* AbstractDOMCachingXMLObject::clone() const Category::getInstance(XMLTOOLING_LOGCAT".DOM").error( "DOM clone failed, unable to locate unmarshaller for element (%s)", q->toString().c_str() ); + domCopy->getOwnerDocument()->release(); + throw UnmarshallingException("Unable to locate unmarshaller for cloned element."); } try { return u->unmarshall(domCopy, true); // bind document @@ -133,3 +135,28 @@ XMLObject* AbstractDOMCachingXMLObject::clone() const } return NULL; } + +XMLObject* AbstractDOMCachingXMLObject::prepareForAssignment(XMLObject* oldValue, XMLObject* newValue) { + + if (newValue && newValue->hasParent()) + throw XMLObjectException("child XMLObject cannot be added - it is already the child of another XMLObject"); + + if (!oldValue) { + if (newValue) { + releaseThisandParentDOM(); + newValue->setParent(this); + return newValue; + } + else { + return NULL; + } + } + + if (oldValue != newValue) { + delete oldValue; + releaseThisandParentDOM(); + newValue->setParent(this); + } + + return newValue; +} diff --git a/xmltooling/AbstractDOMCachingXMLObject.h b/xmltooling/AbstractDOMCachingXMLObject.h index ce8d2c4..501f039 100644 --- a/xmltooling/AbstractDOMCachingXMLObject.h +++ b/xmltooling/AbstractDOMCachingXMLObject.h @@ -124,24 +124,41 @@ namespace xmltooling { /** * A helper function for derived classes. - * This 'normalizes' newString and then if it is different from oldString - * invalidates the DOM. It returns the normalized value. + * This 'normalizes' newString, and then if it is different from oldString, + * it invalidates the DOM, frees the old string, and return the new. + * If not different, it frees the new string and just returns the old value. * * @param oldValue - the current value * @param newValue - the new value * * @return the value that should be assigned */ - XMLCh* prepareForAssignment(const XMLCh* oldValue, const XMLCh* newValue) { + XMLCh* prepareForAssignment(XMLCh* oldValue, const XMLCh* newValue) { XMLCh* newString = XMLString::replicate(newValue); XMLString::trim(newString); - - if (!XMLString::equals(oldValue,newValue)) + if (!XMLString::equals(oldValue,newValue)) { releaseThisandParentDOM(); - - return newString; + XMLString::release(&oldValue); + return newString; + } + XMLString::release(&newString); + return oldValue; } - + + /** + * A helper function for derived classes, for assignment of (singleton) XML objects. + * + * It is indifferent to whether either the old or the new version of the value is null. + * This method will do a safe compare of the objects and will also invalidate the DOM if appropriate + * + * @param oldValue - current value + * @param newValue - proposed new value + * @return the value to assign + * + * @throws XMLObjectException if the new child already has a parent. + */ + XMLObject* prepareForAssignment(XMLObject* oldValue, XMLObject* newValue); + private: DOMElement* m_dom; DOMDocument* m_document; diff --git a/xmltooling/Makefile.am b/xmltooling/Makefile.am index 4749b15..2796597 100644 --- a/xmltooling/Makefile.am +++ b/xmltooling/Makefile.am @@ -6,6 +6,8 @@ libxmltoolingincludedir = $(includedir)/xmltooling ioincludedir = $(includedir)/xmltooling/io +sigincludedir = $(includedir)/xmltooling/signature + utilincludedir = $(includedir)/xmltooling/util valincludedir = $(includedir)/xmltooling/validation @@ -35,6 +37,11 @@ ioinclude_HEADERS = \ io/Marshaller.h \ io/Unmarshaller.h +siginclude_HEADERS = \ + signature/KeyInfo.h \ + signature/Signature.h \ + signature/SigningContext.h + utilinclude_HEADERS = \ util/NDC.h \ util/ParserPool.h \ @@ -47,7 +54,8 @@ valinclude_HEADERS = \ noinst_HEADERS = \ internal.h \ - impl/UnknownElement.h + impl/UnknownElement.h \ + signature/impl/XMLSecSignature.h libxmltooling_la_SOURCES = \ AbstractAttributeExtensibleXMLObject.cpp \ @@ -63,6 +71,7 @@ libxmltooling_la_SOURCES = \ io/AbstractXMLObjectUnmarshaller.cpp \ io/Marshaller.cpp \ io/Unmarshaller.cpp \ + signature/impl/XMLSecSignature.cpp \ util/NDC.cpp \ util/ParserPool.cpp \ util/XMLConstants.cpp \ diff --git a/xmltooling/XMLObject.h b/xmltooling/XMLObject.h index e8fb52a..1ff2ce7 100644 --- a/xmltooling/XMLObject.h +++ b/xmltooling/XMLObject.h @@ -37,7 +37,6 @@ namespace xmltooling { { MAKE_NONCOPYABLE(XMLObject); public: - XMLObject() {} virtual ~XMLObject() {} /** @@ -143,6 +142,9 @@ namespace xmltooling { * @return the list of children */ virtual const std::list& getOrderedChildren() const=0; + + protected: + XMLObject() {} }; }; diff --git a/xmltooling/XMLObjectBuilder.h b/xmltooling/XMLObjectBuilder.h index a4c4311..de34c89 100644 --- a/xmltooling/XMLObjectBuilder.h +++ b/xmltooling/XMLObjectBuilder.h @@ -55,6 +55,17 @@ namespace xmltooling { virtual XMLObject* buildObject() const=0; /** + * Creates an empty XMLObject using the default build method, if a builder can be found. + * + * @param key the key used to locate a builder + * @return the empty object or NULL if no builder is available + */ + static XMLObject* buildObject(const QName& key) { + const XMLObjectBuilder* b=getBuilder(key); + return b ? b->buildObject() : NULL; + } + + /** * Retrieves an XMLObjectBuilder using the key it was registered with. * * @param key the key used to register the builder diff --git a/xmltooling/XMLToolingConfig.cpp b/xmltooling/XMLToolingConfig.cpp index 3f15fac..8170abf 100644 --- a/xmltooling/XMLToolingConfig.cpp +++ b/xmltooling/XMLToolingConfig.cpp @@ -23,6 +23,7 @@ #include "internal.h" #include "XMLToolingConfig.h" #include "impl/UnknownElement.h" +#include "signature/impl/XMLSecSignature.h" #include "util/NDC.h" #ifdef HAVE_DLFCN_H @@ -33,7 +34,9 @@ #include #include #include -#include +#ifndef XMLTOOLING_NO_XMLSEC + #include +#endif #include @@ -126,10 +129,12 @@ bool XMLToolingInternalConfig::init() xercesc::XMLPlatformUtils::Initialize(); log.debug("Xerces initialization complete"); +#ifndef XMLTOOLING_NO_XMLSEC XSECPlatformUtils::Initialise(); - //m_xsec=new XSECProvider(); + m_xsecProvider=new XSECProvider(); log.debug("XMLSec initialization complete"); - +#endif + m_parserPool=new ParserPool(); m_lock=xercesc::XMLPlatformUtils::makeMutex(); @@ -137,6 +142,11 @@ bool XMLToolingInternalConfig::init() XMLObjectBuilder::registerDefaultBuilder(new UnknownElementBuilder()); Marshaller::registerDefaultMarshaller(new UnknownElementMarshaller()); Unmarshaller::registerDefaultUnmarshaller(new UnknownElementUnmarshaller()); + + QName dsig(XMLConstants::XMLSIG_NS,Signature::LOCAL_NAME); + XMLObjectBuilder::registerBuilder(dsig,new XMLSecSignatureBuilder()); + Marshaller::registerMarshaller(dsig,new XMLSecSignatureMarshaller()); + Unmarshaller::registerUnmarshaller(dsig,new XMLSecSignatureUnmarshaller()); } catch (const xercesc::XMLException&) { log.fatal("caught exception while initializing Xerces"); @@ -149,10 +159,9 @@ bool XMLToolingInternalConfig::init() void XMLToolingInternalConfig::term() { - // default registrations - XMLObjectBuilder::deregisterDefaultBuilder(); - Marshaller::deregisterDefaultMarshaller(); - Unmarshaller::deregisterDefaultUnmarshaller(); + XMLObjectBuilder::destroyBuilders(); + Marshaller::destroyMarshallers(); + Unmarshaller::destroyUnmarshallers(); for (vector::reverse_iterator i=m_libhandles.rbegin(); i!=m_libhandles.rend(); i++) { #if defined(WIN32) @@ -174,8 +183,12 @@ void XMLToolingInternalConfig::term() delete m_parserPool; m_parserPool=NULL; - //delete m_xsec; m_xsec=NULL; +#ifndef XMLTOOLING_NO_XMLSEC + delete m_xsecProvider; + m_xsecProvider=NULL; XSECPlatformUtils::Terminate(); +#endif + xercesc::XMLPlatformUtils::closeMutex(m_lock); m_lock=NULL; xercesc::XMLPlatformUtils::Terminate(); diff --git a/xmltooling/exceptions.h b/xmltooling/exceptions.h index 84f47be..f527350 100644 --- a/xmltooling/exceptions.h +++ b/xmltooling/exceptions.h @@ -60,6 +60,7 @@ namespace xmltooling { DECL_XMLTOOLING_EXCEPTION(UnknownElementException); DECL_XMLTOOLING_EXCEPTION(UnknownAttributeException); DECL_XMLTOOLING_EXCEPTION(ValidationException); + DECL_XMLTOOLING_EXCEPTION(SignatureException); }; diff --git a/xmltooling/impl/UnknownElement.cpp b/xmltooling/impl/UnknownElement.cpp index 745abe9..3d638ae 100644 --- a/xmltooling/impl/UnknownElement.cpp +++ b/xmltooling/impl/UnknownElement.cpp @@ -70,7 +70,7 @@ void UnknownElementImpl::serialize(string& s) const XMLHelper::serialize(getDOM(),s); } -DOMElement* UnknownElementMarshaller::marshall(XMLObject* xmlObject, DOMDocument* document) const +DOMElement* UnknownElementMarshaller::marshall(XMLObject* xmlObject, DOMDocument* document, MarshallingContext* ctx) const { #ifdef _DEBUG xmltooling::NDC ndc("marshall"); @@ -93,13 +93,19 @@ DOMElement* UnknownElementMarshaller::marshall(XMLObject* xmlObject, DOMDocument return cachedDOM; } - // We have a DOM but it doesn't match the document we were given. This both sucks and blows. - // Without an adoptNode option to maintain the child pointers, we rely on our custom - // implementation class to preserve the XML when we release the existing DOM. - unk->releaseDOM(); + // We have a DOM but it doesn't match the document we were given, so we import + // it into the new document. + cachedDOM=static_cast(document->importNode(cachedDOM, true)); + + // Recache the DOM. + setDocumentElement(document, cachedDOM); + log.debug("caching imported DOM for XMLObject"); + unk->setDOM(cachedDOM, false); + unk->releaseParentDOM(true); + return cachedDOM; } - // If we get here, we didn't have a usable DOM (and/or we flushed the one we had). + // If we get here, we didn't have a usable DOM. // We need to reparse the XML we saved off into a new DOM. bool bindDocument=false; MemBufInputSource src(reinterpret_cast(unk->m_xml.c_str()),unk->m_xml.length(),"UnknownElementImpl"); @@ -129,7 +135,7 @@ DOMElement* UnknownElementMarshaller::marshall(XMLObject* xmlObject, DOMDocument return cachedDOM; } -DOMElement* UnknownElementMarshaller::marshall(XMLObject* xmlObject, DOMElement* parentElement) const +DOMElement* UnknownElementMarshaller::marshall(XMLObject* xmlObject, DOMElement* parentElement, MarshallingContext* ctx) const { #ifdef _DEBUG xmltooling::NDC ndc("marshall"); @@ -151,10 +157,16 @@ DOMElement* UnknownElementMarshaller::marshall(XMLObject* xmlObject, DOMElement* return cachedDOM; } - // We have a DOM but it doesn't match the document we were given. This both sucks and blows. - // Without an adoptNode option to maintain the child pointers, we rely on our custom - // implementation class to preserve the XML when we release the existing DOM. - unk->releaseDOM(); + // We have a DOM but it doesn't match the document we were given, so we import + // it into the new document. + cachedDOM=static_cast(parentElement->getOwnerDocument()->importNode(cachedDOM, true)); + + // Recache the DOM. + parentElement->appendChild(cachedDOM); + log.debug("caching imported DOM for XMLObject"); + unk->setDOM(cachedDOM, false); + unk->releaseParentDOM(true); + return cachedDOM; } // If we get here, we didn't have a usable DOM (and/or we flushed the one we had). diff --git a/xmltooling/impl/UnknownElement.h b/xmltooling/impl/UnknownElement.h index aff3ae5..1afb6e1 100644 --- a/xmltooling/impl/UnknownElement.h +++ b/xmltooling/impl/UnknownElement.h @@ -46,6 +46,9 @@ namespace xmltooling { class XMLTOOL_DLLLOCAL UnknownElementImpl : public AbstractDOMCachingXMLObject { public: + UnknownElementImpl(const XMLCh* namespaceURI=NULL, const XMLCh* elementLocalName=NULL, const XMLCh* namespacePrefix=NULL) + : AbstractDOMCachingXMLObject(namespaceURI, elementLocalName, namespacePrefix) {} + /** * Overridden to ensure XML content of DOM isn't lost. * @@ -64,9 +67,9 @@ namespace xmltooling { */ std::string m_xml; - private: void serialize(std::string& s) const; - friend class XMLTOOL_API UnknownElementMarshaller; + private: + friend class XMLTOOL_DLLLOCAL UnknownElementMarshaller; }; /** @@ -90,14 +93,14 @@ namespace xmltooling { { public: /** - * @see Marshaller::marshall(XMLObject*,DOMDocument*) + * @see Marshaller::marshall(XMLObject*,DOMDocument*, const MarshallingContext*) */ - DOMElement* marshall(XMLObject* xmlObject, DOMDocument* document=NULL) const; + DOMElement* marshall(XMLObject* xmlObject, DOMDocument* document=NULL, MarshallingContext* ctx=NULL) const; /** - * @see Marshaller::marshall(XMLObject*,DOMElement*) + * @see Marshaller::marshall(XMLObject*,DOMElement*, const MarshallingContext* ctx) */ - DOMElement* marshall(XMLObject* xmlObject, DOMElement* parentElement) const; + DOMElement* marshall(XMLObject* xmlObject, DOMElement* parentElement, MarshallingContext* ctx=NULL) const; protected: void setDocumentElement(DOMDocument* document, DOMElement* element) const { @@ -110,7 +113,7 @@ namespace xmltooling { }; /** - * Marshaller for UnknownElementImpl objects + * Unmarshaller for UnknownElementImpl objects */ class XMLTOOL_DLLLOCAL UnknownElementUnmarshaller : public virtual Unmarshaller { diff --git a/xmltooling/internal.h b/xmltooling/internal.h index 2d7c6fa..42548e0 100644 --- a/xmltooling/internal.h +++ b/xmltooling/internal.h @@ -38,6 +38,9 @@ #include "util/ParserPool.h" #include +#ifndef XMLTOOLING_NO_XMLSEC + #include +#endif #define XMLTOOLING_LOGCAT "XMLTooling" @@ -46,7 +49,7 @@ namespace xmltooling { class XMLToolingInternalConfig : public xmltooling::XMLToolingConfig { public: - XMLToolingInternalConfig() : m_lock(NULL), m_parserPool(NULL) {} + XMLToolingInternalConfig() : m_lock(NULL), m_parserPool(NULL), m_xsecProvider(NULL) {} static XMLToolingInternalConfig& getInternalConfig(); @@ -64,11 +67,11 @@ namespace xmltooling { // internal parser pool xmltooling::ParserPool* m_parserPool; + XSECProvider* m_xsecProvider; private: std::vector m_libhandles; void* m_lock; - //XSECProvider* m_xsec; //PlugManager m_plugMgr; }; }; diff --git a/xmltooling/io/AbstractXMLObjectMarshaller.cpp b/xmltooling/io/AbstractXMLObjectMarshaller.cpp index d5c4ac0..8a718a7 100644 --- a/xmltooling/io/AbstractXMLObjectMarshaller.cpp +++ b/xmltooling/io/AbstractXMLObjectMarshaller.cpp @@ -42,7 +42,7 @@ using namespace std; AbstractXMLObjectMarshaller::AbstractXMLObjectMarshaller() : m_log(&Category::getInstance(XMLTOOLING_LOGCAT".Marshaller")) {} -DOMElement* AbstractXMLObjectMarshaller::marshall(XMLObject* xmlObject, DOMDocument* document) const +DOMElement* AbstractXMLObjectMarshaller::marshall(XMLObject* xmlObject, DOMDocument* document, MarshallingContext* ctx) const { #ifdef _DEBUG xmltooling::NDC ndc("marshall"); @@ -88,7 +88,7 @@ DOMElement* AbstractXMLObjectMarshaller::marshall(XMLObject* xmlObject, DOMDocum xmlObject->getElementQName().getNamespaceURI(), xmlObject->getElementQName().getLocalPart() ); setDocumentElement(document, domElement); - marshallInto(*xmlObject, domElement); + marshallInto(*xmlObject, domElement, ctx); //Recache the DOM. if (dc) { @@ -108,7 +108,7 @@ DOMElement* AbstractXMLObjectMarshaller::marshall(XMLObject* xmlObject, DOMDocum } } -DOMElement* AbstractXMLObjectMarshaller::marshall(XMLObject* xmlObject, DOMElement* parentElement) const +DOMElement* AbstractXMLObjectMarshaller::marshall(XMLObject* xmlObject, DOMElement* parentElement, MarshallingContext* ctx) const { #ifdef _DEBUG xmltooling::NDC ndc("marshall"); @@ -147,7 +147,7 @@ DOMElement* AbstractXMLObjectMarshaller::marshall(XMLObject* xmlObject, DOMEleme xmlObject->getElementQName().getNamespaceURI(), xmlObject->getElementQName().getLocalPart() ); parentElement->appendChild(domElement); - marshallInto(*xmlObject, domElement); + marshallInto(*xmlObject, domElement, ctx); //Recache the DOM. if (dc) { @@ -158,8 +158,19 @@ DOMElement* AbstractXMLObjectMarshaller::marshall(XMLObject* xmlObject, DOMEleme return domElement; } - -void AbstractXMLObjectMarshaller::marshallInto(XMLObject& xmlObject, DOMElement* targetElement) const + +#ifndef XMLTOOLING_NO_XMLSEC + class _signit : public unary_function&, void> { + public: + void operator()(const pair& p) const { + p.first->sign(p.second); + } + }; +#endif + +void AbstractXMLObjectMarshaller::marshallInto( + XMLObject& xmlObject, DOMElement* targetElement, MarshallingContext* ctx + ) const { if (xmlObject.getElementQName().hasPrefix()) targetElement->setPrefix(xmlObject.getElementQName().getPrefix()); @@ -169,15 +180,11 @@ void AbstractXMLObjectMarshaller::marshallInto(XMLObject& xmlObject, DOMElement* marshallChildElements(xmlObject, targetElement); marshallElementContent(xmlObject, targetElement); - /* TODO Signing/Encryption - if (xmlObject instanceof SignableXMLObject) { - signElement(targetElement, xmlObject); - } - - if (xmlObject instanceof EncryptableXMLObject) { - encryptElement(targetElement, xmlObject); +#ifndef XMLTOOLING_NO_XMLSEC + if (ctx) { + for_each(ctx->m_signingContexts.begin(),ctx->m_signingContexts.end(),_signit()); } - */ +#endif } void AbstractXMLObjectMarshaller::marshallElementType(XMLObject& xmlObject, DOMElement* domElement) const @@ -298,7 +305,7 @@ public: ); throw MarshallingException("Marshaller found unknown child element, but no default marshaller was found."); } - element->appendChild(marshaller->marshall(obj, element)); + marshaller->marshall(obj, element); } }; diff --git a/xmltooling/io/AbstractXMLObjectMarshaller.h b/xmltooling/io/AbstractXMLObjectMarshaller.h index beed4b3..7d1928d 100644 --- a/xmltooling/io/AbstractXMLObjectMarshaller.h +++ b/xmltooling/io/AbstractXMLObjectMarshaller.h @@ -36,15 +36,14 @@ namespace xmltooling { virtual ~AbstractXMLObjectMarshaller() {} /** - * @see Marshaller::marshall(XMLObject*,DOMDocument*) + * @see Marshaller::marshall(XMLObject*,DOMDocument*,const MarshallingContext*) */ - DOMElement* marshall(XMLObject* xmlObject, DOMDocument* document=NULL) const; + DOMElement* marshall(XMLObject* xmlObject, DOMDocument* document=NULL, MarshallingContext* ctx=NULL) const; /** - * @see Marshaller::marshall(XMLObject*,DOMElement*) + * @see Marshaller::marshall(XMLObject*,DOMElement*,const MarshallingContext*) */ - DOMElement* marshall(XMLObject* xmlObject, DOMElement* parentElement) const; - + DOMElement* marshall(XMLObject* xmlObject, DOMElement* parentElement, MarshallingContext* ctx=NULL) const; protected: AbstractXMLObjectMarshaller(); @@ -70,9 +69,12 @@ namespace xmltooling { * * @param xmlObject the XMLObject to marshall * @param targetElement the Element into which the XMLObject is marshalled into + * @param ctx optional marshalling context + * * @throws MarshallingException thrown if there is a problem marshalling the object + * @throws SignatureException thrown if a problem occurs during signature creation */ - void marshallInto(XMLObject& xmlObject, DOMElement* targetElement) const; + void marshallInto(XMLObject& xmlObject, DOMElement* targetElement, MarshallingContext* ctx) const; /** * Creates an xsi:type attribute, corresponding to the given type of the XMLObject, on the DOM element. diff --git a/xmltooling/io/AbstractXMLObjectUnmarshaller.cpp b/xmltooling/io/AbstractXMLObjectUnmarshaller.cpp index eeb05fa..e3ae471 100644 --- a/xmltooling/io/AbstractXMLObjectUnmarshaller.cpp +++ b/xmltooling/io/AbstractXMLObjectUnmarshaller.cpp @@ -182,7 +182,7 @@ void AbstractXMLObjectUnmarshaller::unmarshallChildElements(const DOMElement* do // Retain ownership of the unmarshalled child until it's processed by the parent. auto_ptr childObject(unmarshaller->unmarshall(static_cast(childNode))); - processChildElement(xmlObject, childObject.get()); + processChildElement(xmlObject, childObject.get(), static_cast(childNode)); childObject.release(); } else if (childNode->getNodeType() == DOMNode::TEXT_NODE) { diff --git a/xmltooling/io/AbstractXMLObjectUnmarshaller.h b/xmltooling/io/AbstractXMLObjectUnmarshaller.h index 89c8556..f7d05aa 100644 --- a/xmltooling/io/AbstractXMLObjectUnmarshaller.h +++ b/xmltooling/io/AbstractXMLObjectUnmarshaller.h @@ -86,12 +86,13 @@ namespace xmltooling { /** * Called after a child element has been unmarshalled so that it can be added to the parent XMLObject. * - * @param parent the parent XMLObject - * @param child pointer to the child XMLObject + * @param parent the parent XMLObject + * @param child pointer to the child XMLObject + * @param childRoot root element of the child (must not be stored, just a hint) * * @throws UnmarshallingException thrown if there is a problem adding the child to the parent */ - virtual void processChildElement(XMLObject& parent, XMLObject* child) const=0; + virtual void processChildElement(XMLObject& parent, XMLObject* child, const DOMElement* childRoot) const=0; /** * Called after an attribute has been unmarshalled so that it can be added to the XMLObject. diff --git a/xmltooling/io/Marshaller.h b/xmltooling/io/Marshaller.h index 0beb061..ba334c4 100644 --- a/xmltooling/io/Marshaller.h +++ b/xmltooling/io/Marshaller.h @@ -24,8 +24,12 @@ #define __xmltooling_marshaller_h__ #include +#include #include #include +#ifndef XMLTOOLING_NO_XMLSEC + #include +#endif using namespace xercesc; @@ -37,6 +41,27 @@ using namespace xercesc; namespace xmltooling { /** + * Supplies additional information to the marshalling process. + * Currently this only consists of signature related information. + */ + class XMLTOOL_API MarshallingContext + { + MAKE_NONCOPYABLE(MarshallingContext); + public: + MarshallingContext() {} + ~MarshallingContext() {} + +#ifndef XMLTOOLING_NO_XMLSEC + MarshallingContext(Signature* sig, const SigningContext* ctx) { + m_signingContexts.push_back(std::make_pair(sig,ctx)); + } + + /** Array of signing contexts, keyed off of the associated Signature */ + std::vector< std::pair > m_signingContexts; +#endif + }; + + /** * Marshallers are used to marshall an XMLObject into a W3C DOM element. */ class XMLTOOL_API Marshaller @@ -55,11 +80,14 @@ namespace xmltooling { * marshalled, unless an existing DOM can be reused without creating a new document. * * @param xmlObject the object to marshall - * @param document the DOM document the marshalled element will be placed in, or NULL + * @param document the DOM document the marshalled element will be placed in, or NULL + * @param ctx optional marshalling context * @return the DOM element representing this XMLObject + * * @throws MarshallingException thrown if there is a problem marshalling the given object + * @throws SignatureException thrown if a problem occurs during signature creation */ - virtual DOMElement* marshall(XMLObject* xmlObject, DOMDocument* document=NULL) const=0; + virtual DOMElement* marshall(XMLObject* xmlObject, DOMDocument* document=NULL, MarshallingContext* ctx=NULL) const=0; /** * Marshall the given XMLObject and append it as a child of the given parent element. @@ -69,10 +97,13 @@ namespace xmltooling { * * @param xmlObject the XMLObject to be marshalled * @param parentElement the parent element to append the resulting DOM tree + * @param ctx optional marshalling context * @return the marshalled element tree + * @throws MarshallingException thrown if the given XMLObject can not be marshalled. + * @throws SignatureException thrown if a problem occurs during signature creation */ - virtual DOMElement* marshall(XMLObject* xmlObject, DOMElement* parentElement) const=0; + virtual DOMElement* marshall(XMLObject* xmlObject, DOMElement* parentElement, MarshallingContext* ctx=NULL) const=0; /** * Retrieves a Marshaller using the key it was registered with. diff --git a/xmltooling/signature/KeyInfo.h b/xmltooling/signature/KeyInfo.h new file mode 100644 index 0000000..9648a3c --- /dev/null +++ b/xmltooling/signature/KeyInfo.h @@ -0,0 +1,62 @@ +/* + * Copyright 2001-2006 Internet2 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file KeyInfo.h + * + * XMLObject representing XML Digital Signature, version 20020212, KeyInfo element. + */ + +#if !defined(__xmltooling_keyinfo_h__) && !defined(XMLTOOLING_NO_XMLSEC) +#define __xmltooling_keyinfo_h__ + +#include +#include + +namespace xmltooling { + + /** + * XMLObject representing XML Digital Signature, version 20020212, KeyInfo element. + */ + class XMLTOOL_API KeyInfo : public virtual XMLObject + { + public: + virtual ~KeyInfo() {} + + /** Element local name */ + static const XMLCh LOCAL_NAME[]; + + /** + * Returns immutable ds:KeyInfo information. + * + * @return the ds:KeyInfo information + */ + virtual const DSIGKeyInfoList* getKeyInfo() const=0; + + /** + * Returns mutable ds:KeyInfo information. + * + * @return the ds:KeyInfo information + */ + virtual DSIGKeyInfoList* getKeyInfo()=0; + + protected: + KeyInfo() {} + }; + +}; + +#endif /* __xmltooling_keyinfo_h__ */ diff --git a/xmltooling/signature/Signature.h b/xmltooling/signature/Signature.h new file mode 100644 index 0000000..fd219d6 --- /dev/null +++ b/xmltooling/signature/Signature.h @@ -0,0 +1,97 @@ +/* + * Copyright 2001-2006 Internet2 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file Signature.h + * + * XMLObject representing XML Digital Signature, version 20020212, Signature element. + */ + +#if !defined(__xmltooling_sig_h__) && !defined(XMLTOOLING_NO_XMLSEC) +#define __xmltooling_sig_h__ + +#include +#include + +namespace xmltooling { + + /** + * XMLObject representing XML Digital Signature, version 20020212, Signature element. + * The default signature settings include Exclusive c14n w/o comments, SHA-1 digests, + * and RSA-SHA1 signing. + */ + class XMLTOOL_API Signature : public virtual XMLObject + { + public: + virtual ~Signature() {} + + /** Element prefix */ + static const XMLCh PREFIX[]; + + /** Element local name */ + static const XMLCh LOCAL_NAME[]; + + /** + * Gets the canonicalization method for the ds:SignedInfo element. + * + * @return the canonicalization method + */ + virtual const XMLCh* getCanonicalizationMethod() const=0; + + /** + * Gets the signing algorithm for the signature. + * + * @return the signature algorithm + */ + virtual const XMLCh* getSignatureAlgorithm() const=0; + + /** + * Returns the ds:KeyInfo information attached to the signature. + * The Signature object must be marshalled before this will return anything. + * + * @return the ds:KeyInfo information + */ + virtual const DSIGKeyInfoList* getKeyInfo() const=0; + + /** + * Sets the canonicalization method for the ds:SignedInfo element + * + * @param c14n the canonicalization method + */ + virtual void setCanonicalizationMethod(const XMLCh* c14n)=0; + + /** + * Sets the signing algorithm for the signature. + * + * @param sm the signature algorithm + */ + virtual void setSignatureAlgorithm(const XMLCh* sm)=0; + + /** + * Applies an XML signature based on the supplied context. + * + * @param ctx the signing context that determines the signature's content + * @throws SignatureException thrown if the signing operation fails + */ + virtual void sign(const SigningContext* ctx)=0; + + protected: + Signature() {} + }; + +}; + +#endif /* __xmltooling_sig_h__ */ diff --git a/xmltooling/signature/SigningContext.h b/xmltooling/signature/SigningContext.h new file mode 100644 index 0000000..72648d6 --- /dev/null +++ b/xmltooling/signature/SigningContext.h @@ -0,0 +1,79 @@ +/* + * Copyright 2001-2006 Internet2 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file SigningContext.h + * + * Interface to signing process supplied by a signing application + */ + +#if !defined(__xmltooling_signctx_h__) && !defined(XMLTOOLING_NO_XMLSEC) +#define __xmltooling_signctx_h__ + +#include +#include + +#if defined (_MSC_VER) + #pragma warning( push ) + #pragma warning( disable : 4250 4251 ) +#endif + +namespace xmltooling { + + /** + * Interface to signing process supplied by a signing application + */ + class XMLTOOL_API SigningContext + { + MAKE_NONCOPYABLE(SigningContext); + public: + virtual ~SigningContext() {} + + /** + * Given a "blank" native signature, asks the context to define the + * appropriate signature transforms, references, etc. + * This method MAY attach ds:KeyInfo information, or a set of X.509 + * certificates can be returned from the SigningContext::getX509Certificates() + * method instead. + */ + virtual void createSignature(DSIGSignature* sig) const=0; + + /** + * Gets a reference to a collection of certificates to append to + * the ds:KeyInfo element in a ds:X509Data chain. + * The certificate corresponding to the signing key SHOULD be + * first, followed by any additional intermediates to append. + */ + virtual const std::vector& getX509Certificates() const=0; + + /** + * Gets the signing key to use. + * Must be compatible with the intended signature algorithm. Ownership of the key + * MUST be transferred to the caller. + */ + virtual XSECCryptoKey* getSigningKey() const=0; + + protected: + SigningContext() {} + }; + +}; + +#if defined (_MSC_VER) + #pragma warning( pop ) +#endif + +#endif /* __xmltooling_signctx_h__ */ diff --git a/xmltooling/signature/impl/XMLSecSignature.cpp b/xmltooling/signature/impl/XMLSecSignature.cpp new file mode 100644 index 0000000..1462ba6 --- /dev/null +++ b/xmltooling/signature/impl/XMLSecSignature.cpp @@ -0,0 +1,356 @@ +/* +* Copyright 2001-2006 Internet2 + * +* Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * XMLSecSignature.cpp + * + * Signature classes for XMLSec-based signature-handling + */ + +#include "internal.h" +#include "exceptions.h" +#include "signature/impl/XMLSecSignature.h" +#include "util/NDC.h" +#include "util/XMLHelper.h" + +#include +#include +#include +#include +#include +#include +#include + +using namespace xmltooling; +using namespace log4cpp; +using namespace std; + +const XMLCh xmltooling::Signature::LOCAL_NAME[] = { + chLatin_S, chLatin_i, chLatin_g, chLatin_n, chLatin_a, chLatin_t, chLatin_u, chLatin_r, chLatin_e, chNull +}; + +const XMLCh xmltooling::Signature::PREFIX[] = { + chLatin_d, chLatin_s, chNull +}; + +XMLSecSignatureImpl::~XMLSecSignatureImpl() +{ + // Release the associated signature. + if (m_signature) + XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->releaseSignature(m_signature); + + XMLString::release(&m_c14n); + XMLString::release(&m_sm); +} + +void XMLSecSignatureImpl::releaseDOM() +{ + // This should save off the DOM + UnknownElementImpl::releaseDOM(); + + // Release the associated signature. + if (m_signature) { + XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->releaseSignature(m_signature); + m_signature=NULL; + } +} + +XMLObject* XMLSecSignatureImpl::clone() const +{ + XMLSecSignatureImpl* ret=new XMLSecSignatureImpl(); + + ret->m_c14n=XMLString::replicate(m_c14n); + ret->m_sm=XMLString::replicate(m_sm); + + // If there's no XML locally, serialize this object into the new one, otherwise just copy it over. + if (m_xml.empty()) + serialize(ret->m_xml); + else + ret->m_xml=m_xml; + + return ret; +} + +const DSIGKeyInfoList* XMLSecSignatureImpl::getKeyInfo() const +{ + return m_signature ? m_signature->getKeyInfoList() : NULL; +} + +class _addcert : public std::binary_function { +public: + void operator()(DSIGKeyInfoX509* bag, XSECCryptoX509* cert) const { + safeBuffer& buf=cert->getDEREncodingSB(); + bag->appendX509Certificate(buf.sbStrToXMLCh()); + } +}; + +void XMLSecSignatureImpl::sign(const SigningContext* ctx) +{ + Category& log=Category::getInstance(XMLTOOLING_LOGCAT".Signature"); + log.debug("applying signature"); + + if (!m_signature) + throw SignatureException("Only a marshalled Signature object can be signed."); + + try { + log.debug("creating signature content"); + ctx->createSignature(m_signature); + const std::vector& certs=ctx->getX509Certificates(); + if (!certs.empty()) { + DSIGKeyInfoX509* x509Data=m_signature->appendX509Data(); + for_each(certs.begin(),certs.end(),bind1st(_addcert(),x509Data)); + } + + log.debug("computing signature"); + m_signature->setSigningKey(ctx->getSigningKey()); + m_signature->sign(); + } + catch(XSECException& e) { + auto_ptr_char temp(e.getMsg()); + throw SignatureException(string("Caught an XMLSecurity exception while signing: ") + temp.get()); + } + catch(XSECCryptoException& e) { + throw SignatureException(string("Caught an XMLSecurity exception while signing: ") + e.getMsg()); + } +} + +DOMElement* XMLSecSignatureMarshaller::marshall(XMLObject* xmlObject, DOMDocument* document, MarshallingContext* ctx) const +{ +#ifdef _DEBUG + xmltooling::NDC ndc("marshall"); +#endif + + Category& log=Category::getInstance(XMLTOOLING_LOGCAT".Marshaller"); + log.debug("marshalling ds:Signature"); + + XMLSecSignatureImpl* sig=dynamic_cast(xmlObject); + if (!sig) + throw MarshallingException("Only objects of class XMLSecSignatureImpl can be marshalled."); + + DOMElement* cachedDOM=sig->getDOM(); + if (cachedDOM) { + if (!document || document==cachedDOM->getOwnerDocument()) { + log.debug("Signature has a usable cached DOM, reusing it"); + if (document) + setDocumentElement(cachedDOM->getOwnerDocument(),cachedDOM); + sig->releaseParentDOM(true); + return cachedDOM; + } + + // We have a DOM but it doesn't match the document we were given, so we import + // it into the new document. + cachedDOM=static_cast(document->importNode(cachedDOM, true)); + + try { + XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->releaseSignature(sig->m_signature); + sig->m_signature=NULL; + sig->m_signature=XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->newSignatureFromDOM( + document, cachedDOM + ); + } + catch(XSECException& e) { + auto_ptr_char temp(e.getMsg()); + throw MarshallingException(string("Caught an XMLSecurity exception while loading signature: ") + temp.get()); + } + catch(XSECCryptoException& e) { + throw MarshallingException(string("Caught an XMLSecurity exception while loading signature: ") + e.getMsg()); + } + + // Recache the DOM. + setDocumentElement(document, cachedDOM); + log.debug("caching imported DOM for Signature"); + sig->setDOM(cachedDOM, false); + sig->releaseParentDOM(true); + return cachedDOM; + } + + // If we get here, we didn't have a usable DOM. + bool bindDocument=false; + if (sig->m_xml.empty()) { + // Fresh signature, so we just create an empty one. + log.debug("creating empty Signature element"); + if (!document) { + document=DOMImplementationRegistry::getDOMImplementation(NULL)->createDocument(); + bindDocument=true; + } + sig->m_signature=XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->newSignature(); + sig->m_signature->setDSIGNSPrefix(Signature::PREFIX); + cachedDOM=sig->m_signature->createBlankSignature( + document, sig->getCanonicalizationMethod(), sig->getSignatureAlgorithm() + ); + } + else { + // We need to reparse the XML we saved off into a new DOM. + MemBufInputSource src(reinterpret_cast(sig->m_xml.c_str()),sig->m_xml.length(),"XMLSecSignatureImpl"); + Wrapper4InputSource dsrc(&src,false); + log.debug("parsing Signature XML back into DOM tree"); + DOMDocument* internalDoc=XMLToolingInternalConfig::getInternalConfig().m_parserPool->parse(dsrc); + if (document) { + // The caller insists on using his own document, so we now have to import the thing + // into it. Then we're just dumping the one we built. + log.debug("reimporting new DOM into caller-supplied document"); + cachedDOM=static_cast(document->importNode(internalDoc->getDocumentElement(), true)); + internalDoc->release(); + } + else { + // We just bind the document we built to the object as the result. + cachedDOM=static_cast(internalDoc->getDocumentElement()); + document=internalDoc; + bindDocument=true; + } + + // Now reload the signature from the DOM. + try { + sig->m_signature=XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->newSignatureFromDOM( + document, cachedDOM + ); + } + catch(XSECException& e) { + if (bindDocument) + document->release(); + auto_ptr_char temp(e.getMsg()); + throw MarshallingException(string("Caught an XMLSecurity exception while loading signature: ") + temp.get()); + } + catch(XSECCryptoException& e) { + if (bindDocument) + document->release(); + throw MarshallingException(string("Caught an XMLSecurity exception while loading signature: ") + e.getMsg()); + } + } + + // Recache the DOM and clear the serialized copy. + setDocumentElement(document, cachedDOM); + log.debug("caching DOM for Signature (document is %sbound)", bindDocument ? "" : "not "); + sig->setDOM(cachedDOM, bindDocument); + sig->releaseParentDOM(true); + sig->m_xml.erase(); + return cachedDOM; +} + +DOMElement* XMLSecSignatureMarshaller::marshall(XMLObject* xmlObject, DOMElement* parentElement, MarshallingContext* ctx) const +{ +#ifdef _DEBUG + xmltooling::NDC ndc("marshall"); +#endif + + Category& log=Category::getInstance(XMLTOOLING_LOGCAT".Marshaller"); + log.debug("marshalling ds:Signature"); + + XMLSecSignatureImpl* sig=dynamic_cast(xmlObject); + if (!sig) + throw MarshallingException("Only objects of class XMLSecSignatureImpl can be marshalled."); + + DOMElement* cachedDOM=sig->getDOM(); + if (cachedDOM) { + if (parentElement->getOwnerDocument()==cachedDOM->getOwnerDocument()) { + log.debug("Signature has a usable cached DOM, reusing it"); + parentElement->appendChild(cachedDOM); + sig->releaseParentDOM(true); + return cachedDOM; + } + + // We have a DOM but it doesn't match the document we were given, so we import + // it into the new document. + cachedDOM=static_cast(parentElement->getOwnerDocument()->importNode(cachedDOM, true)); + + try { + XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->releaseSignature(sig->m_signature); + sig->m_signature=NULL; + sig->m_signature=XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->newSignatureFromDOM( + parentElement->getOwnerDocument(), cachedDOM + ); + } + catch(XSECException& e) { + auto_ptr_char temp(e.getMsg()); + throw MarshallingException(string("Caught an XMLSecurity exception while loading signature: ") + temp.get()); + } + catch(XSECCryptoException& e) { + throw MarshallingException(string("Caught an XMLSecurity exception while loading signature: ") + e.getMsg()); + } + + // Recache the DOM. + parentElement->appendChild(cachedDOM); + log.debug("caching imported DOM for Signature"); + sig->setDOM(cachedDOM, false); + sig->releaseParentDOM(true); + return cachedDOM; + } + + // If we get here, we didn't have a usable DOM. + if (sig->m_xml.empty()) { + // Fresh signature, so we just create an empty one. + log.debug("creating empty Signature element"); + sig->m_signature=XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->newSignature(); + sig->m_signature->setDSIGNSPrefix(Signature::PREFIX); + cachedDOM=sig->m_signature->createBlankSignature( + parentElement->getOwnerDocument(), sig->getCanonicalizationMethod(), sig->getSignatureAlgorithm() + ); + } + else { + MemBufInputSource src(reinterpret_cast(sig->m_xml.c_str()),sig->m_xml.length(),"XMLSecSignatureImpl"); + Wrapper4InputSource dsrc(&src,false); + log.debug("parsing XML back into DOM tree"); + DOMDocument* internalDoc=XMLToolingInternalConfig::getInternalConfig().m_parserPool->parse(dsrc); + + log.debug("reimporting new DOM into caller-supplied document"); + cachedDOM=static_cast(parentElement->getOwnerDocument()->importNode(internalDoc->getDocumentElement(), true)); + internalDoc->release(); + + // Now reload the signature from the DOM. + try { + sig->m_signature=XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->newSignatureFromDOM( + parentElement->getOwnerDocument(), cachedDOM + ); + } + catch(XSECException& e) { + auto_ptr_char temp(e.getMsg()); + throw MarshallingException(string("Caught an XMLSecurity exception while loading signature: ") + temp.get()); + } + catch(XSECCryptoException& e) { + throw MarshallingException(string("Caught an XMLSecurity exception while loading signature: ") + e.getMsg()); + } + } + + // Recache the DOM and clear the serialized copy. + parentElement->appendChild(cachedDOM); + log.debug("caching DOM for Signature"); + sig->setDOM(cachedDOM, false); + sig->releaseParentDOM(true); + sig->m_xml.erase(); + return cachedDOM; +} + +XMLObject* XMLSecSignatureUnmarshaller::unmarshall(DOMElement* element, bool bindDocument) const +{ + Category::getInstance(XMLTOOLING_LOGCAT".Unmarshaller").debug("unmarshalling ds:Signature"); + + auto_ptr ret(new XMLSecSignatureImpl()); + try { + ret->m_signature=XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->newSignatureFromDOM( + element->getOwnerDocument(), element + ); + } + catch(XSECException& e) { + auto_ptr_char temp(e.getMsg()); + throw UnmarshallingException(string("Caught an XMLSecurity exception while loading signature: ") + temp.get()); + } + catch(XSECCryptoException& e) { + throw UnmarshallingException(string("Caught an XMLSecurity exception while loading signature: ") + e.getMsg()); + } + + ret->setDOM(element, bindDocument); + return ret.release(); +} diff --git a/xmltooling/signature/impl/XMLSecSignature.h b/xmltooling/signature/impl/XMLSecSignature.h new file mode 100644 index 0000000..1bc0c1f --- /dev/null +++ b/xmltooling/signature/impl/XMLSecSignature.h @@ -0,0 +1,132 @@ +/* +* Copyright 2001-2006 Internet2 + * +* Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file XMLSecSignature.h + * + * Signature classes for XMLSec-based signature-handling + */ + +#if !defined(__xmltooling_xmlsecsig_h__) && !defined(XMLTOOLING_NO_XMLSEC) +#define __xmltooling_xmlsecsig_h__ + +#include "internal.h" +#include "impl/UnknownElement.h" +#include "signature/Signature.h" +#include "util/XMLConstants.h" + +#include + +#if defined (_MSC_VER) + #pragma warning( push ) + #pragma warning( disable : 4250 4251 ) +#endif + +namespace xmltooling { + + /** + * XMLObject representing XML Digital Signature, version 20020212, Signature element. + * Manages an Apache XML Signature object and the associated DOM. + */ + class XMLTOOL_DLLLOCAL XMLSecSignatureImpl : public UnknownElementImpl, public virtual Signature + { + public: + XMLSecSignatureImpl() : UnknownElementImpl(XMLConstants::XMLSIG_NS, Signature::LOCAL_NAME), + m_signature(NULL), m_c14n(NULL), m_sm(NULL) {} + virtual ~XMLSecSignatureImpl(); + + void releaseDOM(); + XMLObject* clone() const; + + // Getters + const XMLCh* getCanonicalizationMethod() const { return m_c14n ? m_c14n : DSIGConstants::s_unicodeStrURIEXC_C14N_NOC; } + const XMLCh* getSignatureAlgorithm() const { return m_sm ? m_sm : DSIGConstants::s_unicodeStrURIRSA_SHA1; } + const DSIGKeyInfoList* getKeyInfo() const; + + // Setters + void setCanonicalizationMethod(const XMLCh* c14n) { m_c14n = prepareForAssignment(m_c14n,c14n); } + void setSignatureAlgorithm(const XMLCh* sm) { m_sm = prepareForAssignment(m_sm,sm); } + + void sign(const SigningContext* ctx); + + private: + DSIGSignature* m_signature; + XMLCh* m_c14n; + XMLCh* m_sm; + + friend class XMLTOOL_DLLLOCAL XMLSecSignatureMarshaller; + friend class XMLTOOL_DLLLOCAL XMLSecSignatureUnmarshaller; + }; + + /** + * Factory for XMLSecSignatureImpl objects + */ + class XMLTOOL_DLLLOCAL XMLSecSignatureBuilder : public virtual XMLObjectBuilder + { + public: + /** + * @see XMLObjectBuilder::buildObject() + */ + XMLObject* buildObject() const { + return new XMLSecSignatureImpl(); + } + }; + + /** + * Marshaller for XMLSecSignatureImpl objects + */ + class XMLTOOL_DLLLOCAL XMLSecSignatureMarshaller : public virtual Marshaller + { + public: + /** + * @see Marshaller::marshall(XMLObject*,DOMDocument*, const MarshallingContext*) + */ + DOMElement* marshall(XMLObject* xmlObject, DOMDocument* document=NULL, MarshallingContext* ctx=NULL) const; + + /** + * @see Marshaller::marshall(XMLObject*,DOMElement*, const MarshallingContext* ctx) + */ + DOMElement* marshall(XMLObject* xmlObject, DOMElement* parentElement, MarshallingContext* ctx=NULL) const; + + protected: + void setDocumentElement(DOMDocument* document, DOMElement* element) const { + DOMElement* documentRoot = document->getDocumentElement(); + if (documentRoot) + document->replaceChild(documentRoot, element); + else + document->appendChild(element); + } + }; + + /** + * Unmarshaller for XMLSecSignatureImpl objects + */ + class XMLTOOL_DLLLOCAL XMLSecSignatureUnmarshaller : public virtual Unmarshaller + { + public: + /** + * @see Unmarshaller::unmarshall() + */ + XMLObject* unmarshall(DOMElement* element, bool bindDocument=false) const; + }; + +}; + +#if defined (_MSC_VER) + #pragma warning( pop ) +#endif + +#endif /* __xmltooling_xmlsecsig_h__ */ diff --git a/xmltooling/util/ParserPool.cpp b/xmltooling/util/ParserPool.cpp index 7401037..3eb039d 100644 --- a/xmltooling/util/ParserPool.cpp +++ b/xmltooling/util/ParserPool.cpp @@ -166,7 +166,7 @@ bool ParserPool::loadCatalog(const XMLCh* pathname) // Check root element. const DOMElement* root=doc->getDocumentElement(); - if (!XMLHelper::isElementNamed(root,CATALOG_NS,catalog)) { + if (!XMLHelper::isNodeNamed(root,CATALOG_NS,catalog)) { auto_ptr_char temp(pathname); log.error("unknown root element, failed to load XML catalog from %s", temp.get()); return false; diff --git a/xmltooling/util/XMLHelper.h b/xmltooling/util/XMLHelper.h index c1ad4fe..99697e6 100644 --- a/xmltooling/util/XMLHelper.h +++ b/xmltooling/util/XMLHelper.h @@ -87,15 +87,15 @@ namespace xmltooling { static DOMElement* appendChildElement(DOMElement* parentElement, DOMElement* childElement); /** - * Checks the qualified name of an element. + * Checks the qualified name of a node. * - * @param e element to check + * @param n node to check * @param ns namespace to compare with * @param local local name to compare with - * @return true iff the element's qualified name matches the other parameters + * @return true iff the node's qualified name matches the other parameters */ - static bool isElementNamed(const DOMElement* e, const XMLCh* ns, const XMLCh* local) { - return (e && XMLString::equals(ns,e->getNamespaceURI()) && XMLString::equals(local,e->getLocalName())); + static bool isNodeNamed(const DOMNode* n, const XMLCh* ns, const XMLCh* local) { + return (n && XMLString::equals(local,n->getLocalName()) && XMLString::equals(ns,n->getNamespaceURI())); } /** diff --git a/xmltooling/util/XMLObjectChildrenList.h b/xmltooling/util/XMLObjectChildrenList.h index dd90a19..9de1579 100644 --- a/xmltooling/util/XMLObjectChildrenList.h +++ b/xmltooling/util/XMLObjectChildrenList.h @@ -238,6 +238,8 @@ namespace xmltooling { removeParent(*_Where); if (m_list) removeChild(*_Where); + else + delete *_Where.m_iter; return m_container.erase(_Where.m_iter); } @@ -246,6 +248,8 @@ namespace xmltooling { removeParent(*i); if (m_list) removeChild(*i); + else + delete *i.m_iter; } return m_container.erase(_First,_Last); } diff --git a/xmltooling/xmltooling.vcproj b/xmltooling/xmltooling.vcproj index 705d8f0..deafcaa 100644 --- a/xmltooling/xmltooling.vcproj +++ b/xmltooling/xmltooling.vcproj @@ -269,6 +269,18 @@ > + + + + + + + + + + + + + + + + + + +#include +#include +#include +#include +#include + +class TestContext : public SigningContext +{ + XSECCryptoKey* m_key; + vector m_certs; + XMLCh* m_uri; + +public: + TestContext(const XMLCh* uri) { + string keypath=data_path + "key.pem"; + BIO* in=BIO_new(BIO_s_file_internal()); + if (in && BIO_read_filename(in,keypath.c_str())>0) { + EVP_PKEY* pkey=PEM_read_bio_PrivateKey(in, NULL, NULL, NULL); + if (pkey) { + m_key=new OpenSSLCryptoKeyRSA(pkey); + EVP_PKEY_free(pkey); + } + } + if (in) BIO_free(in); + TS_ASSERT(m_key!=NULL); + + string certpath=data_path + "cert.pem"; + in=BIO_new(BIO_s_file_internal()); + if (in && BIO_read_filename(in,certpath.c_str())>0) { + X509* x=NULL; + while (x=PEM_read_bio_X509(in,NULL,NULL,NULL)) { + m_certs.push_back(new OpenSSLCryptoX509(x)); + X509_free(x); + } + } + if (in) BIO_free(in); + TS_ASSERT(m_certs.size()>0); + + m_uri=XMLString::replicate(uri); + } + + virtual ~TestContext() { + delete m_key; + for_each(m_certs.begin(),m_certs.end(),xmltooling::cleanup()); + XMLString::release(&m_uri); + } + + void createSignature(DSIGSignature* sig) const { + DSIGReference* ref=sig->createReference(m_uri); + ref->appendEnvelopedSignatureTransform(); + ref->appendCanonicalizationTransform(CANON_C14NE_NOC); + } + + const std::vector& getX509Certificates() const { return m_certs; } + XSECCryptoKey* getSigningKey() const { return m_key->clone(); } +}; + +class SignatureTest : public CxxTest::TestSuite { + QName m_qname; +public: + SignatureTest() : m_qname(SimpleXMLObject::NAMESPACE,SimpleXMLObject::LOCAL_NAME) {} + + void setUp() { + XMLObjectBuilder::registerBuilder(m_qname, new SimpleXMLObjectBuilder()); + Marshaller::registerMarshaller(m_qname, new SimpleXMLObjectMarshaller()); + Unmarshaller::registerUnmarshaller(m_qname, new SimpleXMLObjectUnmarshaller()); + } + + void tearDown() { + XMLObjectBuilder::deregisterBuilder(m_qname); + Marshaller::deregisterMarshaller(m_qname); + Unmarshaller::deregisterUnmarshaller(m_qname); + } + + void testSignature() { + TS_TRACE("testSignature"); + + const XMLObjectBuilder* b=XMLObjectBuilder::getBuilder(m_qname); + TS_ASSERT(b!=NULL); + + auto_ptr sxObject(dynamic_cast(b->buildObject())); + TS_ASSERT(sxObject.get()!=NULL); + VectorOf(SimpleXMLObject) kids=sxObject->getSimpleXMLObjects(); + kids.push_back(dynamic_cast(b->buildObject())); + kids.push_back(dynamic_cast(b->buildObject())); + + // Test some collection stuff + auto_ptr_XMLCh foo("Foo"); + auto_ptr_XMLCh bar("Bar"); + kids.begin()->setId(foo.get()); + kids[1]->setValue(bar.get()); + + // Append a Signature. + Signature* sig=dynamic_cast(XMLObjectBuilder::buildObject(QName(XMLConstants::XMLSIG_NS,Signature::LOCAL_NAME))); + sxObject->setSignature(sig); + + // Signing context for the whole document. + TestContext tc(&chNull); + MarshallingContext mctx(sig,&tc); + DOMElement* rootElement = Marshaller::getMarshaller(sxObject.get())->marshall(sxObject.get(),(DOMDocument*)NULL,&mctx); + + string buf; + XMLHelper::serialize(rootElement, buf); + TS_TRACE(buf.c_str()); + + istringstream in(buf); + DOMDocument* doc=nonvalidatingPool->parse(in); + const Unmarshaller* u = Unmarshaller::getUnmarshaller(doc->getDocumentElement()); + auto_ptr sxObject2(dynamic_cast(u->unmarshall(doc->getDocumentElement(),true))); + TS_ASSERT(sxObject2.get()!=NULL); + TS_ASSERT(sxObject2->getSignature()!=NULL); + } + +}; diff --git a/xmltoolingtest/XMLObjectBaseTestCase.h b/xmltoolingtest/XMLObjectBaseTestCase.h index 28aa716..4085128 100644 --- a/xmltoolingtest/XMLObjectBaseTestCase.h +++ b/xmltoolingtest/XMLObjectBaseTestCase.h @@ -23,9 +23,13 @@ #include #include #include +#ifndef XMLTOOLING_NO_XMLSEC + #include +#endif #include -#include +#include #include +#include using namespace xmltooling; using namespace std; @@ -47,7 +51,11 @@ public: static const XMLCh LOCAL_NAME[]; static const XMLCh ID_ATTRIB_NAME[]; - SimpleXMLObject() : AbstractDOMCachingXMLObject(NAMESPACE, LOCAL_NAME, NAMESPACE_PREFIX), m_id(NULL), m_value(NULL) {} + SimpleXMLObject() : AbstractDOMCachingXMLObject(NAMESPACE, LOCAL_NAME, NAMESPACE_PREFIX), m_id(NULL), m_value(NULL) { + m_children.push_back(NULL); + m_signature=m_children.begin(); + } + virtual ~SimpleXMLObject() { XMLString::release(&m_id); XMLString::release(&m_value); @@ -58,7 +66,17 @@ public: const XMLCh* getValue() const { return m_value; } void setValue(const XMLCh* value) { m_value=prepareForAssignment(m_value,value); } - + +#ifndef XMLTOOLING_NO_XMLSEC + Signature* getSignature() const { + return dynamic_cast(*m_signature); + } + + void setSignature(Signature* sig) { + *m_signature=prepareForAssignment(*m_signature,sig); + } +#endif + VectorOf(SimpleXMLObject) getSimpleXMLObjects() { return VectorOf(SimpleXMLObject)(this, m_simples, &m_children, m_children.end()); } @@ -83,6 +101,7 @@ private: XMLCh* m_id; XMLCh* m_value; vector m_simples; + list::iterator m_signature; }; class SimpleXMLObjectBuilder : public XMLObjectBuilder @@ -123,27 +142,24 @@ public: SimpleXMLObjectUnmarshaller() {} private: - void processChildElement(XMLObject& parentXMLObject, XMLObject* childXMLObject) const { + void processChildElement(XMLObject& parentXMLObject, XMLObject* childXMLObject, const DOMElement* root) const { SimpleXMLObject& simpleXMLObject = dynamic_cast(parentXMLObject); - SimpleXMLObject* child = dynamic_cast(childXMLObject); - if (child) { - simpleXMLObject.getSimpleXMLObjects().push_back(child); - } - else { + if (XMLHelper::isNodeNamed(root, SimpleXMLObject::NAMESPACE, SimpleXMLObject::LOCAL_NAME)) + simpleXMLObject.getSimpleXMLObjects().push_back(dynamic_cast(childXMLObject)); + else if (XMLHelper::isNodeNamed(root, XMLConstants::XMLSIG_NS, Signature::LOCAL_NAME)) + simpleXMLObject.setSignature(dynamic_cast(childXMLObject)); + else throw UnmarshallingException("Unknown child element cannot be added to parent object."); - } } void processAttribute(XMLObject& xmlObject, const DOMAttr* attribute) const { SimpleXMLObject& simpleXMLObject = dynamic_cast(xmlObject); - if (XMLString::equals(attribute->getLocalName(),SimpleXMLObject::ID_ATTRIB_NAME)) { + if (XMLHelper::isNodeNamed(attribute, NULL, SimpleXMLObject::ID_ATTRIB_NAME)) simpleXMLObject.setId(attribute->getValue()); - } - else { + else throw UnmarshallingException("Unknown attribute cannot be processed by parent object."); - } } void processElementContent(XMLObject& xmlObject, const XMLCh* elementContent) const { @@ -240,7 +256,7 @@ private: throw UnmarshallingException("Failed to locate WildcardObjectBuilder for element."); } - void processChildElement(XMLObject& parentXMLObject, XMLObject* childXMLObject) const { + void processChildElement(XMLObject& parentXMLObject, XMLObject* childXMLObject, const DOMElement* root) const { WildcardXMLObject& wcXMLObject = dynamic_cast(parentXMLObject); wcXMLObject.getXMLObjects().push_back(childXMLObject); diff --git a/xmltoolingtest/data/cert.pem b/xmltoolingtest/data/cert.pem new file mode 100644 index 0000000..e8261f3 --- /dev/null +++ b/xmltoolingtest/data/cert.pem @@ -0,0 +1,16 @@ +-----BEGIN CERTIFICATE----- +MIICjzCCAfigAwIBAgIJAKk8t1hYcMkhMA0GCSqGSIb3DQEBBAUAMDoxCzAJBgNV +BAYTAlVTMRIwEAYDVQQKEwlJbnRlcm5ldDIxFzAVBgNVBAMTDnNwLmV4YW1wbGUu +b3JnMB4XDTA1MDYyMDE1NDgzNFoXDTMyMTEwNTE1NDgzNFowOjELMAkGA1UEBhMC +VVMxEjAQBgNVBAoTCUludGVybmV0MjEXMBUGA1UEAxMOc3AuZXhhbXBsZS5vcmcw +gZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANlZ1L1mKzYbUVKiMQLhZlfGDyYa +/jjCiaXP0WhLNgvJpOTeajvsrApYNnFX5MLNzuC3NeQIjXUNLN2Yo2MCSthBIOL5 +qE5dka4z9W9zytoflW1LmJ8vXpx8Ay/meG4z//J5iCpYVEquA0xl28HUIlownZUF +7w7bx0cF/02qrR23AgMBAAGjgZwwgZkwHQYDVR0OBBYEFJZiO1qsyAyc3HwMlL9p +JpN6fbGwMGoGA1UdIwRjMGGAFJZiO1qsyAyc3HwMlL9pJpN6fbGwoT6kPDA6MQsw +CQYDVQQGEwJVUzESMBAGA1UEChMJSW50ZXJuZXQyMRcwFQYDVQQDEw5zcC5leGFt +cGxlLm9yZ4IJAKk8t1hYcMkhMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQAD +gYEAMFq/UeSQyngE0GpZueyD2UW0M358uhseYOgGEIfm+qXIFQF6MYwNoX7WFzhC +LJZ2E6mEvZZFHCHUtl7mGDvsRwgZ85YCtRbvleEpqfgNQToto9pLYe+X6vvH9Z6p +gmYsTmak+kxO93JprrOd9xp8aZPMEprL7VCdrhbZEfyYER0= +-----END CERTIFICATE----- diff --git a/xmltoolingtest/data/key.pem b/xmltoolingtest/data/key.pem new file mode 100644 index 0000000..5149449 --- /dev/null +++ b/xmltoolingtest/data/key.pem @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXQIBAAKBgQDZWdS9Zis2G1FSojEC4WZXxg8mGv44womlz9FoSzYLyaTk3mo7 +7KwKWDZxV+TCzc7gtzXkCI11DSzdmKNjAkrYQSDi+ahOXZGuM/Vvc8raH5VtS5if +L16cfAMv5nhuM//yeYgqWFRKrgNMZdvB1CJaMJ2VBe8O28dHBf9Nqq0dtwIDAQAB +AoGAKsaVKdlLs9BYhuzIvIpju+6M2LEDS2Rt9qYZzm7O6i77NtfXDIgdq8OEo3Xq +3bPnfS5Retl8DYdURyBdN4Uh+WR/BUWQjBvOaJLEEdxvuAaLyAjniVREwkc2rXTZ +xoYYFL/XMyAEt/ye2ZbTw2u5R2i7HCYdddZWMkP1+Vabg8ECQQD7VJXWy8KFiyeC +thJiVqG/h5IO0y25dId/n81sW2B55eK0c4+IVsqc0a45/U/y2y1wtNBmIEQQn9yY +pDtWwzVRAkEA3WOgmvxFGTI5V1K5CLCCZzQIUYpzQDQvBu2sKYuy8dK2BMEGe9Zw +cKVyZJuDKHBvrVI5G6CqkHuFD2PwDvwAhwJBAPdfbM/q4/4/VddAz918uV1j2a2/ +y3yDJq7GIhHp6o5wZ3AHYhnmmyw48YxgOGWntxT80zYBwhy+zAhtdX5TStECQEKL +drP/TfnD2e6Ag/Ozso642iNAXWIYDWakvBIE1rXPYzzMlFlW3JdPc7H/+I2INlk/ +lMDUK1CggB9fJ8IpRzMCQQDQmqpWZtH6eaMAN6b/9WBdVzqzpCeTWFlL/SwhVbzI +s+k2zvC4HEAK9Y199g6SHVTQMEAE49wfhhCpY0JdCsQ/ +-----END RSA PRIVATE KEY----- diff --git a/xmltoolingtest/xmltoolingtest.vcproj b/xmltoolingtest/xmltoolingtest.vcproj index 54abfad..5703d4c 100644 --- a/xmltoolingtest/xmltoolingtest.vcproj +++ b/xmltoolingtest/xmltoolingtest.vcproj @@ -62,7 +62,7 @@ /> + + @@ -213,7 +217,7 @@ > @@ -222,7 +226,7 @@ > @@ -250,6 +254,28 @@ + + + + + + + + @@ -266,7 +292,7 @@ > -- 2.1.4