Make WvStreams compile with gcc 4.4.
[wvstreams.git] / crypto / wvx509mgr.cc
blobf249eeca914c7e7a53bb5110e65605677614fc41
1 #include "wvbase64.h"
2 #include "wvsslhacks.h"
3 #include "wvx509mgr.h"
4 #include "wvautoconf.h"
6 #include <openssl/pem.h>
7 #include <openssl/x509v3.h>
8 #include <openssl/err.h>
9 #include <openssl/ssl.h>
10 #include <openssl/sha.h>
11 #include <openssl/pkcs12.h>
14 namespace {
15 class AutoClose {
16 public:
17 AutoClose(FILE *fp): fp(fp) { }
18 ~AutoClose()
20 if (fp)
21 fclose(fp);
24 operator FILE *() const
26 return fp;
29 private:
30 FILE *fp;
32 } // anomymous namespace...
35 WvX509Mgr::WvX509Mgr()
36 : WvX509(),
37 debug("X509 Manager", WvLog::Debug5)
39 rsa = NULL;
43 WvX509Mgr::WvX509Mgr(const WvX509Mgr &x)
44 : WvX509(x),
45 debug("X509 Manager", WvLog::Debug5)
47 rsa = NULL;
48 set_rsa(x.rsa);
52 WvX509Mgr::WvX509Mgr(WvStringParm _dname, WvRSAKey *_rsa, bool ca)
53 : WvX509(),
54 debug("X509 Manager", WvLog::Debug5)
56 debug("Creating new certificate+key pair for %s.\n", _dname);
57 rsa = _rsa;
59 if (!!_dname)
61 create_selfissued(_dname, ca);
62 debug("Ok - Parameters set... now signing certificate.\n");
63 signcert(*this);
65 else
66 debug("Sorry, can't create an anonymous certificate.");
70 WvX509Mgr::WvX509Mgr(WvStringParm _dname, int bits, bool ca)
71 : WvX509(),
72 debug("X509 Manager", WvLog::Debug5)
74 debug("Creating new certificate+key pair for %s.\n", _dname);
75 rsa = NULL;
77 if (!!_dname)
79 rsa = new WvRSAKey(bits);
80 create_selfissued(_dname, ca);
81 debug("Ok - Parameters set... now signing certificate.\n");
82 signcert(*this);
84 else
85 debug("Sorry, can't create an anonymous certificate.");
89 void WvX509Mgr::create_selfissued(WvStringParm dname, bool is_ca)
91 if (cert)
93 debug("Replacing already existant certificate...\n");
94 X509_free(cert);
95 cert = NULL;
98 // double check RSA key
99 if (rsa->isok())
100 debug("RSA Key is fine.\n");
101 else
102 return;
104 if ((cert = X509_new()) == NULL)
105 return;
107 // Completely broken in my mind - this sets the version
108 // string to '3' (I guess version starts at 0)
109 set_version();
111 // RFC2459 says that this number must be unique for each certificate
112 // issued by a CA. It may be that some web browsers get confused if
113 // more than one cert with the same name has the same serial number, so
114 // let's be careful.
115 srand(time(NULL));
116 int serial = rand();
117 set_serial(serial);
119 // 10 years...
120 set_lifetime(60*60*24*3650);
122 set_pubkey(*rsa);
124 set_issuer(dname);
125 set_subject(dname);
126 set_ski();
128 if (is_ca)
130 debug("Setting Extensions with CA Parameters.\n");
131 debug("Setting Key Usage.\n");
132 set_key_usage("critical, keyCertSign, cRLSign");
133 debug("Setting Basic Constraints.\n");
134 set_extension(NID_basic_constraints, "critical, CA:TRUE");
135 debug("Setting Netscape Certificate Type.\n");
136 set_extension(NID_netscape_cert_type,
137 "SSL CA, S/MIME CA, Object Signing CA");
138 #if 0
139 // uncomment this to allow certificate to be used as
140 // an OCSP signer (seems too obscure to enable by default
141 // right now).
142 set_ext_key_usage("OCSP Signing");
143 #endif
144 // debug("Setting Constraints.\n");
145 // set_constraints("requireExplicitPolicy");
147 else
149 debug("Setting Key Usage with normal server parameters\n");
150 set_nsserver(dname);
151 set_key_usage("critical, digitalSignature, keyEncipherment, "
152 "keyAgreement");
153 set_extension(NID_basic_constraints, "CA:FALSE");
154 set_ext_key_usage("TLS Web Server Authentication,"
155 "TLS Web Client Authentication");
158 // we do not actually sign the certificate here: that must be done by the
159 // user (WvX509Mgr most likely)
161 debug("Certificate for %s created\n", dname);
165 WvX509Mgr::~WvX509Mgr()
167 debug("Deleting.\n");
168 WVDELETE(rsa);
172 bool WvX509Mgr::isok() const
174 return WvX509::isok() && rsa && rsa->isok() && test();
178 bool WvX509Mgr::operator! () const
180 return !isok();
184 WvString WvX509Mgr::errstr() const
186 if (!WvX509::isok())
187 return WvX509::errstr();
189 if (!rsa)
190 return "No RSA key set.";
191 else if (!rsa->isok())
192 return "RSA key not valid.";
193 else if (!test())
194 return "RSA key and certificate do not match.";
196 return WvString::empty;
200 bool WvX509Mgr::bind_ssl(SSL_CTX *ctx)
202 if (SSL_CTX_use_certificate(ctx, get_cert()) <= 0)
204 return false;
206 debug("Certificate activated.\n");
208 if (SSL_CTX_use_RSAPrivateKey(ctx, rsa->rsa) <= 0)
210 return false;
212 debug("RSA private key activated.\n");
213 return true;
217 bool WvX509Mgr::test() const
219 if (!cert)
221 debug("No X509 certificate: test fails.\n");
222 return false;
225 if (rsa)
227 EVP_PKEY *pk = EVP_PKEY_new();
228 assert(pk);
230 if (!EVP_PKEY_set1_RSA(pk, rsa->rsa))
232 debug("Error setting RSA keys: test fails.\n");
233 EVP_PKEY_free(pk);
234 return false;
237 bool bad = false;
238 int verify_return = X509_verify(cert, pk);
240 if (verify_return != 1) // only '1' means okay
242 // However let's double check:
243 WvString rsapub = rsa->encode(WvRSAKey::RsaPubPEM);
244 WvRSAKey *temprsa = get_rsa_pub();
245 WvString certpub = temprsa->encode(WvRSAKey::RsaPubPEM);
246 delete temprsa;
247 // debug("rsapub:\n%s\n", rsapub);
248 // debug("certpub:\n%s\n", certpub);
249 if (certpub == rsapub)
250 ; // do nothing, since OpenSSL is lying
251 else
253 // I guess that it really did fail.
254 debug("Certificate test failed: %s\n", wvssl_errstr());
255 bad = true;
259 EVP_PKEY_free(pk);
260 return !bad;
263 return false;
267 WvString WvX509Mgr::signreq(WvStringParm pkcs10req) const
269 debug("Signing a certificate request with: %s\n", get_subject());
270 if (!isok())
272 debug(WvLog::Warning, "Asked to sign certificate request, but not ok! "
273 "Aborting.\n");
274 return false;
277 // Break this next part out into a de-pemify section, since that is what
278 // this part up until the FIXME: is about.
279 WvString pkcs10(pkcs10req);
281 BIO *membuf = BIO_new(BIO_s_mem());
282 BIO_write(membuf, pkcs10req, pkcs10req.len());
284 X509_REQ *certreq = PEM_read_bio_X509_REQ(membuf, NULL, NULL, NULL);
285 BIO_free_all(membuf);
287 if (certreq)
289 WvX509 newcert(X509_new());
291 newcert.set_subject(X509_REQ_get_subject_name(certreq));
292 newcert.set_version();
294 // Set the Serial Number for the certificate
295 srand(time(NULL));
296 int serial = rand();
297 newcert.set_serial(serial);
299 newcert.set_lifetime(60*60*24*3650);
301 // The public key of the new cert should be the same as that from
302 // the request.
303 EVP_PKEY *pk = X509_REQ_get_pubkey(certreq);
304 X509_set_pubkey(newcert.get_cert(), pk);
305 EVP_PKEY_free(pk);
307 // every good cert needs an ski+aki
308 newcert.set_ski();
309 newcert.set_aki(*this);
311 // The Issuer name is the subject name of the current cert
312 newcert.set_issuer(*this);
314 X509_EXTENSION *ex = NULL;
315 // Set the RFC2459-mandated keyUsage field to critical, and restrict
316 // the usage of this cert to digital signature and key encipherment.
317 newcert.set_key_usage("critical, digitalSignature, keyEncipherment");
319 // This could cause Netscape to barf because if we set
320 // basicConstraints to critical, we break RFC2459 compliance. Why
321 // they chose to enforce that bit, and not the rest is beyond me...
322 // but oh well...
323 ex = X509V3_EXT_conf_nid(NULL, NULL, NID_basic_constraints,
324 (char*)"CA:FALSE");
326 X509_add_ext(newcert.get_cert(), ex, -1);
327 X509_EXTENSION_free(ex);
329 newcert.set_ext_key_usage("critical, TLS Web Client Authentication");
331 signcert(newcert);
333 X509_REQ_free(certreq);
334 return WvString(newcert.encode(WvX509::CertPEM));
336 else
338 debug("Can't decode Certificate Request\n");
339 return WvString::null;
344 bool WvX509Mgr::signcert(WvX509 &unsignedcert) const
346 if (!isok())
348 debug(WvLog::Warning, "Asked to sign certificate, but not ok! "
349 "Aborting.\n");
350 return false;
353 if (cert == unsignedcert.cert)
355 debug("Self Signing!\n");
357 #ifdef HAVE_OPENSSL_POLICY_MAPPING
358 else if (!X509_check_ca(cert))
360 debug("This certificate is not a CA, and is thus not allowed to sign "
361 "certificates!\n");
362 return false;
364 #endif
365 else if (!((cert->ex_flags & EXFLAG_KUSAGE) &&
366 (cert->ex_kusage & KU_KEY_CERT_SIGN)))
368 debug("This Certificate is not allowed to sign certificates!\n");
369 return false;
372 debug("Ok, now sign the new cert with the current RSA key.\n");
373 EVP_PKEY *certkey = EVP_PKEY_new();
374 bool cakeyok = EVP_PKEY_set1_RSA(certkey, rsa->rsa);
375 if (cakeyok)
377 X509_sign(unsignedcert.get_cert(), certkey, EVP_sha1());
379 else
381 debug("No keys??\n");
382 EVP_PKEY_free(certkey);
383 return false;
386 EVP_PKEY_free(certkey);
387 return true;
391 bool WvX509Mgr::signcrl(WvCRL &crl) const
393 if (!isok() || !crl.isok())
395 debug(WvLog::Warning, "Asked to sign CRL, but certificate or CRL (or "
396 "both) not ok! Aborting.\n");
397 return false;
399 #ifdef HAVE_OPENSSL_POLICY_MAPPING
400 else if (!X509_check_ca(cert))
402 debug("This certificate is not a CA, and is thus not allowed to sign "
403 "CRLs!\n");
404 return false;
406 else if (!((cert->ex_flags & EXFLAG_KUSAGE) &&
407 (cert->ex_kusage & KU_CRL_SIGN)))
409 debug("Certificate not allowed to sign CRLs! (%s %s)\n",
410 (cert->ex_flags & EXFLAG_KUSAGE),
411 (cert->ex_kusage & KU_CRL_SIGN));
412 return false;
414 #endif
416 EVP_PKEY *certkey = EVP_PKEY_new();
417 bool cakeyok = EVP_PKEY_set1_RSA(certkey, rsa->rsa);
418 if (cakeyok)
420 ASN1_TIME *tmptm = ASN1_TIME_new();
421 // Set the LastUpdate time to now.
422 X509_gmtime_adj(tmptm, 0);
423 X509_CRL_set_lastUpdate(crl.getcrl(), tmptm);
424 // CRL's are valid for 30 days
425 X509_gmtime_adj(tmptm, (long)60*60*24*30);
426 X509_CRL_set_nextUpdate(crl.getcrl(), tmptm);
427 ASN1_TIME_free(tmptm);
429 // OK - now sign it...
430 X509_CRL_sign(crl.getcrl(), certkey, EVP_sha1());
432 else
434 debug(WvLog::Warning, "Asked to sign CRL, but no RSA key associated "
435 "with certificate. Aborting.\n");
436 EVP_PKEY_free(certkey);
437 return false;
439 EVP_PKEY_free(certkey);
441 return true;
445 WvString WvX509Mgr::sign(WvStringParm data) const
447 WvDynBuf buf;
448 buf.putstr(data);
449 return sign(buf);
453 WvString WvX509Mgr::sign(WvBuf &data) const
455 assert(rsa);
457 EVP_MD_CTX sig_ctx;
458 unsigned char sig_buf[4096];
460 EVP_PKEY *pk = EVP_PKEY_new();
461 assert(pk); // OOM
463 if (!EVP_PKEY_set1_RSA(pk, rsa->rsa))
465 debug("Error setting RSA keys.\n");
466 EVP_PKEY_free(pk);
467 return WvString::null;
470 EVP_SignInit(&sig_ctx, EVP_sha1());
471 EVP_SignUpdate(&sig_ctx, data.peek(0, data.used()), data.used());
472 unsigned int sig_len = sizeof(sig_buf);
473 int sig_err = EVP_SignFinal(&sig_ctx, sig_buf,
474 &sig_len, pk);
475 if (sig_err != 1)
477 debug("Error while signing.\n");
478 EVP_PKEY_free(pk);
479 return WvString::null;
482 EVP_PKEY_free(pk);
483 EVP_MD_CTX_cleanup(&sig_ctx); // this isn't my fault ://
484 WvDynBuf buf;
485 buf.put(sig_buf, sig_len);
486 debug("Signature size: %s\n", buf.used());
487 return WvBase64Encoder().strflushbuf(buf, true);
491 bool WvX509Mgr::write_p12(WvStringParm _fname, WvStringParm _pkcs12pass) const
493 debug("Dumping RSA Key and X509 Cert to PKCS12 structure.\n");
495 AutoClose fp = fopen(_fname, "wb");
497 if (!fp)
499 debug(WvLog::Warning, "Unable to open file. Error: %s\n",
500 strerror(errno));
501 return false;
504 if (!!_pkcs12pass)
506 if (rsa && cert)
508 EVP_PKEY *pk = EVP_PKEY_new();
509 assert(pk); // OOM
511 if (!EVP_PKEY_set1_RSA(pk, rsa->rsa))
513 debug("Error setting RSA keys.\n");
514 EVP_PKEY_free(pk);
515 return false;
517 else
519 WvString pkcs12pass(_pkcs12pass);
520 PKCS12 *pkg
521 = PKCS12_create(pkcs12pass.edit(), (char*)"foo", pk,
522 cert, NULL, 0, 0, 0,
523 0, 0);
524 if (pkg)
526 debug("Writing the PKCS12 object out...\n");
527 i2d_PKCS12_fp(fp, pkg);
528 PKCS12_free(pkg);
529 EVP_PKEY_free(pk);
531 else
533 debug(WvLog::Warning, "Unable to create PKCS12 object.");
534 EVP_PKEY_free(pk);
535 return false;
539 else
541 debug(WvLog::Warning,
542 "The RSA key or the certificate is not present.");
543 return false;
546 else
548 debug(WvLog::Warning, "No password specified for PKCS12 dump.");
549 return false;
552 return true;
556 void WvX509Mgr::read_p12(WvStringParm _fname, WvStringParm _pkcs12pass)
558 debug("Reading Certificate and Private Key from PKCS12 file: %s\n",
559 _fname);
561 if (rsa)
562 WVDELETE(rsa);
564 AutoClose fp = fopen(_fname, "r");
566 if (!fp)
568 debug("Unable to open file '%s'!\n", _fname);
569 return;
572 if (!!_pkcs12pass)
574 PKCS12 *pkg = d2i_PKCS12_fp(fp, NULL);
575 if (pkg)
577 EVP_PKEY *pk = NULL;
579 // Parse out the bits out the PKCS12 package.
580 X509 *x;
581 PKCS12_parse(pkg, _pkcs12pass, &pk, &x, NULL);
582 PKCS12_free(pkg);
583 if (!pk || !x)
585 debug("Could not decode pkcs12 file.\n");
586 EVP_PKEY_free(pk);
587 return;
590 cert = x;
592 // Now, cert should be OK, let's try and set up the RSA stuff
593 // since we've essentially got a PKEY, and not a WvRSAKey
594 // We need to create a new WvRSAKey from the PKEY...
595 rsa = new WvRSAKey(EVP_PKEY_get1_RSA(pk), true);
596 EVP_PKEY_free(pk);
598 // Now that we have both, check to make sure that they match
599 if (!test())
601 debug("Could not fill in RSA and certificate with matching "
602 "values! Expect problems.\n");
603 return;
606 else
608 debug("Read in of PKCS12 file '%s' failed", _fname);
609 return;
612 else
614 debug("No password specified for PKCS12 file\n");
615 return;
620 WvString WvX509Mgr::encode(const WvRSAKey::DumpMode mode) const
622 if (rsa)
623 return rsa->encode(mode);
624 return "";
628 WvString WvX509Mgr::encode(const WvX509::DumpMode mode) const
630 return WvX509::encode(mode);
634 void WvX509Mgr::encode(const WvRSAKey::DumpMode mode, WvBuf &buf) const
636 if (rsa)
637 rsa->encode(mode, buf);
641 void WvX509Mgr::encode(const WvX509::DumpMode mode, WvBuf &buf) const
643 WvX509::encode(mode, buf);
647 void WvX509Mgr::decode(const WvRSAKey::DumpMode mode, WvStringParm encoded)
649 if (rsa)
650 rsa->decode(mode, encoded);
651 else
653 rsa = new WvRSAKey();
654 rsa->decode(mode, encoded);
659 void WvX509Mgr::decode(const WvX509::DumpMode mode, WvStringParm encoded)
661 WvX509::decode(mode, encoded);
665 void WvX509Mgr::decode(const WvRSAKey::DumpMode mode, WvBuf &encoded)
667 if (rsa)
668 rsa->decode(mode, encoded);
669 else
671 rsa = new WvRSAKey();
672 rsa->decode(mode, encoded);
677 void WvX509Mgr::decode(const WvX509::DumpMode mode, WvBuf &encoded)
679 WvX509::decode(mode, encoded);