libdigidocpp
X509Cert.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 "X509CertStore.h"
21 
22 #include "../../log.h"
23 #include "../../crypto/OpenSSLHelpers.h"
24 
25 #include <sstream>
26 #include <string.h>
27 
28 #include <openssl/bio.h>
29 #include <openssl/pem.h>
30 #include <openssl/err.h>
31 #include <openssl/x509v3.h>
32 
37 
45  : cert(NULL)
46 {
47  this->cert = copyX509(cert);
48 }
49 
56 digidoc::X509Cert::X509Cert(const std::vector<unsigned char> &bytes) throw(IOException)
57  : cert(NULL)
58 {
59  if(bytes.empty())
60  {
61  THROW_IOEXCEPTION("No bytes given to parse X509.");
62  }
63 
64  // Parse certificate from DER formatted buffer.
65  const unsigned char* pBytes = reinterpret_cast<const unsigned char*>(&bytes[0]);
66  d2i_X509(&cert, &pBytes, (unsigned int)bytes.size());
67  if(cert == NULL)
68  {
69  THROW_IOEXCEPTION("Failed to parse X509 certificate from bytes given: %s", ERR_reason_error_string(ERR_get_error()));
70  }
71 }
72 
80  : cert(NULL)
81 {
82  this->cert = copyX509(copy.cert);
83 }
84 
89 {
90  X509_free(cert);
91 }
92 
101 {
102  this->cert = copyX509(copy.cert);
103  return *this;
104 }
105 
112 {
113  return copyX509(cert);
114 }
115 
123 X509* digidoc::X509Cert::copyX509(X509* cert) throw(IOException)
124 {
125  if(!cert)
126  THROW_IOEXCEPTION("Failed to copy X509 certificate: %s", ERR_reason_error_string(ERR_get_error()));
127  X509* copy = X509_dup(cert);
128  if(!copy)
129  THROW_IOEXCEPTION("Failed to copy X509 certificate: %s", ERR_reason_error_string(ERR_get_error()));
130  return copy;
131 }
132 
143 X509* digidoc::X509Cert::loadX509(const std::string& path) throw(IOException)
144 {
145  // Initialize OpenSSL file.
146  BIO* file = BIO_new_file(path.c_str(), "rb");
147  if(file == NULL)
148  {
149  THROW_IOEXCEPTION("Failed to open X.509 certificate file '%s': %s",
150  path.c_str(), ERR_reason_error_string(ERR_get_error()));
151  }
152 
153  // Parse X.509 certificate from file.
154  X509* cert = PEM_read_bio_X509(file, NULL, NULL, NULL);
155  BIO_free(file);
156  if(cert == NULL)
157  {
158  THROW_IOEXCEPTION("Failed to load X.509 certificate from file '%s': %s",
159  path.c_str(), ERR_reason_error_string(ERR_get_error()));
160  }
161 
162  return cert;
163 }
164 
171 std::vector<unsigned char> digidoc::X509Cert::encodeDER() const throw(IOException)
172 {
173  // Get size of the DER encodes certificate.
174  int bufSize = i2d_X509(cert, NULL);
175  if(bufSize < 0)
176  {
177  THROW_IOEXCEPTION("Failed to encode X509 cert to DER.");
178  }
179 
180  // Allocate memory for the DER encoding.
181  std::vector<unsigned char> derEncodedX509(bufSize, 0);
182 
183  // DER encode X.509 certificate.
184  unsigned char* pBuf = &derEncodedX509[0];
185  bufSize = i2d_X509(cert, &pBuf);
186  if(bufSize < 0)
187  {
188  THROW_IOEXCEPTION("Failed to encode X509 cert to DER.");
189  }
190 
191  return derEncodedX509;
192 }
193 
199 {
200  EVP_PKEY *pubKey = getPublicKey();
201  RSA *rsa = EVP_PKEY_get1_RSA(pubKey);
202  if(!rsa)
203  {
204  EVP_PKEY_free(pubKey);
205  THROW_IOEXCEPTION("Failed to read certificate RSA key: %s", ERR_reason_error_string(ERR_get_error()));
206  }
207  int size = RSA_size(rsa);
208  RSA_free(rsa);
209  EVP_PKEY_free(pubKey);
210  return size;
211 }
212 
217 std::string digidoc::X509Cert::getSerial() const throw(IOException)
218 {
219  std::string serial;
220  BIGNUM *bn = ASN1_INTEGER_to_BN(X509_get_serialNumber(cert), 0);
221  if(bn)
222  {
223  char *str = BN_bn2dec(bn);
224  if(str)
225  serial = str;
226  OPENSSL_free(str);
227  BN_free(bn);
228  }
229 
230  if(serial.empty())
231  {
232  THROW_IOEXCEPTION("Failed to read certificate serial number from X.509 certificate: %s", ERR_reason_error_string(ERR_get_error()));
233  }
234 
235  return serial;
236 }
237 
243 {
244  return X509_get_issuer_name(cert);
245 }
246 
253 std::string digidoc::X509Cert::getIssuerName(const std::string &obj) const throw(IOException)
254 {
255  X509_NAME* issuerName = X509_get_issuer_name(cert);
256  if(!issuerName)
257  THROW_IOEXCEPTION("Failed to convert X.509 certificate issuer name: %s", ERR_reason_error_string(ERR_get_error()));
258 
259  return toString(issuerName, obj);
260 }
261 
267 std::vector<digidoc::X509Cert::KeyUsage> digidoc::X509Cert::getKeyUsage() const throw(IOException)
268 {
269  ASN1_BIT_STRING *keyusage = (ASN1_BIT_STRING*)X509_get_ext_d2i(cert, NID_key_usage, 0, 0);
270  if(!keyusage)
271  return std::vector<KeyUsage>();
272 
273  std::vector<KeyUsage> usage;
274  for(int n = 0; n < 9; ++n)
275  {
276  if(ASN1_BIT_STRING_get_bit(keyusage, n))
277  usage.push_back(KeyUsage(n));
278  }
279  ASN1_BIT_STRING_free(keyusage);
280  return usage;
281 }
282 
288 std::vector<std::string> digidoc::X509Cert::getCertificatePolicies() const throw(IOException)
289 {
290  CERTIFICATEPOLICIES *cp = (CERTIFICATEPOLICIES*)X509_get_ext_d2i(cert, NID_certificate_policies, 0, 0);
291  if(!cp)
292  return std::vector<std::string>();
293 
294  std::vector<std::string> pol;
295  for(int i = 0; i < sk_POLICYINFO_num(cp); ++i)
296  {
297  std::string buf(50, 0);
298  int len = OBJ_obj2txt(&buf[0], buf.size(), sk_POLICYINFO_value(cp, i)->policyid, 1);
299  if(len == NID_undef)
300  continue;
301  buf.resize(len);
302  pol.push_back(buf);
303  }
304  sk_POLICYINFO_pop_free(cp, POLICYINFO_free);
305  return pol;
306 }
307 
314 std::string digidoc::X509Cert::getSubjectName(const std::string &obj) const throw(IOException)
315 {
316  X509_NAME* subject = X509_get_subject_name(cert);
317  if(!subject)
318  THROW_IOEXCEPTION("Failed to convert X.509 certificate subject: %s", ERR_reason_error_string(ERR_get_error()));
319 
320  return toString(subject, obj);
321 }
322 
330 std::string digidoc::X509Cert::toString(X509_NAME* name, const std::string &obj) const throw(IOException)
331 {
332  std::string str;
333 
334  if(!obj.empty())
335  {
336  for(int i = 0; i < X509_NAME_entry_count(name); ++i)
337  {
338  X509_NAME_ENTRY *e = X509_NAME_get_entry(name, i);
339 
340  const char *o = OBJ_nid2sn(OBJ_obj2nid(X509_NAME_ENTRY_get_object(e)));
341  if(obj != o)
342  continue;
343 
344  char *data = 0;
345  int size = ASN1_STRING_to_UTF8((unsigned char**)&data, X509_NAME_ENTRY_get_data(e));
346  str = std::string(data, size);
347  OPENSSL_free(data);
348  }
349 
350  }
351  else
352  {
353  BIO* mem = BIO_new(BIO_s_mem());
354  if(!mem)
355  THROW_IOEXCEPTION("Failed to allocate memory for X509_NAME conversion: %s", ERR_reason_error_string(ERR_get_error()));
356 
357  // Convert the X509_NAME struct to string.
358  if(X509_NAME_print_ex(mem, name, 0, XN_FLAG_RFC2253) < 0)
359  {
360  BIO_free(mem);
361  THROW_IOEXCEPTION("Failed to convert X509_NAME struct to string: %s", ERR_reason_error_string(ERR_get_error()));
362  }
363 
364  // Read the converted string from buffer.
365  char buf[128];
366  int bytesRead;
367  while((bytesRead = BIO_gets(mem, &buf[0], sizeof(buf))) > 0)
368  str.append(buf);
369  BIO_free(mem);
370  }
371 
372  return str;
373 }
374 
380 {
381  EVP_PKEY* pubKey = X509_get_pubkey(cert);
382  if((pubKey == NULL) || (pubKey->type != EVP_PKEY_RSA))
383  {
384  EVP_PKEY_free(pubKey);
385  THROW_IOEXCEPTION("Unable to load RSA public Key: %s", ERR_reason_error_string(ERR_get_error()));
386  }
387  return pubKey;
388 }
389 
394 std::vector<unsigned char> digidoc::X509Cert::getRsaModulus() const throw(IOException)
395 {
396  EVP_PKEY* pubKey = getPublicKey();
397 
398  // Get size of the RSA modulus.
399  int bufSize = BN_num_bytes(pubKey->pkey.rsa->n);
400 
401  if(bufSize <= 0)
402  {
403  EVP_PKEY_free(pubKey);
404  THROW_IOEXCEPTION("Failed to extract RSA modulus.");
405  }
406 
407  // Allocate memory for the RSA modulus.
408  std::vector<unsigned char> rsaModulus(bufSize, 0);
409 
410  // Extract RSA modulus.
411  if(BN_bn2bin(pubKey->pkey.rsa->n, &rsaModulus[0]) <= 0)
412  {
413  EVP_PKEY_free(pubKey);
414  THROW_IOEXCEPTION("Failed to extract RSA modulus.");
415  }
416 
417  EVP_PKEY_free(pubKey);
418  return rsaModulus;
419 }
420 
425 std::vector<unsigned char> digidoc::X509Cert::getRsaExponent() const throw(IOException)
426 {
427  EVP_PKEY* pubKey = getPublicKey();
428 
429  // Get size of the RSA exponent.
430  int bufSize = BN_num_bytes(pubKey->pkey.rsa->e);
431 
432  if(bufSize <= 0)
433  {
434  EVP_PKEY_free(pubKey);
435  THROW_IOEXCEPTION("Failed to extract RSA exponent.");
436  }
437 
438  // Allocate memory for the RSA exponent.
439  std::vector<unsigned char> rsaExponent(bufSize, 0);
440 
441  // Extract RSA exponent.
442  if(BN_bn2bin(pubKey->pkey.rsa->e, &rsaExponent[0]) <= 0)
443  {
444  EVP_PKEY_free(pubKey);
445  THROW_IOEXCEPTION("Failed to extract RSA exponent.");
446  }
447 
448  EVP_PKEY_free(pubKey);
449  return rsaExponent;
450 }
451 
453 {
454  return cert;
455 }
456 
458 {
459  int notBefore = X509_cmp_current_time(cert->cert_info->validity->notBefore);
460  int notAfter = X509_cmp_current_time(cert->cert_info->validity->notAfter);
461  if(notBefore == 0 || notAfter == 0)
462  THROW_IOEXCEPTION("Failed to validate cert",ERR_reason_error_string(ERR_get_error()));
463  return notBefore < 0 && notAfter > 0;
464 }
465 
467 {
468  if(cert && other.cert)
469  return X509_cmp(cert, other.cert) == 0;
470  if(!cert && !other.cert)
471  return true;
472  return false;
473 }
474 
481 {
482  DECLARE_OPENSSL_RELEASE_STRUCT(X509_STORE_CTX)
483  X509_STORE_CTX *csc = X509_STORE_CTX_new();
484  X509_STORE_CTX_scope csct(&csc);
485  if (!csc)
486  THROW_IOEXCEPTION("Failed to create X509_STORE_CTX %s",ERR_reason_error_string(ERR_get_error()));
487 
488  X509_STORE *store = digidoc::X509CertStore::getInstance()->getCertStore();
489  X509* x = getX509(); X509_scope xt(&x);
490  if(!X509_STORE_CTX_init(csc, store, x, NULL))
491  THROW_IOEXCEPTION("Failed to init X509_STORE_CTX %s",ERR_reason_error_string(ERR_get_error()));
492 
493  int ok = X509_verify_cert(csc);
494 
495  if(ok <= 0)
496  {
497  int err = X509_STORE_CTX_get_error(csc);
498  std::ostringstream s;
499  s << "Unable to verify ";
500  s << ". Cause: " << X509_verify_cert_error_string(err);
501  switch(err)
502  {
503  case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
504  {
505  IOException e(__FILE__, __LINE__, s.str());
507  throw e;
508  break;
509  }
510  default: THROW_IOEXCEPTION(s.str().c_str()); break;
511  }
512  }
513 
514  return ok > 0;
515 }
516 
543 int digidoc::X509Cert::compareIssuerToString(const std::string &name) const throw(IOException)
544 {
545  size_t pos = 0, old = 0;
546  while(true)
547  {
548  pos = name.find(",", old);
549  if(pos == std::string::npos)
550  {
551  pos = name.size();
552  if(pos < old)
553  break;
554  }
555  else
556  {
557  if(name.compare(pos-1, 1, "\\") == 0)
558  continue;
559  }
560 
561  std::string nameitem = name.substr(old, pos - old);
562  old = pos + 1;
563 
564  if((pos = nameitem.find("=")) == std::string::npos ||
565  nameitem.compare(pos-1, 1, "\\") == 0)
566  continue;
567 
568  std::string obj = nameitem.substr(0, pos);
569  if(obj.compare("CN") != 0 && obj.compare("commonName") != 0 &&
570  obj.compare("L") != 0 && obj.compare("localityName") != 0 &&
571  obj.compare("ST") != 0 && obj.compare("stateOrProvinceName") != 0 &&
572  obj.compare("O") != 0 && obj.compare("organizationName") != 0 &&
573  obj.compare("OU") != 0 && obj.compare("organizationalUnitName") != 0 &&
574  obj.compare("C" ) != 0 && obj.compare("countryName") != 0 &&
575  obj.compare("STREET") != 0 && obj.compare("streetAddress") != 0 &&
576  obj.compare("DC") != 0 && obj.compare("domainComponent") != 0 &&
577  obj.compare("UID") != 0 && obj.compare("userId") != 0)
578  continue;
579 
580  X509_NAME_ENTRY *enta = X509_NAME_ENTRY_create_by_txt(0, obj.c_str(),
581  MBSTRING_UTF8, (unsigned char*)nameitem.substr(pos+1, pos-old).c_str(), -1);
582  if(!enta)
583  return -1;
584 
585  ASN1_OBJECT *obja = X509_NAME_ENTRY_get_object(enta);
586 
587  bool found = false;
588  for(int i = 0; i < X509_NAME_entry_count(getIssuerNameAsn1()); ++i)
589  {
590  X509_NAME_ENTRY *entb = X509_NAME_get_entry(getIssuerNameAsn1(), i);
591  ASN1_OBJECT *objb = X509_NAME_ENTRY_get_object(entb);
592  if(memcmp(obja->data, objb->data, obja->length) == 0 &&
593  memcmp(enta->value, entb->value, enta->size) == 0)
594  {
595  found = true;
596  break;
597  }
598  }
599  X509_NAME_ENTRY_free(enta);
600  if(!found)
601  return -1;
602  }
603  return 0;
604 }