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}
A base 64 encoder.
Definition wvbase64.h:21
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...
Definition wvbufbase.h:483
size_t used() const
Returns the number of elements in the buffer currently available for reading.
Definition wvbufbase.h:92
CRL Class to handle certificate revocation lists and their related functions.
Definition wvcrl.h:29
bool isok() const
Do we have any errors... convenience function.
Definition wvcrl.cc:89
X509_CRL * getcrl()
Accessor for CRL.
Definition wvcrl.h:55
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...
Definition wvstring.h:94
A WvLog stream accepts log messages from applications and forwards them to all registered WvLogRcv's.
Definition wvlog.h:57
An RSA public key or public/private key pair that can be used for encryption.
Definition wvrsa.h:27
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.
Definition wvrsa.h:36
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.
Definition wvstring.h:330
char * edit()
make the string editable, and return a non-const (char*)
Definition wvstring.h:397
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.
Definition wvx509.h:42
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,...
Definition wvx509.h:89
DumpMode
Type for the encode() and decode() methods.
Definition wvx509.h:56
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