libdigidocpp
OCSP.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 "OCSP.h"
21 
22 #include "../../ADoc.h"
23 #include "../../log.h"
24 #include "../../Conf.h"
25 #include "../OpenSSLHelpers.h"
26 
27 #include <openssl/err.h>
28 #include <openssl/pkcs12.h>
29 
30 #ifdef __APPLE__
31 #include <Security/Security.h>
32 #endif
33 
34 // Declare helper structs to automatically release OpenSSL structs after they are out of scope.
35 #if OPENSSL_VERSION_NUMBER > 0x10000000
37 #endif
39 DECLARE_OPENSSL_RELEASE_STRUCT(OCSP_RESPONSE)
40 DECLARE_OPENSSL_RELEASE_STRUCT(OCSP_BASICRESP)
42 DECLARE_OPENSSL_RELEASE_STRUCT(X509_EXTENSION)
43 
44 #ifdef FRAMEWORK
45 static int password_callback(char *buf, int bufsiz, int, void *)
46 {
47  static const char password[] = "pass";
48  int res = strlen(password);
49  if (res > bufsiz)
50  res = bufsiz;
51  memcpy(buf, password, res);
52  return res;
53 }
54 #endif
55 
60  : ssl(false)
61  , skew(5*60)
62  , maxAge(1*60)
63  , connection(NULL)
64  , ctx(NULL)
65  , ocspCerts(NULL)
66  , certStore(NULL)
67  , signCert(NULL)
68  , signKey(NULL)
69 {}
70 
75 {
76  BIO_free_all(connection);
77  SSL_CTX_free(ctx);
78  X509_free(signCert);
79  EVP_PKEY_free(signKey);
80 }
81 
88 void digidoc::OCSP::setUrl( const std::string& _url ) throw(IOException)
89 {
90  url = _url;
91  // Parse OCSP connection URL.
92  char *_host = NULL, *_port = NULL, *_path = NULL;
93  int sslFlag = 0;
94  if(!OCSP_parse_url(const_cast<char*>(url.c_str()), &_host, &_port, &_path, &sslFlag))
95  THROW_IOEXCEPTION("Incorrect OCSP URL provided: '%s'.", url.c_str());
96  ssl = sslFlag != 0;
97  connhost = _host ? _host : "";
98  connport = _port ? _port : "";
99 
100  // proxy host
102  if(!c->getProxyHost().empty())
103  {
104  connhost = c->getProxyHost();
105  connport = c->getProxyPort();
106  }
107 
108  host = connhost;
109  if(!connport.empty())
110  host += ":" + connport;
111 
112  OPENSSL_free(_host);
113  OPENSSL_free(_port);
114  OPENSSL_free(_path);
115 }
116 
123 void digidoc::OCSP::setOCSPCerts(STACK_OF(X509)* ocspCerts)
124 {
125  this->ocspCerts = ocspCerts;
126 }
127 
137 void digidoc::OCSP::setCertStore(X509_STORE* certStore)
138 {
139  this->certStore = certStore;
140 }
141 
149 void digidoc::OCSP::setSignCert(X509* signCert, EVP_PKEY* signKey)
150 {
151  this->signCert = signCert;
152  this->signKey = signKey;
153 }
154 
159 void digidoc::OCSP::setSkew(long skew)
160 {
161  this->skew = skew;
162 }
163 
168 void digidoc::OCSP::setMaxAge(long maxAge)
169 {
170  this->maxAge = maxAge;
171 }
172 
191  const std::vector<unsigned char>& nonce) throw(IOException, OCSPException)
192 {
193  OCSP_REQUEST* req = NULL; OCSP_REQUEST_scope reqScope(&req);
194  OCSP_RESPONSE* resp = NULL; OCSP_RESPONSE_scope respScope(&resp);
195 
196  // Check and return certificate status.
197  return checkCert(cert, issuer, nonce, &req, &resp);
198 }
199 
221  const std::vector<unsigned char>& nonce, std::vector<unsigned char>& ocspResponseDER,
222  tm& producedAt) throw(IOException, OCSPException)
223 {
224  OCSP_REQUEST* req = NULL; OCSP_REQUEST_scope reqScope(&req);
225  OCSP_RESPONSE* resp = NULL; OCSP_RESPONSE_scope respScope(&resp);
226 
227  // Check and return certificate status.
228  CertStatus certStatus = checkCert(cert, issuer, nonce, &req, &resp);
229 
230 
231  // Encode the OCSP response in DER encoding.
232  int bufSize = i2d_OCSP_RESPONSE(resp, NULL);
233  if(bufSize < 0)
234  {
235  THROW_IOEXCEPTION("Failed to encode OCSP response to DER.");
236  }
237 
238  ocspResponseDER.resize(bufSize);
239  unsigned char* pBuf = &ocspResponseDER[0];
240  bufSize = i2d_OCSP_RESPONSE(resp, &pBuf);
241  if(bufSize < 0)
242  {
243  THROW_IOEXCEPTION("Failed to encode OCSP response to DER.");
244  }
245 
246  // Get 'producedAt' field value from OCSP response.
247  OCSP_BASICRESP* basic = OCSP_response_get1_basic(resp); OCSP_BASICRESP_scope basicScope(&basic);
248  producedAt = convert(basic->tbsResponseData->producedAt);
249 
250  return certStatus;
251 }
252 
272  const std::vector<unsigned char>& nonce, OCSP_REQUEST** req, OCSP_RESPONSE** resp)
273  throw(IOException, OCSPException)
274 {
275  // Check that X.509 certificate is set.
276  if(!cert)
277  THROW_IOEXCEPTION("Can not check X.509 certificate, certificate is NULL pointer.");
278 
279  // Check that X.509 certificates issuer certificate is set.
280  if(!issuer)
281  THROW_IOEXCEPTION("Can not check X.509 certificate, issuer certificate is NULL pointer.");
282 
283  // Connect to OCSP server.
284  connect();
285 
286  // Sign request with PKCS#12 certificate
288  if(!c->getPKCS12Disable())
289  {
290 #ifdef FRAMEWORK
291  SecIdentityRef identity = 0;
292  OSStatus err = SecIdentityCopyPreference(CFSTR("ocsp.sk.ee"), 0, 0, &identity);
293  if(!identity)
294  THROW_IOEXCEPTION("Failed to open PKCS12");
295 
296  SecCertificateRef certref = 0;
297  SecKeyRef keyref = 0;
298  err = SecIdentityCopyCertificate(identity, &certref);
299  err = SecIdentityCopyPrivateKey(identity, &keyref);
300  CFRelease(identity);
301  if(!certref || !keyref)
302  THROW_IOEXCEPTION("Failed to read PKCS12 data");
303 
304  CFDataRef certdata = SecCertificateCopyData(certref);
305  CFRelease(certref);
306  if(!certdata)
307  THROW_IOEXCEPTION("Failed to read PKCS12 certificate");
308  const unsigned char *p = CFDataGetBytePtr(certdata);
309  X509 *cert = d2i_X509(0, &p, CFDataGetLength(certdata));
310  CFRelease(certdata);
311  if(!cert)
312  THROW_IOEXCEPTION("Failed to parse PKCS12 certificate");
313 
314  CFDataRef keydata = 0;
315  SecKeyImportExportParameters params;
316  memset( &params, 0, sizeof(params) );
317  params.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION;
318  params.passphrase = CFSTR("pass");
319  err = SecKeychainItemExport(keyref, kSecFormatWrappedPKCS8, 0, &params, &keydata);
320  CFRelease(keyref);
321  if(!keydata)
322  THROW_IOEXCEPTION("Failed to read PKCS12 key");
323  BIO *bio = BIO_new_mem_buf((void*)CFDataGetBytePtr(keydata), CFDataGetLength(keydata));
324  EVP_PKEY *key = d2i_PKCS8PrivateKey_bio(bio, 0, &password_callback, 0);
325  CFRelease(keydata);
326  BIO_free(bio);
327 
328  if(!key)
329  THROW_IOEXCEPTION("Failed to parse PKCS12 key");
330 #else
331  BIO *bio = BIO_new_file(c->getPKCS12Cert().c_str(), "rb");
332  if(!bio)
333  THROW_IOEXCEPTION("Failed to open PKCS12 certificate: %s.", c->getPKCS12Cert().c_str());
334  PKCS12 *p12 = d2i_PKCS12_bio(bio, 0);
335  BIO_free(bio);
336  if(!p12)
337  THROW_IOEXCEPTION("Failed to read PKCS12 certificate: %s.", c->getPKCS12Cert().c_str());
338 
339  X509 *cert;
340  EVP_PKEY *key;
341  int res = PKCS12_parse(p12, c->getPKCS12Pass().c_str(), &key, &cert, NULL);
342  PKCS12_free(p12);
343  if(!res)
344  THROW_IOEXCEPTION("Failed to parse PKCS12 certificate (%s).", ERR_reason_error_string(ERR_get_error()));
345  else // Hack: clear PKCS12_parse error ERROR: 185073780 - error:0B080074:x509 certificate routines:X509_check_private_key:key values mismatch
346  ERR_get_error();
347 #endif
348  setSignCert(cert, key);
349  }
350 
351  // Create OCSP request.
352  *req = createRequest(cert, issuer, nonce);
353 
354  // Send the request and get a response.
355  *resp = sendRequest(*req);
356 
357  // Validate response.
358  return validateResponse(*req, *resp, cert, issuer);
359 }
360 
367 {
368  // Release old connection if it exists.
369  BIO_free_all(connection);
370  SSL_CTX_free(ctx);
371  connection = NULL;
372  ctx = NULL;
373 
374  // Establish a connection to the OCSP responder.
375  connection = BIO_new_connect(const_cast<char*>(connhost.c_str()));
376  if(connection == NULL)
377  {
378  THROW_IOEXCEPTION("Failed to create connection with host: '%s'", connhost.c_str());
379  }
380 
381  if(!BIO_set_conn_port(connection, const_cast<char*>(connport.c_str())))
382  {
383  THROW_IOEXCEPTION("Failed to set port of the connection: %s", connport.c_str());
384  }
385 
386  if(ssl)
387  connectSSL();
388 
389  if(!BIO_do_connect(connection))
390  THROW_IOEXCEPTION("Failed to connect to host: '%s'", connhost.c_str());
391 }
392 
399 {
400 }
401 
408 {
409  ctx = SSL_CTX_new(SSLv23_client_method());
410  if(!ctx)
411  THROW_IOEXCEPTION("Failed to create connection with host: '%s'", connhost.c_str());
412  SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY);
413  BIO *sconnection = BIO_new_ssl(ctx, 1);
414  if(!sconnection)
415  THROW_IOEXCEPTION("Failed to create ssl connection with host: '%s'", connhost.c_str());
416  connection = BIO_push(sconnection, connection);
417 }
418 
428 OCSP_REQUEST* digidoc::OCSP::createRequest(X509* cert, X509* issuer, const std::vector<unsigned char>& nonce) throw(IOException)
429 {
430  // Create new OCSP request.
431  OCSP_REQUEST* req = OCSP_REQUEST_new(); OCSP_REQUEST_scope reqScope(&req);
432  if(req == NULL)
433  {
434  THROW_IOEXCEPTION("Failed to create new OCSP request, out of memory?");
435  }
436 
437  // Add certificate ID from the certificate being checked.
438  OCSP_CERTID* certId = OCSP_cert_to_id(NULL, cert, issuer);
439  if(certId == NULL)
440  {
441  THROW_IOEXCEPTION("Failed to create ID from the certificate being checked.");
442  }
443 
444  // Add certification ID to the OCSP request.
445  if(OCSP_request_add0_id(req, certId) == NULL)
446  {
447  THROW_IOEXCEPTION("Failed to add certificate ID to OCSP request.");
448  }
449 
450  // Add NONCE value to the request.
451  if(OCSP_request_add1_nonce(req, const_cast<unsigned char*>(&nonce[0]), (unsigned int)nonce.size()) == 0)
452  {
453  THROW_IOEXCEPTION("Failed to add NONCE to OCSP request.");
454  }
455 
456  // Sign the OCSP request.
457  if(signCert && signKey && !OCSP_request_sign(req, signCert, signKey, EVP_sha1(), NULL, 0))
458  {
459  THROW_IOEXCEPTION("Failed to sign OCSP request.");
460  }
461 
462  // Remove request pointer from scope, so that it will not be destroyed.
463  reqScope.p = NULL;
464 
465  return req;
466 }
467 
476 OCSP_RESPONSE* digidoc::OCSP::sendRequest(OCSP_REQUEST* req) throw(IOException)
477 {
478  OCSP_RESPONSE* resp = 0;
479 
480  std::string auth;
482  if(!c->getProxyUser().empty() || !c->getProxyPass().empty())
483  {
484  BIO *b64 = BIO_new(BIO_f_base64());
485  BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
486  BIO *hash = BIO_push(b64, BIO_new(BIO_s_mem()));
487  BIO_printf(hash, "%s:%s", c->getProxyUser().c_str(), c->getProxyPass().c_str());
488  (void)BIO_flush(hash);
489  char *base64 = 0;
490  BIO_get_mem_data(hash, &base64);
491  auth.append("Basic ");
492  auth.append(base64);
493  BIO_free_all(hash);
494  }
495 
496  std::string user_agent;
497  user_agent += "LIB libdigidocpp/";
498  user_agent += VER_STR(MAJOR_VER.MINOR_VER.RELEASE_VER.BUILD_VER);
499  user_agent += " APP " + digidoc::appInfo();
500 
501 #if OPENSSL_VERSION_NUMBER > 0x10000000
502  OCSP_REQ_CTX *rctx = OCSP_sendreq_new(connection, const_cast<char*>(url.c_str()), 0, -1);
503  OCSP_REQ_CTX_scope rctx_scope(&rctx);
504  if(!rctx)
505  THROW_IOEXCEPTION("Failed to set OCSP request headers.");
506  if(!OCSP_REQ_CTX_add1_header(rctx, "Host", const_cast<char*>(host.c_str())))
507  THROW_IOEXCEPTION("Failed to set OCSP request headers.");
508  if(!auth.empty() && !OCSP_REQ_CTX_add1_header(rctx, "Proxy-Authorization", const_cast<char*>(auth.c_str())))
509  THROW_IOEXCEPTION("Failed to set OCSP request headers.");
510  if(!OCSP_REQ_CTX_add1_header(rctx, "User-Agent", user_agent.c_str()))
511  THROW_IOEXCEPTION("Failed to set OCSP request headers.");
512  if(!OCSP_REQ_CTX_set1_req(rctx, req))
513  THROW_IOEXCEPTION("Failed to set OCSP request headers.");
514  if(!OCSP_sendreq_nbio(&resp, rctx))
515  THROW_IOEXCEPTION("Failed to send OCSP request.");
516 #else
517  // HACK to alter openssl OCSP request http header
518  std::string header;
519  header += url + " HTTP/1.0\r\n";
520  header += "Host: " + host + "\r\n";
521  if(!auth.empty())
522  header += "Proxy-Authorization: " + auth + "\r\n";
523  header += "User-Agent: " + user_agent + "\r\n";
524  header += "X-Ignore:"; // needed for disabling OCSP_sendreq_bio "HTTP/1.0" headers
525  resp = OCSP_sendreq_bio(connection, const_cast<char*>(header.c_str()), req);
526 #endif
527 
528  if(!resp)
529  THROW_IOEXCEPTION("Failed to send OCSP request.");
530  return resp;
531 }
532 
546 digidoc::OCSP::CertStatus digidoc::OCSP::validateResponse(OCSP_REQUEST* req, OCSP_RESPONSE* resp, X509* cert, X509* issuer) throw(OCSPException)
547 {
548  // Check OCSP response status code.
549  int respStatus = OCSP_response_status(resp);
550  switch(respStatus)
551  {
552  case OCSP_RESPONSE_STATUS_SUCCESSFUL: break;
553  case OCSP_RESPONSE_STATUS_UNAUTHORIZED:
554  {
555  OCSPException e( __FILE__, __LINE__, "OCSP request failed", respStatus );
557  throw e;
558  }
559  default:
560  THROW_OCSPEXCEPTION(respStatus, "OCSP request failed, response status: %s 0x%02X"
561  , OCSPException::toResponseStatusMessage(respStatus).c_str(), respStatus);
562  }
563 
564  // Verify the OCSP response.
565  OCSP_BASICRESP* basic = OCSP_response_get1_basic(resp); OCSP_BASICRESP_scope basicScope(&basic);
566  if(basic == NULL)
567  {
568  THROW_OCSPEXCEPTION(respStatus, "Incorrect OCSP response.");
569  }
570 
571  // Check NONCE field.
572  if(OCSP_check_nonce(req, basic) <= 0)
573  {
574  THROW_OCSPEXCEPTION(respStatus, "Incorrect NONCE field value.");
575  }
576 
577  // If ocspCerts or certStore exists verify OCSP responder certificates.
578  if(ocspCerts != NULL || certStore != NULL)
579  {
580  int result = -1;
581  if(ocspCerts != NULL)
582  {
583  result = OCSP_basic_verify(basic, ocspCerts, certStore, OCSP_TRUSTOTHER);
584  }
585 
586  if(result < 0 && certStore != NULL)
587  {
588  result = OCSP_basic_verify(basic, NULL, certStore, 0);
589  }
590 
591  if(result <= 0)
592  {
593  THROW_OCSPEXCEPTION(respStatus, "Failed to verify OCSP response.");
594  }
595  }
596 
597  // Check certificate status code.
598  OCSP_CERTID* certId = OCSP_cert_to_id(0, cert, issuer); OCSP_CERTID_scope certIdScope(&certId);
599  int certStatus = -1; int reason = -1;
600  ASN1_GENERALIZEDTIME *producedAt = NULL, *thisUpdate = NULL, *nextUpdate = NULL;
601  if(!OCSP_resp_find_status(basic, certId, &certStatus, &reason, &producedAt, &thisUpdate, &nextUpdate))
602  {
603  THROW_OCSPEXCEPTION(respStatus, "Failed to get status code from OCSP response.");
604  }
605 
606  // Check that response creation time is in valid time slot.
607  if(!OCSP_check_validity(thisUpdate, nextUpdate, skew, maxAge))
608  {
609  OCSPException e( __FILE__, __LINE__, "OCSP response not in valid time slot.", respStatus );
611  throw e;
612  }
613 
614  // Return certificate status.
615  switch(certStatus)
616  {
617  case V_OCSP_CERTSTATUS_GOOD: return GOOD;
618  case V_OCSP_CERTSTATUS_REVOKED: return REVOKED;
619  case V_OCSP_CERTSTATUS_UNKNOWN:
620  default: return UNKNOWN;
621  }
622 }
623 
628 void digidoc::OCSP::verifyResponse(const std::vector<unsigned char>& ocspResponseDER) const throw(IOException)
629 {
630  const unsigned char *p = &ocspResponseDER[0];
631  OCSP_RESPONSE *resp = d2i_OCSP_RESPONSE(0, &p, (unsigned int)ocspResponseDER.size()); OCSP_RESPONSE_scope respScope(&resp);
632  OCSP_BASICRESP *basic = OCSP_response_get1_basic(resp); OCSP_BASICRESP_scope basicScope(&basic);
633 
634  if(ocspCerts != NULL || certStore != NULL)
635  {
636  int result = -1;
637  if(ocspCerts != NULL)
638  {
639  result = OCSP_basic_verify(basic, ocspCerts, certStore, OCSP_TRUSTOTHER);
640  }
641 
642  if(result < 0 && certStore != NULL)
643  {
644  result = OCSP_basic_verify(basic, 0, certStore, OCSP_NOCHECKS);
645  // OCSP_NOEXPLICIT returns 0 not 1???
646  // http://www.mail-archive.com/openssl-dev@openssl.org/msg25794.html
647  // 0 flags returns root ca is not trusted
648  }
649 
650  //something went wrong, try to find reason
651  if(result <= 0)
652  {
653  unsigned long errorCode;
654  char errorMsg[250];
655  while((errorCode = ERR_get_error()) != 0)
656  {
657  ERR_error_string(errorCode, &errorMsg[0]);
658  ERR("OpenSSL ERROR:%ld %s",errorCode, errorMsg);
659  }
660 
661  THROW_IOEXCEPTION("Failed to verify OCSP response.");
662  }
663 
664  for(int i = 0; i < OCSP_resp_count(basic); ++i)
665  {
666  OCSP_SINGLERESP *resp = OCSP_resp_get0(basic, i);
667  switch(resp->certStatus->type)
668  {
669  case V_OCSP_CERTSTATUS_GOOD: break;
670  case V_OCSP_CERTSTATUS_REVOKED:
671  THROW_IOEXCEPTION("OCSP message contains REVOKED status.");
672  case V_OCSP_CERTSTATUS_UNKNOWN:
673  default:
674  THROW_IOEXCEPTION("OCSP message contains UNKNOWN status.");
675  }
676  }
677  }
678 }
679 
685 std::vector<unsigned char> digidoc::OCSP::getNonce(const std::vector<unsigned char>& ocspResponseDER) const
686 {
687  if(ocspResponseDER.empty())
688  return std::vector<unsigned char>();
689 
690  const unsigned char *p = &ocspResponseDER[0];
691  OCSP_RESPONSE *resp = d2i_OCSP_RESPONSE(0, &p, ocspResponseDER.size()); OCSP_RESPONSE_scope respScope(&resp);
692  if(!resp)
693  return std::vector<unsigned char>();
694  OCSP_BASICRESP *basic = OCSP_response_get1_basic(resp); OCSP_BASICRESP_scope basicScope(&basic);
695  if(!basic)
696  return std::vector<unsigned char>();
697 
698  int resp_idx = OCSP_BASICRESP_get_ext_by_NID(basic, NID_id_pkix_OCSP_Nonce, -1);
699  X509_EXTENSION *resp_ext = OCSP_BASICRESP_get_ext(basic, resp_idx);// X509_EXTENSION_scope x509ExtScope(&resp_ext);
700  if(!resp_ext)
701  return std::vector<unsigned char>();
702 
703  int size = ASN1_STRING_length(resp_ext->value);
704  unsigned char *d = ASN1_STRING_data(resp_ext->value);
705  std::vector<unsigned char> nonce(d, d + size);
706 
707  //OpenSSL OCSP created messages NID_id_pkix_OCSP_Nonce field is DER encoded twice, not a problem with java impl
708  //XXX: UglyHackTM check if nonceAsn1 contains ASN1_OCTET_STRING
709  //XXX: if first 2 bytes seem to be beginning of DER ASN1_OCTET_STRING then remove them
710 
711  // We assume that bdoc nonce has always octet string header
712  if(size > 2) // && nonce[0] == V_ASN1_OCTET_STRING && nonce[1] == size-2)
713  nonce.erase(nonce.begin(), nonce.begin() + 2);
714  return nonce;
715 }
716 
722 tm digidoc::OCSP::getProducedAt(const std::vector<unsigned char>& ocspResponseDER) const
723 {
724  const unsigned char *p = &ocspResponseDER[0];
725  OCSP_RESPONSE *resp = d2i_OCSP_RESPONSE(0, &p, (unsigned int)ocspResponseDER.size()); OCSP_RESPONSE_scope respScope(&resp);
726  OCSP_BASICRESP *basic = OCSP_response_get1_basic(resp); OCSP_BASICRESP_scope basicScope(&basic);
727  return convert(basic->tbsResponseData->producedAt);
728 }
729 
737 tm digidoc::OCSP::convert(ASN1_GENERALIZEDTIME* asn1Time) const throw(IOException)
738 {
739  char* t = reinterpret_cast<char*>(asn1Time->data);
740 
741  if(asn1Time->length < 12)
742  {
743  THROW_IOEXCEPTION("Date time field value shorter than 12 characters: '%s'", t);
744  }
745 
746  tm time;
747  time.tm_isdst = 0;
748 
749  // Accept only GMT time.
750  // XXX: What to do, when the time is not in GMT? The time data does not contain
751  // DST value and therefore it is not possible to convert it to GMT time.
752  if(t[asn1Time->length - 1] != 'Z')
753  {
754  THROW_IOEXCEPTION("Time value is not in GMT format: '%s'", t);
755  }
756 
757  for(int i = 0; i< asn1Time->length - 1; i++)
758  {
759  if ((t[i] > '9') || (t[i] < '0'))
760  {
761  THROW_IOEXCEPTION("Date time value in incorrect format: '%s'", t);
762  }
763  }
764 
765  // Extract year.
766  time.tm_year = ((t[0]-'0')*1000 + (t[1]-'0')*100 + (t[2]-'0')*10 + (t[3]-'0')) - 1900;
767 
768  // Extract month.
769  time.tm_mon = ((t[4]-'0')*10 + (t[5]-'0')) - 1;
770  if((time.tm_mon > 11) || (time.tm_mon < 0))
771  {
772  THROW_IOEXCEPTION("Month value incorrect: %d", (time.tm_mon + 1));
773  }
774 
775  // Extract day.
776  time.tm_mday = (t[6]-'0')*10 + (t[7]-'0');
777  if((time.tm_mday > 31) || (time.tm_mday < 1))
778  {
779  THROW_IOEXCEPTION("Day value incorrect: %d", time.tm_mday);
780  }
781 
782  // Extract hour.
783  time.tm_hour = (t[8]-'0')*10 + (t[9]-'0');
784  if((time.tm_hour > 23) || (time.tm_hour < 0))
785  {
786  THROW_IOEXCEPTION("Hour value incorrect: %d", time.tm_hour);
787  }
788 
789  // Extract minutes.
790  time.tm_min = (t[10]-'0')*10 + (t[11]-'0');
791  if((time.tm_min > 59) || (time.tm_min < 0))
792  {
793  THROW_IOEXCEPTION("Minutes value incorrect: %d", time.tm_min);
794  }
795 
796  // Extract seconds.
797  time.tm_sec = 0;
798  if(asn1Time->length >= 14 && (t[12] >= '0') && (t[12] <= '9') && (t[13] >= '0') && (t[13] <= '9'))
799  {
800  time.tm_sec = (t[12]-'0')*10 + (t[13]-'0');
801  if((time.tm_sec > 59) || (time.tm_sec < 0))
802  {
803  THROW_IOEXCEPTION("Seconds value incorrect: %d", time.tm_sec);
804  }
805  }
806 
807  return time;
808 }