Main Page   Class Hierarchy   Compound List   File List   Compound Members   File Members  

wvx509.cc

Go to the documentation of this file.
00001 /*
00002  * Insert Appropriate Copyright header here....
00003  * Also, the license should specify that it is LGPL ;)
00004  * ppatterson@carillonis.com
00005  */
00006 
00007 #include "wvx509.h"
00008 #include "wvdiriter.h"
00009 
00010 #include "pem.h"
00011 #include "x509v3.h"
00012 
00013 WvX509Mgr::WvX509Mgr(X509 *_cert)
00014     : debug("X509",WvLog::Debug5), errstr("")
00015 {
00016     err = false;
00017     cert = _cert;
00018     keypair = NULL;
00019 }
00020 
00021 WvX509Mgr::WvX509Mgr(WvString dName, int bits, WvRSAKey *_keypair)
00022     : debug("X509",WvLog::Debug5), errstr("")
00023 {
00024     debug("Creating New Certificate for %s\n",dName);
00025     keypair = _keypair;
00026     cert = NULL;
00027     err = false;
00028     createSScert(dName, bits);
00029 }
00030 
00031 
00032 WvX509Mgr::~WvX509Mgr()
00033 {
00034     X509_free(cert);
00035 }
00036 
00037 // The people who designed this garbage should be shot!
00038 // Support old versions of openssl...
00039 #ifndef NID_domainComponent
00040 #define NID_domainComponent 391
00041 #endif
00042 
00043 #ifndef NID_Domain
00044 #define NID_Domain 392
00045 #endif
00046 
00047 // returns some approximation of the server's fqdn, or an empty string.
00048 static WvString set_name_entry(X509_NAME *name, WvString dn)
00049 {
00050     WvString fqdn(""), force_fqdn("");
00051     X509_NAME_ENTRY *ne = NULL;
00052     int count = 0, nid;
00053     
00054     WvStringList l;
00055     l.split(dn, ",");
00056     
00057     // dn is of the form: c=ca,o=foo organization,dc=foo,dc=com
00058     // (ie. name=value pairs separated by commas)
00059     WvStringList::Iter i(l);
00060     for (i.rewind(); i.next(); )
00061     {
00062         WvString s(*i), sid;
00063         char *cptr, *value;
00064         
00065         cptr = s.edit();
00066         value = strchr(cptr, '=');
00067         if (value)
00068             *value++ = 0;
00069         else
00070             value = "NULL";
00071         
00072         sid = strlwr(trim_string(cptr));
00073         
00074         if (sid == "c")
00075             nid = NID_countryName;
00076         else if (sid == "st")
00077             nid = NID_stateOrProvinceName;
00078         else if (sid == "o")
00079             nid = NID_organizationName;
00080         else if (sid == "ou")
00081             nid = NID_organizationalUnitName;
00082         else if (sid == "cn")
00083         {
00084             nid = NID_commonName;
00085             force_fqdn = value;
00086             force_fqdn.unique();
00087         }
00088         else if (sid == "dc")
00089         {
00090             nid = NID_domainComponent;
00091             if (!!fqdn)
00092                 fqdn.append(".");
00093             fqdn.append(value);
00094         }
00095         else if (sid == "domain")
00096         {
00097             nid = NID_Domain;
00098             force_fqdn = value;
00099             force_fqdn.unique();
00100         }
00101         else
00102             nid = NID_domainComponent;
00103         
00104         if (!ne)
00105             ne = X509_NAME_ENTRY_create_by_NID(NULL, nid,
00106                                V_ASN1_APP_CHOOSE, (unsigned char *)value, -1);
00107         else
00108             X509_NAME_ENTRY_create_by_NID(&ne, nid,
00109                                V_ASN1_APP_CHOOSE, (unsigned char *)value, -1);
00110         if (!ne)
00111             continue;
00112         X509_NAME_add_entry(name, ne, count++, 0);
00113     }
00114     
00115     X509_NAME_ENTRY_free(ne);
00116     
00117     if (!!force_fqdn)
00118         return force_fqdn;
00119     else
00120         return fqdn;
00121 }
00122 
00123 
00124 void WvX509Mgr::createSScert(WvString dn, int keysize)
00125 {
00126     EVP_PKEY *pk;
00127     X509_NAME *name = NULL;
00128     X509_EXTENSION *ex = NULL;
00129 
00130     // RFC2459 says that this number must be unique for each certificate
00131     // issued by a CA - since this is a self-signed cert, we'll take a
00132     // shortcut, and give a fixed value... saves a couple of cycles rather
00133     // than get a random number.
00134     int serial = 12345;
00135 
00136     WvString serverfqdn;
00137 
00138     if (keypair == NULL)
00139     {
00140         debug("Need a new RSA key, so generating it...\n");
00141         keypair = new WvRSAKey(keysize);
00142         debug("Ok, I've got a new RSA keypair\n");
00143     }
00144 
00145     if ((pk=EVP_PKEY_new()) == NULL)
00146     {
00147         seterr("Error creating key handler for new certificate");
00148         return;
00149     }
00150     if ((cert=X509_new()) == NULL)
00151     {
00152         seterr("Error creating new X509 object");
00153         return;
00154     }
00155     if (!EVP_PKEY_assign_RSA(pk, keypair->rsa))
00156     {
00157         seterr("Error adding RSA keys to certificate");
00158         return;
00159     }
00160 
00161     // Completely broken in my mind - this sets the version
00162     // string to '3'  (I guess version starts at 0)
00163     X509_set_version(cert, 0x2);
00164 
00165     // Set the Serial Number for the certificate
00166     ASN1_INTEGER_set(X509_get_serialNumber(cert), serial);
00167 
00168     // Set the NotBefore time to now.
00169     X509_gmtime_adj(X509_get_notBefore(cert), 0);
00170 
00171     // Now + 10 years... should be shorter, but since we don't currently
00172     // Have a set of routines to refresh the certificates, make it
00173     // REALLY long.
00174     X509_gmtime_adj(X509_get_notAfter(cert), (long)60*60*24*3650);
00175     X509_set_pubkey(cert, pk);
00176 
00177     name = X509_get_subject_name(cert);
00178     serverfqdn = set_name_entry(name, dn);
00179     
00180     if (!serverfqdn)
00181         serverfqdn = "null.noname.null";
00182                                        
00183     X509_set_issuer_name(cert, name);
00184     X509_set_subject_name(cert, name);
00185 
00186     // Add in the netscape-specific server extension
00187     ex = X509V3_EXT_conf_nid(NULL, NULL, NID_netscape_cert_type, "server");
00188     X509_add_ext(cert, ex, -1);
00189     X509_EXTENSION_free(ex);
00190 
00191     debug("Setting netscape SSL server name extension to %s\n", serverfqdn);
00192 
00193     // Set the netscape server name extension to our server name
00194     ex = X509V3_EXT_conf_nid(NULL, NULL, NID_netscape_ssl_server_name,
00195                              serverfqdn.edit());
00196     X509_add_ext(cert, ex, -1);
00197     X509_EXTENSION_free(ex);
00198 
00199     // Set the RFC2459-mandated keyUsage field to critical, and restrict
00200     // the usage of this cert to digital signature and key encipherment.
00201     ex = X509V3_EXT_conf_nid(NULL, NULL, NID_key_usage,
00202                              "critical,digitalSignature,keyEncipherment");
00203     X509_add_ext(cert, ex, -1);
00204     X509_EXTENSION_free(ex);
00205 
00206     // Sign the certificate with our own key ("Self Sign")
00207     if (!X509_sign(cert, pk, EVP_md5()))
00208     {
00209         seterr("Could not self sign the certificate");
00210         X509_free(cert);
00211         EVP_PKEY_free(pk);
00212         return;
00213     }
00214     debug("Certificate for %s created\n", dn);
00215 
00216     // Now that we have the certificate created,
00217     // be nice and leave it in enccert, so if someone needs it, they
00218     // don't have to do that step... I'm not sure that this is going
00219     // to stay... it's really not that hard to call encodecert() ;)
00220     encodecert();
00221 }
00222 
00223 WvString WvX509Mgr::createcertreq(WvString dName, int keysize)
00224 {
00225     EVP_PKEY *pk;
00226     X509_NAME *name = NULL;
00227     X509_REQ *certreq;
00228     WvString pkcs10("");
00229     struct stat stupidstat;
00230 
00231     FILE *stupidtmp = tmpfile();
00232 
00233     // First thing to do is to generate an RSA Keypair if the
00234     // Manager doesn't already have one:
00235     if ( keypair == NULL)
00236     {
00237         keypair = new WvRSAKey(keysize);
00238     }
00239     if ((pk=EVP_PKEY_new()) == NULL)
00240     {
00241         seterr("Error creating key handler for new certificate");
00242         return pkcs10;
00243     }
00244     if ((certreq=X509_REQ_new()) == NULL)
00245     {
00246         seterr("Error creating new PKCS#10 object");
00247         return pkcs10;
00248     }
00249 
00250     if (!EVP_PKEY_assign_RSA(pk, keypair->rsa))
00251     {
00252         seterr("Error adding RSA keys to certificate");
00253         return pkcs10;
00254     }
00255     X509_REQ_set_pubkey(certreq, pk);
00256     name = X509_REQ_get_subject_name(certreq);   
00257 
00258     set_name_entry(name, dName);
00259     
00260     X509_REQ_set_subject_name(certreq, name);
00261 
00262     // Horribly involuted hack to get around the fact that the
00263     // OpenSSL people are too braindead to have a PEM_write function
00264     // that returns a char *
00265     PEM_write_X509_REQ(stupidtmp,certreq);
00266     // With any luck, PEM_write won't close the file ;)
00267   
00268     rewind(stupidtmp);
00269 
00270     fstat(fileno(stupidtmp),&stupidstat);
00271 
00272     pkcs10.setsize(stupidstat.st_size + 1);
00273 
00274     fread(pkcs10.edit(),sizeof(char),stupidstat.st_size,stupidtmp);
00275 
00276     fclose(stupidtmp);
00277 
00278     pkcs10.edit()[stupidstat.st_size] = 0;
00279     X509_REQ_free(certreq);
00280     EVP_PKEY_free(pk);
00281 
00282     return pkcs10;
00283 }
00284 
00285 void WvX509Mgr::decodecert(WvString encodedcert)
00286 {
00287     int hexbytes = strlen((const char *)encodedcert);
00288     int bufsize = hexbytes/2;
00289     unsigned char *certbuf = new unsigned char[bufsize], *cp = certbuf;
00290     X509 *ct;
00291 
00292     unhexify(certbuf,encodedcert);
00293     ct = cert = X509_new();
00294     cert = d2i_X509(&ct, &cp, hexbytes/2);
00295 
00296     delete[] certbuf;
00297 }
00298 
00299 
00300 void WvX509Mgr::encodecert()
00301 {
00302     size_t size;
00303     unsigned char *keybuf, *iend;
00304 
00305     size = i2d_X509(cert, NULL);
00306     iend = keybuf = new unsigned char[size];
00307     i2d_X509(cert, &iend);
00308 
00309     enccert.setsize(size * 2 +1);
00310     hexify(enccert.edit(), keybuf, size);
00311 
00312     delete[] keybuf;
00313 }
00314 
00315 
00316 bool WvX509Mgr::validate()
00317 {
00318     if (cert != NULL)
00319     {
00320         debug("Peer Certificate:\n");
00321         debug("SubjectDN: %s\n",
00322               X509_NAME_oneline(X509_get_subject_name(cert),0,0));
00323         debug("Issuer: %s\n",
00324               X509_NAME_oneline(X509_get_issuer_name(cert),0,0));
00325 
00326         // Check and make sure that the certificate is still valid
00327         if (X509_cmp_current_time(X509_get_notAfter(cert)) == -1)
00328         {
00329             seterr("Peer certificate has expired!");
00330             return false;
00331         }
00332         // Kind of a placeholder thing right now...
00333         // Later on, do CRL, and certificate validity checks here..
00334         // Actually, break these out in signedbyvalidCA(), and isinCRL()
00335         // Maybe have them here and activated by bool values as parameters 
00336         // to validate.
00337     }
00338     else
00339         debug("Peer doesn't have a certificate.\n");
00340     
00341     return true;
00342 }
00343 
00344 bool WvX509Mgr::signedbyCAinfile(WvString certfile)
00345 {
00346     X509_STORE *cert_ctx = NULL;
00347     X509_STORE_CTX csc;
00348     X509_LOOKUP *lookup = NULL;
00349     int result = 0;
00350 
00351     cert_ctx = X509_STORE_new();
00352     if (cert_ctx == NULL)
00353     {
00354         seterr("Unable to create Certificate Store Context");
00355         return false;
00356     }
00357 
00358     lookup=X509_STORE_add_lookup(cert_ctx,X509_LOOKUP_file());
00359     if (lookup == NULL) abort();  
00360 
00361     if (!X509_LOOKUP_load_file(lookup,certfile,X509_FILETYPE_PEM))
00362     {
00363         X509_LOOKUP_load_file(lookup,NULL,X509_FILETYPE_DEFAULT);
00364     }
00365 
00366     X509_STORE_CTX_init(&csc,cert_ctx,cert,NULL);
00367     result = X509_verify_cert(&csc);
00368     X509_STORE_CTX_cleanup(&csc);
00369     
00370     X509_STORE_free(cert_ctx);
00371 
00372     if (result == 1)
00373     {
00374         return true;
00375     }
00376     else
00377     {
00378         return false;
00379     }
00380 }
00381 
00382 bool WvX509Mgr::signedbyCAindir(WvString certdir)
00383 {
00384     WvDirIter i(certdir,false);
00385     for (i.rewind(); i.next() ; )
00386     {
00387         if (!signedbyCAinfile(i->fullname))
00388         {
00389             return false;
00390         }
00391     }    
00392     return true;
00393 }
00394 
00395 bool WvX509Mgr::isinCRL()
00396 {
00397     return true;
00398 }
00399 
00400 void WvX509Mgr::dumpcert(WvString outfile, bool append)
00401 {
00402     FILE *certout;
00403 
00404     if (append)
00405     {
00406         certout = fopen(outfile,"a");
00407         debug("Opening %s for append\n",outfile);
00408     }
00409     else
00410     {
00411         certout = fopen(outfile,"w");
00412         debug("Opening %s for write\n",outfile);
00413     }
00414 
00415     if (certout != NULL)
00416     {
00417         debug("Dumping X509 Certificate...\n");
00418         PEM_write_X509(certout,cert);
00419     }
00420     else
00421     {
00422         seterr("Cannot open file for writing");
00423     }
00424 
00425     fclose(certout);
00426 
00427     return;
00428 }
00429 
00430 void WvX509Mgr::dumpkeypair(WvString outfile, bool append)
00431 {
00432     FILE *keyout;
00433     EVP_CIPHER *enc = NULL;
00434 
00435     if (append)
00436     {
00437         keyout = fopen(outfile,"a");
00438         debug("Opening %s for append\n",outfile);
00439     }
00440     else
00441     {
00442         keyout = fopen(outfile,"w");
00443     }
00444     if (keyout != NULL)
00445     {
00446         debug("Printing keypair...\n");
00447         (const EVP_CIPHER *)enc = EVP_get_cipherbyname("rsa");
00448         PEM_write_RSAPrivateKey(keyout,keypair->rsa, enc, NULL, 0, NULL, NULL);
00449     }
00450     else
00451     {   
00452         seterr("Cannot open file for writing");
00453     }
00454 
00455     fclose(keyout);
00456 
00457     return;
00458 }
00459 
00460 void WvX509Mgr::dumprawkeypair(WvString outfile, bool append)
00461 {
00462     FILE *keyout;
00463     int offset;
00464     struct stat filestat;
00465 
00466     if (append)
00467     {
00468         keyout = fopen(outfile,"a");
00469         fstat(fileno(keyout),&filestat);
00470         offset = filestat.st_size;
00471         debug("Opening %s for append\n",outfile);
00472     }
00473     else
00474     {
00475         keyout = fopen(outfile,"w");
00476         offset = 0;
00477     }
00478     if (keyout != NULL)
00479     {
00480         debug("Printing keypair...\n");
00481         RSA_print_fp(keyout,keypair->rsa, offset);
00482     }
00483     else
00484     {   
00485         seterr("Cannot open file for writing");
00486     }
00487 
00488     fclose(keyout);
00489 
00490     return;
00491 }

Generated on Fri Apr 5 15:16:53 2002 for WvStreams by doxygen1.2.15