2 * Copyright 2001-2006 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 "signature/KeyInfo.h"
27 #include "signature/Signature.h"
29 #include "util/XMLConstants.h"
30 #include "util/XMLHelper.h"
32 #include <log4cpp/Category.hh>
33 #include <xercesc/framework/MemBufInputSource.hpp>
34 #include <xercesc/framework/Wrapper4InputSource.hpp>
35 #include <xercesc/util/XMLUniDefs.hpp>
36 #include <xsec/dsig/DSIGKeyInfoX509.hpp>
37 #include <xsec/dsig/DSIGReference.hpp>
38 #include <xsec/enc/XSECCryptoException.hpp>
39 #include <xsec/framework/XSECAlgorithmHandler.hpp>
40 #include <xsec/framework/XSECAlgorithmMapper.hpp>
41 #include <xsec/framework/XSECException.hpp>
42 #include <xsec/transformers/TXFMSB.hpp>
43 #include <xsec/transformers/TXFMChain.hpp>
44 #include <xsec/transformers/TXFMOutputFile.hpp>
46 using namespace xmlsignature;
47 using namespace xmltooling;
48 using namespace log4cpp;
50 using xmlconstants::XMLSIG_NS;
51 using xmlconstants::XMLSIG_PREFIX;
53 #if defined (_MSC_VER)
54 #pragma warning( push )
55 #pragma warning( disable : 4250 4251 )
58 namespace xmlsignature {
60 class XMLTOOL_DLLLOCAL XMLSecSignatureImpl : public UnknownElementImpl, public virtual Signature
63 XMLSecSignatureImpl() : UnknownElementImpl(XMLSIG_NS, Signature::LOCAL_NAME, XMLSIG_PREFIX),
64 m_signature(NULL), m_c14n(NULL), m_sm(NULL), m_key(NULL), m_keyInfo(NULL), m_reference(NULL) {}
65 virtual ~XMLSecSignatureImpl();
67 void releaseDOM() const;
68 void releaseChildrenDOM(bool propagateRelease=true) const {
70 m_keyInfo->releaseDOM();
72 m_keyInfo->releaseChildrenDOM();
75 XMLObject* clone() const;
76 Signature* cloneSignature() const;
78 DOMElement* marshall(DOMDocument* document=NULL, const vector<Signature*>* sigs=NULL) const;
79 DOMElement* marshall(DOMElement* parentElement, const vector<Signature*>* sigs=NULL) const;
80 XMLObject* unmarshall(DOMElement* element, bool bindDocument=false);
83 const XMLCh* getCanonicalizationMethod() const { return m_c14n ? m_c14n : DSIGConstants::s_unicodeStrURIEXC_C14N_NOC; }
84 const XMLCh* getSignatureAlgorithm() const { return m_sm ? m_sm : DSIGConstants::s_unicodeStrURIRSA_SHA1; }
85 KeyInfo* getKeyInfo() const { return m_keyInfo; }
86 ContentReference* getContentReference() const { return m_reference; }
87 DSIGSignature* getXMLSignature() const { return m_signature; }
90 void setCanonicalizationMethod(const XMLCh* c14n) { m_c14n = prepareForAssignment(m_c14n,c14n); }
91 void setSignatureAlgorithm(const XMLCh* sm) { m_sm = prepareForAssignment(m_sm,sm); }
92 void setSigningKey(XSECCryptoKey* signingKey) {
96 void setKeyInfo(KeyInfo* keyInfo) {
97 prepareForAssignment(m_keyInfo, keyInfo);
100 void setContentReference(ContentReference* reference) {
102 m_reference=reference;
108 mutable DSIGSignature* m_signature;
111 XSECCryptoKey* m_key;
113 ContentReference* m_reference;
118 #if defined (_MSC_VER)
119 #pragma warning( pop )
122 XMLSecSignatureImpl::~XMLSecSignatureImpl()
124 // Release the associated signature.
126 XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->releaseSignature(m_signature);
128 XMLString::release(&m_c14n);
129 XMLString::release(&m_sm);
135 void XMLSecSignatureImpl::releaseDOM() const
138 // This should save off the DOM
139 UnknownElementImpl::releaseDOM();
141 // Release the associated signature.
143 XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->releaseSignature(m_signature);
149 XMLObject* XMLSecSignatureImpl::clone() const
151 return cloneSignature();
154 Signature* XMLSecSignatureImpl::cloneSignature() const
156 XMLSecSignatureImpl* ret=new XMLSecSignatureImpl();
158 ret->m_c14n=XMLString::replicate(m_c14n);
159 ret->m_sm=XMLString::replicate(m_sm);
161 ret->m_key=m_key->clone();
163 ret->m_keyInfo=m_keyInfo->cloneKeyInfo();
165 // If there's no XML locally, serialize this object into the new one, otherwise just copy it over.
167 serialize(ret->m_xml);
174 void XMLSecSignatureImpl::sign()
176 Category& log=Category::getInstance(XMLTOOLING_LOGCAT".Signature");
177 log.debug("applying signature");
180 throw SignatureException("Only a marshalled Signature object can be signed.");
182 throw SignatureException("No signing key available for signature creation.");
183 else if (!m_reference)
184 throw SignatureException("No ContentReference object set for signature creation.");
187 log.debug("creating signature reference(s)");
188 DSIGReferenceList* refs = m_signature->getReferenceList();
189 while (refs && refs->getSize())
190 delete refs->removeReference(0);
191 m_reference->createReferences(m_signature);
193 log.debug("computing signature");
194 m_signature->setSigningKey(m_key->clone());
197 catch(XSECException& e) {
198 auto_ptr_char temp(e.getMsg());
199 throw SignatureException(string("Caught an XMLSecurity exception while signing: ") + temp.get());
201 catch(XSECCryptoException& e) {
202 throw SignatureException(string("Caught an XMLSecurity exception while signing: ") + e.getMsg());
206 DOMElement* XMLSecSignatureImpl::marshall(DOMDocument* document, const vector<Signature*>* sigs) const
209 xmltooling::NDC ndc("marshall");
212 Category& log=Category::getInstance(XMLTOOLING_LOGCAT".Signature");
213 log.debug("marshalling ds:Signature");
215 DOMElement* cachedDOM=getDOM();
217 if (!document || document==cachedDOM->getOwnerDocument()) {
218 log.debug("Signature has a usable cached DOM, reusing it");
220 setDocumentElement(cachedDOM->getOwnerDocument(),cachedDOM);
221 releaseParentDOM(true);
225 // We have a DOM but it doesn't match the document we were given. This both sucks and blows.
226 // Without an adoptNode option to maintain the child pointers, we have to either import the
227 // DOM while somehow reassigning all the nested references (which amounts to a complete
228 // *unmarshall* operation), or we just release the existing DOM and hope that we can get
229 // it back. This depends on all objects being able to preserve their DOM at all costs.
230 releaseChildrenDOM(true);
234 // If we get here, we didn't have a usable DOM.
235 bool bindDocument=false;
237 // Fresh signature, so we just create an empty one.
238 log.debug("creating empty Signature element");
240 document=DOMImplementationRegistry::getDOMImplementation(NULL)->createDocument();
243 m_signature=XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->newSignature();
244 m_signature->setDSIGNSPrefix(XMLSIG_PREFIX);
245 cachedDOM=m_signature->createBlankSignature(document, getCanonicalizationMethod(), getSignatureAlgorithm());
248 // We need to reparse the XML we saved off into a new DOM.
249 MemBufInputSource src(reinterpret_cast<const XMLByte*>(m_xml.c_str()),m_xml.length(),"XMLSecSignatureImpl");
250 Wrapper4InputSource dsrc(&src,false);
251 log.debug("parsing Signature XML back into DOM tree");
252 DOMDocument* internalDoc=XMLToolingConfig::getConfig().getParser().parse(dsrc);
254 // The caller insists on using his own document, so we now have to import the thing
255 // into it. Then we're just dumping the one we built.
256 log.debug("reimporting new DOM into caller-supplied document");
257 cachedDOM=static_cast<DOMElement*>(document->importNode(internalDoc->getDocumentElement(), true));
258 internalDoc->release();
261 // We just bind the document we built to the object as the result.
262 cachedDOM=static_cast<DOMElement*>(internalDoc->getDocumentElement());
263 document=internalDoc;
267 // Now reload the signature from the DOM.
269 m_signature=XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->newSignatureFromDOM(
274 catch(XSECException& e) {
277 auto_ptr_char temp(e.getMsg());
278 throw MarshallingException(string("Caught an XMLSecurity exception while loading signature: ") + temp.get());
280 catch(XSECCryptoException& e) {
283 throw MarshallingException(string("Caught an XMLSecurity exception while loading signature: ") + e.getMsg());
287 // Marshall KeyInfo data.
288 if (m_keyInfo && (!m_signature->getKeyInfoList() || m_signature->getKeyInfoList()->isEmpty())) {
289 m_keyInfo->marshall(cachedDOM);
292 // Recache the DOM and clear the serialized copy.
293 setDocumentElement(document, cachedDOM);
294 log.debug("caching DOM for Signature (document is %sbound)", bindDocument ? "" : "not ");
295 setDOM(cachedDOM, bindDocument);
296 releaseParentDOM(true);
301 DOMElement* XMLSecSignatureImpl::marshall(DOMElement* parentElement, const vector<Signature*>* sigs) const
304 xmltooling::NDC ndc("marshall");
307 Category& log=Category::getInstance(XMLTOOLING_LOGCAT".Signature");
308 log.debug("marshalling ds:Signature");
310 DOMElement* cachedDOM=getDOM();
312 if (parentElement->getOwnerDocument()==cachedDOM->getOwnerDocument()) {
313 log.debug("Signature has a usable cached DOM, reusing it");
314 if (parentElement!=cachedDOM->getParentNode()) {
315 parentElement->appendChild(cachedDOM);
316 releaseParentDOM(true);
321 // We have a DOM but it doesn't match the document we were given. This both sucks and blows.
322 // Without an adoptNode option to maintain the child pointers, we have to either import the
323 // DOM while somehow reassigning all the nested references (which amounts to a complete
324 // *unmarshall* operation), or we just release the existing DOM and hope that we can get
325 // it back. This depends on all objects being able to preserve their DOM at all costs.
326 releaseChildrenDOM(true);
330 // If we get here, we didn't have a usable DOM.
332 // Fresh signature, so we just create an empty one.
333 log.debug("creating empty Signature element");
334 m_signature=XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->newSignature();
335 m_signature->setDSIGNSPrefix(XMLSIG_PREFIX);
336 cachedDOM=m_signature->createBlankSignature(
337 parentElement->getOwnerDocument(), getCanonicalizationMethod(), getSignatureAlgorithm()
341 MemBufInputSource src(reinterpret_cast<const XMLByte*>(m_xml.c_str()),m_xml.length(),"XMLSecSignatureImpl");
342 Wrapper4InputSource dsrc(&src,false);
343 log.debug("parsing XML back into DOM tree");
344 DOMDocument* internalDoc=XMLToolingConfig::getConfig().getParser().parse(dsrc);
346 log.debug("reimporting new DOM into caller-supplied document");
347 cachedDOM=static_cast<DOMElement*>(parentElement->getOwnerDocument()->importNode(internalDoc->getDocumentElement(),true));
348 internalDoc->release();
350 // Now reload the signature from the DOM.
352 m_signature=XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->newSignatureFromDOM(
353 parentElement->getOwnerDocument(), cachedDOM
357 catch(XSECException& e) {
358 auto_ptr_char temp(e.getMsg());
359 throw MarshallingException(string("Caught an XMLSecurity exception while loading signature: ") + temp.get());
361 catch(XSECCryptoException& e) {
362 throw MarshallingException(string("Caught an XMLSecurity exception while loading signature: ") + e.getMsg());
366 // Marshall KeyInfo data.
367 if (m_keyInfo && (!m_signature->getKeyInfoList() || m_signature->getKeyInfoList()->isEmpty())) {
368 m_keyInfo->marshall(cachedDOM);
371 // Recache the DOM and clear the serialized copy.
372 parentElement->appendChild(cachedDOM);
373 log.debug("caching DOM for Signature");
374 setDOM(cachedDOM, false);
375 releaseParentDOM(true);
380 XMLObject* XMLSecSignatureImpl::unmarshall(DOMElement* element, bool bindDocument)
382 Category::getInstance(XMLTOOLING_LOGCAT".Signature").debug("unmarshalling ds:Signature");
385 m_signature=XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->newSignatureFromDOM(
386 element->getOwnerDocument(), element
390 catch(XSECException& e) {
391 auto_ptr_char temp(e.getMsg());
392 throw UnmarshallingException(string("Caught an XMLSecurity exception while loading signature: ") + temp.get());
394 catch(XSECCryptoException& e) {
395 throw UnmarshallingException(string("Caught an XMLSecurity exception while loading signature: ") + e.getMsg());
398 setDOM(element, bindDocument);
402 Signature* SignatureBuilder::buildObject(
403 const XMLCh* nsURI, const XMLCh* localName, const XMLCh* prefix, const QName* schemaType
406 if (!XMLString::equals(nsURI,XMLSIG_NS) || !XMLString::equals(localName,Signature::LOCAL_NAME))
407 throw XMLObjectException("XMLSecSignatureBuilder requires standard Signature element name.");
408 return buildObject();
411 Signature* SignatureBuilder::buildObject() const
413 return new XMLSecSignatureImpl();
416 const XMLCh Signature::LOCAL_NAME[] = UNICODE_LITERAL_9(S,i,g,n,a,t,u,r,e);
418 // Raw signature methods.
420 unsigned int Signature::createRawSignature(
421 XSECCryptoKey* key, const XMLCh* sigAlgorithm, const char* in, unsigned int in_len, char* out, unsigned int out_len
425 XSECAlgorithmHandler* handler = XSECPlatformUtils::g_algorithmMapper->mapURIToHandler(sigAlgorithm);
427 auto_ptr_char alg(sigAlgorithm);
428 throw SignatureException("Unsupported signature algorithm ($1).", params(1,alg.get()));
431 // Move input into a safeBuffer to source the transform chain.
433 sb.sbStrncpyIn(in,in_len);
434 TXFMSB* sbt = new TXFMSB(NULL);
435 sbt->setInput(sb, in_len);
439 unsigned int siglen = handler->signToSafeBuffer(&tx, sigAlgorithm, key, out_len-1, sbout);
440 if (siglen >= out_len)
441 throw SignatureException("Signature size exceeded output buffer size.");
443 // Push all non-whitespace into buffer.
444 unsigned int ret_len = 0;
445 const char* source = sbout.rawCharBuffer();
447 if (isspace(*source))
457 catch(XSECException& e) {
458 auto_ptr_char temp(e.getMsg());
459 throw SignatureException(string("Caught an XMLSecurity exception while creating raw signature: ") + temp.get());
461 catch(XSECCryptoException& e) {
462 throw SignatureException(string("Caught an XMLSecurity exception while creating raw signature: ") + e.getMsg());
466 bool Signature::verifyRawSignature(
467 XSECCryptoKey* key, const XMLCh* sigAlgorithm, const char* signature, const char* in, unsigned int in_len
471 XSECAlgorithmHandler* handler = XSECPlatformUtils::g_algorithmMapper->mapURIToHandler(sigAlgorithm);
473 auto_ptr_char alg(sigAlgorithm);
474 throw SignatureException("Unsupported signature algorithm ($1).", params(1,alg.get()));
477 // Move input into a safeBuffer to source the transform chain.
479 sb.sbStrncpyIn(in,in_len);
480 TXFMSB* sbt = new TXFMSB(NULL);
481 sbt->setInput(sb, in_len);
485 return handler->verifyBase64Signature(&tx, sigAlgorithm, signature, 0, key);
487 catch(XSECException& e) {
488 auto_ptr_char temp(e.getMsg());
489 throw SignatureException(string("Caught an XMLSecurity exception while verifying raw signature: ") + temp.get());
491 catch(XSECCryptoException& e) {
492 throw SignatureException(string("Caught an XMLSecurity exception while verifying raw signature: ") + e.getMsg());