WvStreams
wvx509mgr.cc
1#include "wvbase64.h"
2#include "wvsslhacks.h"
3#include "wvx509mgr.h"
4#include "wvautoconf.h"
5
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>
12
13
14namespace {
15class AutoClose {
16public:
17 AutoClose(FILE *fp): fp(fp) { }
18 ~AutoClose()
19 {
20 if (fp)
21 fclose(fp);
22 }
23
24 operator FILE *() const
25 {
26 return fp;
27 }
28
29private:
30 FILE *fp;
31};
32} // anomymous namespace...
33
34
36 : WvX509(),
37 debug("X509 Manager", WvLog::Debug5)
38{
39 rsa = NULL;
40}
41
42
44 : WvX509(x),
45 debug("X509 Manager", WvLog::Debug5)
46{
47 rsa = NULL;
48 set_rsa(x.rsa);
49}
50
51
53 : WvX509(),
54 debug("X509 Manager", WvLog::Debug5)
55{
56 debug("Creating new certificate+key pair for %s.\n", _dname);
57 rsa = _rsa;
58
59 if (!!_dname)
60 {
61 create_selfissued(_dname, ca);
62 debug("Ok - Parameters set... now signing certificate.\n");
63 signcert(*this);
64 }
65 else
66 debug("Sorry, can't create an anonymous certificate.");
67}
68
69
70WvX509Mgr::WvX509Mgr(WvStringParm _dname, int bits, bool ca)
71 : WvX509(),
72 debug("X509 Manager", WvLog::Debug5)
73{
74 debug("Creating new certificate+key pair for %s.\n", _dname);
75 rsa = NULL;
76
77 if (!!_dname)
78 {
79 rsa = new WvRSAKey(bits);
80 create_selfissued(_dname, ca);
81 debug("Ok - Parameters set... now signing certificate.\n");
82 signcert(*this);
83 }
84 else
85 debug("Sorry, can't create an anonymous certificate.");
86}
87
88
90{
91 if (cert)
92 {
93 debug("Replacing already existant certificate...\n");
94 X509_free(cert);
95 cert = NULL;
96 }
97
98 // double check RSA key
99 if (rsa->isok())
100 debug("RSA Key is fine.\n");
101 else
102 return;
103
104 if ((cert = X509_new()) == NULL)
105 return;
106
107 // Completely broken in my mind - this sets the version
108 // string to '3' (I guess version starts at 0)
109 set_version();
110
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);
118
119 // 10 years...
120 set_lifetime(60*60*24*3650);
121
122 set_pubkey(*rsa);
123
124 set_issuer(dname);
125 set_subject(dname);
126 set_ski();
127
128 if (is_ca)
129 {
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");
146 }
147 else
148 {
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");
156 }
157
158 // we do not actually sign the certificate here: that must be done by the
159 // user (WvX509Mgr most likely)
160
161 debug("Certificate for %s created\n", dname);
162}
163
164
166{
167 debug("Deleting.\n");
168 WVDELETE(rsa);
169}
170
171
172bool WvX509Mgr::isok() const
173{
174 return WvX509::isok() && rsa && rsa->isok() && test();
175}
176
177
179{
180 return !isok();
181}
182
183
185{
186 if (!WvX509::isok())
187 return WvX509::errstr();
188
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.";
195
196 return WvString::empty;
197}
198
199
200bool WvX509Mgr::bind_ssl(SSL_CTX *ctx)
201{
202 if (SSL_CTX_use_certificate(ctx, get_cert()) <= 0)
203 {
204 return false;
205 }
206 debug("Certificate activated.\n");
207
208 if (SSL_CTX_use_RSAPrivateKey(ctx, rsa->rsa) <= 0)
209 {
210 return false;
211 }
212 debug("RSA private key activated.\n");
213 return true;
214}
215
216
217bool WvX509Mgr::test() const
218{
219 if (!cert)
220 {
221 debug("No X509 certificate: test fails.\n");
222 return false;
223 }
224
225 if (rsa)
226 {
227 EVP_PKEY *pk = EVP_PKEY_new();
228 assert(pk);
229
230 if (!EVP_PKEY_set1_RSA(pk, rsa->rsa))
231 {
232 debug("Error setting RSA keys: test fails.\n");
233 EVP_PKEY_free(pk);
234 return false;
235 }
236
237 bool bad = false;
238 int verify_return = X509_verify(cert, pk);
239
240 if (verify_return != 1) // only '1' means okay
241 {
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
252 {
253 // I guess that it really did fail.
254 debug("Certificate test failed: %s\n", wvssl_errstr());
255 bad = true;
256 }
257 }
258
259 EVP_PKEY_free(pk);
260 return !bad;
261 }
262
263 return false;
264}
265
266
268{
269 debug("Signing a certificate request with: %s\n", get_subject());
270 if (!isok())
271 {
272 debug(WvLog::Warning, "Asked to sign certificate request, but not ok! "
273 "Aborting.\n");
274 return false;
275 }
276
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);
280
281 BIO *membuf = BIO_new(BIO_s_mem());
282 BIO_write(membuf, pkcs10req, pkcs10req.len());
283
284 X509_REQ *certreq = PEM_read_bio_X509_REQ(membuf, NULL, NULL, NULL);
285 BIO_free_all(membuf);
286
287 if (certreq)
288 {
289 WvX509 newcert(X509_new());
290
291 newcert.set_subject(X509_REQ_get_subject_name(certreq));
292 newcert.set_version();
293
294 // Set the Serial Number for the certificate
295 srand(time(NULL));
296 int serial = rand();
297 newcert.set_serial(serial);
298
299 newcert.set_lifetime(60*60*24*3650);
300
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);
306
307 // every good cert needs an ski+aki
308 newcert.set_ski();
309 newcert.set_aki(*this);
310
311 // The Issuer name is the subject name of the current cert
312 newcert.set_issuer(*this);
313
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");
318
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");
325
326 X509_add_ext(newcert.get_cert(), ex, -1);
327 X509_EXTENSION_free(ex);
328
329 newcert.set_ext_key_usage("critical, TLS Web Client Authentication");
330
331 signcert(newcert);
332
333 X509_REQ_free(certreq);
334 return WvString(newcert.encode(WvX509::CertPEM));
335 }
336 else
337 {
338 debug("Can't decode Certificate Request\n");
339 return WvString::null;
340 }
341}
342
343
344bool WvX509Mgr::signcert(WvX509 &unsignedcert) const
345{
346 if (!isok())
347 {
348 debug(WvLog::Warning, "Asked to sign certificate, but not ok! "
349 "Aborting.\n");
350 return false;
351 }
352
353 uint32_t ex_flags = X509_get_extension_flags(cert);
354 uint32_t ex_kusage = X509_get_key_usage(cert);
355 if (cert == unsignedcert.cert)
356 {
357 debug("Self Signing!\n");
358 }
359#ifdef HAVE_OPENSSL_POLICY_MAPPING
360 else if (!X509_check_ca(cert))
361 {
362 debug("This certificate is not a CA, and is thus not allowed to sign "
363 "certificates!\n");
364 return false;
365 }
366#endif
367 else if (!((ex_flags & EXFLAG_KUSAGE) &&
368 (ex_kusage & KU_KEY_CERT_SIGN)))
369 {
370 debug("This Certificate is not allowed to sign certificates!\n");
371 return false;
372 }
373
374 debug("Ok, now sign the new cert with the current RSA key.\n");
375 EVP_PKEY *certkey = EVP_PKEY_new();
376 bool cakeyok = EVP_PKEY_set1_RSA(certkey, rsa->rsa);
377 if (cakeyok)
378 {
379 X509_sign(unsignedcert.get_cert(), certkey, EVP_sha1());
380 }
381 else
382 {
383 debug("No keys??\n");
384 EVP_PKEY_free(certkey);
385 return false;
386 }
387
388 EVP_PKEY_free(certkey);
389 return true;
390}
391
392
393bool WvX509Mgr::signcrl(WvCRL &crl) const
394{
395 uint32_t ex_flags = X509_get_extension_flags(cert);
396 uint32_t ex_kusage = X509_get_key_usage(cert);
397 if (!isok() || !crl.isok())
398 {
399 debug(WvLog::Warning, "Asked to sign CRL, but certificate or CRL (or "
400 "both) not ok! Aborting.\n");
401 return false;
402 }
403#ifdef HAVE_OPENSSL_POLICY_MAPPING
404 else if (!X509_check_ca(cert))
405 {
406 debug("This certificate is not a CA, and is thus not allowed to sign "
407 "CRLs!\n");
408 return false;
409 }
410 else if (!((ex_flags & EXFLAG_KUSAGE) &&
411 (ex_kusage & KU_CRL_SIGN)))
412 {
413 debug("Certificate not allowed to sign CRLs! (%s %s)\n",
414 (ex_flags & EXFLAG_KUSAGE),
415 (ex_kusage & KU_CRL_SIGN));
416 return false;
417 }
418#endif
419
420 EVP_PKEY *certkey = EVP_PKEY_new();
421 bool cakeyok = EVP_PKEY_set1_RSA(certkey, rsa->rsa);
422 if (cakeyok)
423 {
424 ASN1_TIME *tmptm = ASN1_TIME_new();
425 // Set the LastUpdate time to now.
426 X509_gmtime_adj(tmptm, 0);
427 X509_CRL_set_lastUpdate(crl.getcrl(), tmptm);
428 // CRL's are valid for 30 days
429 X509_gmtime_adj(tmptm, (long)60*60*24*30);
430 X509_CRL_set_nextUpdate(crl.getcrl(), tmptm);
431 ASN1_TIME_free(tmptm);
432
433 // OK - now sign it...
434 X509_CRL_sign(crl.getcrl(), certkey, EVP_sha1());
435 }
436 else
437 {
438 debug(WvLog::Warning, "Asked to sign CRL, but no RSA key associated "
439 "with certificate. Aborting.\n");
440 EVP_PKEY_free(certkey);
441 return false;
442 }
443 EVP_PKEY_free(certkey);
444
445 return true;
446}
447
448
450{
451 WvDynBuf buf;
452 buf.putstr(data);
453 return sign(buf);
454}
455
456
457WvString WvX509Mgr::sign(WvBuf &data) const
458{
459 assert(rsa);
460
461 unsigned char sig_buf[4096];
462
463 EVP_PKEY *pk = EVP_PKEY_new();
464 assert(pk); // OOM
465
466 if (!EVP_PKEY_set1_RSA(pk, rsa->rsa))
467 {
468 debug("Error setting RSA keys.\n");
469 EVP_PKEY_free(pk);
470 return WvString::null;
471 }
472
473 EVP_MD_CTX *sig_ctx = EVP_MD_CTX_new();
474 EVP_SignInit(sig_ctx, EVP_sha1());
475 EVP_SignUpdate(sig_ctx, data.peek(0, data.used()), data.used());
476 unsigned int sig_len = sizeof(sig_buf);
477 int sig_err = EVP_SignFinal(sig_ctx, sig_buf,
478 &sig_len, pk);
479 if (sig_err != 1)
480 {
481 debug("Error while signing.\n");
482 EVP_PKEY_free(pk);
483 EVP_MD_CTX_free(sig_ctx);
484 return WvString::null;
485 }
486
487 EVP_PKEY_free(pk);
488 EVP_MD_CTX_free(sig_ctx); // this isn't my fault ://
489 WvDynBuf buf;
490 buf.put(sig_buf, sig_len);
491 debug("Signature size: %s\n", buf.used());
492 return WvBase64Encoder().strflushbuf(buf, true);
493}
494
495
496bool WvX509Mgr::write_p12(WvStringParm _fname, WvStringParm _pkcs12pass) const
497{
498 debug("Dumping RSA Key and X509 Cert to PKCS12 structure.\n");
499
500 AutoClose fp = fopen(_fname, "wb");
501
502 if (!fp)
503 {
504 debug(WvLog::Warning, "Unable to open file. Error: %s\n",
505 strerror(errno));
506 return false;
507 }
508
509 if (!!_pkcs12pass)
510 {
511 if (rsa && cert)
512 {
513 EVP_PKEY *pk = EVP_PKEY_new();
514 assert(pk); // OOM
515
516 if (!EVP_PKEY_set1_RSA(pk, rsa->rsa))
517 {
518 debug("Error setting RSA keys.\n");
519 EVP_PKEY_free(pk);
520 return false;
521 }
522 else
523 {
524 WvString pkcs12pass(_pkcs12pass);
525 PKCS12 *pkg
526 = PKCS12_create(pkcs12pass.edit(), (char*)"foo", pk,
527 cert, NULL, 0, 0, 0,
528 0, 0);
529 if (pkg)
530 {
531 debug("Writing the PKCS12 object out...\n");
532 i2d_PKCS12_fp(fp, pkg);
533 PKCS12_free(pkg);
534 EVP_PKEY_free(pk);
535 }
536 else
537 {
538 debug(WvLog::Warning, "Unable to create PKCS12 object.");
539 EVP_PKEY_free(pk);
540 return false;
541 }
542 }
543 }
544 else
545 {
546 debug(WvLog::Warning,
547 "The RSA key or the certificate is not present.");
548 return false;
549 }
550 }
551 else
552 {
553 debug(WvLog::Warning, "No password specified for PKCS12 dump.");
554 return false;
555 }
556
557 return true;
558}
559
560
562{
563 debug("Reading Certificate and Private Key from PKCS12 file: %s\n",
564 _fname);
565
566 if (rsa)
567 WVDELETE(rsa);
568
569 AutoClose fp = fopen(_fname, "r");
570
571 if (!fp)
572 {
573 debug("Unable to open file '%s'!\n", _fname);
574 return;
575 }
576
577 if (!!_pkcs12pass)
578 {
579 PKCS12 *pkg = d2i_PKCS12_fp(fp, NULL);
580 if (pkg)
581 {
582 EVP_PKEY *pk = NULL;
583
584 // Parse out the bits out the PKCS12 package.
585 X509 *x;
586 PKCS12_parse(pkg, _pkcs12pass, &pk, &x, NULL);
587 PKCS12_free(pkg);
588 if (!pk || !x)
589 {
590 debug("Could not decode pkcs12 file.\n");
591 EVP_PKEY_free(pk);
592 return;
593 }
594
595 cert = x;
596
597 // Now, cert should be OK, let's try and set up the RSA stuff
598 // since we've essentially got a PKEY, and not a WvRSAKey
599 // We need to create a new WvRSAKey from the PKEY...
600 rsa = new WvRSAKey(EVP_PKEY_get1_RSA(pk), true);
601 EVP_PKEY_free(pk);
602
603 // Now that we have both, check to make sure that they match
604 if (!test())
605 {
606 debug("Could not fill in RSA and certificate with matching "
607 "values! Expect problems.\n");
608 return;
609 }
610 }
611 else
612 {
613 debug("Read in of PKCS12 file '%s' failed", _fname);
614 return;
615 }
616 }
617 else
618 {
619 debug("No password specified for PKCS12 file\n");
620 return;
621 }
622}
623
624
626{
627 if (rsa)
628 return rsa->encode(mode);
629 return "";
630}
631
632
634{
635 return WvX509::encode(mode);
636}
637
638
639void WvX509Mgr::encode(const WvRSAKey::DumpMode mode, WvBuf &buf) const
640{
641 if (rsa)
642 rsa->encode(mode, buf);
643}
644
645
646void WvX509Mgr::encode(const WvX509::DumpMode mode, WvBuf &buf) const
647{
648 WvX509::encode(mode, buf);
649}
650
651
652void WvX509Mgr::decode(const WvRSAKey::DumpMode mode, WvStringParm encoded)
653{
654 if (rsa)
655 rsa->decode(mode, encoded);
656 else
657 {
658 rsa = new WvRSAKey();
659 rsa->decode(mode, encoded);
660 }
661}
662
663
665{
666 WvX509::decode(mode, encoded);
667}
668
669
670void WvX509Mgr::decode(const WvRSAKey::DumpMode mode, WvBuf &encoded)
671{
672 if (rsa)
673 rsa->decode(mode, encoded);
674 else
675 {
676 rsa = new WvRSAKey();
677 rsa->decode(mode, encoded);
678 }
679}
680
681
682void WvX509Mgr::decode(const WvX509::DumpMode mode, WvBuf &encoded)
683{
684 WvX509::decode(mode, encoded);
685}
void put(const T *data, size_t count)
Writes the specified number of elements from the specified storage location into the buffer at its ta...
size_t used() const
Returns the number of elements in the buffer currently available for reading.
CRL Class to handle certificate revocation lists and their related functions.
bool isok() const
Do we have any errors... convenience function.
Definition wvcrl.cc:89
X509_CRL * getcrl()
Accessor for CRL.
WvString strflushbuf(WvBuf &inbuf, bool finish=false)
Flushes data through the encoder from a buffer to a string.
Definition wvencoder.cc:115
A WvFastString acts exactly like a WvString, but can take (const char *) strings without needing to a...
A WvLog stream accepts log messages from applications and forwards them to all registered WvLogRcv's.
An RSA public key or public/private key pair that can be used for encryption.
virtual WvString encode(const DumpMode mode) const
Return the information requested by mode.
Definition wvrsa.cc:86
DumpMode
Type for the encode() and decode() methods.
virtual void decode(const DumpMode mode, WvStringParm encoded)
Load the information from the format requested by mode into the class - this overwrites the certifica...
Definition wvrsa.cc:147
WvString is an implementation of a simple and efficient printable-string class.
char * edit()
make the string editable, and return a non-const (char*)
virtual void decode(const WvX509::DumpMode mode, WvStringParm encoded)
Load the information from the format requested by mode into the class - this overwrites the certifica...
Definition wvx509mgr.cc:664
virtual WvString errstr() const
Says what the error is, if isok() is not true.
Definition wvx509mgr.cc:184
bool bind_ssl(SSL_CTX *ctx)
Avoid a lot of ugliness by having it so that we are binding to the SSL context, and not the other way...
Definition wvx509mgr.cc:200
void read_p12(WvStringParm _fname, WvStringParm _pkcs12pass)
And this reads from the file specified in filename using the password "_pkcs12pass",...
Definition wvx509mgr.cc:561
bool signcrl(WvCRL &unsignedcrl) const
Sign the CRL with the rsa key associated with this class.
Definition wvx509mgr.cc:393
bool write_p12(WvStringParm _fname, WvStringParm _pkcs12pass) const
This writes the certificate and RSA keys in PKCS12 format to the file specified by filename,...
Definition wvx509mgr.cc:496
WvX509Mgr()
Constructor to create a blank certificate + keypair (useful if, for example, you were going to load t...
Definition wvx509mgr.cc:35
WvString sign(WvBuf &data) const
Sign the contents of data and return the signature as a BASE64 string.
Definition wvx509mgr.cc:457
virtual WvString encode(const WvX509::DumpMode mode) const
Encodes the information requested by mode into a buffer.
Definition wvx509mgr.cc:633
bool test() const
Test to make sure that a certificate and a keypair go together.
Definition wvx509mgr.cc:217
bool signcert(WvX509 &unsignedcert) const
Sign the certificate with the rsa key associated with this class.
Definition wvx509mgr.cc:344
WvString signreq(WvStringParm pkcs10req) const
Take the PKCS#10 request in the string pkcs10req, sign it with the private key in rsa,...
Definition wvx509mgr.cc:267
bool operator!() const
The not operator returns true if !isok()
Definition wvx509mgr.cc:178
virtual ~WvX509Mgr()
Destructor.
Definition wvx509mgr.cc:165
void create_selfissued(WvStringParm dname, bool is_ca=false)
Given the Distinguished Name dname and an already generated keypair in rsa, return a Self Signed Cert...
Definition wvx509mgr.cc:89
virtual bool isok() const
Says if this certificate+key pair is good for use.
Definition wvx509mgr.cc:172
X509 Class to handle certificates and their related functions.
virtual WvString errstr() const
Returns an error string if isok() is not true.
Definition wvx509.cc:1297
void set_version()
Set the Certificate to use X509v3, since that's all modern PKI uses anyways :)
Definition wvx509.cc:722
WvString get_subject() const
get and set the Subject field of the certificate
Definition wvx509.cc:624
X509 * get_cert()
Allow us to access the certificate member - this will be going away eventually, but for now,...
DumpMode
Type for the encode() and decode() methods.
void set_lifetime(long seconds)
Set the lifetime to be used for this certificate... the lifetime starts from the minute that the cert...
Definition wvx509.cc:744
void set_pubkey(WvRSAKey &rsa_pubkey)
Set the public key of the certificate to the public key rsa_pubkey.
Definition wvx509.cc:653
virtual void decode(const DumpMode mode, WvStringParm str)
Load the information from the format requested by mode into the class - this overwrites the certifica...
Definition wvx509.cc:499
static WvString certreq(WvStringParm subject, const WvRSAKey &rsa)
Create a certificate request (PKCS#10) using this function.
Definition wvx509.cc:266
WvString encode(const DumpMode mode) const
Return the information requested by mode.
Definition wvx509.cc:441
virtual bool isok() const
Is the certificate object valid?
Definition wvx509.cc:1285