Saner handling if config.mk doesn't exist: use a default config.defaults.mk.
[wvstreams.git] / crypto / wvx509.cc
blob47ef96b8d417f834eaeeefbbd32d1dcc2441e13c
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"
15 #include <openssl/pem.h>
16 #include <openssl/x509v3.h>
17 #include <openssl/err.h>
18 #include <openssl/sha.h>
19 #include <openssl/ssl.h>
21 // enable this to add some extra debugging trace messages (this can be VERY
22 // verbose)
23 #if 0
24 # define TRACE(x, y...) debug(x, ## y);
25 #else
26 #ifndef _MSC_VER
27 # define TRACE(x, y...)
28 #else
29 # define TRACE
30 #endif
31 #endif
33 // helper method to let us return and warn gracefully when getting/setting an
34 // element in a null certificate
35 static const char * warning_str_set = "Tried to set %s, but certificate not ok.\n";
36 static const char * warning_str_get = "Tried to get %s, but certificate not ok.\n";
37 #define CHECK_CERT_EXISTS_SET(x) \
38 if (!cert) { \
39 debug(WvLog::Warning, warning_str_set, x); \
40 return; \
42 #define CHECK_CERT_EXISTS_GET(x, y) \
43 if (!cert) { \
44 debug(WvLog::Warning, warning_str_get, x); \
45 return y; \
49 UUID_MAP_BEGIN(WvX509)
50 UUID_MAP_ENTRY(IObject)
51 UUID_MAP_END
53 static int ssl_init_count = 0;
56 void wvssl_init()
58 if (!ssl_init_count)
60 SSL_library_init();
61 SSL_load_error_strings();
62 ERR_load_BIO_strings();
63 ERR_load_crypto_strings();
64 OpenSSL_add_all_algorithms();
65 OpenSSL_add_all_ciphers();
66 OpenSSL_add_all_digests();
69 ssl_init_count++;
73 void wvssl_free()
75 if (ssl_init_count >= 1)
76 ssl_init_count--;
78 if (!ssl_init_count)
80 ERR_free_strings();
81 EVP_cleanup();
86 WvString wvssl_errstr()
88 char buf[256];
89 ERR_error_string_n(ERR_get_error(), buf, sizeof(buf));
90 buf[sizeof(buf)-1] = 0;
91 return buf;
95 WvX509::WvX509(X509 *_cert)
96 : debug("X509", WvLog::Debug5)
98 wvssl_init();
99 cert = _cert;
103 WvX509::WvX509()
104 : debug("X509", WvLog::Debug5)
106 wvssl_init();
107 cert = NULL;
111 WvX509::~WvX509()
113 TRACE("Deleting.\n");
115 if (cert)
116 X509_free(cert);
118 wvssl_free();
123 // The people who designed this garbage should be shot!
124 // Support old versions of openssl...
125 #ifndef NID_domainComponent
126 #define NID_domainComponent 391
127 #endif
129 #ifndef NID_Domain
130 #define NID_Domain 392
131 #endif
134 // returns some approximation of the server's fqdn, or an empty string.
135 static WvString set_name_entry(X509_NAME *name, WvStringParm dn)
137 WvString fqdn(""), force_fqdn("");
138 X509_NAME_ENTRY *ne = NULL;
139 int count = 0, nid;
141 WvStringList l;
142 l.split(dn, ",");
144 // dn is of the form: c=ca,o=foo organization,dc=foo,dc=com
145 // (ie. name=value pairs separated by commas)
146 WvStringList::Iter i(l);
147 for (i.rewind(); i.next(); )
149 WvString s(*i), sid;
150 char *cptr, *value;
152 cptr = s.edit();
153 value = strchr(cptr, '=');
154 if (value)
155 *value++ = 0;
156 else
157 value = (char*)"NULL";
159 sid = strlwr(trim_string(cptr));
161 if (sid == "c")
162 nid = NID_countryName;
163 else if (sid == "st")
164 nid = NID_stateOrProvinceName;
165 else if (sid == "l")
166 nid = NID_localityName;
167 else if (sid == "o")
168 nid = NID_organizationName;
169 else if (sid == "ou")
170 nid = NID_organizationalUnitName;
171 else if (sid == "cn")
173 nid = NID_commonName;
174 force_fqdn = value;
176 else if (sid == "dc")
178 nid = NID_domainComponent;
179 if (!!fqdn)
180 fqdn.append(".");
181 fqdn.append(value);
183 else if (sid == "domain")
185 nid = NID_Domain;
186 force_fqdn = value;
188 else if (sid == "email")
189 nid = NID_pkcs9_emailAddress;
190 else
191 nid = NID_domainComponent;
193 // Sometimes we just want to parse dn into fqdn.
194 if (name == NULL)
195 continue;
197 if (!ne)
198 ne = X509_NAME_ENTRY_create_by_NID(NULL, nid,
199 V_ASN1_APP_CHOOSE, (unsigned char *)value, -1);
200 else
201 X509_NAME_ENTRY_create_by_NID(&ne, nid,
202 V_ASN1_APP_CHOOSE, (unsigned char *)value, -1);
203 if (!ne)
204 continue;
206 X509_NAME_add_entry(name, ne, count++, 0);
209 X509_NAME_ENTRY_free(ne);
211 if (!!force_fqdn)
212 return force_fqdn;
214 return fqdn;
218 WvRSAKey *WvX509::get_rsa_pub() const
220 EVP_PKEY *pkcert = X509_get_pubkey(cert);
221 RSA *certrsa = EVP_PKEY_get1_RSA(pkcert);
222 EVP_PKEY_free(pkcert);
223 return new WvRSAKey(certrsa, false);
227 WvString WvX509::certreq(WvStringParm subject, const WvRSAKey &rsa)
229 WvLog debug("X509::certreq", WvLog::Debug5);
231 EVP_PKEY *pk = NULL;
232 X509_NAME *name = NULL;
233 X509_REQ *certreq = NULL;
235 // double check RSA key
236 if (rsa.isok())
237 debug("RSA Key is fine.\n");
238 else
240 debug(WvLog::Warning, "RSA Key is bad");
241 return WvString::null;
244 if ((pk=EVP_PKEY_new()) == NULL)
246 debug(WvLog::Warning, "Error creating key handler for new certificate");
247 return WvString::null;
250 if ((certreq=X509_REQ_new()) == NULL)
252 debug(WvLog::Warning, "Error creating new PKCS#10 object");
253 EVP_PKEY_free(pk);
254 return WvString::null;
257 if (!EVP_PKEY_set1_RSA(pk, rsa.rsa))
259 debug(WvLog::Warning, "Error adding RSA keys to certificate");
260 X509_REQ_free(certreq);
261 EVP_PKEY_free(pk);
262 return WvString::null;
265 X509_REQ_set_version(certreq, 0); /* version 1 */
267 X509_REQ_set_pubkey(certreq, pk);
269 name = X509_REQ_get_subject_name(certreq);
271 debug("Creating Certificate request for %s\n", subject);
272 set_name_entry(name, subject);
273 X509_REQ_set_subject_name(certreq, name);
274 char *sub_name = X509_NAME_oneline(X509_REQ_get_subject_name(certreq),
275 0, 0);
276 debug("SubjectDN: %s\n", sub_name);
277 OPENSSL_free(sub_name);
279 if (!X509_REQ_sign(certreq, pk, EVP_sha1()))
281 debug(WvLog::Warning, "Could not self sign the request");
282 X509_REQ_free(certreq);
283 EVP_PKEY_free(pk);
284 return WvString::null;
287 int verify_result = X509_REQ_verify(certreq, pk);
288 if (verify_result == 0)
290 debug(WvLog::Warning, "Self signed request failed");
291 X509_REQ_free(certreq);
292 EVP_PKEY_free(pk);
293 return WvString::null;
295 else
297 debug("Self Signed Certificate Request verifies OK!\n");
300 // Horribly involuted hack to get around the fact that the
301 // OpenSSL people are too braindead to have a PEM_write function
302 // that returns a char *
303 WvDynBuf retval;
304 BIO *bufbio = BIO_new(BIO_s_mem());
305 BUF_MEM *bm;
307 PEM_write_bio_X509_REQ(bufbio, certreq);
308 BIO_get_mem_ptr(bufbio, &bm);
309 retval.put(bm->data, bm->length);
311 X509_REQ_free(certreq);
312 EVP_PKEY_free(pk);
313 BIO_free(bufbio);
315 return retval.getstr();
319 bool WvX509::validate(WvX509 *cacert) const
321 if (cert == NULL)
323 debug(WvLog::Warning, "Tried to validate certificate against CA, but "
324 "certificate is blank!\n");
325 return false;
328 bool retval = true;
330 // Check and make sure that the certificate is still valid
331 if (X509_cmp_current_time(X509_get_notAfter(cert)) < 0)
333 debug("Certificate has expired.\n");
334 retval = false;
337 if (X509_cmp_current_time(X509_get_notBefore(cert)) > 0)
339 debug("Certificate is not yet valid.\n");
340 retval = false;
343 if (cacert)
345 retval &= signedbyca(*cacert);
346 retval &= issuedbyca(*cacert);
349 return retval;
353 bool WvX509::signedbyca(WvX509 &cacert) const
355 if (!cert || !cacert.cert)
357 debug(WvLog::Warning, "Tried to determine if certificate was signed "
358 "by CA, but either client or CA certificate (or both) are "
359 "blank.\n");
360 return false;
363 EVP_PKEY *pkey = X509_get_pubkey(cacert.cert);
364 int result = X509_verify(cert, pkey);
365 EVP_PKEY_free(pkey);
367 if (result < 0)
369 debug("There was an error determining whether or not we were signed by "
370 "CA '%s'.\n", cacert.get_subject());
371 return false;
373 bool issigned = (result > 0);
375 debug("Certificate was%s signed by CA %s.\n", issigned ? "" : " NOT",
376 cacert.get_subject());
378 return issigned;
382 bool WvX509::issuedbyca(WvX509 &cacert) const
384 if (!cert || !cacert.cert)
386 debug(WvLog::Warning, "Tried to determine if certificate was issued "
387 "by CA, but either client or CA certificate (or both) are "
388 "blank.\n");
389 return false;
392 int ret = X509_check_issued(cacert.cert, cert);
393 debug("issuedbyca: %s==X509_V_OK(%s)\n", ret, X509_V_OK);
394 if (ret != X509_V_OK)
395 return false;
397 return true;
401 WvString WvX509::encode(const DumpMode mode) const
403 WvDynBuf retval;
404 encode(mode, retval);
405 return retval.getstr();
409 void WvX509::encode(const DumpMode mode, WvBuf &buf) const
411 if (mode == CertFileDER || mode == CertFilePEM)
412 return; // file modes are no ops with encode
414 if (!cert)
416 debug(WvLog::Warning, "Tried to encode certificate, but certificate "
417 "is blank!\n");
418 return;
421 debug("Encoding X509 certificate.\n");
423 if (mode == CertHex)
425 size_t size;
426 unsigned char *keybuf, *iend;
427 WvString enccert;
429 size = i2d_X509(cert, NULL);
430 iend = keybuf = new unsigned char[size];
431 i2d_X509(cert, &iend);
433 enccert.setsize(size * 2 +1);
434 ::hexify(enccert.edit(), keybuf, size);
436 deletev keybuf;
437 buf.putstr(enccert);
439 else
441 BIO *bufbio = BIO_new(BIO_s_mem());
442 BUF_MEM *bm;
444 if (mode == CertPEM)
445 PEM_write_bio_X509(bufbio, cert);
446 else if (mode == CertDER)
447 i2d_X509_bio(bufbio, cert);
448 else
449 debug(WvLog::Warning, "Tried to encode certificate with unknown "
450 "mode!\n");
452 BIO_get_mem_ptr(bufbio, &bm);
453 buf.put(bm->data, bm->length);
454 BIO_free(bufbio);
459 void WvX509::decode(const DumpMode mode, WvStringParm str)
461 if (cert)
463 debug("Replacing an already existant X509 certificate.\n");
464 X509_free(cert);
465 cert = NULL;
468 if (mode == CertFileDER)
470 BIO *bio = BIO_new(BIO_s_file());
472 if (BIO_read_filename(bio, str.cstr()) <= 0)
474 debug(WvLog::Warning, "Open '%s': %s\n", str, wvssl_errstr());
475 BIO_free(bio);
476 return;
479 if (!(cert = d2i_X509_bio(bio, NULL)))
480 debug(WvLog::Warning, "Import DER from '%s': %s\n",
481 str, wvssl_errstr());
483 BIO_free(bio);
484 return;
486 else if (mode == CertFilePEM)
488 FILE *fp = fopen(str, "rb");
489 if (!fp)
491 int errnum = errno;
492 debug("Open '%s': %s\n", str, strerror(errnum));
493 return;
496 if (!(cert = PEM_read_X509(fp, NULL, NULL, NULL)))
497 debug(WvLog::Warning, "Import PEM from '%s': %s\n",
498 str, wvssl_errstr());
500 fclose(fp);
501 return;
503 else if (mode == CertHex)
505 int hexbytes = str.len();
506 int bufsize = hexbytes/2;
507 unsigned char *certbuf = new unsigned char[bufsize];
508 unsigned char *cp = certbuf;
509 X509 *tmpcert;
511 ::unhexify(certbuf, str);
512 tmpcert = cert = X509_new();
513 cert = wv_d2i_X509(&tmpcert, &cp, bufsize);
514 delete[] certbuf;
515 return;
518 // we use the buffer decode functions for everything else
519 WvDynBuf buf;
520 buf.putstr(str);
521 decode(mode, buf);
525 void WvX509::decode(const DumpMode mode, WvBuf &encoded)
527 if (cert)
529 debug("Replacing an already existant X509 certificate.\n");
530 X509_free(cert);
531 cert = NULL;
534 if (mode == CertHex || mode == CertFileDER || mode == CertFilePEM)
535 decode(mode, encoded.getstr());
536 else
538 BIO *membuf = BIO_new(BIO_s_mem());
539 BIO_write(membuf, encoded.get(encoded.used()), encoded.used());
541 if (mode == CertPEM)
542 cert = PEM_read_bio_X509(membuf, NULL, NULL, NULL);
543 else if (mode == CertDER)
544 cert = d2i_X509_bio(membuf, NULL);
545 else
546 debug(WvLog::Warning, "Tried to encode certificate with unknown "
547 "mode!\n");
549 BIO_free_all(membuf);
554 WvString WvX509::get_issuer() const
556 CHECK_CERT_EXISTS_GET("issuer", WvString::null);
558 char *name = X509_NAME_oneline(X509_get_issuer_name(cert),0,0);
559 WvString retval(name);
560 OPENSSL_free(name);
561 return retval;
565 void WvX509::set_issuer(WvStringParm issuer)
567 CHECK_CERT_EXISTS_SET("issuer");
569 X509_NAME *name = X509_get_issuer_name(cert);
570 set_name_entry(name, issuer);
571 X509_set_issuer_name(cert, name);
575 void WvX509::set_issuer(const WvX509 &cacert)
577 CHECK_CERT_EXISTS_SET("issuer");
579 X509_NAME *casubj = X509_get_subject_name(cacert.cert);
580 X509_set_issuer_name(cert, casubj);
584 WvString WvX509::get_subject() const
586 CHECK_CERT_EXISTS_GET("subject", WvString::null);
588 char *name = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
589 WvString retval(name);
590 OPENSSL_free(name);
591 return retval;
595 void WvX509::set_subject(WvStringParm subject)
597 CHECK_CERT_EXISTS_SET("subject");
599 X509_NAME *name = X509_get_subject_name(cert);
600 set_name_entry(name, subject);
601 X509_set_subject_name(cert, name);
605 void WvX509::set_subject(X509_NAME *name)
607 CHECK_CERT_EXISTS_SET("subject");
609 X509_set_subject_name(cert, name);
613 void WvX509::set_pubkey(WvRSAKey &_rsa)
615 CHECK_CERT_EXISTS_SET("pubkey");
617 EVP_PKEY *pk = EVP_PKEY_new();
618 assert(pk);
620 // Assign RSA Key from WvRSAKey into stupid package that OpenSSL needs
621 if (!EVP_PKEY_set1_RSA(pk, _rsa.rsa))
623 debug("Error adding RSA keys to certificate.\n");
624 return;
627 X509_set_pubkey(cert, pk);
629 EVP_PKEY_free(pk);
634 void WvX509::set_nsserver(WvStringParm servername)
636 CHECK_CERT_EXISTS_SET("nsserver");
638 WvString fqdn;
640 // FQDN cannot have a = in it, therefore it
641 // must be a distinguished name :)
642 if (strchr(servername, '='))
643 fqdn = set_name_entry(NULL, servername);
644 else
645 fqdn = servername;
647 if (!fqdn)
648 fqdn = "null.noname.null";
650 debug("Setting Netscape SSL server name extension to '%s'.\n", fqdn);
652 // Add in the netscape-specific server extension
653 set_extension(NID_netscape_cert_type, "server");
654 set_extension(NID_netscape_ssl_server_name, fqdn);
658 WvString WvX509::get_nsserver() const
660 return get_extension(NID_netscape_ssl_server_name);
664 WvString WvX509::get_serial() const
666 CHECK_CERT_EXISTS_GET("serial", WvString::null);
668 BIGNUM *bn = BN_new();
669 bn = ASN1_INTEGER_to_BN(X509_get_serialNumber(cert), bn);
670 char * c = BN_bn2dec(bn);
671 WvString ret("%s", c);
672 OPENSSL_free(c);
673 BN_free(bn);
674 return ret;
678 void WvX509::set_version()
680 CHECK_CERT_EXISTS_SET("version");
682 X509_set_version(cert, 0x2);
686 void WvX509::set_serial(long serial)
688 CHECK_CERT_EXISTS_SET("serial");
690 ASN1_INTEGER_set(X509_get_serialNumber(cert), serial);
694 WvString WvX509::get_crl_dp() const
696 return get_extension(NID_crl_distribution_points);
700 void WvX509::set_lifetime(long seconds)
702 CHECK_CERT_EXISTS_SET("lifetime");
704 // Set the NotBefore time to now.
705 X509_gmtime_adj(X509_get_notBefore(cert), 0);
707 // Now + 10 years... should be shorter, but since we don't currently
708 // have a set of routines to refresh the certificates, make it
709 // REALLY long.
710 X509_gmtime_adj(X509_get_notAfter(cert), seconds);
714 void WvX509::set_key_usage(WvStringParm values)
716 set_extension(NID_key_usage, values);
720 WvString WvX509::get_key_usage() const
722 return get_extension(NID_key_usage);
726 void WvX509::set_ext_key_usage(WvStringParm values)
728 set_extension(NID_ext_key_usage, values);
732 WvString WvX509::get_ext_key_usage() const
734 return get_extension(NID_ext_key_usage);
738 WvString WvX509::get_altsubject() const
740 return get_extension(NID_subject_alt_name);
744 bool WvX509::get_basic_constraints(bool &ca, int &pathlen) const
746 CHECK_CERT_EXISTS_GET("basic constraints", false);
748 BASIC_CONSTRAINTS *constraints = NULL;
749 int i;
751 constraints = static_cast<BASIC_CONSTRAINTS *>(X509_get_ext_d2i(
752 cert, NID_basic_constraints,
753 &i, NULL));
754 if (constraints)
756 ca = constraints->ca;
757 if (constraints->pathlen)
759 if ((constraints->pathlen->type == V_ASN1_NEG_INTEGER) || !ca)
761 debug("Path length type not valid when getting basic "
762 "constraints.\n");
763 BASIC_CONSTRAINTS_free(constraints);
764 pathlen = 0;
765 return false;
768 pathlen = ASN1_INTEGER_get(constraints->pathlen);
770 else
771 pathlen = (-1);
773 BASIC_CONSTRAINTS_free(constraints);
774 return true;
777 debug("Basic constraints extension not present.\n");
778 return false;
782 void WvX509::set_basic_constraints(bool ca, int pathlen)
784 CHECK_CERT_EXISTS_SET("basic constraints");
786 BASIC_CONSTRAINTS *constraints = BASIC_CONSTRAINTS_new();
788 constraints->ca = static_cast<int>(ca);
789 if (pathlen != (-1))
791 ASN1_INTEGER *i = ASN1_INTEGER_new();
792 ASN1_INTEGER_set(i, pathlen);
793 constraints->pathlen = i;
796 X509_EXTENSION *ex = X509V3_EXT_i2d(NID_basic_constraints, 0,
797 constraints);
798 while (int idx = X509_get_ext_by_NID(cert, NID_basic_constraints, 0) >= 0)
800 debug("Found extension at idx %s\n", idx);
801 X509_EXTENSION *tmpex = X509_delete_ext(cert, idx);
802 X509_EXTENSION_free(tmpex);
805 X509_add_ext(cert, ex, NID_basic_constraints);
806 X509_EXTENSION_free(ex);
807 BASIC_CONSTRAINTS_free(constraints);
811 bool WvX509::get_policy_constraints(int &require_explicit_policy,
812 int &inhibit_policy_mapping) const
814 CHECK_CERT_EXISTS_GET("policy constraints", false);
816 POLICY_CONSTRAINTS *constraints = NULL;
817 int i;
819 constraints = static_cast<POLICY_CONSTRAINTS *>(X509_get_ext_d2i(
820 cert, NID_policy_constraints,
821 &i, NULL));
822 if (constraints)
824 if (constraints->requireExplicitPolicy)
825 require_explicit_policy = ASN1_INTEGER_get(
826 constraints->requireExplicitPolicy);
827 else
828 require_explicit_policy = (-1);
830 if (constraints->inhibitPolicyMapping)
831 inhibit_policy_mapping = ASN1_INTEGER_get(
832 constraints->inhibitPolicyMapping);
833 else
834 inhibit_policy_mapping = (-1);
835 POLICY_CONSTRAINTS_free(constraints);
836 return true;
839 return false;
843 void WvX509::set_policy_constraints(int require_explicit_policy,
844 int inhibit_policy_mapping)
846 CHECK_CERT_EXISTS_SET("policy constraints");
848 POLICY_CONSTRAINTS *constraints = POLICY_CONSTRAINTS_new();
850 ASN1_INTEGER *i = ASN1_INTEGER_new();
851 ASN1_INTEGER_set(i, require_explicit_policy);
852 constraints->requireExplicitPolicy = i;
853 i = ASN1_INTEGER_new();
854 ASN1_INTEGER_set(i, inhibit_policy_mapping);
855 constraints->inhibitPolicyMapping = i;
857 X509_EXTENSION *ex = X509V3_EXT_i2d(NID_policy_constraints, 0,
858 constraints);
859 X509_add_ext(cert, ex, -1);
860 X509_EXTENSION_free(ex);
861 POLICY_CONSTRAINTS_free(constraints);
865 bool WvX509::get_policy_mapping(PolicyMapList &list) const
867 CHECK_CERT_EXISTS_GET("policy mapping", false);
869 POLICY_MAPPINGS *mappings = NULL;
870 POLICY_MAPPING *map = NULL;
871 int i;
873 mappings = static_cast<POLICY_MAPPINGS *>(X509_get_ext_d2i(
874 cert, NID_policy_mappings,
875 &i, NULL));
876 if (!mappings)
877 return false;
879 const int POLICYID_MAXLEN = 80;
880 char tmp1[80];
881 char tmp2[80];
882 for(int j = 0; j < sk_POLICY_MAPPING_num(mappings); j++)
884 map = sk_POLICY_MAPPING_value(mappings, j);
885 OBJ_obj2txt(tmp1, POLICYID_MAXLEN, map->issuerDomainPolicy, true);
886 OBJ_obj2txt(tmp2, POLICYID_MAXLEN, map->subjectDomainPolicy, true);
887 list.append(new PolicyMap(tmp1, tmp2), true);
890 sk_POLICY_MAPPING_pop_free(mappings, POLICY_MAPPING_free);
892 return true;
896 void WvX509::set_policy_mapping(PolicyMapList &list)
898 CHECK_CERT_EXISTS_SET("policy mapping");
900 POLICY_MAPPINGS *maps = sk_POLICY_MAPPING_new_null();
902 PolicyMapList::Iter i(list);
903 for (i.rewind(); i.next();)
905 POLICY_MAPPING *map = POLICY_MAPPING_new();
906 map->issuerDomainPolicy = OBJ_txt2obj(i().issuer_domain.cstr(), 0);
907 map->subjectDomainPolicy = OBJ_txt2obj(i().subject_domain.cstr(), 0);
908 sk_POLICY_MAPPING_push(maps, map);
909 printf("Push!\n");
912 X509_EXTENSION *ex = X509V3_EXT_i2d(NID_policy_mappings, 0, maps);
913 X509_add_ext(cert, ex, -1);
914 X509_EXTENSION_free(ex);
915 sk_POLICY_MAPPING_pop_free(maps, POLICY_MAPPING_free);
919 static void add_aia(WvStringParm type, WvString identifier, AUTHORITY_INFO_ACCESS *ainfo)
921 ACCESS_DESCRIPTION *acc = ACCESS_DESCRIPTION_new();
922 sk_ACCESS_DESCRIPTION_push(ainfo, acc);
923 acc->method = OBJ_txt2obj(type.cstr(), 0);
924 acc->location->type = GEN_URI;
925 acc->location->d.ia5 = M_ASN1_IA5STRING_new();
926 unsigned char *cident = reinterpret_cast<unsigned char *>(identifier.edit());
927 ASN1_STRING_set(acc->location->d.ia5, cident, identifier.len());
931 void WvX509::set_aia(WvStringList &ca_urls,
932 WvStringList &responders)
934 CHECK_CERT_EXISTS_SET("aia");
936 AUTHORITY_INFO_ACCESS *ainfo = sk_ACCESS_DESCRIPTION_new_null();
938 WvStringList::Iter i(ca_urls);
939 for (i.rewind(); i.next();)
940 add_aia("caIssuers", i(), ainfo);
942 WvStringList::Iter j(responders);
943 for (j.rewind(); j.next();)
944 add_aia("OCSP", j(), ainfo);
946 X509_EXTENSION *ex = X509V3_EXT_i2d(NID_info_access, 0, ainfo);
947 X509_add_ext(cert, ex, -1);
948 X509_EXTENSION_free(ex);
949 sk_ACCESS_DESCRIPTION_pop_free(ainfo, ACCESS_DESCRIPTION_free);
953 WvString WvX509::get_aia() const
955 return get_extension(NID_info_access);
959 static void parse_stack(WvStringParm ext, WvStringList &list, WvStringParm prefix)
961 WvStringList stack;
962 stack.split(ext, ";\n");
963 WvStringList::Iter i(stack);
964 for (i.rewind();i.next();)
966 WvString stack_entry(*i);
967 if (strstr(stack_entry, prefix))
969 WvString uri(stack_entry.edit() + prefix.len());
970 list.append(uri);
976 void WvX509::get_ocsp(WvStringList &responders) const
978 parse_stack(get_aia(), responders, "OCSP - URI:");
982 void WvX509::get_ca_urls(WvStringList &urls) const
984 parse_stack(get_aia(), urls, "CA Issuers - URI:");
988 void WvX509::get_crl_urls(WvStringList &urls) const
990 parse_stack(get_crl_dp(), urls, "URI:");
994 void WvX509::set_crl_urls(WvStringList &urls)
996 CHECK_CERT_EXISTS_SET("CRL urls");
998 STACK_OF(DIST_POINT) *crldp = sk_DIST_POINT_new_null();
999 WvStringList::Iter i(urls);
1000 for (i.rewind(); i.next();)
1002 DIST_POINT *point = DIST_POINT_new();
1003 sk_DIST_POINT_push(crldp, point);
1005 GENERAL_NAMES *uris = GENERAL_NAMES_new();
1006 GENERAL_NAME *uri = GENERAL_NAME_new();
1007 uri->type = GEN_URI;
1008 uri->d.ia5 = M_ASN1_IA5STRING_new();
1009 unsigned char *cident = reinterpret_cast<unsigned char *>(i().edit());
1010 ASN1_STRING_set(uri->d.ia5, cident, i().len());
1011 sk_GENERAL_NAME_push(uris, uri);
1013 point->distpoint = DIST_POINT_NAME_new();
1014 point->distpoint->name.fullname = uris;
1015 point->distpoint->type = 0;
1018 X509_EXTENSION *ex = X509V3_EXT_i2d(NID_crl_distribution_points, 0, crldp);
1019 X509_add_ext(cert, ex, -1);
1020 X509_EXTENSION_free(ex);
1021 sk_DIST_POINT_pop_free(crldp, DIST_POINT_free);
1025 bool WvX509::get_policies(WvStringList &policy_oids) const
1027 CHECK_CERT_EXISTS_GET("policies", false);
1029 int critical;
1030 CERTIFICATEPOLICIES * policies = static_cast<CERTIFICATEPOLICIES *>(
1031 X509_get_ext_d2i(cert, NID_certificate_policies, &critical, NULL));
1032 if (policies)
1034 for (int i = 0; i < sk_POLICYINFO_num(policies); i++)
1036 POLICYINFO * policy = sk_POLICYINFO_value(policies, i);
1037 const int POLICYID_MAXLEN = 80;
1039 char policyid[POLICYID_MAXLEN];
1040 OBJ_obj2txt(policyid, POLICYID_MAXLEN, policy->policyid,
1041 true); // don't substitute human-readable names
1042 policy_oids.append(policyid);
1045 sk_POLICYINFO_pop_free(policies, POLICYINFO_free);
1046 return true;
1049 return false;
1053 void WvX509::set_policies(WvStringList &policy_oids)
1055 CHECK_CERT_EXISTS_SET("policies");
1057 STACK_OF(POLICYINFO) *sk_pinfo = sk_POLICYINFO_new_null();
1059 WvStringList::Iter i(policy_oids);
1060 for (i.rewind(); i.next();)
1062 ASN1_OBJECT *pobj = OBJ_txt2obj(i(), 0);
1063 POLICYINFO *pol = POLICYINFO_new();
1064 pol->policyid = pobj;
1065 sk_POLICYINFO_push(sk_pinfo, pol);
1068 #if 0
1069 // this code would let you set URL information to a policy
1070 // qualifier
1071 POLICYQUALINFO *qual = NULL;
1072 WvString url(_url);
1073 if (!!url)
1075 pol->qualifiers = sk_POLICYQUALINFO_new_null();
1076 qual = POLICYQUALINFO_new();
1077 qual->pqualid = OBJ_nid2obj(NID_id_qt_cps);
1078 qual->d.cpsouri = M_ASN1_IA5STRING_new();
1079 ASN1_STRING_set(qual->d.cpsuri, url.edit(), url.len());
1080 sk_POLICYQUALINFO_push(pol->qualifiers, qual);
1082 #endif
1084 X509_EXTENSION *ex = X509V3_EXT_i2d(NID_certificate_policies, 0,
1085 sk_pinfo);
1086 X509_add_ext(cert, ex, -1);
1087 X509_EXTENSION_free(ex);
1088 sk_POLICYINFO_pop_free(sk_pinfo, POLICYINFO_free);
1092 WvString WvX509::get_extension(int nid) const
1094 CHECK_CERT_EXISTS_GET("extension", WvString::null);
1096 WvString retval = WvString::null;
1098 int index = X509_get_ext_by_NID(cert, nid, -1);
1099 if (index >= 0)
1101 X509_EXTENSION *ext = X509_get_ext(cert, index);
1103 if (ext)
1105 X509V3_EXT_METHOD *method = X509V3_EXT_get(ext);
1106 if (!method)
1108 WvDynBuf buf;
1109 buf.put(ext->value->data, ext->value->length);
1110 retval = buf.getstr();
1112 else
1114 void *ext_data = NULL;
1115 // we NEED to use a temporary pointer for ext_value_data,
1116 // as openssl's ASN1_item_d2i will muck around with it,
1117 // even though it's const (at least as of version 0.9.8e).
1118 // gah.
1119 #if OPENSSL_VERSION_NUMBER >= 0x0090800fL
1120 const unsigned char * ext_value_data = ext->value->data;
1121 #else
1122 unsigned char *ext_value_data = ext->value->data;
1123 #endif
1124 if (method->it)
1126 ext_data = ASN1_item_d2i(NULL, &ext_value_data,
1127 ext->value->length,
1128 ASN1_ITEM_ptr(method->it));
1129 TRACE("Applied generic conversion!\n");
1131 else
1133 ext_data = method->d2i(NULL, &ext_value_data,
1134 ext->value->length);
1135 TRACE("Applied method specific conversion!\n");
1138 if (method->i2s)
1140 TRACE("String Extension!\n");
1141 char *s = method->i2s(method, ext_data);
1142 retval = s;
1143 OPENSSL_free(s);
1145 else if (method->i2v)
1147 TRACE("Stack Extension!\n");
1148 CONF_VALUE *val = NULL;
1149 STACK_OF(CONF_VALUE) *svals = NULL;
1150 svals = method->i2v(method, ext_data, NULL);
1151 if (!sk_CONF_VALUE_num(svals))
1152 retval = "EMPTY";
1153 else
1155 WvStringList list;
1156 for(int i = 0; i < sk_CONF_VALUE_num(svals); i++)
1158 val = sk_CONF_VALUE_value(svals, i);
1159 if (!val->name)
1160 list.append(WvString(val->value));
1161 else if (!val->value)
1162 list.append(WvString(val->name));
1163 else
1165 WvString pair("%s:%s", val->name, val->value);
1166 list.append(pair);
1169 retval = list.join(";\n");
1171 sk_CONF_VALUE_pop_free(svals, X509V3_conf_free);
1173 else if (method->i2r)
1175 TRACE("Raw Extension!\n");
1176 WvDynBuf retvalbuf;
1177 BIO *bufbio = BIO_new(BIO_s_mem());
1178 BUF_MEM *bm;
1179 method->i2r(method, ext_data, bufbio, 0);
1180 BIO_get_mem_ptr(bufbio, &bm);
1181 retvalbuf.put(bm->data, bm->length);
1182 BIO_free(bufbio);
1183 retval = retvalbuf.getstr();
1186 if (method->it)
1187 ASN1_item_free((ASN1_VALUE *)ext_data,
1188 ASN1_ITEM_ptr(method->it));
1189 else
1190 method->ext_free(ext_data);
1195 else
1197 TRACE("Extension not present!\n");
1200 if (!!retval)
1201 TRACE("Returning: %s\n", retval);
1203 return retval;
1207 void WvX509::set_extension(int nid, WvStringParm _values)
1209 CHECK_CERT_EXISTS_SET("extension");
1211 WvString values(_values);
1212 X509_EXTENSION *ex = NULL;
1213 ex = X509V3_EXT_conf_nid(NULL, NULL, nid, values.edit());
1214 X509_add_ext(cert, ex, -1);
1215 X509_EXTENSION_free(ex);
1219 bool WvX509::isok() const
1221 return cert;
1225 WvString WvX509::errstr() const
1227 if (!cert)
1228 return "No certificate.";
1230 return WvString::empty;
1234 bool WvX509::verify(WvStringParm original, WvStringParm signature) const
1236 WvDynBuf buf;
1237 buf.putstr(original);
1238 return verify(buf, signature);
1242 bool WvX509::verify(WvBuf &original, WvStringParm signature) const
1244 unsigned char sig_buf[4096];
1245 size_t sig_size = sizeof(sig_buf);
1246 WvBase64Decoder().flushstrmem(signature, sig_buf, &sig_size, true);
1248 EVP_PKEY *pk = X509_get_pubkey(cert);
1249 if (!pk)
1250 return false;
1252 /* Verify the signature */
1253 EVP_MD_CTX sig_ctx;
1254 EVP_VerifyInit(&sig_ctx, EVP_sha1());
1255 EVP_VerifyUpdate(&sig_ctx, original.peek(0, original.used()), original.used());
1256 int sig_err = EVP_VerifyFinal(&sig_ctx, sig_buf, sig_size, pk);
1257 EVP_PKEY_free(pk);
1258 EVP_MD_CTX_cleanup(&sig_ctx); // Again, not my fault...
1259 if (sig_err != 1)
1261 debug("Verify failed!\n");
1262 return false;
1264 else
1265 return true;
1269 time_t ASN1_TIME_to_time_t(ASN1_TIME *t)
1271 struct tm newtime;
1272 char *p = NULL;
1273 char d[18];
1274 memset(&d,'\0',sizeof(d));
1275 memset(&newtime,'\0',sizeof newtime);
1277 if (t->type == V_ASN1_GENERALIZEDTIME)
1279 // For time values >= 2050, OpenSSL uses
1280 // ASN1_GENERALIZEDTIME - which we'll worry about
1281 // later.
1282 return 0;
1285 p = (char *)t->data;
1286 sscanf(p,"%2s%2s%2s%2s%2s%2sZ", d, &d[3], &d[6], &d[9], &d[12], &d[15]);
1288 int year = strtol(d, (char **)NULL, 10);
1289 if (year < 49)
1290 year += 100;
1291 else
1292 year += 50;
1294 newtime.tm_year = year;
1295 newtime.tm_mon = strtol(&d[3], (char **)NULL, 10) - 1;
1296 newtime.tm_mday = strtol(&d[6], (char **)NULL, 10);
1297 newtime.tm_hour = strtol(&d[9], (char **)NULL, 10);
1298 newtime.tm_min = strtol(&d[12], (char **)NULL, 10);
1299 newtime.tm_sec = strtol(&d[15], (char **)NULL, 10);
1301 return mktime(&newtime);
1305 time_t WvX509::get_notvalid_before() const
1307 CHECK_CERT_EXISTS_GET("not valid before", 0);
1309 return ASN1_TIME_to_time_t(X509_get_notBefore(cert));
1313 time_t WvX509::get_notvalid_after() const
1315 CHECK_CERT_EXISTS_GET("not valid after", 0);
1317 return ASN1_TIME_to_time_t(X509_get_notAfter(cert));
1321 WvString WvX509::get_ski() const
1323 CHECK_CERT_EXISTS_GET("ski", WvString::null);
1325 return get_extension(NID_subject_key_identifier);
1329 WvString WvX509::get_aki() const
1331 CHECK_CERT_EXISTS_GET("aki", WvString::null);
1333 WvStringList aki_list;
1334 parse_stack(get_extension(NID_authority_key_identifier), aki_list, "keyid:");
1335 if (aki_list.count())
1336 return aki_list.popstr();
1338 return WvString::null;
1342 void WvX509::set_ski()
1344 CHECK_CERT_EXISTS_SET("ski");
1346 ASN1_OCTET_STRING *oct = M_ASN1_OCTET_STRING_new();
1347 ASN1_BIT_STRING *pk = cert->cert_info->key->public_key;
1348 unsigned char pkey_dig[EVP_MAX_MD_SIZE];
1349 unsigned int diglen;
1351 EVP_Digest(pk->data, pk->length, pkey_dig, &diglen, EVP_sha1(), NULL);
1353 M_ASN1_OCTET_STRING_set(oct, pkey_dig, diglen);
1354 X509_EXTENSION *ext = X509V3_EXT_i2d(NID_subject_key_identifier, 0,
1355 oct);
1356 X509_add_ext(cert, ext, -1);
1357 X509_EXTENSION_free(ext);
1358 M_ASN1_OCTET_STRING_free(oct);
1362 void WvX509::set_aki(const WvX509 &cacert)
1364 CHECK_CERT_EXISTS_SET("aki");
1366 // can't set a meaningful AKI for subordinate certification without the
1367 // parent having an SKI
1368 ASN1_OCTET_STRING *ikeyid = NULL;
1369 X509_EXTENSION *ext;
1370 int i = X509_get_ext_by_NID(cacert.cert, NID_subject_key_identifier, -1);
1371 if ((i >= 0) && (ext = X509_get_ext(cacert.cert, i)))
1372 ikeyid = static_cast<ASN1_OCTET_STRING *>(X509V3_EXT_d2i(ext));
1374 if (!ikeyid)
1375 return;
1377 AUTHORITY_KEYID *akeyid = AUTHORITY_KEYID_new();
1378 akeyid->issuer = NULL;
1379 akeyid->serial = NULL;
1380 akeyid->keyid = ikeyid;
1381 ext = X509V3_EXT_i2d(NID_authority_key_identifier, 0, akeyid);
1382 X509_add_ext(cert, ext, -1);
1383 X509_EXTENSION_free(ext);
1384 AUTHORITY_KEYID_free(akeyid);