libdigidocpp
SignatureTM.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 "SignatureTM.h"
21 #include "Conf.h"
22 #include "crypto/Digest.h"
23 #include "crypto/OpenSSLHelpers.h"
25 #include "crypto/ocsp/OCSP.h"
26 #include "util/DateTime.h"
27 #include "log.h"
28 
32 const std::string digidoc::SignatureTM::MEDIA_TYPE = "signature/bdoc-1.0/TM";
33 
34 digidoc::SignatureTM::SignatureTM(unsigned int id, const BDoc &_bdoc)
35 : SignatureBES(id, _bdoc)
36 {
37 }
38 
39 digidoc::SignatureTM::SignatureTM(const std::string& path, const BDoc &_bdoc) throw(SignatureException)
40 : SignatureBES(path, _bdoc)
41 {
42 }
43 
45 {
46 }
47 
52 {
53  return MEDIA_TYPE;
54 }
55 
59 std::vector<unsigned char> digidoc::SignatureTM::getNonce() const
60 {
61  return digidoc::OCSP().getNonce(getOCSPResponseValue());
62 }
63 
68 {
69  const xades::UnsignedSignaturePropertiesType::CertificateValuesType::EncapsulatedX509CertificateSequence &certs =
70  unsignedSignatureProperties()->certificateValues()[0].encapsulatedX509Certificate();
71  xades::UnsignedSignaturePropertiesType::CertificateValuesType::EncapsulatedX509CertificateSequence::const_iterator i = certs.begin();
72 
73  try
74  {
75  for( ; i != certs.end(); ++i )
76  {
77  if( i->id() && i->id().get().find( "RESPONDER_CERT" ) != std::string::npos )
78  return X509Cert( std::vector<unsigned char>( i->data(), i->data() + i->size() ) );
79  }
80  if( certs.begin() != certs.end() )
81  return X509Cert( std::vector<unsigned char>(
82  certs.begin()->data(), certs.begin()->data() + certs.begin()->size() ) );
83  }
84  catch( const Exception & ) {}
85  return X509Cert();
86 }
87 
92 {
93  std::vector<unsigned char> respBuf = getOCSPResponseValue();
94  if(respBuf.empty())
95  return "";
96  tm datetime = OCSP().getProducedAt(respBuf);
97  xml_schema::DateTime xmldatetime = digidoc::util::date::makeDateTime(datetime);
98  return digidoc::util::date::xsd2string(xmldatetime);
99 }
100 
105 {
106  return unsignedSignatureProperties()->completeRevocationRefs()[0].oCSPRefs()
107  ->oCSPRef()[0].oCSPIdentifier().responderID().byName().get();
108 }
109 
121 {
123 
124 // 1. Check OCSP response (RevocationValues) was signed by OCSP server
125 // 2. OCSP server certificate is trusted?
126 // 3. Check that nonce field in OCSP response is same as CompleteRevocationRefs->DigestValue
127 // 4. Recalculate hash of signature and compare with nonce
128 
129 #if 0
130  Conf* conf = Conf::getInstance();
131  Conf::OCSPConf ocspConf = conf->getOCSP(getSigningCertificate().getIssuerName("CN"));
132  if(ocspConf.issuer.empty())
133  {
134  SignatureException e(__FILE__, __LINE__, "Failed to find ocsp responder.");
136  throw e;
137  }
138  OCSP ocsp(ocspConf.url);
139  STACK_OF(X509)* ocspCerts = 0;
140  try
141  {
142  ocspCerts = X509CertStore::getInstance()->findCerts( "CN", ocspConf.cert );
143  }
144  catch( const Exception &e )
145  {
146  SignatureException exception(__FILE__, __LINE__, "OCSP certificate loading failed", e);
147  exception.setCode( Exception::OCSPCertMissing );
148  throw exception;
149  }
150  X509Stack_scope x509StackScope(&ocspCerts);
151  ocsp.setOCSPCerts(ocspCerts);
152 #else
153  OCSP ocsp;
154  ocsp.setCertStore(digidoc::X509CertStore::getInstance()->getCertStore());
156 #endif
157  std::vector<unsigned char> respBuf = getOCSPResponseValue();
158  if(respBuf.empty())
159  THROW_SIGNATUREEXCEPTION("OCSP response is empty");
160  try
161  {
162  ocsp.verifyResponse(respBuf);
163  }
164  catch( const Exception &e )
165  {
166  THROW_SIGNATUREEXCEPTION_CAUSE( e, "OCSP response verfiy failed" );
167  }
168  DEBUG("OCSP response was signed by trusted OCSP responder");
169 
170  xades::UnsignedPropertiesType::UnsignedSignaturePropertiesOptional &usp = unsignedSignatureProperties();
171  if(!usp.present())
172  THROW_SIGNATUREEXCEPTION( "Failed to verify OCSP response producedAt" );
173  xades::UnsignedSignaturePropertiesType::CompleteRevocationRefsSequence &crrs = usp->completeRevocationRefs();
174  if(crrs.empty())
175  THROW_SIGNATUREEXCEPTION( "Failed to verify OCSP response producedAt" );
176  xades::CompleteRevocationRefsType::OCSPRefsOptional &orr = crrs[0].oCSPRefs();
177  if(!orr.present())
178  THROW_SIGNATUREEXCEPTION( "Failed to verify OCSP response producedAt" );
179  xades::OCSPRefsType::OCSPRefSequence &ors = orr->oCSPRef();
180  if(ors.empty())
181  THROW_SIGNATUREEXCEPTION( "Failed to verify OCSP response producedAt" );
182  xades::OCSPRefType::OCSPIdentifierType &oit = ors[0].oCSPIdentifier();
183  if(oit.producedAt() != digidoc::util::date::makeDateTime(OCSP().getProducedAt(respBuf)))
184  THROW_SIGNATUREEXCEPTION( "ProducedAt does not match with OCSP response" );
185 
186  std::vector<unsigned char> revocationOCSPRefValue(0);
187  std::string method;
188  getRevocationOCSPRef(revocationOCSPRefValue, method);
189 
190  std::auto_ptr<Digest> calc(new Digest(method));
191  calc->update(getSignatureValue());
192  std::vector<unsigned char> nonce = calc->getDigest();
193 
194  std::vector<unsigned char> respNonce = ocsp.getNonce(respBuf);
195  if(nonce != respNonce)
196  {
197  DEBUGMEM("Calculated signature HASH", &nonce[0], nonce.size());
198  DEBUGMEM("Response nonce", &respNonce[0], respNonce.size());
199  THROW_SIGNATUREEXCEPTION("Calculated signature hash doesn't match to OCSP responder nonce field");
200  }
201 
202  std::auto_ptr<Digest> ocspResponseCalc(new Digest(method));
203  DEBUG("Calculating digest on %d bytes", respBuf.size());
204  ocspResponseCalc->update(respBuf);
205  std::vector<unsigned char> ocspResponseHash = ocspResponseCalc->getDigest();
206 
207  if(ocspResponseHash != revocationOCSPRefValue)
208  {
209  DEBUGMEM("Document ocspResponse HASH:", &revocationOCSPRefValue[0], revocationOCSPRefValue.size());
210  DEBUGMEM("Calculated ocspResponse HASH:", &ocspResponseHash[0], ocspResponseHash.size());
211  THROW_SIGNATUREEXCEPTION("OCSPRef value doesn't match with hash of OCSP response");
212  }
213  else
214  {
215  DEBUG("TM signature valid");
216  }
217 }
218 
225 {
226  DEBUG("SignatureTM::sign()");
227  // Sign with BES profile.
228  SignatureBES::sign(signer);
229  DEBUG("BES signature successful.");
230  notarize();
231 }
232 
238 {
239  // Calculate NONCE value.
240  std::auto_ptr<Digest> calc(new Digest());
241  calc->update(getSignatureValue());
242  std::vector<unsigned char> nonce = calc->getDigest();
243  DEBUGMEM("Calculated signature HASH (nonce):", &nonce[0], nonce.size());
244 
245  // Get issuer certificate from certificate store.
246  X509Cert cert = getSigningCertificate();
247  X509* issuer = X509CertStore::getInstance()->getCert(cert.getIssuerNameAsn1()); X509_scope issuerScope(&issuer);
248  if(issuer == NULL)
249  {
250  THROW_SIGNATUREEXCEPTION("Could not find certificate issuer '%s' in certificate store.",
251  cert.getIssuerName().c_str());
252  }
253 
254  DEBUG("Signing with X.509 cert {serial=%s, subject=%s, issuer=%s})",
255  cert.getSerial().c_str(), cert.getSubjectName().c_str(), cert.getIssuerName().c_str());
256 
257  // Initialize OCSP.
258  DEBUG("Making OCSP request.");
259  Conf* conf = Conf::getInstance();
260  Conf::OCSPConf ocspConf = conf->getOCSP(cert.getIssuerName("CN"));
261  if(ocspConf.issuer.empty())
262  {
263  SignatureException e(__FILE__, __LINE__, "Failed to find ocsp responder.");
265  throw e;
266  }
267 
268  STACK_OF(X509)* ocspCerts = 0;
269  try
270  {
271  ocspCerts = X509CertStore::getInstance()->findCerts( "CN", ocspConf.cert );
272  }
273  catch(const IOException& e)
274  {
275  THROW_SIGNATUREEXCEPTION_CAUSE(e, "Failed to load OCSP certificate");
276  }
277  X509Stack_scope ocspCertsScope(&ocspCerts);
278 
280  std::vector<unsigned char> ocspResponse;
281  struct tm producedAt;
282  try
283  {
284  OCSP ocsp;
285  ocsp.setOCSPCerts(ocspCerts);
286  ocsp.setUrl(ocspConf.url);
287  ocsp.setMaxAge(2*60); // FIXME: remove or move to conf
288  ocsp.setSkew(15*60); // FIXME: remove or move to conf
289  X509 *c = cert.getX509();
290  status = ocsp.checkCert(c, issuer, nonce, ocspResponse, producedAt);
291  X509_free(c);
292  }
293  catch(const IOException& e)
294  {
295  THROW_SIGNATUREEXCEPTION_CAUSE(e, "Failed to get OCSP response");
296  }
297  catch(const OCSPException& e)
298  {
299  THROW_SIGNATUREEXCEPTION_CAUSE(e, "Failed to get OCSP response");
300  }
301 
302  switch(status)
303  {
304  case digidoc::OCSP::GOOD: DEBUG("OCSP status: GOOD"); break;
306  {
307  DEBUG("OCSP status: REVOKED");
308  SignatureException e( __FILE__, __LINE__, "Certificate status: revoked" );
310  throw e;
311  break;
312  }
314  {
315  DEBUG("OCSP status: UNKNOWN");
316  SignatureException e( __FILE__, __LINE__, "Certificate status: unknown" );
318  throw e;
319  break;
320  }
321  }
322  DEBUG("OCSP response size %d", ocspResponse.size());
323 
324  // FIXME: get from ocsp instead
325  // FIXME: This file can contain multiple certs. X509Cert class supports only one cert per file
326  // loadX509Stack loads multiple certs from one file
327  // X509* ocspCert = X509Cert::loadX509(Conf::getInstance()->getOCSPCertPath()); X509_scope ocspCertScope(&ocspCert);
328  if(sk_X509_num(ocspCerts) > 1)
329  {
330  ERR("More than one OCSP cert in file.");
331  }
332  X509Cert ocspCert_(sk_X509_value(ocspCerts, 0));
333 
334  std::auto_ptr<Digest> ocspResponseCalc(new Digest());
335  ocspResponseCalc->update(ocspResponse);
336  std::vector<unsigned char> ocspResponseHash = ocspResponseCalc->getDigest();
337  DEBUGMEM("Calculated ocspResponse HASH:", &ocspResponseHash[0], ocspResponseHash.size());
338 
339  // Set TM profile signature parameters.
340  createTMProperties();
341  setOCSPCertificate(ocspCert_);
342  setCACertificate(X509Cert(issuer));
343  setOCSPResponseValue(ocspResponse);
344  setCompleteRevocationRefs(ocspCert_.getIssuerName(), calc->getUri(), ocspResponseHash, producedAt);
345  validateOffline();
346 }
347 
352 {
353  xades::CertificateValuesType certValue;
354 
355  xades::UnsignedSignaturePropertiesType usSignatureProp;
356  usSignatureProp.certificateValues().push_back(certValue);
357 
358  xades::UnsignedPropertiesType usProp;
359  usProp.unsignedSignatureProperties(usSignatureProp);
360 
361  signature->object()[0].qualifyingProperties()[0].unsignedProperties(usProp);
362 }
363 
364 
372 void digidoc::SignatureTM::setCompleteRevocationRefs(const std::string& responderName, const std::string& digestMethodUri,
373  const std::vector<unsigned char>& ocspResponseHash, const struct tm& producedAt )
374 {
375  dsig::DigestMethodType method(xml_schema::Uri(digestMethodUri.c_str()));
376  dsig::DigestValueType value(xml_schema::Base64Binary(&ocspResponseHash[0], ocspResponseHash.size()));
377 
378  xades::DigestAlgAndValueType digest(method, value);
379 
380  xades::ResponderIDType responderId;
381  responderId.byName(responderName);
382 
383  xml_schema::DateTime dateTime(util::date::makeDateTime(producedAt));
384  xades::OCSPIdentifierType ocspId(responderId, dateTime);
385  std::string id = getId();
386  id.replace(0, 1, "N");
387  ocspId.uRI(xml_schema::Uri("#" + id));
388 
389  xades::OCSPRefType ocspRef(ocspId);
390  ocspRef.digestAlgAndValue(digest);
391 
392  xades::OCSPRefsType ocspRefs;
393  ocspRefs.oCSPRef().push_back(ocspRef);
394 
395  xades::CompleteRevocationRefsType completeRevocationRefs;
396  completeRevocationRefs.oCSPRefs(ocspRefs);
397  completeRevocationRefs.id(xml_schema::Id(getId() + "-REVOCREFS"));
398 
399  unsignedSignatureProperties()->completeRevocationRefs().push_back(completeRevocationRefs);
400 }
401 
407 {
408  //XXX: copied from digidoc::Signature::setSigningCertificate
409 
410  DEBUG("digidoc::SignatureTM::setOCSPCertificate()");
411 
412  std::vector<unsigned char> derEncodedX509 = x509.encodeDER();
413 
414  // Calculate SHA digest of the certificate.
415  std::auto_ptr<Digest> calc(new Digest());
416  calc->update(derEncodedX509);
417  dsig::DigestMethodType digestMethod(xml_schema::Uri(calc->getUri()));
418  dsig::DigestValueType digestValue(xml_schema::Base64Binary(&calc->getDigest()[0], calc->getSize()));
419  xades::DigestAlgAndValueType certDigest(digestMethod, digestValue);
420 
421  // Add certificate issuer info.
422  dsig::X509IssuerSerialType issuerSerial(x509.getIssuerName(), x509.getSerial());
423 
424  digidoc::xades::CertIDType cert(certDigest, issuerSerial);
425  xades::CertIDListType certList;
426  certList.cert().push_back(cert);
427 
428  xades::CompleteCertificateRefsType certificateRefs(certList);
429 
430  //certificateRefs.certRefs(certList);
431 
432  unsignedSignatureProperties()->completeCertificateRefs().push_back(certificateRefs);
433 
434  //CertificateValues
435  addCertificateValue(getId() + "-RESPONDER_CERT", x509);
436 }
437 
439 {
440  addCertificateValue(getId() + "-CA-CERT", x509);
441 }
442 
448 void digidoc::SignatureTM::addCertificateValue(const std::string& certId, const X509Cert& x509)
449 {
450  DEBUG("digidoc::SignatureTM::setCertificateValue(%s, X509Cert{%s,%s})",
451  certId.c_str(), x509.getSerial().c_str(), x509.getSubjectName().c_str());
452  //CertificateValues
453  std::vector<unsigned char> certBytes = x509.encodeDER();
454  xades::CertificateValuesType::EncapsulatedX509CertificateType certData(
455  xml_schema::Base64Binary(&certBytes[0], certBytes.size()));
456  certData.id(xml_schema::Id(certId.c_str()));
457  unsignedSignatureProperties()->certificateValues()[0].encapsulatedX509Certificate().push_back(certData);
458 }
459 
464 void digidoc::SignatureTM::setOCSPResponseValue(const std::vector<unsigned char>& data)
465 {
466  std::string id = getId();
467  id.replace(0, 1, "N");
468  xades::OCSPValuesType::EncapsulatedOCSPValueType ocspValueData(
469  xml_schema::Base64Binary(&data[0], data.size()));
470  ocspValueData.id(id);
471 
472  xades::OCSPValuesType ocspValue;
473  ocspValue.encapsulatedOCSPValue().push_back(ocspValueData);
474 
475  xades::RevocationValuesType revocationValues;
476  revocationValues.oCSPValues(ocspValue);
477 
478  unsignedSignatureProperties()->revocationValues().push_back(revocationValues);
479 }
485 std::vector<unsigned char> digidoc::SignatureTM::getOCSPResponseValue() const
486 {
487  try
488  {
489  //FIXME: check that all elements exist
490  xades::RevocationValuesType &t = unsignedSignatureProperties()->revocationValues()[0];
491  xades::OCSPValuesType &tt = t.oCSPValues().get();
492  xades::OCSPValuesType::EncapsulatedOCSPValueType &resp = tt.encapsulatedOCSPValue()[0];
493  return std::vector<unsigned char>(resp.data(), resp.data()+resp.size());
494  }
495  catch(const Exception &)
496  {}
497  return std::vector<unsigned char>();
498 }
499 
507 void digidoc::SignatureTM::getRevocationOCSPRef(std::vector<unsigned char>& data, std::string& digestMethodUri)
508  const throw(SignatureException)
509 {
510  xades::UnsignedSignaturePropertiesType::CompleteRevocationRefsSequence &crrSeq =
511  unsignedSignatureProperties()->completeRevocationRefs();
512 
513  if(!crrSeq.empty())
514  {
515  xades::CompleteRevocationRefsType::OCSPRefsOptional &ocspRefsOpt = crrSeq[0].oCSPRefs();
516  if(ocspRefsOpt.present())
517  {
518  xades::OCSPRefsType::OCSPRefSequence &ocspRefSeq = ocspRefsOpt->oCSPRef();
519  if(!ocspRefSeq.empty())
520  {
521  xades::OCSPRefType::DigestAlgAndValueOptional &digestOpt = ocspRefSeq[0].digestAlgAndValue();
522  if(digestOpt.present())
523  {
524  dsig::DigestValueType &digestValue = digestOpt->digestValue();
525  data.resize(digestValue.size());
526  std::copy(digestValue.data(), digestValue.data()+digestValue.size(), data.begin());
527 
528  xml_schema::Uri &uri = digestOpt->digestMethod().algorithm();
529  digestMethodUri = uri;
530 
531  return;
532  }
533  }
534  }
535  }
536 
537 
538  //XXX: improve exception message
539  THROW_SIGNATUREEXCEPTION("Missing UnsignedProperties/UnsignedSignatureProperties/CompleteRevocationRefs/OCSPRefs/OCSPRef/DigestAlgAndValue element or its parent");
540 }
541 
542 digidoc::xades::UnsignedPropertiesType::UnsignedSignaturePropertiesOptional&
544 {
545  // Object
546  dsig::SignatureType::ObjectSequence &os = signature->object();
547  if ( os.empty() )
548  THROW_SIGNATUREEXCEPTION("Signature block 'Object' is missing.");
549  else if ( os.size() != 1 )
550  THROW_SIGNATUREEXCEPTION("Signature block contains more than one 'Object' block.");
551 
552  // QualifyingProperties
553  dsig::ObjectType::QualifyingPropertiesSequence &qpSeq = os[0].qualifyingProperties();
554  if ( qpSeq.empty() )
555  THROW_SIGNATUREEXCEPTION("Signature block 'QualifyingProperties' is missing.");
556  else if ( qpSeq.size() != 1 )
557  THROW_SIGNATUREEXCEPTION("Signature block 'Object' contains more than one 'QualifyingProperties' block.");
558 
559  // UnsignedProperties
560  xades::QualifyingPropertiesType::UnsignedPropertiesOptional &unsignedPropsOptional = qpSeq[0].unsignedProperties();
561  if ( !unsignedPropsOptional.present() )
562  THROW_SIGNATUREEXCEPTION("QualifyingProperties block 'UnsignedProperties' is missing.");
563 
564  // UnsignedSignatureProperties
565  digidoc::xades::UnsignedPropertiesType::UnsignedSignaturePropertiesOptional &unsignedSigProps =
566  unsignedPropsOptional->unsignedSignatureProperties();
567  if ( !unsignedSigProps.present() )
568  THROW_SIGNATUREEXCEPTION("QualifyingProperties block 'UnsignedSignatureProperties' is missing.");
569 
570  return unsignedSigProps;
571 }