2 * Copyright 2001-2007 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"
25 #include "impl/UnknownElement.h"
26 #include "security/Credential.h"
27 #include "signature/KeyInfo.h"
28 #include "signature/Signature.h"
30 #include "util/XMLConstants.h"
31 #include "util/XMLHelper.h"
33 #include <log4cpp/Category.hh>
34 #include <xercesc/framework/MemBufInputSource.hpp>
35 #include <xercesc/framework/Wrapper4InputSource.hpp>
36 #include <xercesc/util/XMLUniDefs.hpp>
37 #include <xsec/dsig/DSIGKeyInfoX509.hpp>
38 #include <xsec/dsig/DSIGReference.hpp>
39 #include <xsec/enc/XSECCryptoException.hpp>
40 #include <xsec/framework/XSECAlgorithmHandler.hpp>
41 #include <xsec/framework/XSECAlgorithmMapper.hpp>
42 #include <xsec/framework/XSECException.hpp>
43 #include <xsec/transformers/TXFMSB.hpp>
44 #include <xsec/transformers/TXFMChain.hpp>
45 #include <xsec/transformers/TXFMOutputFile.hpp>
47 using namespace xmlsignature;
48 using namespace xmltooling;
49 using namespace log4cpp;
51 using xmlconstants::XMLSIG_NS;
52 using xmlconstants::XMLSIG_PREFIX;
54 #if defined (_MSC_VER)
55 #pragma warning( push )
56 #pragma warning( disable : 4250 4251 )
59 namespace xmlsignature {
61 class XMLTOOL_DLLLOCAL XMLSecSignatureImpl : public UnknownElementImpl, public virtual Signature
64 XMLSecSignatureImpl() : AbstractXMLObject(XMLSIG_NS, Signature::LOCAL_NAME, XMLSIG_PREFIX),
65 UnknownElementImpl(XMLSIG_NS, Signature::LOCAL_NAME, XMLSIG_PREFIX),
66 m_signature(NULL), m_c14n(NULL), m_sm(NULL), m_key(NULL), m_keyInfo(NULL), m_reference(NULL) {}
67 virtual ~XMLSecSignatureImpl();
69 void releaseDOM() const;
70 void releaseChildrenDOM(bool propagateRelease=true) const {
72 m_keyInfo->releaseDOM();
74 m_keyInfo->releaseChildrenDOM();
77 XMLObject* clone() const;
78 Signature* cloneSignature() const;
80 DOMElement* marshall(DOMDocument* document=NULL, const vector<Signature*>* sigs=NULL, const Credential* credential=NULL) const;
81 DOMElement* marshall(DOMElement* parentElement, const vector<Signature*>* sigs=NULL, const Credential* credential=NULL) const;
82 XMLObject* unmarshall(DOMElement* element, bool bindDocument=false);
85 const XMLCh* getCanonicalizationMethod() const {
87 return canonicalizationMethod2UNICODEURI(m_signature->getCanonicalizationMethod());
88 return m_c14n ? m_c14n : DSIGConstants::s_unicodeStrURIEXC_C14N_NOC;
90 const XMLCh* getSignatureAlgorithm() const {
91 if (!m_sm && m_signature) {
93 if (signatureHashMethod2URI(sURI, m_signature->getSignatureMethod(), m_signature->getHashMethod()) == false)
95 m_sm = XMLString::replicate(sURI.sbStrToXMLCh());
97 return m_sm ? m_sm : DSIGConstants::s_unicodeStrURIRSA_SHA1;
100 KeyInfo* getKeyInfo() const { return m_keyInfo; }
101 ContentReference* getContentReference() const { return m_reference; }
102 DSIGSignature* getXMLSignature() const { return m_signature; }
105 void setCanonicalizationMethod(const XMLCh* c14n) { m_c14n = prepareForAssignment(m_c14n,c14n); }
106 void setSignatureAlgorithm(const XMLCh* sm) { m_sm = prepareForAssignment(m_sm,sm); }
107 void setSigningKey(XSECCryptoKey* signingKey) {
111 void setKeyInfo(KeyInfo* keyInfo) {
112 prepareForAssignment(m_keyInfo, keyInfo);
115 void setContentReference(ContentReference* reference) {
117 m_reference=reference;
120 void sign(const Credential* credential=NULL);
123 mutable DSIGSignature* m_signature;
126 XSECCryptoKey* m_key;
127 mutable KeyInfo* m_keyInfo;
128 ContentReference* m_reference;
133 #if defined (_MSC_VER)
134 #pragma warning( pop )
137 XMLSecSignatureImpl::~XMLSecSignatureImpl()
139 // Release the associated signature.
141 XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->releaseSignature(m_signature);
143 XMLString::release(&m_c14n);
144 XMLString::release(&m_sm);
150 void XMLSecSignatureImpl::releaseDOM() const
153 // This should save off the DOM
154 UnknownElementImpl::releaseDOM();
156 // Release the associated signature.
158 XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->releaseSignature(m_signature);
164 XMLObject* XMLSecSignatureImpl::clone() const
166 return cloneSignature();
169 Signature* XMLSecSignatureImpl::cloneSignature() const
171 XMLSecSignatureImpl* ret=new XMLSecSignatureImpl();
173 ret->m_c14n=XMLString::replicate(m_c14n);
174 ret->m_sm=XMLString::replicate(m_sm);
176 ret->m_key=m_key->clone();
178 ret->m_keyInfo=m_keyInfo->cloneKeyInfo();
180 // If there's no XML locally, serialize this object into the new one, otherwise just copy it over.
182 serialize(ret->m_xml);
189 void XMLSecSignatureImpl::sign(const Credential* credential)
191 Category& log=Category::getInstance(XMLTOOLING_LOGCAT".Signature");
192 log.debug("applying signature");
195 throw SignatureException("Only a marshalled Signature object can be signed.");
196 else if (!m_reference)
197 throw SignatureException("No ContentReference object set for signature creation.");
199 XSECCryptoKey* key = credential ? credential->getPrivateKey() : m_key;
201 throw SignatureException("No signing key available for signature creation.");
204 log.debug("creating signature reference(s)");
205 DSIGReferenceList* refs = m_signature->getReferenceList();
206 while (refs && refs->getSize())
207 delete refs->removeReference(0);
208 m_reference->createReferences(m_signature);
210 log.debug("computing signature");
211 m_signature->setSigningKey(key->clone());
214 catch(XSECException& e) {
215 auto_ptr_char temp(e.getMsg());
216 throw SignatureException(string("Caught an XMLSecurity exception while signing: ") + temp.get());
218 catch(XSECCryptoException& e) {
219 throw SignatureException(string("Caught an XMLSecurity exception while signing: ") + e.getMsg());
223 DOMElement* XMLSecSignatureImpl::marshall(DOMDocument* document, const vector<Signature*>* sigs, const Credential* credential) const
226 xmltooling::NDC ndc("marshall");
229 Category& log=Category::getInstance(XMLTOOLING_LOGCAT".XMLObject.Signature");
230 log.debug("marshalling ds:Signature");
232 DOMElement* cachedDOM=getDOM();
234 if (!document || document==cachedDOM->getOwnerDocument()) {
235 log.debug("Signature has a usable cached DOM, reusing it");
237 setDocumentElement(cachedDOM->getOwnerDocument(),cachedDOM);
238 releaseParentDOM(true);
242 // We have a DOM but it doesn't match the document we were given. This both sucks and blows.
243 // Without an adoptNode option to maintain the child pointers, we have to either import the
244 // DOM while somehow reassigning all the nested references (which amounts to a complete
245 // *unmarshall* operation), or we just release the existing DOM and hope that we can get
246 // it back. This depends on all objects being able to preserve their DOM at all costs.
247 releaseChildrenDOM(true);
251 // If we get here, we didn't have a usable DOM.
252 bool bindDocument=false;
254 // Fresh signature, so we just create an empty one.
255 log.debug("creating empty Signature element");
257 document=DOMImplementationRegistry::getDOMImplementation(NULL)->createDocument();
260 DSIGSignature* temp=XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->newSignature();
261 temp->setDSIGNSPrefix(XMLSIG_PREFIX);
262 cachedDOM=temp->createBlankSignature(document, getCanonicalizationMethod(), getSignatureAlgorithm());
266 // We need to reparse the XML we saved off into a new DOM.
267 MemBufInputSource src(reinterpret_cast<const XMLByte*>(m_xml.c_str()),m_xml.length(),"XMLSecSignatureImpl");
268 Wrapper4InputSource dsrc(&src,false);
269 log.debug("parsing Signature XML back into DOM tree");
270 DOMDocument* internalDoc=XMLToolingConfig::getConfig().getParser().parse(dsrc);
272 // The caller insists on using his own document, so we now have to import the thing
273 // into it. Then we're just dumping the one we built.
274 log.debug("reimporting new DOM into caller-supplied document");
275 cachedDOM=static_cast<DOMElement*>(document->importNode(internalDoc->getDocumentElement(), true));
276 internalDoc->release();
279 // We just bind the document we built to the object as the result.
280 cachedDOM=static_cast<DOMElement*>(internalDoc->getDocumentElement());
281 document=internalDoc;
285 // Now reload the signature from the DOM.
287 m_signature=XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->newSignatureFromDOM(
292 catch(XSECException& e) {
295 auto_ptr_char temp(e.getMsg());
296 throw MarshallingException(string("Caught an XMLSecurity exception while loading signature: ") + temp.get());
298 catch(XSECCryptoException& e) {
301 throw MarshallingException(string("Caught an XMLSecurity exception while loading signature: ") + e.getMsg());
305 // Marshall KeyInfo data.
309 const KeyInfo* fromcred = credential->getKeyInfo();
311 m_keyInfo = fromcred->cloneKeyInfo();
313 if (m_keyInfo && (!m_signature->getKeyInfoList() || m_signature->getKeyInfoList()->isEmpty())) {
314 m_keyInfo->marshall(cachedDOM);
317 // Recache the DOM and clear the serialized copy.
318 setDocumentElement(document, cachedDOM);
319 log.debug("caching DOM for Signature (document is %sbound)", bindDocument ? "" : "not ");
320 setDOM(cachedDOM, bindDocument);
321 releaseParentDOM(true);
326 DOMElement* XMLSecSignatureImpl::marshall(DOMElement* parentElement, const vector<Signature*>* sigs, const Credential* credential) const
329 xmltooling::NDC ndc("marshall");
332 Category& log=Category::getInstance(XMLTOOLING_LOGCAT".XMLObject.Signature");
333 log.debug("marshalling ds:Signature");
335 DOMElement* cachedDOM=getDOM();
337 if (parentElement->getOwnerDocument()==cachedDOM->getOwnerDocument()) {
338 log.debug("Signature has a usable cached DOM, reusing it");
339 if (parentElement!=cachedDOM->getParentNode()) {
340 parentElement->appendChild(cachedDOM);
341 releaseParentDOM(true);
346 // We have a DOM but it doesn't match the document we were given. This both sucks and blows.
347 // Without an adoptNode option to maintain the child pointers, we have to either import the
348 // DOM while somehow reassigning all the nested references (which amounts to a complete
349 // *unmarshall* operation), or we just release the existing DOM and hope that we can get
350 // it back. This depends on all objects being able to preserve their DOM at all costs.
351 releaseChildrenDOM(true);
355 // If we get here, we didn't have a usable DOM.
357 // Fresh signature, so we just create an empty one.
358 log.debug("creating empty Signature element");
359 DSIGSignature* temp=XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->newSignature();
360 temp->setDSIGNSPrefix(XMLSIG_PREFIX);
361 cachedDOM=temp->createBlankSignature(parentElement->getOwnerDocument(), getCanonicalizationMethod(), getSignatureAlgorithm());
365 MemBufInputSource src(reinterpret_cast<const XMLByte*>(m_xml.c_str()),m_xml.length(),"XMLSecSignatureImpl");
366 Wrapper4InputSource dsrc(&src,false);
367 log.debug("parsing XML back into DOM tree");
368 DOMDocument* internalDoc=XMLToolingConfig::getConfig().getParser().parse(dsrc);
370 log.debug("reimporting new DOM into caller-supplied document");
371 cachedDOM=static_cast<DOMElement*>(parentElement->getOwnerDocument()->importNode(internalDoc->getDocumentElement(),true));
372 internalDoc->release();
374 // Now reload the signature from the DOM.
376 m_signature=XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->newSignatureFromDOM(
377 parentElement->getOwnerDocument(), cachedDOM
381 catch(XSECException& e) {
382 auto_ptr_char temp(e.getMsg());
383 throw MarshallingException(string("Caught an XMLSecurity exception while loading signature: ") + temp.get());
385 catch(XSECCryptoException& e) {
386 throw MarshallingException(string("Caught an XMLSecurity exception while loading signature: ") + e.getMsg());
390 // Marshall KeyInfo data.
394 const KeyInfo* fromcred = credential->getKeyInfo();
396 m_keyInfo = fromcred->cloneKeyInfo();
398 if (m_keyInfo && (!m_signature->getKeyInfoList() || m_signature->getKeyInfoList()->isEmpty())) {
399 m_keyInfo->marshall(cachedDOM);
402 // Recache the DOM and clear the serialized copy.
403 parentElement->appendChild(cachedDOM);
404 log.debug("caching DOM for Signature");
405 setDOM(cachedDOM, false);
406 releaseParentDOM(true);
411 XMLObject* XMLSecSignatureImpl::unmarshall(DOMElement* element, bool bindDocument)
413 Category::getInstance(XMLTOOLING_LOGCAT".XMLObject.Signature").debug("unmarshalling ds:Signature");
416 m_signature=XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->newSignatureFromDOM(
417 element->getOwnerDocument(), element
421 catch(XSECException& e) {
422 auto_ptr_char temp(e.getMsg());
423 throw UnmarshallingException(string("Caught an XMLSecurity exception while loading signature: ") + temp.get());
425 catch(XSECCryptoException& e) {
426 throw UnmarshallingException(string("Caught an XMLSecurity exception while loading signature: ") + e.getMsg());
429 setDOM(element, bindDocument);
433 #ifdef HAVE_COVARIANT_RETURNS
438 SignatureBuilder::buildObject(
439 const XMLCh* nsURI, const XMLCh* localName, const XMLCh* prefix, const QName* schemaType
442 if (!XMLString::equals(nsURI,XMLSIG_NS) || !XMLString::equals(localName,Signature::LOCAL_NAME))
443 throw XMLObjectException("XMLSecSignatureBuilder requires standard Signature element name.");
444 return buildObject();
447 #ifdef HAVE_COVARIANT_RETURNS
452 SignatureBuilder::buildObject() const
454 return new XMLSecSignatureImpl();
457 const XMLCh Signature::LOCAL_NAME[] = UNICODE_LITERAL_9(S,i,g,n,a,t,u,r,e);
459 // Raw signature methods.
461 unsigned int Signature::createRawSignature(
462 XSECCryptoKey* key, const XMLCh* sigAlgorithm, const char* in, unsigned int in_len, char* out, unsigned int out_len
466 XSECAlgorithmHandler* handler = XSECPlatformUtils::g_algorithmMapper->mapURIToHandler(sigAlgorithm);
468 auto_ptr_char alg(sigAlgorithm);
469 throw SignatureException("Unsupported signature algorithm ($1).", params(1,alg.get()));
472 // Move input into a safeBuffer to source the transform chain.
474 sb.sbStrncpyIn(in,in_len);
475 TXFMSB* sbt = new TXFMSB(NULL);
476 sbt->setInput(sb, in_len);
480 unsigned int siglen = handler->signToSafeBuffer(&tx, sigAlgorithm, key, out_len-1, sbout);
481 if (siglen >= out_len)
482 throw SignatureException("Signature size exceeded output buffer size.");
484 // Push all non-whitespace into buffer.
485 unsigned int ret_len = 0;
486 const char* source = sbout.rawCharBuffer();
488 if (isspace(*source))
498 catch(XSECException& e) {
499 auto_ptr_char temp(e.getMsg());
500 throw SignatureException(string("Caught an XMLSecurity exception while creating raw signature: ") + temp.get());
502 catch(XSECCryptoException& e) {
503 throw SignatureException(string("Caught an XMLSecurity exception while creating raw signature: ") + e.getMsg());
507 bool Signature::verifyRawSignature(
508 XSECCryptoKey* key, const XMLCh* sigAlgorithm, const char* signature, const char* in, unsigned int in_len
512 XSECAlgorithmHandler* handler = XSECPlatformUtils::g_algorithmMapper->mapURIToHandler(sigAlgorithm);
514 auto_ptr_char alg(sigAlgorithm);
515 throw SignatureException("Unsupported signature algorithm ($1).", params(1,alg.get()));
518 // Move input into a safeBuffer to source the transform chain.
520 sb.sbStrncpyIn(in,in_len);
521 TXFMSB* sbt = new TXFMSB(NULL);
522 sbt->setInput(sb, in_len);
526 return handler->verifyBase64Signature(&tx, sigAlgorithm, signature, 0, key);
528 catch(XSECException& e) {
529 auto_ptr_char temp(e.getMsg());
530 throw SignatureException(string("Caught an XMLSecurity exception while verifying raw signature: ") + temp.get());
532 catch(XSECCryptoException& e) {
533 throw SignatureException(string("Caught an XMLSecurity exception while verifying raw signature: ") + e.getMsg());
537 void Signature::extractNames(DSIGKeyInfoList* keyInfo, set<string>& names)
542 for (size_t s=0; s<keyInfo->getSize(); s++) {
543 n=keyInfo->item(s)->getKeyName();