Restore support for openssl 0.9.7 (Debian Sarge).
[wvstreams.git] / crypto / wvx509.cc
blob53ffee2580d366194c1e35e7923f1038fc6a48eb
1 /*
2 * Worldvisions Weaver Software:
3 * Copyright (C) 1997-2005 Net Integration Technologies, Inc.
4 *
5 * X.509 certificate management classes.
6 */
7 #include "wvx509.h"
8 #include "wvcrl.h"
9 #include "wvsslhacks.h"
10 #include "wvcrypto.h"
11 #include "wvstringlist.h"
12 #include "wvbase64.h"
13 #include "wvstrutils.h"
14 #include "wvautoconf.h"
16 #include <openssl/pem.h>
17 #include <openssl/x509v3.h>
18 #include <openssl/err.h>
19 #include <openssl/sha.h>
20 #include <openssl/ssl.h>
22 // enable this to add some extra debugging trace messages (this can be VERY
23 // verbose)
24 #if 0
25 # define TRACE(x, y...) debug(x, ## y);
26 #else
27 #ifndef _MSC_VER
28 # define TRACE(x, y...)
29 #else
30 # define TRACE
31 #endif
32 #endif
34 // helper method to let us return and warn gracefully when getting/setting an
35 // element in a null certificate
36 static const char * warning_str_set = "Tried to set %s, but certificate not ok.\n";
37 static const char * warning_str_get = "Tried to get %s, but certificate not ok.\n";
38 #define CHECK_CERT_EXISTS_SET(x) \
39 if (!cert) { \
40 debug(WvLog::Warning, warning_str_set, x); \
41 return; \
43 #define CHECK_CERT_EXISTS_GET(x, y) \
44 if (!cert) { \
45 debug(WvLog::Warning, warning_str_get, x); \
46 return y; \
50 UUID_MAP_BEGIN(WvX509)
51 UUID_MAP_ENTRY(IObject)
52 UUID_MAP_END
54 static int ssl_init_count = 0;
57 void wvssl_init()
59 if (!ssl_init_count)
61 SSL_library_init();
62 SSL_load_error_strings();
63 ERR_load_BIO_strings();
64 ERR_load_crypto_strings();
65 OpenSSL_add_all_algorithms();
66 OpenSSL_add_all_ciphers();
67 OpenSSL_add_all_digests();
70 ssl_init_count++;
74 void wvssl_free()
76 if (ssl_init_count >= 1)
77 ssl_init_count--;
79 if (!ssl_init_count)
81 ERR_free_strings();
82 EVP_cleanup();
87 WvString wvssl_errstr()
89 char buf[256];
90 ERR_error_string_n(ERR_get_error(), buf, sizeof(buf));
91 buf[sizeof(buf)-1] = 0;
92 return buf;
96 WvX509::WvX509(X509 *_cert)
97 : debug("X509", WvLog::Debug5)
99 wvssl_init();
100 cert = _cert;
104 WvX509::WvX509()
105 : debug("X509", WvLog::Debug5)
107 wvssl_init();
108 cert = NULL;
112 WvX509::~WvX509()
114 TRACE("Deleting.\n");
116 if (cert)
117 X509_free(cert);
119 wvssl_free();
124 // The people who designed this garbage should be shot!
125 // Support old versions of openssl...
126 #ifndef NID_domainComponent
127 #define NID_domainComponent 391
128 #endif
130 #ifndef NID_Domain
131 #define NID_Domain 392
132 #endif
135 // returns some approximation of the server's fqdn, or an empty string.
136 static WvString set_name_entry(X509_NAME *name, WvStringParm dn)
138 WvString fqdn(""), force_fqdn("");
139 X509_NAME_ENTRY *ne = NULL;
140 int count = 0, nid;
142 WvStringList l;
143 l.split(dn, ",");
145 // dn is of the form: c=ca,o=foo organization,dc=foo,dc=com
146 // (ie. name=value pairs separated by commas)
147 WvStringList::Iter i(l);
148 for (i.rewind(); i.next(); )
150 WvString s(*i), sid;
151 char *cptr, *value;
153 cptr = s.edit();
154 value = strchr(cptr, '=');
155 if (value)
156 *value++ = 0;
157 else
158 value = (char*)"NULL";
160 sid = strlwr(trim_string(cptr));
162 if (sid == "c")
163 nid = NID_countryName;
164 else if (sid == "st")
165 nid = NID_stateOrProvinceName;
166 else if (sid == "l")
167 nid = NID_localityName;
168 else if (sid == "o")
169 nid = NID_organizationName;
170 else if (sid == "ou")
171 nid = NID_organizationalUnitName;
172 else if (sid == "cn")
174 nid = NID_commonName;
175 force_fqdn = value;
177 else if (sid == "dc")
179 nid = NID_domainComponent;
180 if (!!fqdn)
181 fqdn.append(".");
182 fqdn.append(value);
184 else if (sid == "domain")
186 nid = NID_Domain;
187 force_fqdn = value;
189 else if (sid == "email")
190 nid = NID_pkcs9_emailAddress;
191 else
192 nid = NID_domainComponent;
194 // Sometimes we just want to parse dn into fqdn.
195 if (name == NULL)
196 continue;
198 if (!ne)
199 ne = X509_NAME_ENTRY_create_by_NID(NULL, nid,
200 V_ASN1_APP_CHOOSE, (unsigned char *)value, -1);
201 else
202 X509_NAME_ENTRY_create_by_NID(&ne, nid,
203 V_ASN1_APP_CHOOSE, (unsigned char *)value, -1);
204 if (!ne)
205 continue;
207 X509_NAME_add_entry(name, ne, count++, 0);
210 X509_NAME_ENTRY_free(ne);
212 if (!!force_fqdn)
213 return force_fqdn;
215 return fqdn;
219 WvRSAKey *WvX509::get_rsa_pub() const
221 EVP_PKEY *pkcert = X509_get_pubkey(cert);
222 RSA *certrsa = EVP_PKEY_get1_RSA(pkcert);
223 EVP_PKEY_free(pkcert);
224 return new WvRSAKey(certrsa, false);
228 WvString WvX509::certreq(WvStringParm subject, const WvRSAKey &rsa)
230 WvLog debug("X509::certreq", WvLog::Debug5);
232 EVP_PKEY *pk = NULL;
233 X509_NAME *name = NULL;
234 X509_REQ *certreq = NULL;
236 // double check RSA key
237 if (rsa.isok())
238 debug("RSA Key is fine.\n");
239 else
241 debug(WvLog::Warning, "RSA Key is bad");
242 return WvString::null;
245 if ((pk=EVP_PKEY_new()) == NULL)
247 debug(WvLog::Warning, "Error creating key handler for new certificate");
248 return WvString::null;
251 if ((certreq=X509_REQ_new()) == NULL)
253 debug(WvLog::Warning, "Error creating new PKCS#10 object");
254 EVP_PKEY_free(pk);
255 return WvString::null;
258 if (!EVP_PKEY_set1_RSA(pk, rsa.rsa))
260 debug(WvLog::Warning, "Error adding RSA keys to certificate");
261 X509_REQ_free(certreq);
262 EVP_PKEY_free(pk);
263 return WvString::null;
266 X509_REQ_set_version(certreq, 0); /* version 1 */
268 X509_REQ_set_pubkey(certreq, pk);
270 name = X509_REQ_get_subject_name(certreq);
272 debug("Creating Certificate request for %s\n", subject);
273 set_name_entry(name, subject);
274 X509_REQ_set_subject_name(certreq, name);
275 char *sub_name = X509_NAME_oneline(X509_REQ_get_subject_name(certreq),
276 0, 0);
277 debug("SubjectDN: %s\n", sub_name);
278 OPENSSL_free(sub_name);
280 if (!X509_REQ_sign(certreq, pk, EVP_sha1()))
282 debug(WvLog::Warning, "Could not self sign the request");
283 X509_REQ_free(certreq);
284 EVP_PKEY_free(pk);
285 return WvString::null;
288 int verify_result = X509_REQ_verify(certreq, pk);
289 if (verify_result == 0)
291 debug(WvLog::Warning, "Self signed request failed");
292 X509_REQ_free(certreq);
293 EVP_PKEY_free(pk);
294 return WvString::null;
296 else
298 debug("Self Signed Certificate Request verifies OK!\n");
301 // Horribly involuted hack to get around the fact that the
302 // OpenSSL people are too braindead to have a PEM_write function
303 // that returns a char *
304 WvDynBuf retval;
305 BIO *bufbio = BIO_new(BIO_s_mem());
306 BUF_MEM *bm;
308 PEM_write_bio_X509_REQ(bufbio, certreq);
309 BIO_get_mem_ptr(bufbio, &bm);
310 retval.put(bm->data, bm->length);
312 X509_REQ_free(certreq);
313 EVP_PKEY_free(pk);
314 BIO_free(bufbio);
316 return retval.getstr();
320 bool WvX509::validate(WvX509 *cacert) const
322 if (cert == NULL)
324 debug(WvLog::Warning, "Tried to validate certificate against CA, but "
325 "certificate is blank!\n");
326 return false;
329 bool retval = true;
331 // Check and make sure that the certificate is still valid
332 if (X509_cmp_current_time(X509_get_notAfter(cert)) < 0)
334 debug("Certificate has expired.\n");
335 retval = false;
338 if (X509_cmp_current_time(X509_get_notBefore(cert)) > 0)
340 debug("Certificate is not yet valid.\n");
341 retval = false;
344 if (cacert)
346 retval &= signedbyca(*cacert);
347 retval &= issuedbyca(*cacert);
350 return retval;
354 bool WvX509::signedbyca(WvX509 &cacert) const
356 if (!cert || !cacert.cert)
358 debug(WvLog::Warning, "Tried to determine if certificate was signed "
359 "by CA, but either client or CA certificate (or both) are "
360 "blank.\n");
361 return false;
364 EVP_PKEY *pkey = X509_get_pubkey(cacert.cert);
365 int result = X509_verify(cert, pkey);
366 EVP_PKEY_free(pkey);
368 if (result < 0)
370 debug("There was an error determining whether or not we were signed by "
371 "CA '%s'.\n", cacert.get_subject());
372 return false;
374 bool issigned = (result > 0);
376 debug("Certificate was%s signed by CA %s.\n", issigned ? "" : " NOT",
377 cacert.get_subject());
379 return issigned;
383 bool WvX509::issuedbyca(WvX509 &cacert) const
385 if (!cert || !cacert.cert)
387 debug(WvLog::Warning, "Tried to determine if certificate was issued "
388 "by CA, but either client or CA certificate (or both) are "
389 "blank.\n");
390 return false;
393 int ret = X509_check_issued(cacert.cert, cert);
394 debug("issuedbyca: %s==X509_V_OK(%s)\n", ret, X509_V_OK);
395 if (ret != X509_V_OK)
396 return false;
398 return true;
402 WvString WvX509::encode(const DumpMode mode) const
404 WvDynBuf retval;
405 encode(mode, retval);
406 return retval.getstr();
410 void WvX509::encode(const DumpMode mode, WvBuf &buf) const
412 if (mode == CertFileDER || mode == CertFilePEM)
413 return; // file modes are no ops with encode
415 if (!cert)
417 debug(WvLog::Warning, "Tried to encode certificate, but certificate "
418 "is blank!\n");
419 return;
422 debug("Encoding X509 certificate.\n");
424 if (mode == CertHex)
426 size_t size;
427 unsigned char *keybuf, *iend;
428 WvString enccert;
430 size = i2d_X509(cert, NULL);
431 iend = keybuf = new unsigned char[size];
432 i2d_X509(cert, &iend);
434 enccert.setsize(size * 2 +1);
435 ::hexify(enccert.edit(), keybuf, size);
437 deletev keybuf;
438 buf.putstr(enccert);
440 else
442 BIO *bufbio = BIO_new(BIO_s_mem());
443 BUF_MEM *bm;
445 if (mode == CertPEM)
446 PEM_write_bio_X509(bufbio, cert);
447 else if (mode == CertDER)
448 i2d_X509_bio(bufbio, cert);
449 else
450 debug(WvLog::Warning, "Tried to encode certificate with unknown "
451 "mode!\n");
453 BIO_get_mem_ptr(bufbio, &bm);
454 buf.put(bm->data, bm->length);
455 BIO_free(bufbio);
460 void WvX509::decode(const DumpMode mode, WvStringParm str)
462 if (cert)
464 debug("Replacing an already existant X509 certificate.\n");
465 X509_free(cert);
466 cert = NULL;
469 if (mode == CertFileDER)
471 BIO *bio = BIO_new(BIO_s_file());
473 if (BIO_read_filename(bio, str.cstr()) <= 0)
475 debug(WvLog::Warning, "Open '%s': %s\n", str, wvssl_errstr());
476 BIO_free(bio);
477 return;
480 if (!(cert = d2i_X509_bio(bio, NULL)))
481 debug(WvLog::Warning, "Import DER from '%s': %s\n",
482 str, wvssl_errstr());
484 BIO_free(bio);
485 return;
487 else if (mode == CertFilePEM)
489 FILE *fp = fopen(str, "rb");
490 if (!fp)
492 int errnum = errno;
493 debug("Open '%s': %s\n", str, strerror(errnum));
494 return;
497 if (!(cert = PEM_read_X509(fp, NULL, NULL, NULL)))
498 debug(WvLog::Warning, "Import PEM from '%s': %s\n",
499 str, wvssl_errstr());
501 fclose(fp);
502 return;
504 else if (mode == CertHex)
506 int hexbytes = str.len();
507 int bufsize = hexbytes/2;
508 unsigned char *certbuf = new unsigned char[bufsize];
509 unsigned char *cp = certbuf;
510 X509 *tmpcert;
512 ::unhexify(certbuf, str);
513 tmpcert = cert = X509_new();
514 cert = wv_d2i_X509(&tmpcert, &cp, bufsize);
515 delete[] certbuf;
516 return;
519 // we use the buffer decode functions for everything else
520 WvDynBuf buf;
521 buf.putstr(str);
522 decode(mode, buf);
526 void WvX509::decode(const DumpMode mode, WvBuf &encoded)
528 if (cert)
530 debug("Replacing an already existant X509 certificate.\n");
531 X509_free(cert);
532 cert = NULL;
535 if (mode == CertHex || mode == CertFileDER || mode == CertFilePEM)
536 decode(mode, encoded.getstr());
537 else
539 BIO *membuf = BIO_new(BIO_s_mem());
540 BIO_write(membuf, encoded.get(encoded.used()), encoded.used());
542 if (mode == CertPEM)
543 cert = PEM_read_bio_X509(membuf, NULL, NULL, NULL);
544 else if (mode == CertDER)
545 cert = d2i_X509_bio(membuf, NULL);
546 else
547 debug(WvLog::Warning, "Tried to encode certificate with unknown "
548 "mode!\n");
550 BIO_free_all(membuf);
555 WvString WvX509::get_issuer() const
557 CHECK_CERT_EXISTS_GET("issuer", WvString::null);
559 char *name = X509_NAME_oneline(X509_get_issuer_name(cert),0,0);
560 WvString retval(name);
561 OPENSSL_free(name);
562 return retval;
566 void WvX509::set_issuer(WvStringParm issuer)
568 CHECK_CERT_EXISTS_SET("issuer");
570 X509_NAME *name = X509_get_issuer_name(cert);
571 set_name_entry(name, issuer);
572 X509_set_issuer_name(cert, name);
576 void WvX509::set_issuer(const WvX509 &cacert)
578 CHECK_CERT_EXISTS_SET("issuer");
580 X509_NAME *casubj = X509_get_subject_name(cacert.cert);
581 X509_set_issuer_name(cert, casubj);
585 WvString WvX509::get_subject() const
587 CHECK_CERT_EXISTS_GET("subject", WvString::null);
589 char *name = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
590 WvString retval(name);
591 OPENSSL_free(name);
592 return retval;
596 void WvX509::set_subject(WvStringParm subject)
598 CHECK_CERT_EXISTS_SET("subject");
600 X509_NAME *name = X509_get_subject_name(cert);
601 set_name_entry(name, subject);
602 X509_set_subject_name(cert, name);
606 void WvX509::set_subject(X509_NAME *name)
608 CHECK_CERT_EXISTS_SET("subject");
610 X509_set_subject_name(cert, name);
614 void WvX509::set_pubkey(WvRSAKey &_rsa)
616 CHECK_CERT_EXISTS_SET("pubkey");
618 EVP_PKEY *pk = EVP_PKEY_new();
619 assert(pk);
621 // Assign RSA Key from WvRSAKey into stupid package that OpenSSL needs
622 if (!EVP_PKEY_set1_RSA(pk, _rsa.rsa))
624 debug("Error adding RSA keys to certificate.\n");
625 return;
628 X509_set_pubkey(cert, pk);
630 EVP_PKEY_free(pk);
635 void WvX509::set_nsserver(WvStringParm servername)
637 CHECK_CERT_EXISTS_SET("nsserver");
639 WvString fqdn;
641 // FQDN cannot have a = in it, therefore it
642 // must be a distinguished name :)
643 if (strchr(servername, '='))
644 fqdn = set_name_entry(NULL, servername);
645 else
646 fqdn = servername;
648 if (!fqdn)
649 fqdn = "null.noname.null";
651 debug("Setting Netscape SSL server name extension to '%s'.\n", fqdn);
653 // Add in the netscape-specific server extension
654 set_extension(NID_netscape_cert_type, "server");
655 set_extension(NID_netscape_ssl_server_name, fqdn);
659 WvString WvX509::get_nsserver() const
661 return get_extension(NID_netscape_ssl_server_name);
665 WvString WvX509::get_serial() const
667 CHECK_CERT_EXISTS_GET("serial", WvString::null);
669 BIGNUM *bn = BN_new();
670 bn = ASN1_INTEGER_to_BN(X509_get_serialNumber(cert), bn);
671 char * c = BN_bn2dec(bn);
672 WvString ret("%s", c);
673 OPENSSL_free(c);
674 BN_free(bn);
675 return ret;
679 void WvX509::set_version()
681 CHECK_CERT_EXISTS_SET("version");
683 X509_set_version(cert, 0x2);
687 void WvX509::set_serial(long serial)
689 CHECK_CERT_EXISTS_SET("serial");
691 ASN1_INTEGER_set(X509_get_serialNumber(cert), serial);
695 WvString WvX509::get_crl_dp() const
697 return get_extension(NID_crl_distribution_points);
701 void WvX509::set_lifetime(long seconds)
703 CHECK_CERT_EXISTS_SET("lifetime");
705 // Set the NotBefore time to now.
706 X509_gmtime_adj(X509_get_notBefore(cert), 0);
708 // Now + 10 years... should be shorter, but since we don't currently
709 // have a set of routines to refresh the certificates, make it
710 // REALLY long.
711 X509_gmtime_adj(X509_get_notAfter(cert), seconds);
715 void WvX509::set_key_usage(WvStringParm values)
717 set_extension(NID_key_usage, values);
721 WvString WvX509::get_key_usage() const
723 return get_extension(NID_key_usage);
727 void WvX509::set_ext_key_usage(WvStringParm values)
729 set_extension(NID_ext_key_usage, values);
733 WvString WvX509::get_ext_key_usage() const
735 return get_extension(NID_ext_key_usage);
739 WvString WvX509::get_altsubject() const
741 return get_extension(NID_subject_alt_name);
745 bool WvX509::get_basic_constraints(bool &ca, int &pathlen) const
747 CHECK_CERT_EXISTS_GET("basic constraints", false);
749 BASIC_CONSTRAINTS *constraints = NULL;
750 int i;
752 constraints = static_cast<BASIC_CONSTRAINTS *>(X509_get_ext_d2i(
753 cert, NID_basic_constraints,
754 &i, NULL));
755 if (constraints)
757 ca = constraints->ca;
758 if (constraints->pathlen)
760 if ((constraints->pathlen->type == V_ASN1_NEG_INTEGER) || !ca)
762 debug("Path length type not valid when getting basic "
763 "constraints.\n");
764 BASIC_CONSTRAINTS_free(constraints);
765 pathlen = 0;
766 return false;
769 pathlen = ASN1_INTEGER_get(constraints->pathlen);
771 else
772 pathlen = (-1);
774 BASIC_CONSTRAINTS_free(constraints);
775 return true;
778 debug("Basic constraints extension not present.\n");
779 return false;
783 void WvX509::set_basic_constraints(bool ca, int pathlen)
785 CHECK_CERT_EXISTS_SET("basic constraints");
787 BASIC_CONSTRAINTS *constraints = BASIC_CONSTRAINTS_new();
789 constraints->ca = static_cast<int>(ca);
790 if (pathlen != (-1))
792 ASN1_INTEGER *i = ASN1_INTEGER_new();
793 ASN1_INTEGER_set(i, pathlen);
794 constraints->pathlen = i;
797 X509_EXTENSION *ex = X509V3_EXT_i2d(NID_basic_constraints, 0,
798 constraints);
799 while (int idx = X509_get_ext_by_NID(cert, NID_basic_constraints, 0) >= 0)
801 debug("Found extension at idx %s\n", idx);
802 X509_EXTENSION *tmpex = X509_delete_ext(cert, idx);
803 X509_EXTENSION_free(tmpex);
806 X509_add_ext(cert, ex, NID_basic_constraints);
807 X509_EXTENSION_free(ex);
808 BASIC_CONSTRAINTS_free(constraints);
813 * These functions are optional to the API. If OpenSSL doesn't support them,
814 * we simply won't include them here, and apps that need them won't compile.
816 #ifdef HAVE_OPENSSL_POLICY_MAPPING
818 bool WvX509::get_policy_constraints(int &require_explicit_policy,
819 int &inhibit_policy_mapping) const
821 CHECK_CERT_EXISTS_GET("policy constraints", false);
823 POLICY_CONSTRAINTS *constraints = NULL;
824 int i;
826 constraints = static_cast<POLICY_CONSTRAINTS *>(X509_get_ext_d2i(
827 cert, NID_policy_constraints,
828 &i, NULL));
829 if (constraints)
831 if (constraints->requireExplicitPolicy)
832 require_explicit_policy = ASN1_INTEGER_get(
833 constraints->requireExplicitPolicy);
834 else
835 require_explicit_policy = (-1);
837 if (constraints->inhibitPolicyMapping)
838 inhibit_policy_mapping = ASN1_INTEGER_get(
839 constraints->inhibitPolicyMapping);
840 else
841 inhibit_policy_mapping = (-1);
842 POLICY_CONSTRAINTS_free(constraints);
843 return true;
846 return false;
850 void WvX509::set_policy_constraints(int require_explicit_policy,
851 int inhibit_policy_mapping)
853 CHECK_CERT_EXISTS_SET("policy constraints");
855 POLICY_CONSTRAINTS *constraints = POLICY_CONSTRAINTS_new();
857 ASN1_INTEGER *i = ASN1_INTEGER_new();
858 ASN1_INTEGER_set(i, require_explicit_policy);
859 constraints->requireExplicitPolicy = i;
860 i = ASN1_INTEGER_new();
861 ASN1_INTEGER_set(i, inhibit_policy_mapping);
862 constraints->inhibitPolicyMapping = i;
864 X509_EXTENSION *ex = X509V3_EXT_i2d(NID_policy_constraints, 0,
865 constraints);
866 X509_add_ext(cert, ex, -1);
867 X509_EXTENSION_free(ex);
868 POLICY_CONSTRAINTS_free(constraints);
872 bool WvX509::get_policy_mapping(PolicyMapList &list) const
874 CHECK_CERT_EXISTS_GET("policy mapping", false);
876 POLICY_MAPPINGS *mappings = NULL;
877 POLICY_MAPPING *map = NULL;
878 int i;
880 mappings = static_cast<POLICY_MAPPINGS *>(X509_get_ext_d2i(
881 cert, NID_policy_mappings,
882 &i, NULL));
883 if (!mappings)
884 return false;
886 const int POLICYID_MAXLEN = 80;
887 char tmp1[80];
888 char tmp2[80];
889 for(int j = 0; j < sk_POLICY_MAPPING_num(mappings); j++)
891 map = sk_POLICY_MAPPING_value(mappings, j);
892 OBJ_obj2txt(tmp1, POLICYID_MAXLEN, map->issuerDomainPolicy, true);
893 OBJ_obj2txt(tmp2, POLICYID_MAXLEN, map->subjectDomainPolicy, true);
894 list.append(new PolicyMap(tmp1, tmp2), true);
897 sk_POLICY_MAPPING_pop_free(mappings, POLICY_MAPPING_free);
899 return true;
903 void WvX509::set_policy_mapping(PolicyMapList &list)
905 CHECK_CERT_EXISTS_SET("policy mapping");
907 POLICY_MAPPINGS *maps = sk_POLICY_MAPPING_new_null();
909 PolicyMapList::Iter i(list);
910 for (i.rewind(); i.next();)
912 POLICY_MAPPING *map = POLICY_MAPPING_new();
913 map->issuerDomainPolicy = OBJ_txt2obj(i().issuer_domain.cstr(), 0);
914 map->subjectDomainPolicy = OBJ_txt2obj(i().subject_domain.cstr(), 0);
915 sk_POLICY_MAPPING_push(maps, map);
916 printf("Push!\n");
919 X509_EXTENSION *ex = X509V3_EXT_i2d(NID_policy_mappings, 0, maps);
920 X509_add_ext(cert, ex, -1);
921 X509_EXTENSION_free(ex);
922 sk_POLICY_MAPPING_pop_free(maps, POLICY_MAPPING_free);
925 #endif // HAVE_OPENSSL_POLICY_MAPPING
928 static void add_aia(WvStringParm type, WvString identifier, AUTHORITY_INFO_ACCESS *ainfo)
930 ACCESS_DESCRIPTION *acc = ACCESS_DESCRIPTION_new();
931 sk_ACCESS_DESCRIPTION_push(ainfo, acc);
932 acc->method = OBJ_txt2obj(type.cstr(), 0);
933 acc->location->type = GEN_URI;
934 acc->location->d.ia5 = M_ASN1_IA5STRING_new();
935 unsigned char *cident = reinterpret_cast<unsigned char *>(identifier.edit());
936 ASN1_STRING_set(acc->location->d.ia5, cident, identifier.len());
940 void WvX509::set_aia(WvStringList &ca_urls,
941 WvStringList &responders)
943 CHECK_CERT_EXISTS_SET("aia");
945 AUTHORITY_INFO_ACCESS *ainfo = sk_ACCESS_DESCRIPTION_new_null();
947 WvStringList::Iter i(ca_urls);
948 for (i.rewind(); i.next();)
949 add_aia("caIssuers", i(), ainfo);
951 WvStringList::Iter j(responders);
952 for (j.rewind(); j.next();)
953 add_aia("OCSP", j(), ainfo);
955 X509_EXTENSION *ex = X509V3_EXT_i2d(NID_info_access, 0, ainfo);
956 X509_add_ext(cert, ex, -1);
957 X509_EXTENSION_free(ex);
958 sk_ACCESS_DESCRIPTION_pop_free(ainfo, ACCESS_DESCRIPTION_free);
962 WvString WvX509::get_aia() const
964 return get_extension(NID_info_access);
968 static void parse_stack(WvStringParm ext, WvStringList &list, WvStringParm prefix)
970 WvStringList stack;
971 stack.split(ext, ";\n");
972 WvStringList::Iter i(stack);
973 for (i.rewind();i.next();)
975 WvString stack_entry(*i);
976 if (strstr(stack_entry, prefix))
978 WvString uri(stack_entry.edit() + prefix.len());
979 list.append(uri);
985 void WvX509::get_ocsp(WvStringList &responders) const
987 parse_stack(get_aia(), responders, "OCSP - URI:");
991 void WvX509::get_ca_urls(WvStringList &urls) const
993 parse_stack(get_aia(), urls, "CA Issuers - URI:");
997 void WvX509::get_crl_urls(WvStringList &urls) const
999 parse_stack(get_crl_dp(), urls, "URI:");
1003 void WvX509::set_crl_urls(WvStringList &urls)
1005 CHECK_CERT_EXISTS_SET("CRL urls");
1007 STACK_OF(DIST_POINT) *crldp = sk_DIST_POINT_new_null();
1008 WvStringList::Iter i(urls);
1009 for (i.rewind(); i.next();)
1011 DIST_POINT *point = DIST_POINT_new();
1012 sk_DIST_POINT_push(crldp, point);
1014 GENERAL_NAMES *uris = GENERAL_NAMES_new();
1015 GENERAL_NAME *uri = GENERAL_NAME_new();
1016 uri->type = GEN_URI;
1017 uri->d.ia5 = M_ASN1_IA5STRING_new();
1018 unsigned char *cident = reinterpret_cast<unsigned char *>(i().edit());
1019 ASN1_STRING_set(uri->d.ia5, cident, i().len());
1020 sk_GENERAL_NAME_push(uris, uri);
1022 point->distpoint = DIST_POINT_NAME_new();
1023 point->distpoint->name.fullname = uris;
1024 point->distpoint->type = 0;
1027 X509_EXTENSION *ex = X509V3_EXT_i2d(NID_crl_distribution_points, 0, crldp);
1028 X509_add_ext(cert, ex, -1);
1029 X509_EXTENSION_free(ex);
1030 sk_DIST_POINT_pop_free(crldp, DIST_POINT_free);
1034 bool WvX509::get_policies(WvStringList &policy_oids) const
1036 CHECK_CERT_EXISTS_GET("policies", false);
1038 int critical;
1039 CERTIFICATEPOLICIES * policies = static_cast<CERTIFICATEPOLICIES *>(
1040 X509_get_ext_d2i(cert, NID_certificate_policies, &critical, NULL));
1041 if (policies)
1043 for (int i = 0; i < sk_POLICYINFO_num(policies); i++)
1045 POLICYINFO * policy = sk_POLICYINFO_value(policies, i);
1046 const int POLICYID_MAXLEN = 80;
1048 char policyid[POLICYID_MAXLEN];
1049 OBJ_obj2txt(policyid, POLICYID_MAXLEN, policy->policyid,
1050 true); // don't substitute human-readable names
1051 policy_oids.append(policyid);
1054 sk_POLICYINFO_pop_free(policies, POLICYINFO_free);
1055 return true;
1058 return false;
1062 void WvX509::set_policies(WvStringList &policy_oids)
1064 CHECK_CERT_EXISTS_SET("policies");
1066 STACK_OF(POLICYINFO) *sk_pinfo = sk_POLICYINFO_new_null();
1068 WvStringList::Iter i(policy_oids);
1069 for (i.rewind(); i.next();)
1071 ASN1_OBJECT *pobj = OBJ_txt2obj(i(), 0);
1072 POLICYINFO *pol = POLICYINFO_new();
1073 pol->policyid = pobj;
1074 sk_POLICYINFO_push(sk_pinfo, pol);
1077 #if 0
1078 // this code would let you set URL information to a policy
1079 // qualifier
1080 POLICYQUALINFO *qual = NULL;
1081 WvString url(_url);
1082 if (!!url)
1084 pol->qualifiers = sk_POLICYQUALINFO_new_null();
1085 qual = POLICYQUALINFO_new();
1086 qual->pqualid = OBJ_nid2obj(NID_id_qt_cps);
1087 qual->d.cpsouri = M_ASN1_IA5STRING_new();
1088 ASN1_STRING_set(qual->d.cpsuri, url.edit(), url.len());
1089 sk_POLICYQUALINFO_push(pol->qualifiers, qual);
1091 #endif
1093 X509_EXTENSION *ex = X509V3_EXT_i2d(NID_certificate_policies, 0,
1094 sk_pinfo);
1095 X509_add_ext(cert, ex, -1);
1096 X509_EXTENSION_free(ex);
1097 sk_POLICYINFO_pop_free(sk_pinfo, POLICYINFO_free);
1101 WvString WvX509::get_extension(int nid) const
1103 CHECK_CERT_EXISTS_GET("extension", WvString::null);
1105 WvString retval = WvString::null;
1107 int index = X509_get_ext_by_NID(cert, nid, -1);
1108 if (index >= 0)
1110 X509_EXTENSION *ext = X509_get_ext(cert, index);
1112 if (ext)
1114 X509V3_EXT_METHOD *method = X509V3_EXT_get(ext);
1115 if (!method)
1117 WvDynBuf buf;
1118 buf.put(ext->value->data, ext->value->length);
1119 retval = buf.getstr();
1121 else
1123 void *ext_data = NULL;
1124 // we NEED to use a temporary pointer for ext_value_data,
1125 // as openssl's ASN1_item_d2i will muck around with it,
1126 // even though it's const (at least as of version 0.9.8e).
1127 // gah.
1128 #if OPENSSL_VERSION_NUMBER >= 0x0090800fL
1129 const unsigned char * ext_value_data = ext->value->data;
1130 #else
1131 unsigned char *ext_value_data = ext->value->data;
1132 #endif
1133 if (method->it)
1135 ext_data = ASN1_item_d2i(NULL, &ext_value_data,
1136 ext->value->length,
1137 ASN1_ITEM_ptr(method->it));
1138 TRACE("Applied generic conversion!\n");
1140 else
1142 ext_data = method->d2i(NULL, &ext_value_data,
1143 ext->value->length);
1144 TRACE("Applied method specific conversion!\n");
1147 if (method->i2s)
1149 TRACE("String Extension!\n");
1150 char *s = method->i2s(method, ext_data);
1151 retval = s;
1152 OPENSSL_free(s);
1154 else if (method->i2v)
1156 TRACE("Stack Extension!\n");
1157 CONF_VALUE *val = NULL;
1158 STACK_OF(CONF_VALUE) *svals = NULL;
1159 svals = method->i2v(method, ext_data, NULL);
1160 if (!sk_CONF_VALUE_num(svals))
1161 retval = "EMPTY";
1162 else
1164 WvStringList list;
1165 for(int i = 0; i < sk_CONF_VALUE_num(svals); i++)
1167 val = sk_CONF_VALUE_value(svals, i);
1168 if (!val->name)
1169 list.append(WvString(val->value));
1170 else if (!val->value)
1171 list.append(WvString(val->name));
1172 else
1174 WvString pair("%s:%s", val->name, val->value);
1175 list.append(pair);
1178 retval = list.join(";\n");
1180 sk_CONF_VALUE_pop_free(svals, X509V3_conf_free);
1182 else if (method->i2r)
1184 TRACE("Raw Extension!\n");
1185 WvDynBuf retvalbuf;
1186 BIO *bufbio = BIO_new(BIO_s_mem());
1187 BUF_MEM *bm;
1188 method->i2r(method, ext_data, bufbio, 0);
1189 BIO_get_mem_ptr(bufbio, &bm);
1190 retvalbuf.put(bm->data, bm->length);
1191 BIO_free(bufbio);
1192 retval = retvalbuf.getstr();
1195 if (method->it)
1196 ASN1_item_free((ASN1_VALUE *)ext_data,
1197 ASN1_ITEM_ptr(method->it));
1198 else
1199 method->ext_free(ext_data);
1204 else
1206 TRACE("Extension not present!\n");
1209 if (!!retval)
1210 TRACE("Returning: %s\n", retval);
1212 return retval;
1216 void WvX509::set_extension(int nid, WvStringParm _values)
1218 CHECK_CERT_EXISTS_SET("extension");
1220 WvString values(_values);
1221 X509_EXTENSION *ex = NULL;
1222 ex = X509V3_EXT_conf_nid(NULL, NULL, nid, values.edit());
1223 X509_add_ext(cert, ex, -1);
1224 X509_EXTENSION_free(ex);
1228 bool WvX509::isok() const
1230 return cert;
1234 WvString WvX509::errstr() const
1236 if (!cert)
1237 return "No certificate.";
1239 return WvString::empty;
1243 bool WvX509::verify(WvStringParm original, WvStringParm signature) const
1245 WvDynBuf buf;
1246 buf.putstr(original);
1247 return verify(buf, signature);
1251 bool WvX509::verify(WvBuf &original, WvStringParm signature) const
1253 unsigned char sig_buf[4096];
1254 size_t sig_size = sizeof(sig_buf);
1255 WvBase64Decoder().flushstrmem(signature, sig_buf, &sig_size, true);
1257 EVP_PKEY *pk = X509_get_pubkey(cert);
1258 if (!pk)
1259 return false;
1261 /* Verify the signature */
1262 EVP_MD_CTX sig_ctx;
1263 EVP_VerifyInit(&sig_ctx, EVP_sha1());
1264 EVP_VerifyUpdate(&sig_ctx, original.peek(0, original.used()), original.used());
1265 int sig_err = EVP_VerifyFinal(&sig_ctx, sig_buf, sig_size, pk);
1266 EVP_PKEY_free(pk);
1267 EVP_MD_CTX_cleanup(&sig_ctx); // Again, not my fault...
1268 if (sig_err != 1)
1270 debug("Verify failed!\n");
1271 return false;
1273 else
1274 return true;
1278 time_t ASN1_TIME_to_time_t(ASN1_TIME *t)
1280 struct tm newtime;
1281 char *p = NULL;
1282 char d[18];
1283 memset(&d,'\0',sizeof(d));
1284 memset(&newtime,'\0',sizeof newtime);
1286 if (t->type == V_ASN1_GENERALIZEDTIME)
1288 // For time values >= 2050, OpenSSL uses
1289 // ASN1_GENERALIZEDTIME - which we'll worry about
1290 // later.
1291 return 0;
1294 p = (char *)t->data;
1295 sscanf(p,"%2s%2s%2s%2s%2s%2sZ", d, &d[3], &d[6], &d[9], &d[12], &d[15]);
1297 int year = strtol(d, (char **)NULL, 10);
1298 if (year < 49)
1299 year += 100;
1300 else
1301 year += 50;
1303 newtime.tm_year = year;
1304 newtime.tm_mon = strtol(&d[3], (char **)NULL, 10) - 1;
1305 newtime.tm_mday = strtol(&d[6], (char **)NULL, 10);
1306 newtime.tm_hour = strtol(&d[9], (char **)NULL, 10);
1307 newtime.tm_min = strtol(&d[12], (char **)NULL, 10);
1308 newtime.tm_sec = strtol(&d[15], (char **)NULL, 10);
1310 return mktime(&newtime);
1314 time_t WvX509::get_notvalid_before() const
1316 CHECK_CERT_EXISTS_GET("not valid before", 0);
1318 return ASN1_TIME_to_time_t(X509_get_notBefore(cert));
1322 time_t WvX509::get_notvalid_after() const
1324 CHECK_CERT_EXISTS_GET("not valid after", 0);
1326 return ASN1_TIME_to_time_t(X509_get_notAfter(cert));
1330 WvString WvX509::get_ski() const
1332 CHECK_CERT_EXISTS_GET("ski", WvString::null);
1334 return get_extension(NID_subject_key_identifier);
1338 WvString WvX509::get_aki() const
1340 CHECK_CERT_EXISTS_GET("aki", WvString::null);
1342 WvStringList aki_list;
1343 parse_stack(get_extension(NID_authority_key_identifier), aki_list, "keyid:");
1344 if (aki_list.count())
1345 return aki_list.popstr();
1347 return WvString::null;
1351 WvString WvX509::get_fingerprint(const FprintMode mode) const
1353 CHECK_CERT_EXISTS_GET("fingerprint", WvString::null);
1355 /* Default to SHA-1 because OpenSSL does too */
1356 const EVP_MD *digest = EVP_sha1();
1357 if (mode == FingerMD5)
1358 digest = EVP_md5();
1360 unsigned char md[EVP_MAX_MD_SIZE];
1361 unsigned int n;
1362 if (!X509_digest(cert, digest, md, &n))
1364 errno = -ENOMEM;
1365 debug("get_fingerprint: Out of memory\n");
1366 return WvString::null;
1369 WvDynBuf store;
1370 char buf[3];
1371 unsigned int i = 0;
1372 do {
1373 sprintf(buf, "%02X", md[i]);
1374 store.putstr(buf);
1375 } while (++i < n && (store.putch(':'), 1));
1377 return store.getstr();
1381 void WvX509::set_ski()
1383 CHECK_CERT_EXISTS_SET("ski");
1385 ASN1_OCTET_STRING *oct = M_ASN1_OCTET_STRING_new();
1386 ASN1_BIT_STRING *pk = cert->cert_info->key->public_key;
1387 unsigned char pkey_dig[EVP_MAX_MD_SIZE];
1388 unsigned int diglen;
1390 EVP_Digest(pk->data, pk->length, pkey_dig, &diglen, EVP_sha1(), NULL);
1392 M_ASN1_OCTET_STRING_set(oct, pkey_dig, diglen);
1393 X509_EXTENSION *ext = X509V3_EXT_i2d(NID_subject_key_identifier, 0,
1394 oct);
1395 X509_add_ext(cert, ext, -1);
1396 X509_EXTENSION_free(ext);
1397 M_ASN1_OCTET_STRING_free(oct);
1401 void WvX509::set_aki(const WvX509 &cacert)
1403 CHECK_CERT_EXISTS_SET("aki");
1405 // can't set a meaningful AKI for subordinate certification without the
1406 // parent having an SKI
1407 ASN1_OCTET_STRING *ikeyid = NULL;
1408 X509_EXTENSION *ext;
1409 int i = X509_get_ext_by_NID(cacert.cert, NID_subject_key_identifier, -1);
1410 if ((i >= 0) && (ext = X509_get_ext(cacert.cert, i)))
1411 ikeyid = static_cast<ASN1_OCTET_STRING *>(X509V3_EXT_d2i(ext));
1413 if (!ikeyid)
1414 return;
1416 AUTHORITY_KEYID *akeyid = AUTHORITY_KEYID_new();
1417 akeyid->issuer = NULL;
1418 akeyid->serial = NULL;
1419 akeyid->keyid = ikeyid;
1420 ext = X509V3_EXT_i2d(NID_authority_key_identifier, 0, akeyid);
1421 X509_add_ext(cert, ext, -1);
1422 X509_EXTENSION_free(ext);
1423 AUTHORITY_KEYID_free(akeyid);