2 * Copyright 2001-2010 Internet2
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 * XMLSecSignatureImpl.cpp
20 * Signature class for XMLSec-based signature-handling.
24 #include "exceptions.h"
26 #include "impl/UnknownElement.h"
27 #include "security/Credential.h"
28 #include "signature/ContentReference.h"
29 #include "signature/KeyInfo.h"
30 #include "signature/Signature.h"
32 #include "util/XMLConstants.h"
33 #include "util/XMLHelper.h"
35 #include <xercesc/framework/MemBufInputSource.hpp>
36 #include <xercesc/framework/Wrapper4InputSource.hpp>
37 #include <xercesc/util/XMLUniDefs.hpp>
38 #include <xsec/dsig/DSIGKeyInfoX509.hpp>
39 #include <xsec/dsig/DSIGReference.hpp>
40 #include <xsec/enc/XSECCryptoException.hpp>
41 #include <xsec/framework/XSECAlgorithmHandler.hpp>
42 #include <xsec/framework/XSECAlgorithmMapper.hpp>
43 #include <xsec/framework/XSECException.hpp>
44 #include <xsec/transformers/TXFMSB.hpp>
45 #include <xsec/transformers/TXFMChain.hpp>
46 #include <xsec/transformers/TXFMOutputFile.hpp>
48 using namespace xmlsignature;
49 using namespace xmltooling::logging;
50 using namespace xmltooling;
51 using namespace xercesc;
53 using xmlconstants::XMLSIG_NS;
54 using xmlconstants::XMLSIG_PREFIX;
56 namespace xmlsignature {
58 #if defined (_MSC_VER)
59 #pragma warning( push )
60 #pragma warning( disable : 4250 4251 )
63 class XMLTOOL_DLLLOCAL XMLSecSignatureImpl : public UnknownElementImpl, public virtual Signature
66 XMLSecSignatureImpl() : AbstractXMLObject(XMLSIG_NS, Signature::LOCAL_NAME, XMLSIG_PREFIX),
67 UnknownElementImpl(XMLSIG_NS, Signature::LOCAL_NAME, XMLSIG_PREFIX),
68 m_signature(nullptr), m_c14n(nullptr), m_sm(nullptr), m_key(nullptr), m_keyInfo(nullptr), m_reference(nullptr) {}
69 virtual ~XMLSecSignatureImpl();
71 void releaseDOM() const;
72 void releaseChildrenDOM(bool propagateRelease=true) const {
74 m_keyInfo->releaseDOM();
76 m_keyInfo->releaseChildrenDOM();
79 XMLObject* clone() const;
80 Signature* cloneSignature() const;
82 DOMElement* marshall(DOMDocument* document=nullptr, const vector<Signature*>* sigs=nullptr, const Credential* credential=nullptr) const;
83 DOMElement* marshall(DOMElement* parentElement, const vector<Signature*>* sigs=nullptr, const Credential* credential=nullptr) const;
84 XMLObject* unmarshall(DOMElement* element, bool bindDocument=false);
87 const XMLCh* getCanonicalizationMethod() const {
89 return canonicalizationMethod2UNICODEURI(m_signature->getCanonicalizationMethod());
90 return m_c14n ? m_c14n : DSIGConstants::s_unicodeStrURIEXC_C14N_NOC;
92 const XMLCh* getSignatureAlgorithm() const {
93 if (!m_sm && m_signature) {
94 #ifdef XMLTOOLING_XMLSEC_SIGALGORITHM
95 m_sm = XMLString::replicate(m_signature->getAlgorithmURI());
98 if (signatureHashMethod2URI(sURI, m_signature->getSignatureMethod(), m_signature->getHashMethod()))
99 m_sm = XMLString::replicate(sURI.sbStrToXMLCh());
105 KeyInfo* getKeyInfo() const { return m_keyInfo; }
106 ContentReference* getContentReference() const { return m_reference; }
107 DSIGSignature* getXMLSignature() const { return m_signature; }
110 void setCanonicalizationMethod(const XMLCh* c14n) { m_c14n = prepareForAssignment(m_c14n,c14n); }
111 void setSignatureAlgorithm(const XMLCh* sm) { m_sm = prepareForAssignment(m_sm,sm); }
112 void setSigningKey(XSECCryptoKey* signingKey) {
116 void setKeyInfo(KeyInfo* keyInfo) {
117 prepareForAssignment(m_keyInfo, keyInfo);
120 void setContentReference(ContentReference* reference) {
122 m_reference=reference;
125 void sign(const Credential* credential=nullptr);
128 mutable DSIGSignature* m_signature;
131 XSECCryptoKey* m_key;
132 mutable KeyInfo* m_keyInfo;
133 ContentReference* m_reference;
136 #if defined (_MSC_VER)
137 #pragma warning( pop )
141 ContentReference::ContentReference()
145 ContentReference::~ContentReference()
149 Signature::Signature()
153 Signature::~Signature()
157 XMLSecSignatureImpl::~XMLSecSignatureImpl()
159 // Release the associated signature.
161 XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->releaseSignature(m_signature);
163 XMLString::release(&m_c14n);
164 XMLString::release(&m_sm);
170 void XMLSecSignatureImpl::releaseDOM() const
173 // This should save off the DOM
174 UnknownElementImpl::releaseDOM();
176 // Release the associated signature.
178 XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->releaseSignature(m_signature);
184 XMLObject* XMLSecSignatureImpl::clone() const
186 return cloneSignature();
189 Signature* XMLSecSignatureImpl::cloneSignature() const
191 XMLSecSignatureImpl* ret=new XMLSecSignatureImpl();
193 ret->m_c14n=XMLString::replicate(m_c14n);
194 ret->m_sm=XMLString::replicate(m_sm);
196 ret->m_key=m_key->clone();
198 ret->m_keyInfo=m_keyInfo->cloneKeyInfo();
200 // If there's no XML locally, serialize this object into the new one, otherwise just copy it over.
202 serialize(ret->m_xml);
209 void XMLSecSignatureImpl::sign(const Credential* credential)
211 Category& log=Category::getInstance(XMLTOOLING_LOGCAT".Signature");
212 log.debug("applying signature");
215 throw SignatureException("Only a marshalled Signature object can be signed.");
216 else if (!m_reference)
217 throw SignatureException("No ContentReference object set for signature creation.");
219 XSECCryptoKey* key = credential ? credential->getPrivateKey() : m_key;
221 throw SignatureException("No signing key available for signature creation.");
224 log.debug("creating signature reference(s)");
225 DSIGReferenceList* refs = m_signature->getReferenceList();
226 while (refs && refs->getSize())
227 delete refs->removeReference(0);
228 m_reference->createReferences(m_signature);
230 log.debug("computing signature");
231 m_signature->setSigningKey(key->clone());
234 catch(XSECException& e) {
235 auto_ptr_char temp(e.getMsg());
236 throw SignatureException(string("Caught an XMLSecurity exception while signing: ") + temp.get());
238 catch(XSECCryptoException& e) {
239 throw SignatureException(string("Caught an XMLSecurity exception while signing: ") + e.getMsg());
243 DOMElement* XMLSecSignatureImpl::marshall(DOMDocument* document, const vector<Signature*>* sigs, const Credential* credential) const
246 xmltooling::NDC ndc("marshall");
249 Category& log=Category::getInstance(XMLTOOLING_LOGCAT".XMLObject.Signature");
250 log.debug("marshalling ds:Signature");
252 DOMElement* cachedDOM=getDOM();
254 if (!document || document==cachedDOM->getOwnerDocument()) {
255 log.debug("Signature has a usable cached DOM, reusing it");
257 setDocumentElement(cachedDOM->getOwnerDocument(),cachedDOM);
258 releaseParentDOM(true);
262 // We have a DOM but it doesn't match the document we were given. This both sucks and blows.
263 // Without an adoptNode option to maintain the child pointers, we have to either import the
264 // DOM while somehow reassigning all the nested references (which amounts to a complete
265 // *unmarshall* operation), or we just release the existing DOM and hope that we can get
266 // it back. This depends on all objects being able to preserve their DOM at all costs.
267 releaseChildrenDOM(true);
271 // If we get here, we didn't have a usable DOM.
272 bool bindDocument=false;
274 // Fresh signature, so we just create an empty one.
275 log.debug("creating empty Signature element");
277 document=DOMImplementationRegistry::getDOMImplementation(nullptr)->createDocument();
280 DSIGSignature* temp=XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->newSignature();
281 temp->setDSIGNSPrefix(XMLSIG_PREFIX);
282 const XMLCh* alg = getSignatureAlgorithm();
284 alg = DSIGConstants::s_unicodeStrURIRSA_SHA1;
285 cachedDOM=temp->createBlankSignature(document, getCanonicalizationMethod(), alg);
289 // We need to reparse the XML we saved off into a new DOM.
290 MemBufInputSource src(reinterpret_cast<const XMLByte*>(m_xml.c_str()),m_xml.length(),"XMLSecSignatureImpl");
291 Wrapper4InputSource dsrc(&src,false);
292 log.debug("parsing Signature XML back into DOM tree");
293 DOMDocument* internalDoc=XMLToolingConfig::getConfig().getParser().parse(dsrc);
295 // The caller insists on using his own document, so we now have to import the thing
296 // into it. Then we're just dumping the one we built.
297 log.debug("reimporting new DOM into caller-supplied document");
299 cachedDOM=static_cast<DOMElement*>(document->importNode(internalDoc->getDocumentElement(), true));
301 catch (XMLException& ex) {
302 internalDoc->release();
303 auto_ptr_char temp(ex.getMessage());
304 throw XMLParserException(
305 string("Error importing DOM into caller-supplied document: ") + (temp.get() ? temp.get() : "no message")
308 internalDoc->release();
311 // We just bind the document we built to the object as the result.
312 cachedDOM=static_cast<DOMElement*>(internalDoc->getDocumentElement());
313 document=internalDoc;
317 // Now reload the signature from the DOM.
319 m_signature=XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->newSignatureFromDOM(
324 catch(XSECException& e) {
327 auto_ptr_char temp(e.getMsg());
328 throw MarshallingException(string("Caught an XMLSecurity exception while loading signature: ") + temp.get());
330 catch(XSECCryptoException& e) {
333 throw MarshallingException(string("Caught an XMLSecurity exception while loading signature: ") + e.getMsg());
337 // Marshall KeyInfo data.
341 m_keyInfo = credential->getKeyInfo();
343 if (m_keyInfo && (!m_signature->getKeyInfoList() || m_signature->getKeyInfoList()->isEmpty())) {
344 m_keyInfo->marshall(cachedDOM);
347 // Recache the DOM and clear the serialized copy.
348 setDocumentElement(document, cachedDOM);
349 log.debug("caching DOM for Signature (document is %sbound)", bindDocument ? "" : "not ");
350 setDOM(cachedDOM, bindDocument);
351 releaseParentDOM(true);
356 DOMElement* XMLSecSignatureImpl::marshall(DOMElement* parentElement, const vector<Signature*>* sigs, const Credential* credential) const
359 xmltooling::NDC ndc("marshall");
362 Category& log=Category::getInstance(XMLTOOLING_LOGCAT".XMLObject.Signature");
363 log.debug("marshalling ds:Signature");
365 DOMElement* cachedDOM=getDOM();
367 if (parentElement->getOwnerDocument()==cachedDOM->getOwnerDocument()) {
368 log.debug("Signature has a usable cached DOM, reusing it");
369 if (parentElement!=cachedDOM->getParentNode()) {
370 parentElement->appendChild(cachedDOM);
371 releaseParentDOM(true);
376 // We have a DOM but it doesn't match the document we were given. This both sucks and blows.
377 // Without an adoptNode option to maintain the child pointers, we have to either import the
378 // DOM while somehow reassigning all the nested references (which amounts to a complete
379 // *unmarshall* operation), or we just release the existing DOM and hope that we can get
380 // it back. This depends on all objects being able to preserve their DOM at all costs.
381 releaseChildrenDOM(true);
385 // If we get here, we didn't have a usable DOM.
387 // Fresh signature, so we just create an empty one.
388 log.debug("creating empty Signature element");
389 DSIGSignature* temp=XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->newSignature();
390 temp->setDSIGNSPrefix(XMLSIG_PREFIX);
391 const XMLCh* alg = getSignatureAlgorithm();
393 alg = DSIGConstants::s_unicodeStrURIRSA_SHA1;
394 cachedDOM=temp->createBlankSignature(parentElement->getOwnerDocument(), getCanonicalizationMethod(), alg);
398 MemBufInputSource src(reinterpret_cast<const XMLByte*>(m_xml.c_str()),m_xml.length(),"XMLSecSignatureImpl");
399 Wrapper4InputSource dsrc(&src,false);
400 log.debug("parsing XML back into DOM tree");
401 DOMDocument* internalDoc=XMLToolingConfig::getConfig().getParser().parse(dsrc);
403 log.debug("reimporting new DOM into caller-supplied document");
405 cachedDOM=static_cast<DOMElement*>(parentElement->getOwnerDocument()->importNode(internalDoc->getDocumentElement(),true));
407 catch (XMLException& ex) {
408 internalDoc->release();
409 auto_ptr_char temp(ex.getMessage());
410 throw XMLParserException(
411 string("Error importing DOM into caller-supplied document: ") + (temp.get() ? temp.get() : "no message")
414 internalDoc->release();
416 // Now reload the signature from the DOM.
418 m_signature=XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->newSignatureFromDOM(
419 parentElement->getOwnerDocument(), cachedDOM
423 catch(XSECException& e) {
424 auto_ptr_char temp(e.getMsg());
425 throw MarshallingException(string("Caught an XMLSecurity exception while loading signature: ") + temp.get());
427 catch(XSECCryptoException& e) {
428 throw MarshallingException(string("Caught an XMLSecurity exception while loading signature: ") + e.getMsg());
432 // Marshall KeyInfo data.
436 m_keyInfo = credential->getKeyInfo();
438 if (m_keyInfo && (!m_signature->getKeyInfoList() || m_signature->getKeyInfoList()->isEmpty())) {
439 m_keyInfo->marshall(cachedDOM);
442 // Recache the DOM and clear the serialized copy.
443 parentElement->appendChild(cachedDOM);
444 log.debug("caching DOM for Signature");
445 setDOM(cachedDOM, false);
446 releaseParentDOM(true);
451 XMLObject* XMLSecSignatureImpl::unmarshall(DOMElement* element, bool bindDocument)
453 Category::getInstance(XMLTOOLING_LOGCAT".XMLObject.Signature").debug("unmarshalling ds:Signature");
456 m_signature=XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->newSignatureFromDOM(
457 element->getOwnerDocument(), element
461 catch(XSECException& e) {
462 auto_ptr_char temp(e.getMsg());
463 throw UnmarshallingException(string("Caught an XMLSecurity exception while loading signature: ") + temp.get());
465 catch(XSECCryptoException& e) {
466 throw UnmarshallingException(string("Caught an XMLSecurity exception while loading signature: ") + e.getMsg());
469 setDOM(element, bindDocument);
473 #ifdef HAVE_COVARIANT_RETURNS
478 SignatureBuilder::buildObject(
479 const XMLCh* nsURI, const XMLCh* localName, const XMLCh* prefix, const xmltooling::QName* schemaType
482 if (!XMLString::equals(nsURI,XMLSIG_NS) || !XMLString::equals(localName,Signature::LOCAL_NAME))
483 throw XMLObjectException("XMLSecSignatureBuilder requires standard Signature element name.");
484 return buildObject();
487 #ifdef HAVE_COVARIANT_RETURNS
492 SignatureBuilder::buildObject() const
494 return new XMLSecSignatureImpl();
497 Signature* SignatureBuilder::buildSignature() {
498 const SignatureBuilder* b = dynamic_cast<const SignatureBuilder*>(
499 XMLObjectBuilder::getBuilder(xmltooling::QName(xmlconstants::XMLSIG_NS,Signature::LOCAL_NAME))
502 #ifdef HAVE_COVARIANT_RETURNS
503 return b->buildObject();
505 return dynamic_cast<Signature*>(b->buildObject());
508 throw XMLObjectException("Unable to obtain typed builder for Signature.");
511 const XMLCh Signature::LOCAL_NAME[] = UNICODE_LITERAL_9(S,i,g,n,a,t,u,r,e);
513 // Raw signature methods.
515 unsigned int Signature::createRawSignature(
516 XSECCryptoKey* key, const XMLCh* sigAlgorithm, const char* in, unsigned int in_len, char* out, unsigned int out_len
520 XSECAlgorithmHandler* handler = XSECPlatformUtils::g_algorithmMapper->mapURIToHandler(sigAlgorithm);
522 auto_ptr_char alg(sigAlgorithm);
523 throw SignatureException("Unsupported signature algorithm ($1).", params(1,alg.get()));
526 // Move input into a safeBuffer to source the transform chain.
528 sb.sbStrncpyIn(in,in_len);
529 TXFMSB* sbt = new TXFMSB(nullptr);
530 sbt->setInput(sb, in_len);
534 unsigned int siglen = handler->signToSafeBuffer(&tx, sigAlgorithm, key, out_len-1, sbout);
535 if (siglen >= out_len)
536 throw SignatureException("Signature size exceeded output buffer size.");
538 // Push all non-whitespace into buffer.
539 unsigned int ret_len = 0;
540 const char* source = sbout.rawCharBuffer();
542 if (isspace(*source))
552 catch(XSECException& e) {
553 auto_ptr_char temp(e.getMsg());
554 throw SignatureException(string("Caught an XMLSecurity exception while creating raw signature: ") + temp.get());
556 catch(XSECCryptoException& e) {
557 throw SignatureException(string("Caught an XMLSecurity exception while creating raw signature: ") + e.getMsg());
561 bool Signature::verifyRawSignature(
562 XSECCryptoKey* key, const XMLCh* sigAlgorithm, const char* signature, const char* in, unsigned int in_len
566 XSECAlgorithmHandler* handler = XSECPlatformUtils::g_algorithmMapper->mapURIToHandler(sigAlgorithm);
568 auto_ptr_char alg(sigAlgorithm);
569 throw SignatureException("Unsupported signature algorithm ($1).", params(1,alg.get()));
572 // Move input into a safeBuffer to source the transform chain.
574 sb.sbStrncpyIn(in,in_len);
575 TXFMSB* sbt = new TXFMSB(nullptr);
576 sbt->setInput(sb, in_len);
580 return handler->verifyBase64Signature(&tx, sigAlgorithm, signature, 0, key);
582 catch(XSECException& e) {
583 auto_ptr_char temp(e.getMsg());
584 throw SignatureException(string("Caught an XMLSecurity exception while verifying raw signature: ") + temp.get());
586 catch(XSECCryptoException& e) {
587 throw SignatureException(string("Caught an XMLSecurity exception while verifying raw signature: ") + e.getMsg());