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 
14 namespace {
15 class AutoClose {
16 public:
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 
29 private:
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 
70 WvX509Mgr::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 
172 bool 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 
200 bool 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 
217 bool 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 
344 bool 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  if (cert == unsignedcert.cert)
354  {
355  debug("Self Signing!\n");
356  }
357 #ifdef HAVE_OPENSSL_POLICY_MAPPING
358  else if (!X509_check_ca(cert))
359  {
360  debug("This certificate is not a CA, and is thus not allowed to sign "
361  "certificates!\n");
362  return false;
363  }
364 #endif
365  else if (!((cert->ex_flags & EXFLAG_KUSAGE) &&
366  (cert->ex_kusage & KU_KEY_CERT_SIGN)))
367  {
368  debug("This Certificate is not allowed to sign certificates!\n");
369  return false;
370  }
371 
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)
376  {
377  X509_sign(unsignedcert.get_cert(), certkey, EVP_sha1());
378  }
379  else
380  {
381  debug("No keys??\n");
382  EVP_PKEY_free(certkey);
383  return false;
384  }
385 
386  EVP_PKEY_free(certkey);
387  return true;
388 }
389 
390 
391 bool WvX509Mgr::signcrl(WvCRL &crl) const
392 {
393  if (!isok() || !crl.isok())
394  {
395  debug(WvLog::Warning, "Asked to sign CRL, but certificate or CRL (or "
396  "both) not ok! Aborting.\n");
397  return false;
398  }
399 #ifdef HAVE_OPENSSL_POLICY_MAPPING
400  else if (!X509_check_ca(cert))
401  {
402  debug("This certificate is not a CA, and is thus not allowed to sign "
403  "CRLs!\n");
404  return false;
405  }
406  else if (!((cert->ex_flags & EXFLAG_KUSAGE) &&
407  (cert->ex_kusage & KU_CRL_SIGN)))
408  {
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;
413  }
414 #endif
415 
416  EVP_PKEY *certkey = EVP_PKEY_new();
417  bool cakeyok = EVP_PKEY_set1_RSA(certkey, rsa->rsa);
418  if (cakeyok)
419  {
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);
428 
429  // OK - now sign it...
430  X509_CRL_sign(crl.getcrl(), certkey, EVP_sha1());
431  }
432  else
433  {
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;
438  }
439  EVP_PKEY_free(certkey);
440 
441  return true;
442 }
443 
444 
446 {
447  WvDynBuf buf;
448  buf.putstr(data);
449  return sign(buf);
450 }
451 
452 
454 {
455  assert(rsa);
456 
457  EVP_MD_CTX sig_ctx;
458  unsigned char sig_buf[4096];
459 
460  EVP_PKEY *pk = EVP_PKEY_new();
461  assert(pk); // OOM
462 
463  if (!EVP_PKEY_set1_RSA(pk, rsa->rsa))
464  {
465  debug("Error setting RSA keys.\n");
466  EVP_PKEY_free(pk);
467  return WvString::null;
468  }
469 
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)
476  {
477  debug("Error while signing.\n");
478  EVP_PKEY_free(pk);
479  return WvString::null;
480  }
481 
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);
488 }
489 
490 
491 bool WvX509Mgr::write_p12(WvStringParm _fname, WvStringParm _pkcs12pass) const
492 {
493  debug("Dumping RSA Key and X509 Cert to PKCS12 structure.\n");
494 
495  AutoClose fp = fopen(_fname, "wb");
496 
497  if (!fp)
498  {
499  debug(WvLog::Warning, "Unable to open file. Error: %s\n",
500  strerror(errno));
501  return false;
502  }
503 
504  if (!!_pkcs12pass)
505  {
506  if (rsa && cert)
507  {
508  EVP_PKEY *pk = EVP_PKEY_new();
509  assert(pk); // OOM
510 
511  if (!EVP_PKEY_set1_RSA(pk, rsa->rsa))
512  {
513  debug("Error setting RSA keys.\n");
514  EVP_PKEY_free(pk);
515  return false;
516  }
517  else
518  {
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)
525  {
526  debug("Writing the PKCS12 object out...\n");
527  i2d_PKCS12_fp(fp, pkg);
528  PKCS12_free(pkg);
529  EVP_PKEY_free(pk);
530  }
531  else
532  {
533  debug(WvLog::Warning, "Unable to create PKCS12 object.");
534  EVP_PKEY_free(pk);
535  return false;
536  }
537  }
538  }
539  else
540  {
541  debug(WvLog::Warning,
542  "The RSA key or the certificate is not present.");
543  return false;
544  }
545  }
546  else
547  {
548  debug(WvLog::Warning, "No password specified for PKCS12 dump.");
549  return false;
550  }
551 
552  return true;
553 }
554 
555 
557 {
558  debug("Reading Certificate and Private Key from PKCS12 file: %s\n",
559  _fname);
560 
561  if (rsa)
562  WVDELETE(rsa);
563 
564  AutoClose fp = fopen(_fname, "r");
565 
566  if (!fp)
567  {
568  debug("Unable to open file '%s'!\n", _fname);
569  return;
570  }
571 
572  if (!!_pkcs12pass)
573  {
574  PKCS12 *pkg = d2i_PKCS12_fp(fp, NULL);
575  if (pkg)
576  {
577  EVP_PKEY *pk = NULL;
578 
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)
584  {
585  debug("Could not decode pkcs12 file.\n");
586  EVP_PKEY_free(pk);
587  return;
588  }
589 
590  cert = x;
591 
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);
597 
598  // Now that we have both, check to make sure that they match
599  if (!test())
600  {
601  debug("Could not fill in RSA and certificate with matching "
602  "values! Expect problems.\n");
603  return;
604  }
605  }
606  else
607  {
608  debug("Read in of PKCS12 file '%s' failed", _fname);
609  return;
610  }
611  }
612  else
613  {
614  debug("No password specified for PKCS12 file\n");
615  return;
616  }
617 }
618 
619 
621 {
622  if (rsa)
623  return rsa->encode(mode);
624  return "";
625 }
626 
627 
629 {
630  return WvX509::encode(mode);
631 }
632 
633 
634 void WvX509Mgr::encode(const WvRSAKey::DumpMode mode, WvBuf &buf) const
635 {
636  if (rsa)
637  rsa->encode(mode, buf);
638 }
639 
640 
641 void WvX509Mgr::encode(const WvX509::DumpMode mode, WvBuf &buf) const
642 {
643  WvX509::encode(mode, buf);
644 }
645 
646 
647 void WvX509Mgr::decode(const WvRSAKey::DumpMode mode, WvStringParm encoded)
648 {
649  if (rsa)
650  rsa->decode(mode, encoded);
651  else
652  {
653  rsa = new WvRSAKey();
654  rsa->decode(mode, encoded);
655  }
656 }
657 
658 
660 {
661  WvX509::decode(mode, encoded);
662 }
663 
664 
665 void WvX509Mgr::decode(const WvRSAKey::DumpMode mode, WvBuf &encoded)
666 {
667  if (rsa)
668  rsa->decode(mode, encoded);
669  else
670  {
671  rsa = new WvRSAKey();
672  rsa->decode(mode, encoded);
673  }
674 }
675 
676 
677 void WvX509Mgr::decode(const WvX509::DumpMode mode, WvBuf &encoded)
678 {
679  WvX509::decode(mode, encoded);
680 }
A WvFastString acts exactly like a WvString, but can take (const char *) strings without needing to a...
Definition: wvstring.h:93
bool isok() const
Do we have any errors...
Definition: wvcrl.cc:89
void read_p12(WvStringParm _fname, WvStringParm _pkcs12pass)
And this reads from the file specified in filename using the password "_pkcs12pass", and fills the RSA and cert members with the decoded information.
Definition: wvx509mgr.cc:556
X509_CRL * getcrl()
Accessor for CRL.
Definition: wvcrl.h:55
virtual ~WvX509Mgr()
Destructor.
Definition: wvx509mgr.cc:165
WvString sign(WvBuf &data) const
Sign the contents of data and return the signature as a BASE64 string.
Definition: wvx509mgr.cc:453
WvString encode(const DumpMode mode) const
Return the information requested by mode.
Definition: wvx509.cc:441
WvString strflushbuf(WvBuf &inbuf, bool finish=false)
Flushes data through the encoder from a buffer to a string.
Definition: wvencoder.cc:115
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 WvX509::DumpMode mode, WvStringParm encoded)
Load the information from the format requested by mode into the class - this overwrites the certifica...
Definition: wvx509mgr.cc:659
virtual WvString encode(const WvX509::DumpMode mode) const
Encodes the information requested by mode into a buffer.
Definition: wvx509mgr.cc:628
bool signcrl(WvCRL &unsignedcrl) const
Sign the CRL with the rsa key associated with this class.
Definition: wvx509mgr.cc:391
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
WvString get_subject() const
get and set the Subject field of the certificate
Definition: wvx509.cc:624
X509 Class to handle certificates and their related functions.
Definition: wvx509.h:41
virtual WvString errstr() const
Says what the error is, if isok() is not true.
Definition: wvx509mgr.cc:184
virtual WvString encode(const DumpMode mode) const
Return the information requested by mode.
Definition: wvrsa.cc:86
bool signcert(WvX509 &unsignedcert) const
Sign the certificate with the rsa key associated with this class.
Definition: wvx509mgr.cc:344
char * edit()
make the string editable, and return a non-const (char*)
Definition: wvstring.h:397
virtual bool isok() const
Is the certificate object valid?
Definition: wvx509.cc:1284
bool test() const
Test to make sure that a certificate and a keypair go together.
Definition: wvx509mgr.cc:217
A base 64 encoder.
Definition: wvbase64.h:20
WvX509Mgr()
Constructor to create a blank certificate + keypair (useful if, for example, you were going to load t...
Definition: wvx509mgr.cc:35
Specialization of WvBufBase for unsigned char type buffers intended for use with raw memory buffers...
Definition: wvbuf.h:22
size_t used() const
Returns the number of elements in the buffer currently available for reading.
Definition: wvbufbase.h:92
void set_version()
Set the Certificate to use X509v3, since that&#39;s all modern PKI uses anyways :)
Definition: wvx509.cc:722
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:491
DumpMode
Type for the encode() and decode() methods.
Definition: wvx509.h:56
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
X509 * get_cert()
Allow us to access the certificate member - this will be going away eventually, but for now...
Definition: wvx509.h:89
const T * peek(int offset, size_t count)
Returns a const pointer into the buffer at the specified offset to the specified number of elements w...
Definition: wvbufbase.h:225
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
bool operator!() const
The not operator returns true if !isok()
Definition: wvx509mgr.cc:178
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 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 WvString errstr() const
Returns an error string if isok() is not true.
Definition: wvx509.cc:1296
An RSA public key or public/private key pair that can be used for encryption.
Definition: wvrsa.h:26
WvString is an implementation of a simple and efficient printable-string class.
Definition: wvstring.h:329
void set_lifetime(long seconds)
Set the lifetime to be used for this certificate...
Definition: wvx509.cc:744
void putstr(WvStringParm str)
Copies a WvString into the buffer, excluding the null-terminator.
Definition: wvbuffer.cc:11
virtual bool isok() const
Says if this certificate+key pair is good for use.
Definition: wvx509mgr.cc:172
A WvLog stream accepts log messages from applications and forwards them to all registered WvLogRcv&#39;s...
Definition: wvlog.h:56
CRL Class to handle certificate revocation lists and their related functions.
Definition: wvcrl.h:28
static WvString certreq(WvStringParm subject, const WvRSAKey &rsa)
Create a certificate request (PKCS#10) using this function.
Definition: wvx509.cc:266
DumpMode
Type for the encode() and decode() methods.
Definition: wvrsa.h:36