Try another fix for wvlinkerhack and static libraries.
[wvstreams.git] / crypto / wvx509mgr.cc
blobbcb5247490d7dc8215d63e3fca8a1aa91d67b75c
1 #include "wvbase64.h"
2 #include "wvsslhacks.h"
3 #include "wvx509mgr.h"
5 #include <openssl/pem.h>
6 #include <openssl/x509v3.h>
7 #include <openssl/err.h>
8 #include <openssl/ssl.h>
9 #include <openssl/sha.h>
10 #include <openssl/pkcs12.h>
13 namespace {
14 class AutoClose {
15 public:
16 AutoClose(FILE *fp): fp(fp) { }
17 ~AutoClose()
19 if (fp)
20 fclose(fp);
23 operator FILE *() const
25 return fp;
28 private:
29 FILE *fp;
31 } // anomymous namespace...
34 WvX509Mgr::WvX509Mgr()
35 : WvX509(),
36 debug("X509 Manager", WvLog::Debug5)
38 rsa = NULL;
42 WvX509Mgr::WvX509Mgr(WvStringParm _dname, WvRSAKey *_rsa, bool ca)
43 : WvX509(),
44 debug("X509 Manager", WvLog::Debug5)
46 debug("Creating new certificate+key pair for %s.\n", _dname);
47 rsa = _rsa;
49 if (!!_dname)
51 create_selfissued(_dname, ca);
52 debug("Ok - Parameters set... now signing certificate.\n");
53 signcert(*this);
55 else
56 debug("Sorry, can't create an anonymous certificate.");
60 WvX509Mgr::WvX509Mgr(WvStringParm _dname, int bits, bool ca)
61 : WvX509(),
62 debug("X509 Manager", WvLog::Debug5)
64 debug("Creating new certificate+key pair for %s.\n", _dname);
65 rsa = NULL;
67 if (!!_dname)
69 rsa = new WvRSAKey(bits);
70 create_selfissued(_dname, ca);
71 debug("Ok - Parameters set... now signing certificate.\n");
72 signcert(*this);
74 else
75 debug("Sorry, can't create an anonymous certificate.");
79 void WvX509Mgr::create_selfissued(WvStringParm dname, bool is_ca)
81 if (cert)
83 debug("Replacing already existant certificate...\n");
84 X509_free(cert);
85 cert = NULL;
88 // double check RSA key
89 if (rsa->isok())
90 debug("RSA Key is fine.\n");
91 else
92 return;
94 if ((cert = X509_new()) == NULL)
95 return;
97 // Completely broken in my mind - this sets the version
98 // string to '3' (I guess version starts at 0)
99 set_version();
101 // RFC2459 says that this number must be unique for each certificate
102 // issued by a CA. It may be that some web browsers get confused if
103 // more than one cert with the same name has the same serial number, so
104 // let's be careful.
105 srand(time(NULL));
106 int serial = rand();
107 set_serial(serial);
109 // 10 years...
110 set_lifetime(60*60*24*3650);
112 set_pubkey(*rsa);
114 set_issuer(dname);
115 set_subject(dname);
116 set_ski();
118 if (is_ca)
120 debug("Setting Extensions with CA Parameters.\n");
121 debug("Setting Key Usage.\n");
122 set_key_usage("critical, keyCertSign, cRLSign");
123 debug("Setting Basic Constraints.\n");
124 set_extension(NID_basic_constraints, "critical, CA:TRUE");
125 debug("Setting Netscape Certificate Type.\n");
126 set_extension(NID_netscape_cert_type, "SSL CA, S/MIME CA, Object Signing CA");
127 // debug("Setting Constraints.\n");
128 // set_constraints("requireExplicitPolicy");
130 else
132 debug("Setting Key Usage with normal server parameters\n");
133 set_nsserver(dname);
134 set_key_usage("critical, digitalSignature, keyEncipherment, keyAgreement");
135 set_extension(NID_basic_constraints, "CA:FALSE");
136 set_ext_key_usage("TLS Web Server Authentication,"
137 "TLS Web Client Authentication");
140 // we do not actually sign the certificate here: that must be done by the
141 // user (WvX509Mgr most likely)
143 debug("Certificate for %s created\n", dname);
147 WvX509Mgr::~WvX509Mgr()
149 debug("Deleting.\n");
150 WVDELETE(rsa);
151 wvssl_free();
155 bool WvX509Mgr::isok() const
157 return WvX509::isok() && rsa && rsa->isok() && test();
161 WvString WvX509Mgr::errstr() const
163 if (!WvX509::isok())
164 return WvX509::errstr();
166 if (!rsa)
167 return "No RSA key set.";
168 else if (!rsa->isok())
169 return "RSA key not valid.";
170 else if (!test())
171 return "RSA key and certificate do not match.";
173 return WvString::empty;
177 bool WvX509Mgr::bind_ssl(SSL_CTX *ctx)
179 if (SSL_CTX_use_certificate(ctx, get_cert()) <= 0)
181 return false;
183 debug("Certificate activated.\n");
185 if (SSL_CTX_use_RSAPrivateKey(ctx, rsa->rsa) <= 0)
187 return false;
189 debug("RSA private key activated.\n");
190 return true;
194 bool WvX509Mgr::test() const
196 if (!cert)
198 debug("No X509 certificate: test fails.\n");
199 return false;
202 if (rsa)
204 EVP_PKEY *pk = EVP_PKEY_new();
205 assert(pk);
207 if (!EVP_PKEY_set1_RSA(pk, rsa->rsa))
209 debug("Error setting RSA keys: test fails.\n");
210 EVP_PKEY_free(pk);
211 return false;
214 bool bad = false;
215 int verify_return = X509_verify(cert, pk);
217 if (verify_return != 1) // only '1' means okay
219 // However let's double check:
220 WvString rsapub = rsa->encode(WvRSAKey::RsaPubPEM);
221 WvRSAKey *temprsa = get_rsa_pub();
222 WvString certpub = temprsa->encode(WvRSAKey::RsaPubPEM);
223 delete temprsa;
224 // debug("rsapub:\n%s\n", rsapub);
225 // debug("certpub:\n%s\n", certpub);
226 if (certpub == rsapub)
227 ; // do nothing, since OpenSSL is lying
228 else
230 // I guess that it really did fail.
231 debug("Certificate test failed: %s\n", wvssl_errstr());
232 bad = true;
236 EVP_PKEY_free(pk);
237 return !bad;
240 return false;
244 WvString WvX509Mgr::signreq(WvStringParm pkcs10req) const
246 debug("Signing a certificate request with: %s\n", get_subject());
247 if (!isok())
249 debug(WvLog::Warning, "Asked to sign certificate request, but not ok! "
250 "Aborting.\n");
251 return false;
254 // Break this next part out into a de-pemify section, since that is what
255 // this part up until the FIXME: is about.
256 WvString pkcs10(pkcs10req);
258 char *begin = strstr(pkcs10.edit(), "\nMII");
259 if (!begin)
261 debug("This doesn't look like PEM Encoded information...\n");
262 return WvString::null;
264 char *end = strstr(begin + 1, "\n---");
265 if (!end)
267 debug("Is this a complete certificate request?\n");
268 return WvString::null;
270 *++end = '\0';
271 WvString body(begin); // just the PKCS#10 request,
272 // without the ---BEGIN and ---END
274 WvDynBuf reqbuf;
275 WvBase64Decoder dec;
276 dec.flushstrbuf(body, reqbuf, true);
278 // FIXME: Duplicates code from cert_selfsign
279 size_t reqlen = reqbuf.used();
280 const unsigned char *req = reqbuf.get(reqlen);
281 X509_REQ *certreq = wv_d2i_X509_REQ(NULL, &req, reqlen);
282 if (certreq)
284 WvX509 newcert(X509_new());
286 newcert.set_subject(X509_REQ_get_subject_name(certreq));
287 newcert.set_version();
289 // Set the Serial Number for the certificate
290 srand(time(NULL));
291 int serial = rand();
292 newcert.set_serial(serial);
294 newcert.set_lifetime(60*60*24*3650);
296 // The public key of the new cert should be the same as that from
297 // the request.
298 EVP_PKEY *pk = X509_REQ_get_pubkey(certreq);
299 X509_set_pubkey(newcert.get_cert(), pk);
300 EVP_PKEY_free(pk);
302 // every good cert needs an ski+aki
303 newcert.set_ski();
304 newcert.set_aki(*this);
306 // The Issuer name is the subject name of the current cert
307 newcert.set_issuer(*this);
309 X509_EXTENSION *ex = NULL;
310 // Set the RFC2459-mandated keyUsage field to critical, and restrict
311 // the usage of this cert to digital signature and key encipherment.
312 newcert.set_key_usage("critical, digitalSignature, keyEncipherment");
314 // This could cause Netscape to barf because if we set basicConstraints
315 // to critical, we break RFC2459 compliance. Why they chose to enforce
316 // that bit, and not the rest is beyond me... but oh well...
317 ex = X509V3_EXT_conf_nid(NULL, NULL, NID_basic_constraints,
318 (char*)"CA:FALSE");
320 X509_add_ext(newcert.get_cert(), ex, -1);
321 X509_EXTENSION_free(ex);
323 newcert.set_ext_key_usage("critical, TLS Web Client Authentication");
325 signcert(newcert);
327 X509_REQ_free(certreq);
328 return WvString(newcert.encode(WvX509::CertPEM));
330 else
332 debug("Can't decode Certificate Request\n");
333 return WvString::null;
338 bool WvX509Mgr::signcert(WvX509 &unsignedcert) const
340 if (!isok())
342 debug(WvLog::Warning, "Asked to sign certificate, but not ok! "
343 "Aborting.\n");
344 return false;
347 if (cert == unsignedcert.cert)
349 debug("Self Signing!\n");
351 else if (!X509_check_ca(cert))
353 debug("This certificate is not a CA, and is thus not allowed to sign "
354 "certificates!\n");
355 return false;
357 else if (!((cert->ex_flags & EXFLAG_KUSAGE) &&
358 (cert->ex_kusage & KU_KEY_CERT_SIGN)))
360 debug("This Certificate is not allowed to sign certificates!\n");
361 return false;
364 debug("Ok, now sign the new cert with the current RSA key.\n");
365 EVP_PKEY *certkey = EVP_PKEY_new();
366 bool cakeyok = EVP_PKEY_set1_RSA(certkey, rsa->rsa);
367 if (cakeyok)
369 X509_sign(unsignedcert.get_cert(), certkey, EVP_sha1());
371 else
373 debug("No keys??\n");
374 EVP_PKEY_free(certkey);
375 return false;
378 EVP_PKEY_free(certkey);
379 return true;
383 bool WvX509Mgr::signcrl(WvCRL &crl) const
385 if (!isok() || !crl.isok())
387 debug(WvLog::Warning, "Asked to sign CRL, but certificate or CRL (or "
388 "both) not ok! Aborting.\n");
389 return false;
391 else if (!X509_check_ca(cert))
393 debug("This certificate is not a CA, and is thus not allowed to sign "
394 "CRLs!\n");
395 return false;
397 else if (!((cert->ex_flags & EXFLAG_KUSAGE) &&
398 (cert->ex_kusage & KU_CRL_SIGN)))
400 debug("Certificate not allowed to sign CRLs! (%s %s)\n",
401 (cert->ex_flags & EXFLAG_KUSAGE), (cert->ex_kusage & KU_CRL_SIGN));
402 return false;
405 EVP_PKEY *certkey = EVP_PKEY_new();
406 bool cakeyok = EVP_PKEY_set1_RSA(certkey, rsa->rsa);
407 if (cakeyok)
409 // Use Version 2 CRLs - Of COURSE that means
410 // to set it to 1 here... grumble..
411 X509_CRL_set_version(crl.getcrl(), 1);
413 X509_CRL_set_issuer_name(crl.getcrl(), X509_get_subject_name(cert));
415 ASN1_TIME *tmptm = ASN1_TIME_new();
416 // Set the LastUpdate time to now.
417 X509_gmtime_adj(tmptm, 0);
418 X509_CRL_set_lastUpdate(crl.getcrl(), tmptm);
419 // CRL's are valid for 30 days
420 X509_gmtime_adj(tmptm, (long)60*60*24*30);
421 X509_CRL_set_nextUpdate(crl.getcrl(), tmptm);
422 ASN1_TIME_free(tmptm);
424 // OK - now sign it...
425 X509_CRL_sign(crl.getcrl(), certkey, EVP_sha1());
427 else
429 debug(WvLog::Warning, "Asked to sign CRL, but no RSA key associated "
430 "with certificate. Aborting.\n");
431 EVP_PKEY_free(certkey);
432 return false;
434 EVP_PKEY_free(certkey);
436 return true;
440 WvString WvX509Mgr::sign(WvStringParm data) const
442 WvDynBuf buf;
443 buf.putstr(data);
444 return sign(buf);
448 WvString WvX509Mgr::sign(WvBuf &data) const
450 assert(rsa);
452 EVP_MD_CTX sig_ctx;
453 unsigned char sig_buf[4096];
455 EVP_PKEY *pk = EVP_PKEY_new();
456 assert(pk); // OOM
458 if (!EVP_PKEY_set1_RSA(pk, rsa->rsa))
460 debug("Error setting RSA keys.\n");
461 EVP_PKEY_free(pk);
462 return WvString::null;
465 EVP_SignInit(&sig_ctx, EVP_sha1());
466 EVP_SignUpdate(&sig_ctx, data.peek(0, data.used()), data.used());
467 unsigned int sig_len = sizeof(sig_buf);
468 int sig_err = EVP_SignFinal(&sig_ctx, sig_buf,
469 &sig_len, pk);
470 if (sig_err != 1)
472 debug("Error while signing.\n");
473 EVP_PKEY_free(pk);
474 return WvString::null;
477 EVP_PKEY_free(pk);
478 EVP_MD_CTX_cleanup(&sig_ctx); // this isn't my fault ://
479 WvDynBuf buf;
480 buf.put(sig_buf, sig_len);
481 debug("Signature size: %s\n", buf.used());
482 return WvBase64Encoder().strflushbuf(buf, true);
486 bool WvX509Mgr::write_p12(WvStringParm _fname, WvStringParm _pkcs12pass) const
488 debug("Dumping RSA Key and X509 Cert to PKCS12 structure.\n");
490 AutoClose fp = fopen(_fname, "wb");
492 if (!fp)
494 debug(WvLog::Warning, "Unable to open file. Error: %s\n", strerror(errno));
495 return false;
498 if (!!_pkcs12pass)
500 if (rsa && cert)
502 EVP_PKEY *pk = EVP_PKEY_new();
503 assert(pk); // OOM
505 if (!EVP_PKEY_set1_RSA(pk, rsa->rsa))
507 debug("Error setting RSA keys.\n");
508 EVP_PKEY_free(pk);
509 return false;
511 else
513 WvString pkcs12pass(_pkcs12pass);
514 PKCS12 *pkg = PKCS12_create(pkcs12pass.edit(), (char*)"foo", pk,
515 cert, NULL, 0, 0, 0,
516 0, 0);
517 if (pkg)
519 debug("Writing the PKCS12 object out...\n");
520 i2d_PKCS12_fp(fp, pkg);
521 PKCS12_free(pkg);
522 EVP_PKEY_free(pk);
524 else
526 debug(WvLog::Warning, "Unable to create PKCS12 object.");
527 EVP_PKEY_free(pk);
528 return false;
532 else
534 debug(WvLog::Warning, "The RSA key or the certificate is not present.");
535 return false;
538 else
540 debug(WvLog::Warning, "No password specified for PKCS12 dump.");
541 return false;
544 return true;
548 void WvX509Mgr::read_p12(WvStringParm _fname, WvStringParm _pkcs12pass)
550 debug("Reading Certificate and Private Key from PKCS12 file: %s\n", _fname);
552 if (rsa)
553 WVDELETE(rsa);
555 AutoClose fp = fopen(_fname, "r");
557 if (!fp)
559 debug("Unable to open file '%s'!\n", _fname);
560 return;
563 if (!!_pkcs12pass)
565 PKCS12 *pkg = d2i_PKCS12_fp(fp, NULL);
566 if (pkg)
568 EVP_PKEY *pk = NULL;
570 // Parse out the bits out the PKCS12 package.
571 X509 *x;
572 PKCS12_parse(pkg, _pkcs12pass, &pk, &x, NULL);
573 PKCS12_free(pkg);
574 if (!pk || !x)
576 debug("Could not decode pkcs12 file.\n");
577 EVP_PKEY_free(pk);
578 return;
581 cert = x;
583 // Now, cert should be OK, let's try and set up the RSA stuff
584 // since we've essentially got a PKEY, and not a WvRSAKey
585 // We need to create a new WvRSAKey from the PKEY...
586 rsa = new WvRSAKey(EVP_PKEY_get1_RSA(pk), true);
587 EVP_PKEY_free(pk);
589 // Now that we have both, check to make sure that they match
590 if (!test())
592 debug("Could not fill in RSA and certificate with matching "
593 "values! Expect problems.\n");
594 return;
597 else
599 debug("Read in of PKCS12 file '%s' failed", _fname);
600 return;
603 else
605 debug("No password specified for PKCS12 file\n");
606 return;
611 WvString WvX509Mgr::encode(const WvRSAKey::DumpMode mode) const
613 if (rsa)
614 return rsa->encode(mode);
615 return "";
619 WvString WvX509Mgr::encode(const WvX509::DumpMode mode) const
621 return WvX509::encode(mode);
625 void WvX509Mgr::encode(const WvRSAKey::DumpMode mode, WvBuf &buf) const
627 if (rsa)
628 rsa->encode(mode, buf);
632 void WvX509Mgr::encode(const WvX509::DumpMode mode, WvBuf &buf) const
634 WvX509::encode(mode, buf);
638 void WvX509Mgr::decode(const WvRSAKey::DumpMode mode, WvStringParm encoded)
640 if (rsa)
641 rsa->decode(mode, encoded);
642 else
644 rsa = new WvRSAKey();
645 rsa->decode(mode, encoded);
650 void WvX509Mgr::decode(const WvX509::DumpMode mode, WvStringParm encoded)
652 WvX509::decode(mode, encoded);
656 void WvX509Mgr::decode(const WvRSAKey::DumpMode mode, WvBuf &encoded)
658 if (rsa)
659 rsa->decode(mode, encoded);
660 else
662 rsa = new WvRSAKey();
663 rsa->decode(mode, encoded);
668 void WvX509Mgr::decode(const WvX509::DumpMode mode, WvBuf &encoded)
670 WvX509::decode(mode, encoded);