Restore support for openssl 0.9.7 (Debian Sarge).
[wvstreams.git] / crypto / wvx509mgr.cc
blobd974ecfe9b0587a789846620d78e28fb22b8727f
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(WvStringParm _dname, WvRSAKey *_rsa, bool ca)
44 : WvX509(),
45 debug("X509 Manager", WvLog::Debug5)
47 debug("Creating new certificate+key pair for %s.\n", _dname);
48 rsa = _rsa;
50 if (!!_dname)
52 create_selfissued(_dname, ca);
53 debug("Ok - Parameters set... now signing certificate.\n");
54 signcert(*this);
56 else
57 debug("Sorry, can't create an anonymous certificate.");
61 WvX509Mgr::WvX509Mgr(WvStringParm _dname, int bits, bool ca)
62 : WvX509(),
63 debug("X509 Manager", WvLog::Debug5)
65 debug("Creating new certificate+key pair for %s.\n", _dname);
66 rsa = NULL;
68 if (!!_dname)
70 rsa = new WvRSAKey(bits);
71 create_selfissued(_dname, ca);
72 debug("Ok - Parameters set... now signing certificate.\n");
73 signcert(*this);
75 else
76 debug("Sorry, can't create an anonymous certificate.");
80 void WvX509Mgr::create_selfissued(WvStringParm dname, bool is_ca)
82 if (cert)
84 debug("Replacing already existant certificate...\n");
85 X509_free(cert);
86 cert = NULL;
89 // double check RSA key
90 if (rsa->isok())
91 debug("RSA Key is fine.\n");
92 else
93 return;
95 if ((cert = X509_new()) == NULL)
96 return;
98 // Completely broken in my mind - this sets the version
99 // string to '3' (I guess version starts at 0)
100 set_version();
102 // RFC2459 says that this number must be unique for each certificate
103 // issued by a CA. It may be that some web browsers get confused if
104 // more than one cert with the same name has the same serial number, so
105 // let's be careful.
106 srand(time(NULL));
107 int serial = rand();
108 set_serial(serial);
110 // 10 years...
111 set_lifetime(60*60*24*3650);
113 set_pubkey(*rsa);
115 set_issuer(dname);
116 set_subject(dname);
117 set_ski();
119 if (is_ca)
121 debug("Setting Extensions with CA Parameters.\n");
122 debug("Setting Key Usage.\n");
123 set_key_usage("critical, keyCertSign, cRLSign");
124 debug("Setting Basic Constraints.\n");
125 set_extension(NID_basic_constraints, "critical, CA:TRUE");
126 debug("Setting Netscape Certificate Type.\n");
127 set_extension(NID_netscape_cert_type, "SSL CA, S/MIME CA, Object Signing CA");
128 // debug("Setting Constraints.\n");
129 // set_constraints("requireExplicitPolicy");
131 else
133 debug("Setting Key Usage with normal server parameters\n");
134 set_nsserver(dname);
135 set_key_usage("critical, digitalSignature, keyEncipherment, keyAgreement");
136 set_extension(NID_basic_constraints, "CA:FALSE");
137 set_ext_key_usage("TLS Web Server Authentication,"
138 "TLS Web Client Authentication");
141 // we do not actually sign the certificate here: that must be done by the
142 // user (WvX509Mgr most likely)
144 debug("Certificate for %s created\n", dname);
148 WvX509Mgr::~WvX509Mgr()
150 debug("Deleting.\n");
151 WVDELETE(rsa);
152 wvssl_free();
156 bool WvX509Mgr::isok() const
158 return WvX509::isok() && rsa && rsa->isok() && test();
162 WvString WvX509Mgr::errstr() const
164 if (!WvX509::isok())
165 return WvX509::errstr();
167 if (!rsa)
168 return "No RSA key set.";
169 else if (!rsa->isok())
170 return "RSA key not valid.";
171 else if (!test())
172 return "RSA key and certificate do not match.";
174 return WvString::empty;
178 bool WvX509Mgr::bind_ssl(SSL_CTX *ctx)
180 if (SSL_CTX_use_certificate(ctx, get_cert()) <= 0)
182 return false;
184 debug("Certificate activated.\n");
186 if (SSL_CTX_use_RSAPrivateKey(ctx, rsa->rsa) <= 0)
188 return false;
190 debug("RSA private key activated.\n");
191 return true;
195 bool WvX509Mgr::test() const
197 if (!cert)
199 debug("No X509 certificate: test fails.\n");
200 return false;
203 if (rsa)
205 EVP_PKEY *pk = EVP_PKEY_new();
206 assert(pk);
208 if (!EVP_PKEY_set1_RSA(pk, rsa->rsa))
210 debug("Error setting RSA keys: test fails.\n");
211 EVP_PKEY_free(pk);
212 return false;
215 bool bad = false;
216 int verify_return = X509_verify(cert, pk);
218 if (verify_return != 1) // only '1' means okay
220 // However let's double check:
221 WvString rsapub = rsa->encode(WvRSAKey::RsaPubPEM);
222 WvRSAKey *temprsa = get_rsa_pub();
223 WvString certpub = temprsa->encode(WvRSAKey::RsaPubPEM);
224 delete temprsa;
225 // debug("rsapub:\n%s\n", rsapub);
226 // debug("certpub:\n%s\n", certpub);
227 if (certpub == rsapub)
228 ; // do nothing, since OpenSSL is lying
229 else
231 // I guess that it really did fail.
232 debug("Certificate test failed: %s\n", wvssl_errstr());
233 bad = true;
237 EVP_PKEY_free(pk);
238 return !bad;
241 return false;
245 WvString WvX509Mgr::signreq(WvStringParm pkcs10req) const
247 debug("Signing a certificate request with: %s\n", get_subject());
248 if (!isok())
250 debug(WvLog::Warning, "Asked to sign certificate request, but not ok! "
251 "Aborting.\n");
252 return false;
255 // Break this next part out into a de-pemify section, since that is what
256 // this part up until the FIXME: is about.
257 WvString pkcs10(pkcs10req);
259 char *begin = strstr(pkcs10.edit(), "\nMII");
260 if (!begin)
262 debug("This doesn't look like PEM Encoded information...\n");
263 return WvString::null;
265 char *end = strstr(begin + 1, "\n---");
266 if (!end)
268 debug("Is this a complete certificate request?\n");
269 return WvString::null;
271 *++end = '\0';
272 WvString body(begin); // just the PKCS#10 request,
273 // without the ---BEGIN and ---END
275 WvDynBuf reqbuf;
276 WvBase64Decoder dec;
277 dec.flushstrbuf(body, reqbuf, true);
279 // FIXME: Duplicates code from cert_selfsign
280 size_t reqlen = reqbuf.used();
281 const unsigned char *req = reqbuf.get(reqlen);
282 X509_REQ *certreq = wv_d2i_X509_REQ(NULL, &req, reqlen);
283 if (certreq)
285 WvX509 newcert(X509_new());
287 newcert.set_subject(X509_REQ_get_subject_name(certreq));
288 newcert.set_version();
290 // Set the Serial Number for the certificate
291 srand(time(NULL));
292 int serial = rand();
293 newcert.set_serial(serial);
295 newcert.set_lifetime(60*60*24*3650);
297 // The public key of the new cert should be the same as that from
298 // the request.
299 EVP_PKEY *pk = X509_REQ_get_pubkey(certreq);
300 X509_set_pubkey(newcert.get_cert(), pk);
301 EVP_PKEY_free(pk);
303 // every good cert needs an ski+aki
304 newcert.set_ski();
305 newcert.set_aki(*this);
307 // The Issuer name is the subject name of the current cert
308 newcert.set_issuer(*this);
310 X509_EXTENSION *ex = NULL;
311 // Set the RFC2459-mandated keyUsage field to critical, and restrict
312 // the usage of this cert to digital signature and key encipherment.
313 newcert.set_key_usage("critical, digitalSignature, keyEncipherment");
315 // This could cause Netscape to barf because if we set basicConstraints
316 // to critical, we break RFC2459 compliance. Why they chose to enforce
317 // that bit, and not the rest is beyond me... but oh well...
318 ex = X509V3_EXT_conf_nid(NULL, NULL, NID_basic_constraints,
319 (char*)"CA:FALSE");
321 X509_add_ext(newcert.get_cert(), ex, -1);
322 X509_EXTENSION_free(ex);
324 newcert.set_ext_key_usage("critical, TLS Web Client Authentication");
326 signcert(newcert);
328 X509_REQ_free(certreq);
329 return WvString(newcert.encode(WvX509::CertPEM));
331 else
333 debug("Can't decode Certificate Request\n");
334 return WvString::null;
339 bool WvX509Mgr::signcert(WvX509 &unsignedcert) const
341 if (!isok())
343 debug(WvLog::Warning, "Asked to sign certificate, but not ok! "
344 "Aborting.\n");
345 return false;
348 if (cert == unsignedcert.cert)
350 debug("Self Signing!\n");
352 #ifdef HAVE_OPENSSL_POLICY_MAPPING
353 else if (!X509_check_ca(cert))
355 debug("This certificate is not a CA, and is thus not allowed to sign "
356 "certificates!\n");
357 return false;
359 #endif
360 else if (!((cert->ex_flags & EXFLAG_KUSAGE) &&
361 (cert->ex_kusage & KU_KEY_CERT_SIGN)))
363 debug("This Certificate is not allowed to sign certificates!\n");
364 return false;
367 debug("Ok, now sign the new cert with the current RSA key.\n");
368 EVP_PKEY *certkey = EVP_PKEY_new();
369 bool cakeyok = EVP_PKEY_set1_RSA(certkey, rsa->rsa);
370 if (cakeyok)
372 X509_sign(unsignedcert.get_cert(), certkey, EVP_sha1());
374 else
376 debug("No keys??\n");
377 EVP_PKEY_free(certkey);
378 return false;
381 EVP_PKEY_free(certkey);
382 return true;
386 bool WvX509Mgr::signcrl(WvCRL &crl) const
388 if (!isok() || !crl.isok())
390 debug(WvLog::Warning, "Asked to sign CRL, but certificate or CRL (or "
391 "both) not ok! Aborting.\n");
392 return false;
394 #ifdef HAVE_OPENSSL_POLICY_MAPPING
395 else if (!X509_check_ca(cert))
397 debug("This certificate is not a CA, and is thus not allowed to sign "
398 "CRLs!\n");
399 return false;
401 #endif
402 else if (!((cert->ex_flags & EXFLAG_KUSAGE) &&
403 (cert->ex_kusage & KU_CRL_SIGN)))
405 debug("Certificate not allowed to sign CRLs! (%s %s)\n",
406 (cert->ex_flags & EXFLAG_KUSAGE), (cert->ex_kusage & KU_CRL_SIGN));
407 return false;
410 EVP_PKEY *certkey = EVP_PKEY_new();
411 bool cakeyok = EVP_PKEY_set1_RSA(certkey, rsa->rsa);
412 if (cakeyok)
414 // Use Version 2 CRLs - Of COURSE that means
415 // to set it to 1 here... grumble..
416 X509_CRL_set_version(crl.getcrl(), 1);
418 X509_CRL_set_issuer_name(crl.getcrl(), X509_get_subject_name(cert));
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", strerror(errno));
500 return false;
503 if (!!_pkcs12pass)
505 if (rsa && cert)
507 EVP_PKEY *pk = EVP_PKEY_new();
508 assert(pk); // OOM
510 if (!EVP_PKEY_set1_RSA(pk, rsa->rsa))
512 debug("Error setting RSA keys.\n");
513 EVP_PKEY_free(pk);
514 return false;
516 else
518 WvString pkcs12pass(_pkcs12pass);
519 PKCS12 *pkg = PKCS12_create(pkcs12pass.edit(), (char*)"foo", pk,
520 cert, NULL, 0, 0, 0,
521 0, 0);
522 if (pkg)
524 debug("Writing the PKCS12 object out...\n");
525 i2d_PKCS12_fp(fp, pkg);
526 PKCS12_free(pkg);
527 EVP_PKEY_free(pk);
529 else
531 debug(WvLog::Warning, "Unable to create PKCS12 object.");
532 EVP_PKEY_free(pk);
533 return false;
537 else
539 debug(WvLog::Warning, "The RSA key or the certificate is not present.");
540 return false;
543 else
545 debug(WvLog::Warning, "No password specified for PKCS12 dump.");
546 return false;
549 return true;
553 void WvX509Mgr::read_p12(WvStringParm _fname, WvStringParm _pkcs12pass)
555 debug("Reading Certificate and Private Key from PKCS12 file: %s\n", _fname);
557 if (rsa)
558 WVDELETE(rsa);
560 AutoClose fp = fopen(_fname, "r");
562 if (!fp)
564 debug("Unable to open file '%s'!\n", _fname);
565 return;
568 if (!!_pkcs12pass)
570 PKCS12 *pkg = d2i_PKCS12_fp(fp, NULL);
571 if (pkg)
573 EVP_PKEY *pk = NULL;
575 // Parse out the bits out the PKCS12 package.
576 X509 *x;
577 PKCS12_parse(pkg, _pkcs12pass, &pk, &x, NULL);
578 PKCS12_free(pkg);
579 if (!pk || !x)
581 debug("Could not decode pkcs12 file.\n");
582 EVP_PKEY_free(pk);
583 return;
586 cert = x;
588 // Now, cert should be OK, let's try and set up the RSA stuff
589 // since we've essentially got a PKEY, and not a WvRSAKey
590 // We need to create a new WvRSAKey from the PKEY...
591 rsa = new WvRSAKey(EVP_PKEY_get1_RSA(pk), true);
592 EVP_PKEY_free(pk);
594 // Now that we have both, check to make sure that they match
595 if (!test())
597 debug("Could not fill in RSA and certificate with matching "
598 "values! Expect problems.\n");
599 return;
602 else
604 debug("Read in of PKCS12 file '%s' failed", _fname);
605 return;
608 else
610 debug("No password specified for PKCS12 file\n");
611 return;
616 WvString WvX509Mgr::encode(const WvRSAKey::DumpMode mode) const
618 if (rsa)
619 return rsa->encode(mode);
620 return "";
624 WvString WvX509Mgr::encode(const WvX509::DumpMode mode) const
626 return WvX509::encode(mode);
630 void WvX509Mgr::encode(const WvRSAKey::DumpMode mode, WvBuf &buf) const
632 if (rsa)
633 rsa->encode(mode, buf);
637 void WvX509Mgr::encode(const WvX509::DumpMode mode, WvBuf &buf) const
639 WvX509::encode(mode, buf);
643 void WvX509Mgr::decode(const WvRSAKey::DumpMode mode, WvStringParm encoded)
645 if (rsa)
646 rsa->decode(mode, encoded);
647 else
649 rsa = new WvRSAKey();
650 rsa->decode(mode, encoded);
655 void WvX509Mgr::decode(const WvX509::DumpMode mode, WvStringParm encoded)
657 WvX509::decode(mode, encoded);
661 void WvX509Mgr::decode(const WvRSAKey::DumpMode mode, WvBuf &encoded)
663 if (rsa)
664 rsa->decode(mode, encoded);
665 else
667 rsa = new WvRSAKey();
668 rsa->decode(mode, encoded);
673 void WvX509Mgr::decode(const WvX509::DumpMode mode, WvBuf &encoded)
675 WvX509::decode(mode, encoded);