Async 1.8.0
AsyncSslX509.h
Go to the documentation of this file.
1
31#ifndef ASYNC_SSL_X509_INCLUDED
32#define ASYNC_SSL_X509_INCLUDED
33
34
35/****************************************************************************
36 *
37 * System Includes
38 *
39 ****************************************************************************/
40
41#include <openssl/x509.h>
42#include <openssl/bn.h>
43
44#include <iomanip>
45#include <numeric>
46#include <ctime>
47#include <limits>
48
49
50/****************************************************************************
51 *
52 * Project Includes
53 *
54 ****************************************************************************/
55
56#include <AsyncSslKeypair.h>
58
59
60/****************************************************************************
61 *
62 * Local Includes
63 *
64 ****************************************************************************/
65
66
67
68/****************************************************************************
69 *
70 * Forward declarations
71 *
72 ****************************************************************************/
73
74
75
76/****************************************************************************
77 *
78 * Namespace
79 *
80 ****************************************************************************/
81
82namespace Async
83{
84
85
86/****************************************************************************
87 *
88 * Forward declarations of classes inside of the declared namespace
89 *
90 ****************************************************************************/
91
92
93
94/****************************************************************************
95 *
96 * Defines & typedefs
97 *
98 ****************************************************************************/
99
100
101
102/****************************************************************************
103 *
104 * Exported Global Variables
105 *
106 ****************************************************************************/
107
108
109
110/****************************************************************************
111 *
112 * Class definitions
113 *
114 ****************************************************************************/
115
124{
125 public:
126 enum : long
127 {
130 VERSION_3 = 2
131 };
132
136 SslX509(void) : m_cert(X509_new())
137 {
138 //std::cout << "### SslX509()" << std::endl;
139 }
140
146 SslX509(X509* cert, bool managed=true)
147 : m_cert(cert), m_managed(managed)
148 {
149 //std::cout << "### SslX509(X509*)" << std::endl;
150 }
151
161 explicit SslX509(X509_STORE_CTX& ctx)
162 : m_cert(X509_STORE_CTX_get_current_cert(&ctx)), m_managed(false)
163 {
164 //std::cout << "### SslX509(X509_STORE_CTX&)" << std::endl;
165 //int ret = X509_up_ref(m_cert);
166 //assert(ret == 1);
167 }
168
174 {
175 //std::cout << "### SslX509(SslX509&&)" << std::endl;
176 set(other.m_cert, other.m_managed);
177 //if (m_managed && (m_cert != nullptr))
178 //{
179 // X509_free(m_cert);
180 //}
181 //m_cert = other.m_cert;
182 //m_managed = other.m_managed;
183 other.m_cert = nullptr;
184 other.m_managed = true;
185 }
186
192 {
193 //std::cout << "### SslX509::operator=(SslX509&&)" << std::endl;
194 set(other.m_cert, other.m_managed);
195 //if (m_cert != nullptr)
196 //{
197 // X509_free(m_cert);
198 //}
199 //m_cert = other.m_cert;
200 //m_managed = other.m_managed;
201 other.m_cert = nullptr;
202 other.m_managed = true;
203 return *this;
204 }
205
209 SslX509(const SslX509&) = delete;
210
215 //explicit SslX509(const std::string& pem)
216 //{
217 // std::cout << "### SslX509(const std::string&)" << std::endl;
218 // readPem(pem);
219 //}
220
225 {
226 //std::cout << "### ~SslX509()" << std::endl;
227 set(nullptr);
228 }
229
235 void set(X509* cert, bool managed=true)
236 {
237 if (m_managed && (m_cert != nullptr))
238 {
239 X509_free(m_cert);
240 }
241 m_cert = cert;
242 m_managed = managed;
243 }
244
251 void clear(void)
252 {
253 if (m_managed && (m_cert != nullptr))
254 {
255 X509_free(m_cert);
256 }
257 m_cert = X509_new();
258 }
259
264 bool isNull(void) const { return m_cert == nullptr; }
265
269 SslX509& operator=(const SslX509&) = delete;
270
276 bool setIssuerName(const X509_NAME* name)
277 {
278 assert(m_cert != nullptr);
279#if OPENSSL_VERSION_MAJOR >= 3
280 return (X509_set_issuer_name(m_cert, name) == 1);
281#else
282 auto n = X509_NAME_dup(const_cast<X509_NAME*>(name));
283 int ret = X509_set_issuer_name(m_cert, n);
284 X509_NAME_free(n);
285 return (ret == 1);
286#endif
287 }
288
293 const X509_NAME* issuerName(void) const
294 {
295 assert(m_cert != nullptr);
296 return X509_get_issuer_name(m_cert);
297 }
298
304 bool setSubjectName(const X509_NAME* name)
305 {
306 assert(m_cert != nullptr);
307#if OPENSSL_VERSION_MAJOR >= 3
308 return (X509_set_subject_name(m_cert, name) == 1);
309#else
310 auto n = X509_NAME_dup(const_cast<X509_NAME*>(name));
311 int ret = X509_set_subject_name(m_cert, n);
312 X509_NAME_free(n);
313 return (ret == 1);
314#endif
315 }
316
321 const X509_NAME* subjectName(void) const
322 {
323 assert(m_cert != nullptr);
324 return X509_get_subject_name(m_cert);
325 }
326
331 operator const X509*(void) const { return m_cert; }
332
337 std::string commonName(void) const
338 {
339 std::string cn;
340
341 const X509_NAME* subj = subjectName();
342 if (subj == nullptr)
343 {
344 return cn;
345 }
346
347 // Assume there is only one CN
348#if OPENSSL_VERSION_MAJOR >= 3
349 int lastpos = X509_NAME_get_index_by_NID(subj, NID_commonName, -1);
350#else
351 auto s = X509_NAME_dup(const_cast<X509_NAME*>(subj));
352 int lastpos = X509_NAME_get_index_by_NID(s, NID_commonName, -1);
353 X509_NAME_free(s);
354#endif
355 if (lastpos >= 0)
356 {
357 X509_NAME_ENTRY *e = X509_NAME_get_entry(subj, lastpos);
358 ASN1_STRING *d = X509_NAME_ENTRY_get_data(e);
359 cn = reinterpret_cast<const char*>(ASN1_STRING_get0_data(d));
360 }
361 return cn;
362 }
363
369 bool verify(SslKeypair& keypair)
370 {
371 assert(m_cert != nullptr);
372 return (X509_verify(m_cert, keypair) == 1);
373 }
374
380 bool readPem(const std::string& pem)
381 {
382 BIO *mem = BIO_new(BIO_s_mem());
383 BIO_puts(mem, pem.c_str());
384 if (m_managed && (m_cert != nullptr))
385 {
386 X509_free(m_cert);
387 }
388 m_cert = PEM_read_bio_X509(mem, nullptr, nullptr, nullptr);
389 BIO_free(mem);
390 return (m_cert != nullptr);
391 }
392
397 std::string pem(void) const
398 {
399 assert(m_cert != nullptr);
400 BIO *mem = BIO_new(BIO_s_mem());
401 int ret = PEM_write_bio_X509(mem, m_cert);
402 assert(ret == 1);
403 char buf[16384];
404 int len = BIO_read(mem, buf, sizeof(buf));
405 assert(len > 0);
406 BIO_free(mem);
407 return std::string(buf, len);
408 }
409
415 bool readPemFile(const std::string& filename)
416 {
417 FILE *p_file = fopen(filename.c_str(), "r");
418 if (p_file == nullptr)
419 {
420 //std::cerr << "### Failed to open file '" << filename
421 // << "' for reading certificate" << std::endl;
422 return false;
423 }
424 if (m_managed && (m_cert != nullptr))
425 {
426 X509_free(m_cert);
427 }
428 m_cert = PEM_read_X509(p_file, nullptr, nullptr, nullptr);
429 fclose(p_file);
430 return (m_cert != nullptr);
431 }
432
438 bool writePemFile(FILE* f)
439 {
440 assert(m_cert != nullptr);
441 if (f == nullptr)
442 {
443 //std::cerr << "### Failed to open file '" << filename
444 // << "' for writing certificate" << std::endl;
445 return false;
446 }
447 int ret = PEM_write_X509(f, m_cert);
448 fclose(f);
449 return (ret == 1);
450 }
451
457 bool writePemFile(const std::string& filename)
458 {
459 return writePemFile(fopen(filename.c_str(), "w"));
460 }
461
467 bool appendPemFile(const std::string& filename)
468 {
469 return writePemFile(fopen(filename.c_str(), "a"));
470 }
471
480 {
481 return (X509_set_version(m_cert, version) == 1);
482 }
483
488 long version(void) const { return X509_get_version(m_cert); }
489
494 void setNotBefore(std::time_t in_time)
495 {
496 X509_time_adj_ex(X509_get_notBefore(m_cert), 0L, 0L, &in_time);
497 }
498
503 std::time_t notBefore(void) const
504 {
505 ASN1_TIME* epoch = ASN1_TIME_set(nullptr, 0);
506 const ASN1_TIME* not_before = X509_get0_notBefore(m_cert);
507 int pday=0, psec=0;
508 ASN1_TIME_diff(&pday, &psec, epoch, not_before);
509 ASN1_STRING_free(epoch);
510 return static_cast<std::time_t>(pday)*24*3600 + psec;
511 }
512
517 std::string notBeforeString(void) const
518 {
519 const ASN1_TIME* t = X509_get_notBefore(m_cert);
520 int len = ASN1_STRING_length(t);
521 if (t == nullptr)
522 {
523 return std::string();
524 }
525 const unsigned char* data = ASN1_STRING_get0_data(t);
526 return std::string(data, data+len);
527 }
528
533 std::string notBeforeLocaltimeString(void) const
534 {
535 std::time_t t = notBefore();
536 std::ostringstream ss;
537 ss << std::put_time(std::localtime(&t), "%c");
538 return ss.str();
539 }
540
545 void setNotAfter(std::time_t in_time)
546 {
547 X509_time_adj_ex(X509_get_notAfter(m_cert), 0L, 0L, &in_time);
548 }
549
554 std::time_t notAfter(void) const
555 {
556 ASN1_TIME* epoch = ASN1_TIME_set(nullptr, 0);
557 const ASN1_TIME* not_after = X509_get0_notAfter(m_cert);
558 int pday=0, psec=0;
559 ASN1_TIME_diff(&pday, &psec, epoch, not_after);
560 ASN1_STRING_free(epoch);
561 return static_cast<std::time_t>(pday)*24*3600 + psec;
562 }
563
568 std::string notAfterString(void) const
569 {
570 const ASN1_TIME* t = X509_get_notAfter(m_cert);
571 const unsigned char* data = ASN1_STRING_get0_data(t);
572 int len = ASN1_STRING_length(t);
573 if (t == nullptr)
574 {
575 return std::string();
576 }
577 return std::string(data, data+len);
578 }
579
584 std::string notAfterLocaltimeString(void) const
585 {
586 std::time_t t = notAfter();
587 std::ostringstream ss;
588 ss << std::put_time(std::localtime(&t), "%c");
589 return ss.str();
590 }
591
597 void setValidityTime(unsigned days, int offset_days=0)
598 {
599 std::time_t validity_secs = days * 24 * 60 * 60;
600 std::time_t tnow = time(NULL);
601 tnow += offset_days * 24 * 60 * 60;
602 if (std::numeric_limits<time_t>::max() - validity_secs < tnow)
603 {
604 validity_secs = std::numeric_limits<time_t>::max() - tnow;
605 }
606 setNotBefore(tnow);
607 setNotAfter(tnow + validity_secs);
608 }
609
615 void validityTime(int& days, int& seconds) const
616 {
617 const ASN1_TIME* not_before = X509_get_notBefore(m_cert);
618 const ASN1_TIME* not_after = X509_get_notAfter(m_cert);
619 ASN1_TIME_diff(&days, &seconds, not_before, not_after);
620 }
621
628 bool timeIsWithinRange(std::time_t tbegin=time(NULL),
629 std::time_t tend=time(NULL)) const
630 {
631 const ASN1_TIME* not_before = X509_get_notBefore(m_cert);
632 const ASN1_TIME* not_after = X509_get_notAfter(m_cert);
633 return ((not_before == nullptr) ||
634 (X509_cmp_time(not_before, &tbegin) == -1)) &&
635 ((not_after == nullptr) ||
636 (X509_cmp_time(not_after, &tend) == 1));
637 }
638
646 int signatureType(void) const
647 {
648 return X509_get_signature_type(m_cert);
649 }
650
657 void setSerialNumber(long serial_number=-1)
658 {
659 // FIXME: Error handling
660 ASN1_INTEGER *p_serial_number = ASN1_INTEGER_new();
661 if (serial_number < 0)
662 {
663 randSerial(p_serial_number);
664 }
665 else
666 {
667 ASN1_INTEGER_set(p_serial_number, serial_number);
668 }
669 X509_set_serialNumber(m_cert, p_serial_number);
670 ASN1_INTEGER_free(p_serial_number);
671 }
672
677 std::string serialNumberString(void) const
678 {
679 const ASN1_INTEGER* i = X509_get0_serialNumber(m_cert);
680 if (i == nullptr)
681 {
682 return std::string();
683 }
684 BIGNUM *bn = ASN1_INTEGER_to_BN(i, nullptr);
685 if (bn == nullptr)
686 {
687 return std::string();
688 }
689 char *hex = BN_bn2hex(bn);
690 BN_free(bn);
691 if (hex == nullptr)
692 {
693 return std::string();
694 }
695 std::string ret(hex, hex+strlen(hex));
696 ret = std::string("0x") + ret;
697 OPENSSL_free(hex);
698 return ret;
699 }
700
701 //void setIssuerName()
702 //{
703 // X509_set_issuer_name(p_generated_cert, X509_REQ_get_subject_name(pCertReq));
704 //}
705
711 void addIssuerName(const std::string& field, const std::string& value)
712 {
713 // FIXME: Error handling
714 assert(m_cert != nullptr);
715 X509_NAME* name = X509_get_issuer_name(m_cert);
716 if (name == nullptr)
717 {
718 name = X509_NAME_new();
719 }
720 assert(name != nullptr);
721 int ret = X509_NAME_add_entry_by_txt(name, field.c_str(), MBSTRING_UTF8,
722 reinterpret_cast<const unsigned char*>(value.c_str()),
723 value.size(), -1, 0);
724 assert(ret == 1);
725 ret = X509_set_issuer_name(m_cert, name);
726 assert(ret == 1);
727 }
728
734 void addSubjectName(const std::string& field, const std::string& value)
735 {
736 // FIXME: Error handling
737 assert(m_cert != nullptr);
738 X509_NAME* name = X509_get_subject_name(m_cert);
739 if (name == nullptr)
740 {
741 name = X509_NAME_new();
742 }
743 assert(name != nullptr);
744 int ret = X509_NAME_add_entry_by_txt(name, field.c_str(), MBSTRING_UTF8,
745 reinterpret_cast<const unsigned char*>(value.c_str()),
746 value.size(), -1, 0);
747 assert(ret == 1);
748 ret = X509_set_subject_name(m_cert, name);
749 assert(ret == 1);
750 }
751
756 std::string issuerNameString(void) const
757 {
758 std::string str;
759 const X509_NAME* nm = issuerName();
760 if (nm != nullptr)
761 {
762 BIO *mem = BIO_new(BIO_s_mem());
763 assert(mem != nullptr);
764 // Print all subject names on one line. Don't escape multibyte chars.
765 int len = X509_NAME_print_ex(mem, nm, 0,
766 XN_FLAG_ONELINE & ~ASN1_STRFLGS_ESC_MSB);
767 if (len > 0)
768 {
769 char buf[len+1];
770 len = BIO_read(mem, buf, sizeof(buf));
771 if (len > 0)
772 {
773 str = std::string(buf, len);
774 }
775 }
776 BIO_free(mem);
777 }
778 return str;
779 }
780
785 std::string subjectNameString(void) const
786 {
787 std::string str;
788 const X509_NAME* nm = subjectName();
789 if (nm != nullptr)
790 {
791 BIO *mem = BIO_new(BIO_s_mem());
792 assert(mem != nullptr);
793 // Print all subject names on one line. Don't escape multibyte chars.
794 int len = X509_NAME_print_ex(mem, nm, 0,
795 XN_FLAG_ONELINE & ~ASN1_STRFLGS_ESC_MSB);
796 if (len > 0)
797 {
798 char buf[len+1];
799 len = BIO_read(mem, buf, sizeof(buf));
800 if (len > 0)
801 {
802 str = std::string(buf, len);
803 }
804 }
805 BIO_free(mem);
806 }
807 return str;
808 }
809
815 {
816 for (int i=0; i<X509v3_get_ext_count(exts); ++i)
817 {
818 auto ext = X509v3_get_ext(exts, i);
819 X509_add_ext(m_cert, ext, -1);
820 }
821 }
822
828 {
829 assert(m_cert != nullptr);
830 return SslKeypair(X509_get_pubkey(m_cert));
831 }
832
839 {
840 return (X509_set_pubkey(m_cert, pkey) == 1);
841 }
842
848 bool sign(SslKeypair& pkey)
849 {
850 auto md = EVP_sha256();
851 auto md_size = EVP_MD_size(md);
852 return (X509_sign(m_cert, pkey, md) != md_size);
853 }
854
859 std::vector<unsigned char> digest(void) const
860 {
861 assert(m_cert != nullptr);
862 std::vector<unsigned char> md(EVP_MAX_MD_SIZE);
863 unsigned int len = md.size();
864 if (X509_digest(m_cert, EVP_sha256(), md.data(), &len) != 1)
865 {
866 len = 0;
867 }
868 md.resize(len);
869 return md;
870 }
871
872#if 0
873 int certificateType(void) const
874 {
875 auto pkey = X509_get0_pubkey(m_cert);
876 if (pkey == nullptr)
877 {
878 return -1;
879 }
880 return X509_certificate_type(m_cert, pkey);
881 }
882#endif
883
889 bool matchHost(const std::string& name) const
890 {
891 int chk = X509_check_host(m_cert, name.c_str(), name.size(), 0, nullptr);
892 return chk > 0;
893 }
894
900 bool matchIp(const IpAddress& ip) const
901 {
902 int chk = X509_check_ip_asc(m_cert, ip.toString().c_str(), 0);
903 return chk > 0;
904 }
905
910 void print(const std::string& prefix="") const
911 {
912 if (isNull())
913 {
914 std::cout << "NULL" << std::endl;
915 return;
916 }
917
918 int ext_idx = X509_get_ext_by_NID(m_cert, NID_subject_alt_name, -1);
919 auto ext = X509_get_ext(m_cert, ext_idx);
920 auto sanstr = SslX509ExtSubjectAltName(ext).toString();
921 const auto md = digest();
922 std::cout
923 //<< prefix << "Version : " << (version()+1) << "\n"
924 << prefix << "Serial No. : " << serialNumberString() << "\n"
925 << prefix << "Issuer : " << issuerNameString() << "\n"
926 << prefix << "Subject : " << subjectNameString() << "\n"
927 << prefix << "Not Before : "
928 << notBeforeLocaltimeString() << "\n"
929 << prefix << "Not After : "
930 << notAfterLocaltimeString() << "\n"
931 //<< prefix << "Signature Type : " << signatureType() << "\n"
932 ;
933 if (!sanstr.empty())
934 {
935 std::cout << prefix << "Subject Alt Name : " << sanstr << "\n";
936 }
937 //std::cout << prefix << "SHA256 Digest : " << std::accumulate(
938 // md.begin(),
939 // md.end(),
940 // std::ostringstream() << std::hex << std::setfill('0'),
941 // [](std::ostringstream& ss, unsigned char x)
942 // {
943 // if (!ss.str().empty()) { ss << ":"; }
944 // return std::move(ss) << std::setw(2) << unsigned(x);
945 // }).str() + "\n"
946 std::cout << std::flush;
947 }
948
949 protected:
950
951 private:
952 X509* m_cert = nullptr;
953 bool m_managed = true;
954
955 int randSerial(ASN1_INTEGER *ai)
956 {
957 BIGNUM *p_bignum = NULL;
958 int ret = -1;
959
960 if (NULL == (p_bignum = BN_new())) {
961 goto CLEANUP;
962 }
963
964 if (!BN_rand(p_bignum, 159, 0, 0)) {
965 goto CLEANUP;
966 }
967
968 if (ai && !BN_to_ASN1_INTEGER(p_bignum, ai)) {
969 goto CLEANUP;
970 }
971
972 ret = 1;
973
974 CLEANUP:
975 BN_free(p_bignum);
976 return ret;
977 }
978}; /* class SslX509 */
979
980
981} /* namespace */
982
983#endif /* ASYNC_SSL_X509_INCLUDED */
984
985/*
986 * This file has not been truncated
987 */
Represent private and public keys.
A class representing X.509 v3 extensions.
A class for representing an IP address in an OS independent way.
std::string toString(void) const
Return the string representation of the IP address.
A class representing private and public keys.
A class representing the X.509 Subject Alternative Name extension.
std::string toString(int type=-1) const
Convert all SANs to a string.
A class representing X.509 extensions.
A class representing an X.509 certificate.
std::string serialNumberString(void) const
Get the serial number as a string.
void setValidityTime(unsigned days, int offset_days=0)
Set the validity time relative to current time.
bool appendPemFile(const std::string &filename)
Append this certificate to file in PEM format.
bool readPemFile(const std::string &filename)
Initialize this object with PEM data read from given file.
bool matchHost(const std::string &name) const
Check if the given hostname match this certificate.
std::string commonName(void) const
Get the common name of the subject.
void addSubjectName(const std::string &field, const std::string &value)
Add a name to the subject distinguished name.
void addIssuerName(const std::string &field, const std::string &value)
Add a name to the issuer distinguished name.
SslKeypair publicKey(void) const
Get the public key @retrun Returns the public key.
std::time_t notBefore(void) const
Get the date and time from which this certificate is valid.
SslX509(X509_STORE_CTX &ctx)
Constructor.
long version(void) const
Get the version of this certificate.
SslX509 & operator=(const SslX509 &)=delete
Disallow use of the copy assignment operator.
void validityTime(int &days, int &seconds) const
The duration that this certificate is valid.
SslX509(void)
Default constructor.
int signatureType(void) const
Get the signature type.
bool writePemFile(const std::string &filename)
Write this certificate to file in PEM format.
void print(const std::string &prefix="") const
Print this certificate to std::cout.
void setSerialNumber(long serial_number=-1)
Set the serial number of the certificate.
bool setPublicKey(SslKeypair &pkey)
Set the public key for this certificate.
~SslX509(void)
Constructor taking PEM data.
bool verify(SslKeypair &keypair)
Verify that this certificate is signed by the given key.
const X509_NAME * subjectName(void) const
Get the subject distinguished name.
std::string subjectNameString(void) const
Get the subject distinguished name as a string.
std::string notAfterString(void) const
Get the date and time up to which this certificate is valid.
bool timeIsWithinRange(std::time_t tbegin=time(NULL), std::time_t tend=time(NULL)) const
Check if the certificate is valid within the given range.
bool readPem(const std::string &pem)
Initialize this certificate from a string containing PEM data.
SslX509(const SslX509 &)=delete
Don't allow copy construction.
bool setSubjectName(const X509_NAME *name)
Set the subject distinguished name.
SslX509 & operator=(SslX509 &&other)
Move assignment operator.
bool isNull(void) const
Check if this object is empty.
void set(X509 *cert, bool managed=true)
Set the internal X509 object to use.
std::time_t notAfter(void) const
Get the date and time up to which this certificate is valid.
bool writePemFile(FILE *f)
Write this certificate to file in PEM format.
bool matchIp(const IpAddress &ip) const
Check if the given IP address match this certificate.
bool setIssuerName(const X509_NAME *name)
Set the issuer distinguished name.
void addExtensions(const SslX509Extensions &exts)
Add v3 extensions to this certificate.
std::string issuerNameString(void) const
Get the issuer distinguished name as a string.
SslX509(X509 *cert, bool managed=true)
Constructor.
bool sign(SslKeypair &pkey)
Sign this certificate using the given key.
std::string notBeforeString(void) const
Get the date and time from which this certificate is valid.
std::string pem(void) const
Get this certificate as PEM data.
const X509_NAME * issuerName(void) const
Get the issuer distinguished name.
void clear(void)
Set this object to empty.
void setNotBefore(std::time_t in_time)
Set the date and time from which this certificate is valid.
SslX509(SslX509 &&other)
Move constructor.
void setNotAfter(std::time_t in_time)
Set the date and time up to which this certificate is valid.
std::vector< unsigned char > digest(void) const
Get the digest of this certificate.
std::string notAfterLocaltimeString(void) const
Get the date and time up to which this certificate is valid.
std::string notBeforeLocaltimeString(void) const
Get the date and time from which this certificate is valid.
bool setVersion(long version)
Set the version of this certificate.
Namespace for the asynchronous programming classes.