libdigidocpp
Signature.cpp
Go to the documentation of this file.
1 /*
2  * libdigidocpp
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17  *
18  */
19 
20 #include "Signature.h"
21 
22 #include "log.h"
23 #include "Conf.h"
24 #include "crypto/cert/X509Cert.h"
25 #include "crypto/Digest.h"
26 #include "util/File.h"
27 #include "util/DateTime.h"
28 
29 #include <fstream>
30 #include <iostream>
31 
32 #include <xercesc/dom/DOM.hpp>
33 #include <xercesc/parsers/XercesDOMParser.hpp>
34 
35 #include <xsec/canon/XSECC14n20010315.hpp>
36 #include <xsec/dsig/DSIGConstants.hpp>
37 
38 const std::string digidoc::Signature::XADES_NAMESPACE = "http://uri.etsi.org/01903/v1.3.2#";
39 
40 using namespace digidoc;
41 
46  : signature(0)
47 {
48  std::string nr = Log::format("S%u", id);
49 
50  // Signature->SignedInfo
51 
52  // Default canonicalization method
53  // Should be either URI_ID_EXC_C14N_NOC or URI_ID_C14N11_NOC
54  // because URI_ID_C14N_NOC is not recommended when xml:id
55  // attributes are in use ....
56  // http://www.w3.org/TR/DSig-usage chapter 2.1
57  // But the people that created the BDOC-1.0 standard do not
58  // seem to give a damn about W3C recommendations ...
59  // http://www.signature.lt/-TOOLS/BDoc-1.0.pdf chapter 5.2
60  dsig::SignedInfoType signedInfo(xml_schema::Uri(URI_ID_C14N_NOC), xml_schema::Uri(URI_ID_RSA_SHA1));
61 
62  // Signature->SignatureValue
63  digidoc::dsig::SignatureValueType signatureValue;
64  signatureValue.id(nr + "-SIG");
65 
66  // Signature (root)
67  signature = new dsig::SignatureType(signedInfo, signatureValue);
68  signature->id(nr);
69 
70  // Signature->Object->QualifyingProperties->SignedProperties->SignedSignatureProperties
71  xades::SignedSignaturePropertiesType signedSignatureProperties;
72 
73  // Signature->Object->QualifyingProperties->SignedProperties
74  xades::SignedPropertiesType signedProperties(signedSignatureProperties);
75  signedProperties.id(nr + "-SignedProperties");
76 
77  // Signature->Object->QualifyingProperties
78  xades::QualifyingPropertiesType qualifyingProperties("#" + nr);
79  qualifyingProperties.signedProperties(signedProperties);
80 
81  // Signature->Object
82  dsig::ObjectType object;
83  object.qualifyingProperties().push_back(qualifyingProperties);
84 
85  signature->object().push_back(object);
86 }
87 
95 digidoc::Signature::Signature(const std::string& path) throw(SignatureException)
96  : signature(0)
97  , path(path)
98 {
99  try
100  {
101  // Validate against XML DSig and XAdES schemas.
102  xml_schema::Properties properties;
103  properties.schema_location(XADES_NAMESPACE, Conf::getInstance()->getXadesXsdPath());
104  properties.schema_location(URI_ID_DSIG, Conf::getInstance()->getDsigXsdPath());
105  signature = dsig::signature(path, xml_schema::Flags::dont_initialize, properties).release();
106  }
107  catch(const xml_schema::Parsing& e)
108  {
109  THROW_SIGNATUREEXCEPTION("Failed to parse signature XML: %s", e.what());
110  }
111  catch(const xsd::cxx::exception& e)
112  {
113  THROW_SIGNATUREEXCEPTION("Failed to parse signature XML: %s", e.what());
114  }
115 }
116 
121 {
122  delete signature;
123 }
124 
136 void digidoc::Signature::addReference(const std::string& uri, const std::string& digestUri,
137  const std::vector<unsigned char> &digestValue, const std::string& type) throw(SignatureException)
138 {
139  dsig::DigestMethodType method(xml_schema::Uri(digestUri.c_str()));
140  dsig::DigestValueType value(xml_schema::Base64Binary(&digestValue[0], digestValue.size()));
141  digidoc::dsig::ReferenceType reference(method, value);
142  reference.uRI(xml_schema::Uri(uri));
143  if(!type.empty())
144  reference.type(type);
145  signature->signedInfo().reference().push_back(reference);
146 }
147 
156 {
157  DEBUG("digidoc::Signature::setSigningCertificate()");
158 
159  // Add the provided certificate to XML tree.
160  dsig::KeyInfoType keyInfo;
161 
162  // Signature->KeyInfo->KeyValue->RSAKeyValue
163  std::vector<unsigned char> rsaModulus = x509.getRsaModulus();
164  dsig::RSAKeyValueType::ModulusType modulus(xml_schema::Base64Binary(&rsaModulus[0], rsaModulus.size()));
165  std::vector<unsigned char> rsaExponent = x509.getRsaExponent();
166  dsig::RSAKeyValueType::ExponentType exponent(xml_schema::Base64Binary(&rsaExponent[0], rsaExponent.size()));
167 
168  dsig::KeyValueType keyValue;
169  keyValue.rSAKeyValue(dsig::RSAKeyValueType(modulus, exponent));
170  keyInfo.keyValue().push_back(keyValue);
171 
172  // Signature->KeyInfo->X509Data->X509Certificate
173  // BASE64 encoding of a DER-encoded X.509 certificate = PEM encoded.
174  std::vector<unsigned char> derEncodedX509 = x509.encodeDER();
175  dsig::X509DataType x509Data;
176  x509Data.x509Certificate().push_back(xml_schema::Base64Binary(&derEncodedX509[0], derEncodedX509.size()));
177  keyInfo.x509Data().push_back(x509Data);
178 
179  signature->keyInfo(keyInfo);
180 
181 
182  // Signature->Object->QualifyingProperties->SignedProperties->SignedSignatureProperties->SigningCertificate
183  // Calculate digest of the X.509 certificate.
184  std::auto_ptr<Digest> digest(new Digest());
185  digest->update(derEncodedX509);
186  dsig::DigestMethodType digestMethod(xml_schema::Uri(digest->getUri()));
187  dsig::DigestValueType digestValue(xml_schema::Base64Binary(&digest->getDigest()[0], digest->getSize()));
188  xades::DigestAlgAndValueType certDigest(digestMethod, digestValue);
189 
190  // Add certificate issuer info.
191  dsig::X509IssuerSerialType issuerSerial(x509.getIssuerName(), x509.getSerial());
192  xades::SignedSignaturePropertiesType::SigningCertificateType::CertType _cert(certDigest, issuerSerial);
193 
194  xades::CertIDListType signingCertificate;
195  signingCertificate.cert().push_back(_cert);
196 
197  signature->object()[0].qualifyingProperties()[0].signedProperties()
198  ->signedSignatureProperties().signingCertificate(signingCertificate);
199 }
200 
207 {
208  DEBUG("setSignatureProductionPlace(spp={city='%s',stateOrProvince='%s',postalCode='%s',countryName='%s'})", spp.city.c_str(), spp.countryName.c_str(), spp.postalCode.c_str(), spp.stateOrProvince.c_str());
209 
210  xades::SignatureProductionPlaceType signatureProductionPlace;
211  signatureProductionPlace.city(spp.city);
212  signatureProductionPlace.stateOrProvince(spp.stateOrProvince);
213  signatureProductionPlace.postalCode(spp.postalCode);
214  signatureProductionPlace.countryName(spp.countryName);
215 
216  signature->object()[0].qualifyingProperties()[0].signedProperties()
217  ->signedSignatureProperties().signatureProductionPlace(signatureProductionPlace);
218 }
219 
227 {
228  //DEBUG("setSignerRole(signerRole={claimedRoles={'%s'}})", roles.claimedRoles[0].c_str())
229 
230  xades::ClaimedRolesListType claimedRoles;
231  for(std::vector<std::string>::const_iterator iter = roles.claimedRoles.begin(); iter != roles.claimedRoles.end(); iter++)
232  {
233  claimedRoles.claimedRole().push_back(*iter);
234  }
235 
236  xades::SignerRoleType signerRole;
237  if(!claimedRoles.claimedRole().empty())
238  {
239  signerRole.claimedRoles(claimedRoles);
240  signature->object()[0].qualifyingProperties()[0].signedProperties()
241  ->signedSignatureProperties().signerRole(signerRole);
242  }
243 }
244 
250 void digidoc::Signature::setSigningTime(const xml_schema::DateTime& signingTime)
251 {
252  signature->object()[0].qualifyingProperties()[0].signedProperties()
253  ->signedSignatureProperties().signingTime(signingTime);
254 }
255 
262 {
263  dsig::SignatureValueType signatureValue(xml_schema::Base64Binary(sigValue.signature, sigValue.length));
264 
265  // Make copy of current signature value id.
266  std::string id(signature->signatureValue().id()->c_str());
267 
268  // Set new signature value.
269  signature->signatureValue(signatureValue);
270 
271  // Set signature value id back to its old value.
272  signature->signatureValue().id(id);
273 }
274 
278 std::vector<unsigned char> digidoc::Signature::getSignatureValue() const
279 {
280  dsig::SignatureType::SignatureValueType signatureValueType = signature->signatureValue();
281 
282  std::vector<unsigned char> signatureValue(signatureValueType.size(), 0);
283  memcpy(&signatureValue[0], signatureValueType.data(), signatureValueType.size());
284 
285  return signatureValue;
286 }
287 
297 std::vector<unsigned char> digidoc::Signature::calcDigestOnNode(Digest* calc, const std::string& ns,
298  const std::string& tagName) const throw(SignatureException)
299 {
300  try
301  {
302  // Parse Xerces DOM from file, to preserve the white spaces "as is"
303  // and get the same digest value on XML node.
304  // Canonical XML 1.0 specification (http://www.w3.org/TR/2001/REC-xml-c14n-20010315)
305  // needs all the white spaces from XML file "as is", otherwise the digests won't match.
306  // Therefore we have to use Xerces to parse the XML file each time a digest needs to be
307  // calculated on a XML node. If you are parsing XML files with a parser that doesn't
308  // preserve the white spaces you are DOOMED!
309 
310  // Initialize Xerces parser.
311  std::auto_ptr<xercesc::XercesDOMParser> parser(new xercesc::XercesDOMParser());
312  parser->setValidationScheme(xercesc::XercesDOMParser::Val_Always);
313  parser->setDoNamespaces(true);
314  //xercesc::ErrorHandler* errorHandler = /*(xercesc::ErrorHandler*)*/ new xercesc::HandlerBase();
315  //parser->setErrorHandler(errorHandler);
316 
317  // Parse and return a copy of the Xerces DOM tree.
318  // Save to file an parse it again, to make XML Canonicalization work
319  // correctly as expected by the Canonical XML 1.0 specification.
320  // Hope, the next Canonical XMl specification fixes the white spaces preserving "bug".
321  if(path.empty())
322  {
323  std::string tmp = util::File::tempFileName();
324  saveToXml(tmp);
325  parser->parse(tmp.c_str());
326  }
327  else
328  parser->parse(path.c_str());
329  xercesc::DOMNode* domnode = parser->getDocument()->cloneNode(true);
330  std::auto_ptr<xercesc::DOMDocument> dom(static_cast<xercesc::DOMDocument*>(domnode));
331 
332  // Select node, on which the digest is calculated.
333  xercesc::ArrayJanitor<XMLCh> tagNs(xercesc::XMLString::transcode(ns.c_str()));
334  xercesc::ArrayJanitor<XMLCh> tag(xercesc::XMLString::transcode(tagName.c_str()));
335  xercesc::DOMNodeList* nodeList = dom->getElementsByTagNameNS(tagNs.get(), tag.get());
336 
337  // Make sure that exactly one node was found.
338  if((nodeList == NULL) || (nodeList->getLength() < 1))
339  {
340  THROW_SIGNATUREEXCEPTION("Could not find '%s' node which is in '%s' namespace in signature XML.", tagName.c_str(), ns.c_str());
341  }
342 
343  if(nodeList->getLength() > 1)
344  {
345  THROW_SIGNATUREEXCEPTION("Found %d '%s' nodes which are in '%s' namespace in signature XML, can not calculate digest on XML node.",
346  nodeList->getLength(), tagName.c_str(), ns.c_str());
347  }
348 
349  // Canocalize XML using one of the three methods supported by XML-DSIG
350  XSECC14n20010315 canonicalizer(dom.get(), nodeList->item(0));
351  canonicalizer.setCommentsProcessing(false);
352  canonicalizer.setUseNamespaceStack(true);
353 
354  // Find the method identifier
355  dsig::SignedInfoType& signedInfo = signature->signedInfo();
356  dsig::CanonicalizationMethodType& canonMethod = signedInfo.canonicalizationMethod();
357  dsig::CanonicalizationMethodType::AlgorithmType& algorithmType = canonMethod.algorithm();
358 
359  DEBUG("C14N algorithmType = '%s'", algorithmType.c_str());
360 
361  // Set processing flags according to algorithm type.
362  if(algorithmType == URI_ID_C14N_NOC) {
363  // Default behaviour, nothing needs to be changed
364  } else if(algorithmType == URI_ID_C14N_COM) {
365  canonicalizer.setCommentsProcessing(true);
366  } else if(algorithmType == URI_ID_EXC_C14N_NOC) {
367  // Exclusive mode needs to include xml-dsig in root element
368  // in order to maintain compatibility with existing implementations
369  canonicalizer.setExclusive((char*)"ds");
370 #ifdef URI_ID_C14N11_NOC
371  } else if(algorithmType == URI_ID_C14N11_NOC) {
372  canonicalizer.setInclusive11();
373  } else if(algorithmType == URI_ID_C14N11_COM) {
374  canonicalizer.setInclusive11();
375  canonicalizer.setCommentsProcessing(true);
376 #endif
377  } else {
378  // Unknown algorithm.
379  THROW_SIGNATUREEXCEPTION("Unsupported SignedInfo canonicalization method '%s'", algorithmType.c_str());
380  }
381 
382  std::vector<unsigned char> c14n;
383  unsigned char buffer[1024];
384  xsecsize_t bytes = 0;
385  while((bytes = canonicalizer.outputBuffer(buffer, 1024)) > 0)
386  {
387  calc->update(buffer, (unsigned int)bytes);
388  c14n.insert(c14n.end(), buffer[0], size_t(bytes));
389  }
390  DEBUG("c14n = '%s'", std::string(reinterpret_cast<char*>(&c14n[0]), 0, c14n.size()).c_str());
391 
392  return calc->getDigest();
393  }
394  catch(const IOException& e)
395  {
396  THROW_SIGNATUREEXCEPTION_CAUSE(e, "Failed to create Xerces DOM from signature XML.");
397  }
398  catch( const xercesc::XMLException& e )
399  {
400  xercesc::ArrayJanitor<char> msg(xercesc::XMLString::transcode(e.getMessage()));
401  THROW_SIGNATUREEXCEPTION( "Failed to parse signature XML: %s", msg.get() );
402  }
403  catch( const xercesc::DOMException& e )
404  {
405  xercesc::ArrayJanitor<char> msg(xercesc::XMLString::transcode(e.getMessage()));
406  THROW_SIGNATUREEXCEPTION( "Failed to parse signature XML: %s", msg.get() );
407  }
408  catch(...)
409  {
410  THROW_SIGNATUREEXCEPTION("Failed to parse signature XML.");
411  }
412  return std::vector<unsigned char>();
413 }
414 
422 {
423  if(!path.empty())
424  return path;
425 
426  // Serialize XML to file.
427  path = util::File::tempFileName();
428  saveToXml(path);
429  return path;
430 }
431 
438 void digidoc::Signature::saveToXml(const std::string &path) const throw(IOException)
439 {
440  // Serialize XML to file.
441  DEBUG("Serializing XML to '%s'", path.c_str());
442 
443  std::ofstream ofs(digidoc::util::File::encodeName(path).c_str());
444  try
445  {
446  xml_schema::NamespaceInfomap map;
447  map["ds"].name = URI_ID_DSIG;
448  map["xades"].name = XADES_NAMESPACE;
449  dsig::signature(ofs, *signature, map);
450  }
451  catch ( xsd::cxx::xml::invalid_utf8_string )
452  {
453  THROW_IOEXCEPTION( "Failed to create signature XML file. Parameters must be in UTF-8." );
454  }
455  if(ofs.fail())
456  {
457  THROW_IOEXCEPTION("Failed to create signature XML file to '%s'.", path.c_str());
458  }
459 }
460 
467 {
468  SignatureProductionPlace productionPlace;
469  const xades::SignedSignaturePropertiesType& signedSigProps = getSignedSignatureProperties();
470  const xades::SignedSignaturePropertiesType::SignatureProductionPlaceOptional& sigProdPlaceOptional =
471  signedSigProps.signatureProductionPlace();
472  if ( !sigProdPlaceOptional.present() )
473  return productionPlace;
474  const xades::SignatureProductionPlaceType& sigProdPlace = sigProdPlaceOptional.get();
475  if ( sigProdPlace.city().present() )
476  productionPlace.city = sigProdPlace.city().get();
477  if ( sigProdPlace.stateOrProvince().present() )
478  productionPlace.stateOrProvince = sigProdPlace.stateOrProvince().get();
479  if ( sigProdPlace.postalCode().present() )
480  productionPlace.postalCode = sigProdPlace.postalCode().get();
481  if ( sigProdPlace.countryName().present() )
482  productionPlace.countryName = sigProdPlace.countryName().get();
483  return productionPlace;
484 }
485 
492 {
493  SignerRole roles;
494  // SignedSignatureProperties
495  const xades::SignedSignaturePropertiesType& signedSigProps =
496  getSignedSignatureProperties();
497  // SignerRole
498  const xades::SignedSignaturePropertiesType::SignerRoleOptional& roleOpt =
499  signedSigProps.signerRole();
500  if ( !roleOpt.present() )
501  return roles;
502  const xades::SignerRoleType& signerRole = roleOpt.get();
503  // ClaimedRole
504  const xades::SignerRoleType::ClaimedRolesOptional& claimedRoleOpt = signerRole.claimedRoles();
505  if ( !claimedRoleOpt.present() )
506  return roles;
507  // ClaimedRole - list
508  const xades::ClaimedRolesListType& claimedRolesList = claimedRoleOpt.get();
509  // ClaimedRole - sequence
510  const xades::ClaimedRolesListType::ClaimedRoleSequence& claimedRolesSequence =
511  claimedRolesList.claimedRole();
512  for ( xades::ClaimedRolesListType::ClaimedRoleSequence::const_iterator itRoles =
513  claimedRolesSequence.begin(); itRoles != claimedRolesSequence.end(); itRoles++ )
514  roles.claimedRoles.push_back(*itRoles);
515  return roles;
516 }
517 
518 // FIXME: return date object not string.
519 // FIXME: wrong comments
526 {
527  const xades::SignedSignaturePropertiesType& signedSigProps = getSignedSignatureProperties();
528  const xades::SignedSignaturePropertiesType::SigningTimeOptional& sigTimeOpt =
529  signedSigProps.signingTime();
530  if ( !sigTimeOpt.present() )
531  return "";
532  const xades::SignedSignaturePropertiesType::SigningTimeType& sigTime = sigTimeOpt.get();
533  return util::date::xsd2string(sigTime);
534 }
535 
536 
544 {
545  const dsig::X509DataType::X509CertificateType& data = getSigningX509CertificateType();
546  try
547  {
548  return X509Cert(std::vector<unsigned char>(data.data(), data.data()+data.size()));
549  }
550  catch( const IOException &e )
551  {
552  THROW_SIGNATUREEXCEPTION_CAUSE( e, "Failed to read X509 certificate" );
553  }
554  return X509Cert();
555 }
556 
562 std::string digidoc::Signature::getId() const
563 {
564  return signature->id().get();
565 }
566 
573 {
574  return signature->signedInfo().signatureMethod().algorithm();
575 }
576 
582 digidoc::dsig::X509DataType::X509CertificateType& digidoc::Signature::getSigningX509CertificateType()
583 const throw(SignatureException)
584 {
585  // KeyInfo
586  dsig::SignatureType::KeyInfoOptional& keyInfoOptional = signature->keyInfo();
587  if ( !keyInfoOptional.present() )
588  THROW_SIGNATUREEXCEPTION("Signature does not contain signer certificate");
589  dsig::KeyInfoType& keyInfo = keyInfoOptional.get();
590 
591  // X509Data
592  dsig::KeyInfoType::X509DataSequence& x509DataSeq = keyInfo.x509Data();
593  if ( x509DataSeq.empty() )
594  THROW_SIGNATUREEXCEPTION("Signature does not contain signer certificate");
595  else if ( x509DataSeq.size() != 1 )
596  THROW_SIGNATUREEXCEPTION("Signature contains more than one signers certificate");
597  dsig::X509DataType& x509Data = x509DataSeq.front();
598 
599  // X509Certificate
600  dsig::X509DataType::X509CertificateSequence& x509CertSeq = x509Data.x509Certificate();
601  if ( x509CertSeq.empty() )
602  THROW_SIGNATUREEXCEPTION("Signature does not contain signer certificate");
603  else if ( x509CertSeq.size() != 1 )
604  THROW_SIGNATUREEXCEPTION("Signature contains more than one signers certificate");
605  dsig::X509DataType::X509CertificateType& certBase64Buf = x509CertSeq.front();
606 
607  return certBase64Buf;
608 }
609 
616 digidoc::xades::SignedSignaturePropertiesType&
618 {
619  // Object
620  dsig::SignatureType::ObjectSequence& os = signature->object();
621  if ( os.empty() )
622  THROW_SIGNATUREEXCEPTION("Signature block 'Object' is missing.");
623  else if ( os.size() != 1 )
624  THROW_SIGNATUREEXCEPTION("Signature block contains more than one 'Object' block.");
625  dsig::ObjectType& o = os[0];
626 
627  // QualifyingProperties
628  dsig::ObjectType::QualifyingPropertiesSequence& qpSeq = o.qualifyingProperties();
629  if ( qpSeq.empty() )
630  THROW_SIGNATUREEXCEPTION("Signature block 'QualifyingProperties' is missing.");
631  else if ( qpSeq.size() != 1 )
632  THROW_SIGNATUREEXCEPTION("Signature block 'Object' contains more than one 'QualifyingProperties' block.");
633  xades::QualifyingPropertiesType& qp = qpSeq[0];
634 
635  // SignedProperties
636  xades::QualifyingPropertiesType::SignedPropertiesOptional& signedPropsOptional =
637  qp.signedProperties();
638  if ( !signedPropsOptional.present() )
639  THROW_SIGNATUREEXCEPTION("QualifyingProperties block 'SignedProperties' is missing.");
640  xades::SignedPropertiesType& signedProps = qp.signedProperties().get();
641 
642  // SignedSignatureProperties
643  xades::SignedSignaturePropertiesType& signedSigProps =
644  signedProps.signedSignatureProperties();
645 
646  return signedSigProps;
647 }