WvStreams
wvx509.cc
00001 /*
00002  * Worldvisions Weaver Software:
00003  *   Copyright (C) 1997-2005 Net Integration Technologies, Inc.
00004  * 
00005  * X.509 certificate management classes.
00006  */ 
00007 #include "wvx509.h"
00008 #include "wvcrl.h"
00009 #include "wvsslhacks.h"
00010 #include "wvcrypto.h"
00011 #include "wvstringlist.h"
00012 #include "wvbase64.h"
00013 #include "wvstrutils.h"
00014 #include "wvautoconf.h"
00015 
00016 #include <openssl/pem.h>
00017 #include <openssl/x509v3.h>
00018 #include <openssl/err.h>
00019 #include <openssl/sha.h>
00020 #include <openssl/ssl.h>
00021 
00022 // enable this to add some extra debugging trace messages (this can be VERY
00023 // verbose)
00024 #if 0
00025 # define TRACE(x, y...) debug(x, ## y); 
00026 #else
00027 #ifndef _MSC_VER
00028 # define TRACE(x, y...)
00029 #else
00030 # define TRACE
00031 #endif
00032 #endif
00033 
00034 // helper method to let us return and warn gracefully when getting/setting an 
00035 // element in a null certificate
00036 static const char * warning_str_set 
00037     = "Tried to set %s, but certificate not ok.\n";
00038 static const char * warning_str_get 
00039     = "Tried to get %s, but certificate not ok.\n";
00040 #define CHECK_CERT_EXISTS_SET(x)                                        \
00041     if (!cert) {                                                        \
00042         debug(WvLog::Warning, warning_str_set, x);                      \
00043         return;                                                         \
00044     }
00045 #define CHECK_CERT_EXISTS_GET(x, y)                                     \
00046     if (!cert) {                                                        \
00047         debug(WvLog::Warning, warning_str_get, x);                      \
00048         return y;                                                       \
00049     }
00050         
00051 
00052 UUID_MAP_BEGIN(WvX509)
00053   UUID_MAP_ENTRY(IObject)
00054   UUID_MAP_END
00055 
00056 static int ssl_init_count = 0;
00057 
00058 #if !HAVE_OPENSSL_POLICY_MAPPING
00059 
00060 // HACK: old versions of OpenSSL can't handle ERR_free_strings() being called
00061 // more than once in the same process; the next wvssl_init won't work.  So
00062 // let's make sure to make a global variable that holds the refcount at 1
00063 // even when all the objects go away, then clean it up at exit.
00064 class WvSSL_Stupid_Refcount
00065 {
00066 public:
00067    WvSSL_Stupid_Refcount()
00068    {
00069        wvssl_init();
00070    }
00071    
00072    ~WvSSL_Stupid_Refcount()
00073    {
00074        wvssl_free();
00075    }
00076 };
00077 
00078 WvSSL_Stupid_Refcount wvssl_stupid_refcount;
00079 
00080 #endif // !HAVE_OPENSSL_POLICY_MAPPING
00081 
00082 
00083 void wvssl_init()
00084 {
00085     if (!ssl_init_count)
00086     {
00087         SSL_library_init();
00088         SSL_load_error_strings();
00089         ERR_load_BIO_strings();
00090         ERR_load_crypto_strings();
00091         OpenSSL_add_all_algorithms();
00092         OpenSSL_add_all_ciphers();
00093         OpenSSL_add_all_digests();
00094     }
00095     
00096     ssl_init_count++;
00097 }
00098 
00099 
00100 void wvssl_free()
00101 {
00102     assert(ssl_init_count >= 1);
00103     if (ssl_init_count >= 1)
00104         ssl_init_count--;
00105 
00106     if (!ssl_init_count)
00107     {
00108         ERR_free_strings();
00109         EVP_cleanup();
00110     }
00111 }
00112 
00113 
00114 WvString wvssl_errstr()
00115 {
00116     char buf[256];
00117     ERR_error_string_n(ERR_get_error(), buf, sizeof(buf));
00118     buf[sizeof(buf)-1] = 0;
00119     return buf;
00120 }
00121 
00122 
00123 WvX509::WvX509(X509 *_cert)
00124     : debug("X509", WvLog::Debug5)
00125 {
00126     wvssl_init();
00127     cert = _cert;
00128 }
00129 
00130 
00131 WvX509::WvX509()
00132     : debug("X509", WvLog::Debug5)
00133 {
00134     wvssl_init();
00135     cert = NULL;
00136 }
00137 
00138 
00139 WvX509::WvX509(const WvX509 &x509)
00140     : debug("X509", WvLog::Debug5)
00141 {
00142     wvssl_init();
00143     if (x509.cert)
00144         cert = X509_dup(x509.cert);
00145     else
00146         cert = NULL;
00147 }
00148 
00149 
00150 WvX509::~WvX509()
00151 {
00152     TRACE("Deleting.\n");
00153     
00154     if (cert)
00155         X509_free(cert);
00156 
00157     wvssl_free();
00158 }
00159 
00160 
00161 
00162 // The people who designed this garbage should be shot!
00163 // Support old versions of openssl...
00164 #ifndef NID_domainComponent
00165 #define NID_domainComponent 391
00166 #endif
00167 
00168 #ifndef NID_Domain
00169 #define NID_Domain 392
00170 #endif
00171 
00172 
00173 // returns some approximation of the server's fqdn, or an empty string.
00174 static WvString set_name_entry(X509_NAME *name, WvStringParm dn)
00175 {
00176     WvString fqdn(""), force_fqdn("");
00177     X509_NAME_ENTRY *ne = NULL;
00178     int count = 0, nid;
00179     
00180     WvStringList l;
00181     l.split(dn, ",");
00182     
00183     // dn is of the form: c=ca,o=foo organization,dc=foo,dc=com
00184     // (ie. name=value pairs separated by commas)
00185     WvStringList::Iter i(l);
00186     for (i.rewind(); i.next(); )
00187     {
00188         WvString s(*i), sid;
00189         char *cptr, *value;
00190         
00191         cptr = s.edit();
00192         value = strchr(cptr, '=');
00193         if (value)
00194             *value++ = 0;
00195         else
00196             value = (char*)"NULL";
00197         
00198         sid = strlwr(trim_string(cptr));
00199         
00200         if (sid == "c")
00201             nid = NID_countryName;
00202         else if (sid == "st")
00203             nid = NID_stateOrProvinceName;
00204         else if (sid == "l")
00205             nid = NID_localityName;
00206         else if (sid == "o")
00207             nid = NID_organizationName;
00208         else if (sid == "ou")
00209             nid = NID_organizationalUnitName;
00210         else if (sid == "cn")
00211         {
00212             nid = NID_commonName;
00213             force_fqdn = value;
00214         }
00215         else if (sid == "dc")
00216         {
00217             nid = NID_domainComponent;
00218             if (!!fqdn)
00219                 fqdn.append(".");
00220             fqdn.append(value);
00221         }
00222         else if (sid == "domain")
00223         {
00224             nid = NID_Domain;
00225             force_fqdn = value;
00226         }
00227         else if (sid == "email")
00228             nid = NID_pkcs9_emailAddress;
00229         else
00230             nid = NID_domainComponent;
00231         
00232         // Sometimes we just want to parse dn into fqdn.
00233         if (name == NULL)
00234             continue;
00235         
00236         if (!ne)
00237             ne = X509_NAME_ENTRY_create_by_NID(NULL, nid,
00238                                V_ASN1_APP_CHOOSE, (unsigned char *)value, -1);
00239         else
00240             X509_NAME_ENTRY_create_by_NID(&ne, nid,
00241                                V_ASN1_APP_CHOOSE, (unsigned char *)value, -1);
00242         if (!ne)
00243             continue;
00244         
00245         X509_NAME_add_entry(name, ne, count++, 0);
00246     }
00247     
00248     X509_NAME_ENTRY_free(ne);
00249     
00250     if (!!force_fqdn)
00251         return force_fqdn;
00252 
00253     return fqdn;
00254 }
00255 
00256 
00257 WvRSAKey *WvX509::get_rsa_pub() const
00258 {
00259     EVP_PKEY *pkcert = X509_get_pubkey(cert);
00260     RSA *certrsa = EVP_PKEY_get1_RSA(pkcert);
00261     EVP_PKEY_free(pkcert);
00262     return new WvRSAKey(certrsa, false); 
00263 }
00264 
00265 
00266 WvString WvX509::certreq(WvStringParm subject, const WvRSAKey &rsa)
00267 {
00268     WvLog debug("X509::certreq", WvLog::Debug5);
00269 
00270     EVP_PKEY *pk = NULL;
00271     X509_NAME *name = NULL;
00272     X509_REQ *certreq = NULL;
00273 
00274     // double check RSA key
00275     if (rsa.isok())
00276         debug("RSA Key is fine.\n");
00277     else
00278     {
00279         debug(WvLog::Warning, "RSA Key is bad");
00280         return WvString::null;
00281     }
00282 
00283     if ((pk=EVP_PKEY_new()) == NULL)
00284     {
00285         debug(WvLog::Warning,
00286               "Error creating key handler for new certificate");
00287         return WvString::null;
00288     }
00289     
00290     if ((certreq=X509_REQ_new()) == NULL)
00291     {
00292         debug(WvLog::Warning, "Error creating new PKCS#10 object");
00293         EVP_PKEY_free(pk);
00294         return WvString::null;
00295     }
00296 
00297     if (!EVP_PKEY_set1_RSA(pk, rsa.rsa))
00298     {
00299         debug(WvLog::Warning, "Error adding RSA keys to certificate");
00300         X509_REQ_free(certreq);
00301         EVP_PKEY_free(pk);
00302         return WvString::null;
00303     }
00304     
00305     X509_REQ_set_version(certreq, 0); /* version 1 */
00306 
00307     X509_REQ_set_pubkey(certreq, pk);
00308 
00309     name = X509_REQ_get_subject_name(certreq);
00310 
00311     debug("Creating Certificate request for %s\n", subject);
00312     set_name_entry(name, subject);
00313     X509_REQ_set_subject_name(certreq, name);
00314     char *sub_name = X509_NAME_oneline(X509_REQ_get_subject_name(certreq), 
00315                                        0, 0);
00316     debug("SubjectDN: %s\n", sub_name);
00317     OPENSSL_free(sub_name);
00318     
00319     if (!X509_REQ_sign(certreq, pk, EVP_sha1()))
00320     {
00321         debug(WvLog::Warning, "Could not self sign the request");
00322         X509_REQ_free(certreq);
00323         EVP_PKEY_free(pk);
00324         return WvString::null;
00325     }
00326 
00327     int verify_result = X509_REQ_verify(certreq, pk);
00328     if (verify_result == 0)
00329     {
00330         debug(WvLog::Warning, "Self signed request failed");
00331         X509_REQ_free(certreq);
00332         EVP_PKEY_free(pk);
00333         return WvString::null;
00334     }
00335     else
00336     {
00337         debug("Self Signed Certificate Request verifies OK!\n");
00338     }
00339 
00340     // Horribly involuted hack to get around the fact that the
00341     // OpenSSL people are too braindead to have a PEM_write function
00342     // that returns a char *
00343     WvDynBuf retval;
00344     BIO *bufbio = BIO_new(BIO_s_mem());
00345     BUF_MEM *bm;
00346     
00347     PEM_write_bio_X509_REQ(bufbio, certreq);
00348     BIO_get_mem_ptr(bufbio, &bm);
00349     retval.put(bm->data, bm->length);
00350     
00351     X509_REQ_free(certreq);
00352     EVP_PKEY_free(pk);
00353     BIO_free(bufbio);
00354 
00355     return retval.getstr();
00356 }
00357 
00358 
00359 bool WvX509::validate(WvX509 *cacert) const
00360 {
00361     if (cert == NULL)
00362     {
00363         debug(WvLog::Warning, "Tried to validate certificate against CA, but "
00364               "certificate is blank!\n");
00365         return false;
00366     }
00367 
00368     bool retval = true;
00369 
00370     // Check and make sure that the certificate is still valid
00371     if (X509_cmp_current_time(X509_get_notAfter(cert)) < 0)
00372     {
00373         debug("Certificate has expired.\n");
00374         retval = false;
00375     }
00376     
00377     if (X509_cmp_current_time(X509_get_notBefore(cert)) > 0)
00378     {
00379         debug("Certificate is not yet valid.\n");
00380         retval = false;
00381     }
00382 
00383     if (cacert)
00384     {
00385         retval &= signedbyca(*cacert);
00386         retval &= issuedbyca(*cacert);
00387     }
00388     
00389     return retval;
00390 }
00391 
00392 
00393 bool WvX509::signedbyca(WvX509 &cacert) const
00394 {
00395     if (!cert || !cacert.cert)
00396     {
00397         debug(WvLog::Warning, "Tried to determine if certificate was signed "
00398               "by CA, but either client or CA certificate (or both) are "
00399               "blank.\n");
00400         return false;
00401     } 
00402 
00403     EVP_PKEY *pkey = X509_get_pubkey(cacert.cert);
00404     int result = X509_verify(cert, pkey); 
00405     EVP_PKEY_free(pkey);
00406 
00407     if (result < 0)
00408     {
00409         debug("Can't determine if we were signed by CA %s: %s\n",
00410               cacert.get_subject(), wvssl_errstr());
00411         return false;
00412     }
00413     bool issigned = (result > 0);
00414 
00415     debug("Certificate was%s signed by CA %s.\n", issigned ? "" : " NOT", 
00416           cacert.get_subject());
00417 
00418     return issigned;
00419 }
00420 
00421 
00422 bool WvX509::issuedbyca(WvX509 &cacert) const
00423 {
00424     if (!cert || !cacert.cert)
00425     {
00426         debug(WvLog::Warning, "Tried to determine if certificate was issued "
00427               "by CA, but either client or CA certificate (or both) are "
00428               "blank.\n");
00429         return false;
00430     } 
00431 
00432     int ret = X509_check_issued(cacert.cert, cert);
00433     debug("issuedbyca: %s==X509_V_OK(%s)\n", ret, X509_V_OK);
00434     if (ret != X509_V_OK)
00435         return false;
00436 
00437     return true;
00438 }
00439 
00440 
00441 WvString WvX509::encode(const DumpMode mode) const
00442 {
00443     WvDynBuf retval;
00444     encode(mode, retval);
00445     return retval.getstr();
00446 }
00447 
00448 
00449 void WvX509::encode(const DumpMode mode, WvBuf &buf) const
00450 {
00451     if (mode == CertFileDER || mode == CertFilePEM)
00452         return; // file modes are no ops with encode
00453 
00454     if (!cert)
00455     {
00456         debug(WvLog::Warning, "Tried to encode certificate, but certificate "
00457               "is blank!\n");
00458         return;
00459     }
00460 
00461     debug("Encoding X509 certificate.\n");
00462 
00463     if (mode == CertHex)
00464     {
00465         size_t size;
00466         unsigned char *keybuf, *iend;
00467         WvString enccert;
00468         
00469         size = i2d_X509(cert, NULL);
00470         iend = keybuf = new unsigned char[size];
00471         i2d_X509(cert, &iend);
00472         
00473         enccert.setsize(size * 2 +1);
00474         ::hexify(enccert.edit(), keybuf, size);
00475         
00476         deletev keybuf;
00477         buf.putstr(enccert);
00478     }
00479     else
00480     {
00481         BIO *bufbio = BIO_new(BIO_s_mem());
00482         BUF_MEM *bm;
00483         
00484         if (mode == CertPEM)
00485             PEM_write_bio_X509(bufbio, cert);
00486         else if (mode == CertDER)
00487             i2d_X509_bio(bufbio, cert);
00488         else
00489             debug(WvLog::Warning, "Tried to encode certificate with unknown "
00490                   "mode!\n");
00491 
00492         BIO_get_mem_ptr(bufbio, &bm);
00493         buf.put(bm->data, bm->length);
00494         BIO_free(bufbio);
00495     }
00496 }
00497 
00498 
00499 void WvX509::decode(const DumpMode mode, WvStringParm str)
00500 {
00501     if (cert)
00502     {
00503         debug("Replacing an already existant X509 certificate.\n");
00504         X509_free(cert);
00505         cert = NULL;
00506     }
00507 
00508     if (mode == CertFileDER)
00509     {
00510         BIO *bio = BIO_new(BIO_s_file());
00511         
00512         if (BIO_read_filename(bio, str.cstr()) <= 0)
00513         {
00514             debug(WvLog::Warning, "Open '%s': %s\n", str, wvssl_errstr());
00515             BIO_free(bio);
00516             return;
00517         }
00518         
00519         if (!(cert = d2i_X509_bio(bio, NULL)))
00520             debug(WvLog::Warning, "Import DER from '%s': %s\n",
00521                   str, wvssl_errstr());
00522         
00523         BIO_free(bio);
00524         return;
00525     }
00526     else if (mode == CertFilePEM)
00527     {
00528         FILE *fp = fopen(str, "rb");
00529         if (!fp)
00530         {
00531             int errnum = errno;
00532             debug("Open '%s': %s\n", str, strerror(errnum));
00533             return;
00534         }
00535 
00536         if (!(cert = PEM_read_X509(fp, NULL, NULL, NULL)))
00537             debug(WvLog::Warning, "Import PEM from '%s': %s\n",
00538                   str, wvssl_errstr());
00539         
00540         fclose(fp);
00541         return;
00542     }
00543     else if (mode == CertHex)
00544     {
00545         int hexbytes = str.len();
00546         int bufsize = hexbytes/2;
00547         unsigned char *certbuf = new unsigned char[bufsize];
00548         unsigned char *cp = certbuf;
00549         X509 *tmpcert;
00550         
00551         ::unhexify(certbuf, str);
00552         tmpcert = cert = X509_new();
00553         cert = wv_d2i_X509(&tmpcert, &cp, bufsize);    
00554         delete[] certbuf;
00555         return;
00556     }
00557 
00558     // we use the buffer decode functions for everything else
00559     WvDynBuf buf;
00560     buf.putstr(str);
00561     decode(mode, buf);
00562 }
00563 
00564 
00565 void WvX509::decode(const DumpMode mode, WvBuf &encoded)
00566 {
00567     if (cert)
00568     {
00569         debug("Replacing an already existant X509 certificate.\n");
00570         X509_free(cert);
00571         cert = NULL;
00572     }
00573     
00574     if (mode == CertHex || mode == CertFileDER || mode == CertFilePEM)
00575         decode(mode, encoded.getstr());
00576     else
00577     {        
00578         BIO *membuf = BIO_new(BIO_s_mem());
00579         BIO_write(membuf, encoded.get(encoded.used()), encoded.used());
00580 
00581         if (mode == CertPEM)
00582             cert = PEM_read_bio_X509(membuf, NULL, NULL, NULL);
00583         else if (mode == CertDER)
00584             cert = d2i_X509_bio(membuf, NULL);
00585         else
00586             debug(WvLog::Warning, "Tried to decode certificate with unknown "
00587                   "mode!\n");
00588 
00589         BIO_free_all(membuf);
00590     }
00591 }
00592 
00593 
00594 WvString WvX509::get_issuer() const
00595 { 
00596     CHECK_CERT_EXISTS_GET("issuer", WvString::null);
00597 
00598     char *name = X509_NAME_oneline(X509_get_issuer_name(cert),0,0);
00599     WvString retval(name);
00600     OPENSSL_free(name);
00601     return retval;
00602 }
00603 
00604 
00605 void WvX509::set_issuer(WvStringParm issuer)
00606 {
00607     CHECK_CERT_EXISTS_SET("issuer");
00608 
00609     X509_NAME *name = X509_get_issuer_name(cert);
00610     set_name_entry(name, issuer);
00611     X509_set_issuer_name(cert, name);
00612 }
00613 
00614 
00615 void WvX509::set_issuer(const WvX509 &cacert)
00616 {
00617     CHECK_CERT_EXISTS_SET("issuer");
00618 
00619     X509_NAME *casubj = X509_get_subject_name(cacert.cert);
00620     X509_set_issuer_name(cert, casubj);
00621 }
00622 
00623 
00624 WvString WvX509::get_subject() const
00625 {
00626     CHECK_CERT_EXISTS_GET("subject", WvString::null);
00627 
00628     char *name = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
00629     WvString retval(name);
00630     OPENSSL_free(name);
00631     return retval;
00632 }
00633 
00634 
00635 void WvX509::set_subject(WvStringParm subject)
00636 {    
00637     CHECK_CERT_EXISTS_SET("subject");
00638 
00639     X509_NAME *name = X509_get_subject_name(cert);
00640     set_name_entry(name, subject);
00641     X509_set_subject_name(cert, name);
00642 }
00643 
00644 
00645 void WvX509::set_subject(X509_NAME *name)
00646 {
00647     CHECK_CERT_EXISTS_SET("subject");
00648 
00649     X509_set_subject_name(cert, name);
00650 }
00651 
00652 
00653 void WvX509::set_pubkey(WvRSAKey &_rsa)
00654 {
00655     CHECK_CERT_EXISTS_SET("pubkey");
00656 
00657     EVP_PKEY *pk = EVP_PKEY_new();
00658     assert(pk);
00659 
00660     // Assign RSA Key from WvRSAKey into stupid package that OpenSSL needs
00661     if (!EVP_PKEY_set1_RSA(pk, _rsa.rsa))
00662     {
00663         debug("Error adding RSA keys to certificate.\n");
00664         return;
00665     }
00666     
00667     X509_set_pubkey(cert, pk);
00668 
00669     EVP_PKEY_free(pk);
00670 }
00671 
00672 
00673 
00674 void WvX509::set_nsserver(WvStringParm servername)
00675 {
00676     CHECK_CERT_EXISTS_SET("nsserver");
00677     
00678     WvString fqdn;
00679     
00680     // FQDN cannot have a = in it, therefore it
00681     // must be a distinguished name :)
00682     if (strchr(servername, '='))
00683         fqdn = set_name_entry(NULL, servername);
00684     else
00685         fqdn = servername;
00686     
00687     if (!fqdn)
00688         fqdn = "null.noname.null";
00689     
00690     debug("Setting Netscape SSL server name extension to '%s'.\n", fqdn);
00691 
00692     // Add in the netscape-specific server extension
00693     set_extension(NID_netscape_cert_type, "server");
00694     set_extension(NID_netscape_ssl_server_name, fqdn);
00695 }
00696 
00697 
00698 WvString WvX509::get_nsserver() const
00699 {
00700     return get_extension(NID_netscape_ssl_server_name);
00701 }
00702 
00703 
00704 WvString WvX509::get_serial(bool hex) const
00705 {
00706     CHECK_CERT_EXISTS_GET("serial", WvString::null);
00707 
00708     BIGNUM *bn = BN_new();
00709     bn = ASN1_INTEGER_to_BN(X509_get_serialNumber(cert), bn);
00710     char * c;
00711     if (hex)
00712         c = BN_bn2hex(bn);
00713     else
00714         c = BN_bn2dec(bn);
00715     WvString ret("%s", c);
00716     OPENSSL_free(c);
00717     BN_free(bn);
00718     return ret;
00719 }
00720 
00721 
00722 void WvX509::set_version()
00723 {
00724     CHECK_CERT_EXISTS_SET("version");
00725 
00726     X509_set_version(cert, 0x2);
00727 }
00728 
00729 
00730 void WvX509::set_serial(long serial)
00731 {
00732     CHECK_CERT_EXISTS_SET("serial");
00733 
00734     ASN1_INTEGER_set(X509_get_serialNumber(cert), serial);
00735 }
00736 
00737 
00738 WvString WvX509::get_crl_dp() const
00739 {
00740     return get_extension(NID_crl_distribution_points);
00741 }
00742 
00743 
00744 void WvX509::set_lifetime(long seconds)
00745 {
00746     CHECK_CERT_EXISTS_SET("lifetime");
00747 
00748     // Set the NotBefore time to now.
00749     X509_gmtime_adj(X509_get_notBefore(cert), 0);
00750     
00751     // Now + 10 years... should be shorter, but since we don't currently
00752     // have a set of routines to refresh the certificates, make it
00753     // REALLY long.
00754     X509_gmtime_adj(X509_get_notAfter(cert), seconds);
00755 }
00756 
00757 
00758 void WvX509::set_key_usage(WvStringParm values)
00759 {
00760     set_extension(NID_key_usage, values);
00761 }
00762 
00763 
00764 WvString WvX509::get_key_usage() const
00765 {
00766     return get_extension(NID_key_usage);
00767 }
00768 
00769 
00770 void WvX509::set_ext_key_usage(WvStringParm values)
00771 {
00772     set_extension(NID_ext_key_usage, values);
00773 }
00774 
00775 
00776 WvString WvX509::get_ext_key_usage() const
00777 {
00778     return get_extension(NID_ext_key_usage);
00779 }
00780 
00781 
00782 WvString WvX509::get_altsubject() const
00783 {
00784     return get_extension(NID_subject_alt_name);
00785 }
00786 
00787 
00788 bool WvX509::get_basic_constraints(bool &ca, int &pathlen) const
00789 {
00790     CHECK_CERT_EXISTS_GET("basic constraints", false);
00791     
00792     BASIC_CONSTRAINTS *constraints = NULL;
00793     int i;
00794 
00795     constraints = static_cast<BASIC_CONSTRAINTS *>
00796         (X509_get_ext_d2i(cert, NID_basic_constraints, &i, NULL));
00797     if (constraints)
00798     {
00799         ca = constraints->ca;
00800         if (constraints->pathlen)
00801         {
00802             if ((constraints->pathlen->type == V_ASN1_NEG_INTEGER) || !ca)
00803             {
00804                 debug("Path length type not valid when getting basic "
00805                       "constraints.\n");
00806                 BASIC_CONSTRAINTS_free(constraints);
00807                 pathlen = 0;
00808                 return false;
00809             }
00810             
00811             pathlen = ASN1_INTEGER_get(constraints->pathlen);
00812         }
00813         else
00814             pathlen = (-1);
00815 
00816         BASIC_CONSTRAINTS_free(constraints);
00817         return true;
00818     }
00819     
00820     debug("Basic constraints extension not present.\n");
00821     return false;
00822 }
00823 
00824 
00825 void WvX509::set_basic_constraints(bool ca, int pathlen)
00826 {
00827     CHECK_CERT_EXISTS_SET("basic constraints");
00828 
00829     BASIC_CONSTRAINTS *constraints = BASIC_CONSTRAINTS_new();
00830     
00831     constraints->ca = static_cast<int>(ca);
00832     if (pathlen != (-1))
00833     {
00834         ASN1_INTEGER *i = ASN1_INTEGER_new();
00835         ASN1_INTEGER_set(i, pathlen);
00836         constraints->pathlen = i;
00837     }
00838 
00839     X509_EXTENSION *ex = X509V3_EXT_i2d(NID_basic_constraints, 0, 
00840                                         constraints);
00841     while (int idx = X509_get_ext_by_NID(cert, NID_basic_constraints, 0) >= 0)
00842     {
00843         debug("Found extension at idx %s\n", idx);
00844         X509_EXTENSION *tmpex = X509_delete_ext(cert, idx);
00845         X509_EXTENSION_free(tmpex);
00846     }
00847 
00848     X509_add_ext(cert, ex, NID_basic_constraints);
00849     X509_EXTENSION_free(ex);
00850     BASIC_CONSTRAINTS_free(constraints);
00851 }
00852 
00853 
00854 /*
00855  * These functions are optional to the API.  If OpenSSL doesn't support them,
00856  * we simply won't include them here, and apps that need them won't compile.
00857  */
00858 #ifdef HAVE_OPENSSL_POLICY_MAPPING
00859 
00860 bool WvX509::get_policy_constraints(int &require_explicit_policy, 
00861                                     int &inhibit_policy_mapping) const
00862 {
00863     CHECK_CERT_EXISTS_GET("policy constraints", false);
00864 
00865     POLICY_CONSTRAINTS *constraints = NULL;
00866     int i;
00867     
00868     constraints = static_cast<POLICY_CONSTRAINTS *>(X509_get_ext_d2i(
00869                                                 cert, NID_policy_constraints, 
00870                                                 &i, NULL));
00871     if (constraints)
00872     {
00873         if (constraints->requireExplicitPolicy)
00874             require_explicit_policy = ASN1_INTEGER_get(
00875                 constraints->requireExplicitPolicy);
00876         else
00877             require_explicit_policy = (-1);
00878 
00879         if (constraints->inhibitPolicyMapping)
00880             inhibit_policy_mapping = ASN1_INTEGER_get(
00881                 constraints->inhibitPolicyMapping);
00882         else
00883             inhibit_policy_mapping = (-1);
00884         POLICY_CONSTRAINTS_free(constraints);
00885         return true;
00886     }
00887 
00888     return false;
00889 }
00890 
00891 
00892 void WvX509::set_policy_constraints(int require_explicit_policy, 
00893                                        int inhibit_policy_mapping)
00894 {
00895     CHECK_CERT_EXISTS_SET("policy constraints");
00896 
00897     POLICY_CONSTRAINTS *constraints = POLICY_CONSTRAINTS_new();
00898     
00899     ASN1_INTEGER *i = ASN1_INTEGER_new();
00900     ASN1_INTEGER_set(i, require_explicit_policy);
00901     constraints->requireExplicitPolicy = i;
00902     i = ASN1_INTEGER_new();
00903     ASN1_INTEGER_set(i, inhibit_policy_mapping);
00904     constraints->inhibitPolicyMapping = i;
00905 
00906     X509_EXTENSION *ex = X509V3_EXT_i2d(NID_policy_constraints, 0, 
00907                                         constraints);
00908     X509_add_ext(cert, ex, -1);
00909     X509_EXTENSION_free(ex);
00910     POLICY_CONSTRAINTS_free(constraints);
00911 }
00912 
00913 
00914 bool WvX509::get_policy_mapping(PolicyMapList &list) const
00915 {
00916     CHECK_CERT_EXISTS_GET("policy mapping", false);
00917 
00918     POLICY_MAPPINGS *mappings = NULL;
00919     POLICY_MAPPING *map = NULL;
00920     int i;
00921 
00922     mappings = static_cast<POLICY_MAPPINGS *>(X509_get_ext_d2i(
00923                                                 cert, NID_policy_mappings, 
00924                                                 &i, NULL));
00925     if (!mappings)
00926         return false;
00927 
00928     const int POLICYID_MAXLEN = 80;
00929     char tmp1[80];
00930     char tmp2[80];
00931     for(int j = 0; j < sk_POLICY_MAPPING_num(mappings); j++) 
00932     {
00933         map = sk_POLICY_MAPPING_value(mappings, j);
00934         OBJ_obj2txt(tmp1, POLICYID_MAXLEN, map->issuerDomainPolicy, true);
00935         OBJ_obj2txt(tmp2, POLICYID_MAXLEN, map->subjectDomainPolicy, true);
00936         list.append(new PolicyMap(tmp1, tmp2), true);
00937     }
00938 
00939     sk_POLICY_MAPPING_pop_free(mappings, POLICY_MAPPING_free);
00940     
00941     return true;
00942 }
00943 
00944 
00945 void WvX509::set_policy_mapping(PolicyMapList &list)
00946 {
00947     CHECK_CERT_EXISTS_SET("policy mapping");
00948 
00949     POLICY_MAPPINGS *maps = sk_POLICY_MAPPING_new_null();
00950     
00951     PolicyMapList::Iter i(list);
00952     for (i.rewind(); i.next();)
00953     {
00954         POLICY_MAPPING *map = POLICY_MAPPING_new();
00955         map->issuerDomainPolicy = OBJ_txt2obj(i().issuer_domain.cstr(), 0);
00956         map->subjectDomainPolicy = OBJ_txt2obj(i().subject_domain.cstr(), 0);
00957         sk_POLICY_MAPPING_push(maps, map);
00958         printf("Push!\n");
00959     }
00960 
00961     X509_EXTENSION *ex = X509V3_EXT_i2d(NID_policy_mappings, 0, maps);
00962     X509_add_ext(cert, ex, -1);
00963     X509_EXTENSION_free(ex);
00964     sk_POLICY_MAPPING_pop_free(maps, POLICY_MAPPING_free);
00965 }
00966 
00967 #endif // HAVE_OPENSSL_POLICY_MAPPING
00968 
00969 
00970 static void add_aia(WvStringParm type, WvString identifier,
00971                     AUTHORITY_INFO_ACCESS *ainfo)
00972 {
00973     ACCESS_DESCRIPTION *acc = ACCESS_DESCRIPTION_new();
00974     sk_ACCESS_DESCRIPTION_push(ainfo, acc);
00975     acc->method = OBJ_txt2obj(type.cstr(), 0);
00976     acc->location->type = GEN_URI;
00977     acc->location->d.ia5 = M_ASN1_IA5STRING_new();
00978     unsigned char *cident 
00979         = reinterpret_cast<unsigned char *>(identifier.edit());
00980     ASN1_STRING_set(acc->location->d.ia5, cident, identifier.len());
00981 }
00982 
00983 
00984 void WvX509::set_aia(WvStringList &ca_urls,
00985                      WvStringList &responders)
00986 {
00987     CHECK_CERT_EXISTS_SET("aia");
00988 
00989     AUTHORITY_INFO_ACCESS *ainfo = sk_ACCESS_DESCRIPTION_new_null();
00990 
00991     WvStringList::Iter i(ca_urls);
00992     for (i.rewind(); i.next();)
00993         add_aia("caIssuers", i(), ainfo);
00994 
00995     WvStringList::Iter j(responders);
00996     for (j.rewind(); j.next();)
00997         add_aia("OCSP", j(), ainfo);
00998 
00999     X509_EXTENSION *ex = X509V3_EXT_i2d(NID_info_access, 0, ainfo);
01000     X509_add_ext(cert, ex, -1);
01001     X509_EXTENSION_free(ex);
01002     sk_ACCESS_DESCRIPTION_pop_free(ainfo, ACCESS_DESCRIPTION_free);
01003 }
01004 
01005 
01006 WvString WvX509::get_aia() const
01007 {
01008     return get_extension(NID_info_access);
01009 }
01010 
01011 
01012 static void parse_stack(WvStringParm ext, WvStringList &list,
01013                         WvStringParm prefix)
01014 {
01015     WvStringList stack;
01016     stack.split(ext, ";\n");
01017     WvStringList::Iter i(stack);
01018     for (i.rewind();i.next();)
01019     {
01020         WvString stack_entry(*i);
01021         if (strstr(stack_entry, prefix))
01022         {
01023             WvString uri(stack_entry.edit() + prefix.len());
01024             list.append(uri);  
01025         }
01026     }
01027 }
01028 
01029 
01030 void WvX509::get_ocsp(WvStringList &responders) const
01031 {
01032     parse_stack(get_aia(), responders, "OCSP - URI:");
01033 }
01034 
01035 
01036 void WvX509::get_ca_urls(WvStringList &urls) const
01037 {
01038     parse_stack(get_aia(), urls, "CA Issuers - URI:");
01039 }
01040 
01041 
01042 void WvX509::get_crl_urls(WvStringList &urls) const
01043 {
01044     parse_stack(get_crl_dp(), urls, "URI:");
01045 }
01046 
01047 
01048 void WvX509::set_crl_urls(WvStringList &urls)
01049 {
01050     CHECK_CERT_EXISTS_SET("CRL urls");
01051 
01052     STACK_OF(DIST_POINT) *crldp = sk_DIST_POINT_new_null();
01053     WvStringList::Iter i(urls);
01054     for (i.rewind(); i.next();)
01055     {
01056         DIST_POINT *point = DIST_POINT_new();
01057         sk_DIST_POINT_push(crldp, point);
01058 
01059         GENERAL_NAMES *uris = GENERAL_NAMES_new();
01060         GENERAL_NAME *uri = GENERAL_NAME_new();
01061         uri->type = GEN_URI;
01062         uri->d.ia5 = M_ASN1_IA5STRING_new();
01063         unsigned char *cident
01064             = reinterpret_cast<unsigned char *>(i().edit());    
01065         ASN1_STRING_set(uri->d.ia5, cident, i().len());
01066         sk_GENERAL_NAME_push(uris, uri);
01067 
01068         point->distpoint = DIST_POINT_NAME_new();
01069         point->distpoint->name.fullname = uris;
01070         point->distpoint->type = 0;
01071     }
01072 
01073     X509_EXTENSION *ex = X509V3_EXT_i2d(NID_crl_distribution_points, 0, crldp);
01074     X509_add_ext(cert, ex, -1);
01075     X509_EXTENSION_free(ex);
01076     sk_DIST_POINT_pop_free(crldp, DIST_POINT_free);
01077 }
01078 
01079 
01080 bool WvX509::get_policies(WvStringList &policy_oids) const
01081 {
01082     CHECK_CERT_EXISTS_GET("policies", false);
01083 
01084     int critical;
01085     CERTIFICATEPOLICIES * policies = static_cast<CERTIFICATEPOLICIES *>(
01086         X509_get_ext_d2i(cert, NID_certificate_policies, &critical, NULL));
01087     if (policies)
01088     {
01089         for (int i = 0; i < sk_POLICYINFO_num(policies); i++)
01090         {
01091             POLICYINFO * policy = sk_POLICYINFO_value(policies, i);
01092             const int POLICYID_MAXLEN = 80;
01093 
01094             char policyid[POLICYID_MAXLEN];
01095             OBJ_obj2txt(policyid, POLICYID_MAXLEN, policy->policyid, 
01096                         true); // don't substitute human-readable names
01097             policy_oids.append(policyid);
01098         }
01099 
01100         sk_POLICYINFO_pop_free(policies, POLICYINFO_free);
01101         return true;
01102     }
01103 
01104     return false;
01105 }
01106 
01107 
01108 void WvX509::set_policies(WvStringList &policy_oids)
01109 {
01110     CHECK_CERT_EXISTS_SET("policies");
01111 
01112     STACK_OF(POLICYINFO) *sk_pinfo = sk_POLICYINFO_new_null();
01113 
01114     WvStringList::Iter i(policy_oids);
01115     for (i.rewind(); i.next();)
01116     {
01117         ASN1_OBJECT *pobj = OBJ_txt2obj(i(), 0);
01118         POLICYINFO *pol = POLICYINFO_new();
01119         pol->policyid = pobj;
01120         sk_POLICYINFO_push(sk_pinfo, pol);
01121     }
01122 
01123 #if 0
01124     // this code would let you set URL information to a policy
01125     // qualifier
01126     POLICYQUALINFO *qual = NULL;
01127     WvString url(_url);
01128     if (!!url)
01129     {
01130         pol->qualifiers = sk_POLICYQUALINFO_new_null();
01131         qual = POLICYQUALINFO_new();
01132         qual->pqualid = OBJ_nid2obj(NID_id_qt_cps);
01133         qual->d.cpsouri = M_ASN1_IA5STRING_new();
01134         ASN1_STRING_set(qual->d.cpsuri, url.edit(), url.len());
01135         sk_POLICYQUALINFO_push(pol->qualifiers, qual);
01136     }
01137 #endif
01138 
01139     X509_EXTENSION *ex = X509V3_EXT_i2d(NID_certificate_policies, 0, 
01140                                         sk_pinfo);
01141     X509_add_ext(cert, ex, -1);
01142     X509_EXTENSION_free(ex);
01143     sk_POLICYINFO_pop_free(sk_pinfo, POLICYINFO_free);
01144 }
01145 
01146 
01147 WvString WvX509::get_extension(int nid) const
01148 {
01149     CHECK_CERT_EXISTS_GET("extension", WvString::null);
01150 
01151     WvString retval = WvString::null;
01152     
01153     int index = X509_get_ext_by_NID(cert, nid, -1);
01154     if (index >= 0)
01155     {
01156         X509_EXTENSION *ext = X509_get_ext(cert, index);
01157         
01158         if (ext)
01159         {
01160             X509V3_EXT_METHOD *method = (X509V3_EXT_METHOD*)X509V3_EXT_get(ext);
01161             if (!method)
01162             {
01163                 WvDynBuf buf;
01164                 buf.put(ext->value->data, ext->value->length);
01165                 retval = buf.getstr();
01166             }
01167             else
01168             {
01169                 void *ext_data = NULL;
01170                 // we NEED to use a temporary pointer for ext_value_data,
01171                 // as openssl's ASN1_item_d2i will muck around with it, 
01172                 // even though it's const (at least as of version 0.9.8e). 
01173                 // gah.
01174 #if OPENSSL_VERSION_NUMBER >= 0x0090800fL
01175                 const unsigned char * ext_value_data = ext->value->data;
01176 #else
01177                 unsigned char *ext_value_data = ext->value->data;
01178 #endif
01179                 if (method->it)
01180                 {
01181                     ext_data = ASN1_item_d2i(NULL, &ext_value_data,
01182                                              ext->value->length, 
01183                                              ASN1_ITEM_ptr(method->it));
01184                     TRACE("Applied generic conversion!\n");
01185                 }
01186                 else
01187                 {
01188                     ext_data = method->d2i(NULL, &ext_value_data,
01189                                            ext->value->length);
01190                     TRACE("Applied method specific conversion!\n");
01191                 }
01192                 
01193                 if (method->i2s)
01194                 {
01195                     TRACE("String Extension!\n");
01196                     char *s = method->i2s(method, ext_data); 
01197                     retval = s;
01198                     OPENSSL_free(s);
01199                 }
01200                 else if (method->i2v)
01201                 {
01202                     TRACE("Stack Extension!\n");
01203                     CONF_VALUE *val = NULL;
01204                     STACK_OF(CONF_VALUE) *svals = NULL;
01205                     svals = method->i2v(method, ext_data, NULL);
01206                     if (!sk_CONF_VALUE_num(svals))
01207                         retval = "EMPTY";
01208                     else
01209                     {
01210                         WvStringList list;
01211                         for(int i = 0; i < sk_CONF_VALUE_num(svals); i++)
01212                         {
01213                             val = sk_CONF_VALUE_value(svals, i);
01214                             if (!val->name)
01215                                 list.append(WvString(val->value));
01216                             else if (!val->value)
01217                                 list.append(WvString(val->name));
01218                             else 
01219                             {
01220                                 WvString pair("%s:%s", val->name, val->value);
01221                                 list.append(pair);
01222                             }
01223                         }
01224                         retval = list.join(";\n");
01225                         }
01226                     sk_CONF_VALUE_pop_free(svals, X509V3_conf_free);
01227                 }
01228                 else if (method->i2r)
01229                 {
01230                     TRACE("Raw Extension!\n");
01231                     WvDynBuf retvalbuf;
01232                     BIO *bufbio = BIO_new(BIO_s_mem());
01233                     BUF_MEM *bm;
01234                     method->i2r(method, ext_data, bufbio, 0);
01235                     BIO_get_mem_ptr(bufbio, &bm);
01236                     retvalbuf.put(bm->data, bm->length);
01237                     BIO_free(bufbio);
01238                     retval = retvalbuf.getstr();
01239                 }
01240                     
01241                 if (method->it)
01242                     ASN1_item_free((ASN1_VALUE *)ext_data, 
01243                                    ASN1_ITEM_ptr(method->it));
01244                 else
01245                     method->ext_free(ext_data);
01246 
01247             }
01248         }
01249     }
01250     else
01251     {
01252         TRACE("Extension not present!\n");
01253     }
01254 
01255     if (!!retval)
01256         TRACE("Returning: %s\n", retval);
01257 
01258     return retval;
01259 }
01260 
01261 
01262 void WvX509::set_extension(int nid, WvStringParm _values)
01263 {
01264     CHECK_CERT_EXISTS_SET("extension");
01265 
01266     // first we check to see if the extension already exists, if so we need to
01267     // kill it
01268     int index = X509_get_ext_by_NID(cert, nid, -1);
01269     if (index >= 0)
01270     {
01271         X509_EXTENSION *ex = X509_delete_ext(cert, index);
01272         X509_EXTENSION_free(ex);
01273     }    
01274 
01275     // now set the extension
01276     WvString values(_values);
01277     X509_EXTENSION *ex = NULL;
01278     ex = X509V3_EXT_conf_nid(NULL, NULL, nid, values.edit());
01279     X509_add_ext(cert, ex, -1);
01280     X509_EXTENSION_free(ex);
01281 }
01282 
01283 
01284 bool WvX509::isok() const
01285 {
01286     return cert;
01287 }
01288 
01289 
01290 bool WvX509::operator! () const
01291 {
01292     return !isok();
01293 }
01294 
01295 
01296 WvString WvX509::errstr() const
01297 {
01298     if (!cert)
01299         return "No certificate.";
01300 
01301     return WvString::empty;
01302 }
01303 
01304 
01305 bool WvX509::verify(WvStringParm original, WvStringParm signature) const
01306 {
01307     WvDynBuf buf;
01308     buf.putstr(original);
01309     return verify(buf, signature);
01310 }
01311 
01312 
01313 bool WvX509::verify(WvBuf &original, WvStringParm signature) const
01314 {    
01315     unsigned char sig_buf[4096];
01316     size_t sig_size = sizeof(sig_buf);
01317     WvBase64Decoder().flushstrmem(signature, sig_buf, &sig_size, true);
01318     
01319     EVP_PKEY *pk = X509_get_pubkey(cert);
01320     if (!pk) 
01321         return false;
01322     
01323     /* Verify the signature */
01324     EVP_MD_CTX sig_ctx;
01325     EVP_VerifyInit(&sig_ctx, EVP_sha1());
01326     EVP_VerifyUpdate(&sig_ctx, original.peek(0, original.used()),
01327                      original.used());
01328     int sig_err = EVP_VerifyFinal(&sig_ctx, sig_buf, sig_size, pk);
01329     EVP_PKEY_free(pk);
01330     EVP_MD_CTX_cleanup(&sig_ctx); // Again, not my fault... 
01331     if (sig_err != 1) 
01332     {
01333         debug("Verify failed!\n");
01334         return false;
01335     }
01336     else
01337         return true;
01338 }
01339 
01340 
01341 static time_t ASN1_TIME_to_time_t(ASN1_TIME *t)
01342 {
01343     struct tm newtime;
01344     char *p = NULL;
01345     char d[18];
01346     memset(&d,'\0',sizeof(d));    
01347     memset(&newtime,'\0',sizeof newtime);
01348     
01349     if (t->type == V_ASN1_GENERALIZEDTIME) 
01350     {
01351          // For time values >= 2050, OpenSSL uses
01352          // ASN1_GENERALIZEDTIME - which we'll worry about
01353          // later.
01354         return 0;
01355     }
01356 
01357     p = (char *)t->data;
01358     sscanf(p,"%2s%2s%2s%2s%2s%2sZ", d, &d[3], &d[6], &d[9], &d[12], &d[15]);
01359     
01360     int year = strtol(d, (char **)NULL, 10);
01361     if (year < 49)
01362         year += 100;
01363     else
01364         year += 50;
01365     
01366     newtime.tm_year = year;
01367     newtime.tm_mon = strtol(&d[3], (char **)NULL, 10) - 1;
01368     newtime.tm_mday = strtol(&d[6], (char **)NULL, 10);
01369     newtime.tm_hour = strtol(&d[9], (char **)NULL, 10);
01370     newtime.tm_min = strtol(&d[12], (char **)NULL, 10);
01371     newtime.tm_sec = strtol(&d[15], (char **)NULL, 10);
01372 
01373     return mktime(&newtime);
01374 }
01375 
01376 
01377 time_t WvX509::get_notvalid_before() const
01378 {
01379     CHECK_CERT_EXISTS_GET("not valid before", 0);
01380 
01381     return ASN1_TIME_to_time_t(X509_get_notBefore(cert));
01382 }
01383 
01384 
01385 time_t WvX509::get_notvalid_after() const
01386 {
01387     CHECK_CERT_EXISTS_GET("not valid after", 0);
01388 
01389     return ASN1_TIME_to_time_t(X509_get_notAfter(cert));
01390 }
01391 
01392 
01393 WvString WvX509::get_ski() const
01394 {
01395     CHECK_CERT_EXISTS_GET("ski", WvString::null);
01396 
01397     return get_extension(NID_subject_key_identifier);
01398 }
01399 
01400 
01401 WvString WvX509::get_aki() const
01402 {
01403     CHECK_CERT_EXISTS_GET("aki", WvString::null);
01404 
01405     WvStringList aki_list;
01406     parse_stack(get_extension(NID_authority_key_identifier), aki_list,
01407                 "keyid:");
01408     if (aki_list.count())
01409         return aki_list.popstr();
01410 
01411     return WvString::null;
01412 }
01413 
01414 
01415 WvString WvX509::get_fingerprint(const FprintMode mode) const
01416 {
01417     CHECK_CERT_EXISTS_GET("fingerprint", WvString::null);
01418    
01419     /* Default to SHA-1 because OpenSSL does too */
01420     const EVP_MD *digest = EVP_sha1();
01421     if (mode == FingerMD5)
01422         digest = EVP_md5();
01423 
01424     unsigned char md[EVP_MAX_MD_SIZE];
01425     unsigned int n;
01426     if (!X509_digest(cert, digest, md, &n))
01427     {
01428         errno = -ENOMEM;
01429         debug("get_fingerprint: Out of memory\n");
01430         return WvString::null;
01431     }
01432 
01433     WvDynBuf store;
01434     char buf[3];
01435     unsigned int i = 0;
01436     do {
01437         sprintf(buf, "%02X", md[i]);
01438         store.putstr(buf);
01439     } while (++i < n && (store.putch(':'), 1));
01440 
01441     return store.getstr();
01442 }
01443 
01444 
01445 void WvX509::set_ski()
01446 {
01447     CHECK_CERT_EXISTS_SET("ski");
01448 
01449     ASN1_OCTET_STRING *oct = M_ASN1_OCTET_STRING_new();
01450     ASN1_BIT_STRING *pk = cert->cert_info->key->public_key;
01451     unsigned char pkey_dig[EVP_MAX_MD_SIZE];
01452     unsigned int diglen;
01453 
01454     EVP_Digest(pk->data, pk->length, pkey_dig, &diglen, EVP_sha1(), NULL);
01455 
01456     M_ASN1_OCTET_STRING_set(oct, pkey_dig, diglen);
01457     X509_EXTENSION *ext = X509V3_EXT_i2d(NID_subject_key_identifier, 0, 
01458                                         oct);
01459     X509_add_ext(cert, ext, -1);
01460     X509_EXTENSION_free(ext);
01461     M_ASN1_OCTET_STRING_free(oct);
01462 }
01463 
01464 
01465 void WvX509::set_aki(const WvX509 &cacert)
01466 {
01467     CHECK_CERT_EXISTS_SET("aki");
01468 
01469     // can't set a meaningful AKI for subordinate certification without the 
01470     // parent having an SKI
01471     ASN1_OCTET_STRING *ikeyid = NULL;
01472     X509_EXTENSION *ext;
01473     int i = X509_get_ext_by_NID(cacert.cert, NID_subject_key_identifier, -1);
01474     if ((i >= 0) && (ext = X509_get_ext(cacert.cert, i)))
01475         ikeyid = static_cast<ASN1_OCTET_STRING *>(X509V3_EXT_d2i(ext));
01476 
01477     if (!ikeyid)
01478         return;
01479 
01480     AUTHORITY_KEYID *akeyid = AUTHORITY_KEYID_new();
01481     akeyid->issuer = NULL;
01482     akeyid->serial = NULL;
01483     akeyid->keyid = ikeyid;
01484     ext = X509V3_EXT_i2d(NID_authority_key_identifier, 0, akeyid);
01485     X509_add_ext(cert, ext, -1);
01486     X509_EXTENSION_free(ext); 
01487     AUTHORITY_KEYID_free(akeyid);
01488 }
01489