2 * Worldvisions Weaver Software:
3 * Copyright (C) 1997-2005 Net Integration Technologies, Inc.
5 * X.509 certificate management classes.
9 #include "wvsslhacks.h"
11 #include "wvstringlist.h"
13 #include "wvstrutils.h"
14 #include "wvautoconf.h"
16 #include <openssl/pem.h>
17 #include <openssl/x509v3.h>
18 #include <openssl/err.h>
19 #include <openssl/sha.h>
20 #include <openssl/ssl.h>
22 // enable this to add some extra debugging trace messages (this can be VERY
25 # define TRACE(x, y...) debug(x, ## y);
28 # define TRACE(x, y...)
34 // helper method to let us return and warn gracefully when getting/setting an
35 // element in a null certificate
36 static const char * warning_str_set
37 = "Tried to set %s, but certificate not ok.\n";
38 static const char * warning_str_get
39 = "Tried to get %s, but certificate not ok.\n";
40 #define CHECK_CERT_EXISTS_SET(x) \
42 debug(WvLog::Warning, warning_str_set, x); \
45 #define CHECK_CERT_EXISTS_GET(x, y) \
47 debug(WvLog::Warning, warning_str_get, x); \
52 UUID_MAP_BEGIN(WvX509
)
53 UUID_MAP_ENTRY(IObject
)
56 static int ssl_init_count
= 0;
58 #if !HAVE_OPENSSL_POLICY_MAPPING
60 // HACK: old versions of OpenSSL can't handle ERR_free_strings() being called
61 // more than once in the same process; the next wvssl_init won't work. So
62 // let's make sure to make a global variable that holds the refcount at 1
63 // even when all the objects go away, then clean it up at exit.
64 class WvSSL_Stupid_Refcount
67 WvSSL_Stupid_Refcount()
72 ~WvSSL_Stupid_Refcount()
78 WvSSL_Stupid_Refcount wvssl_stupid_refcount
;
80 #endif // !HAVE_OPENSSL_POLICY_MAPPING
88 SSL_load_error_strings();
89 ERR_load_BIO_strings();
90 ERR_load_crypto_strings();
91 OpenSSL_add_all_algorithms();
92 OpenSSL_add_all_ciphers();
93 OpenSSL_add_all_digests();
102 assert(ssl_init_count
>= 1);
103 if (ssl_init_count
>= 1)
114 WvString
wvssl_errstr()
117 ERR_error_string_n(ERR_get_error(), buf
, sizeof(buf
));
118 buf
[sizeof(buf
)-1] = 0;
123 WvX509::WvX509(X509
*_cert
)
124 : debug("X509", WvLog::Debug5
)
132 : debug("X509", WvLog::Debug5
)
139 WvX509::WvX509(const WvX509
&x509
)
140 : debug("X509", WvLog::Debug5
)
144 cert
= X509_dup(x509
.cert
);
152 TRACE("Deleting.\n");
162 // The people who designed this garbage should be shot!
163 // Support old versions of openssl...
164 #ifndef NID_domainComponent
165 #define NID_domainComponent 391
169 #define NID_Domain 392
173 // returns some approximation of the server's fqdn, or an empty string.
174 static WvString
set_name_entry(X509_NAME
*name
, WvStringParm dn
)
176 WvString
fqdn(""), force_fqdn("");
177 X509_NAME_ENTRY
*ne
= NULL
;
183 // dn is of the form: c=ca,o=foo organization,dc=foo,dc=com
184 // (ie. name=value pairs separated by commas)
185 WvStringList::Iter
i(l
);
186 for (i
.rewind(); i
.next(); )
192 value
= strchr(cptr
, '=');
196 value
= (char*)"NULL";
198 sid
= strlwr(trim_string(cptr
));
201 nid
= NID_countryName
;
202 else if (sid
== "st")
203 nid
= NID_stateOrProvinceName
;
205 nid
= NID_localityName
;
207 nid
= NID_organizationName
;
208 else if (sid
== "ou")
209 nid
= NID_organizationalUnitName
;
210 else if (sid
== "cn")
212 nid
= NID_commonName
;
215 else if (sid
== "dc")
217 nid
= NID_domainComponent
;
222 else if (sid
== "domain")
227 else if (sid
== "email")
228 nid
= NID_pkcs9_emailAddress
;
230 nid
= NID_domainComponent
;
232 // Sometimes we just want to parse dn into fqdn.
237 ne
= X509_NAME_ENTRY_create_by_NID(NULL
, nid
,
238 V_ASN1_APP_CHOOSE
, (unsigned char *)value
, -1);
240 X509_NAME_ENTRY_create_by_NID(&ne
, nid
,
241 V_ASN1_APP_CHOOSE
, (unsigned char *)value
, -1);
245 X509_NAME_add_entry(name
, ne
, count
++, 0);
248 X509_NAME_ENTRY_free(ne
);
257 WvRSAKey
*WvX509::get_rsa_pub() const
259 EVP_PKEY
*pkcert
= X509_get_pubkey(cert
);
260 RSA
*certrsa
= EVP_PKEY_get1_RSA(pkcert
);
261 EVP_PKEY_free(pkcert
);
262 return new WvRSAKey(certrsa
, false);
266 WvString
WvX509::certreq(WvStringParm subject
, const WvRSAKey
&rsa
)
268 WvLog
debug("X509::certreq", WvLog::Debug5
);
271 X509_NAME
*name
= NULL
;
272 X509_REQ
*certreq
= NULL
;
274 // double check RSA key
276 debug("RSA Key is fine.\n");
279 debug(WvLog::Warning
, "RSA Key is bad");
280 return WvString::null
;
283 if ((pk
=EVP_PKEY_new()) == NULL
)
285 debug(WvLog::Warning
,
286 "Error creating key handler for new certificate");
287 return WvString::null
;
290 if ((certreq
=X509_REQ_new()) == NULL
)
292 debug(WvLog::Warning
, "Error creating new PKCS#10 object");
294 return WvString::null
;
297 if (!EVP_PKEY_set1_RSA(pk
, rsa
.rsa
))
299 debug(WvLog::Warning
, "Error adding RSA keys to certificate");
300 X509_REQ_free(certreq
);
302 return WvString::null
;
305 X509_REQ_set_version(certreq
, 0); /* version 1 */
307 X509_REQ_set_pubkey(certreq
, pk
);
309 name
= X509_REQ_get_subject_name(certreq
);
311 debug("Creating Certificate request for %s\n", subject
);
312 set_name_entry(name
, subject
);
313 X509_REQ_set_subject_name(certreq
, name
);
314 char *sub_name
= X509_NAME_oneline(X509_REQ_get_subject_name(certreq
),
316 debug("SubjectDN: %s\n", sub_name
);
317 OPENSSL_free(sub_name
);
319 if (!X509_REQ_sign(certreq
, pk
, EVP_sha1()))
321 debug(WvLog::Warning
, "Could not self sign the request");
322 X509_REQ_free(certreq
);
324 return WvString::null
;
327 int verify_result
= X509_REQ_verify(certreq
, pk
);
328 if (verify_result
== 0)
330 debug(WvLog::Warning
, "Self signed request failed");
331 X509_REQ_free(certreq
);
333 return WvString::null
;
337 debug("Self Signed Certificate Request verifies OK!\n");
340 // Horribly involuted hack to get around the fact that the
341 // OpenSSL people are too braindead to have a PEM_write function
342 // that returns a char *
344 BIO
*bufbio
= BIO_new(BIO_s_mem());
347 PEM_write_bio_X509_REQ(bufbio
, certreq
);
348 BIO_get_mem_ptr(bufbio
, &bm
);
349 retval
.put(bm
->data
, bm
->length
);
351 X509_REQ_free(certreq
);
355 return retval
.getstr();
359 bool WvX509::validate(WvX509
*cacert
) const
363 debug(WvLog::Warning
, "Tried to validate certificate against CA, but "
364 "certificate is blank!\n");
370 // Check and make sure that the certificate is still valid
371 if (X509_cmp_current_time(X509_get_notAfter(cert
)) < 0)
373 debug("Certificate has expired.\n");
377 if (X509_cmp_current_time(X509_get_notBefore(cert
)) > 0)
379 debug("Certificate is not yet valid.\n");
385 retval
&= signedbyca(*cacert
);
386 retval
&= issuedbyca(*cacert
);
393 bool WvX509::signedbyca(WvX509
&cacert
) const
395 if (!cert
|| !cacert
.cert
)
397 debug(WvLog::Warning
, "Tried to determine if certificate was signed "
398 "by CA, but either client or CA certificate (or both) are "
403 EVP_PKEY
*pkey
= X509_get_pubkey(cacert
.cert
);
404 int result
= X509_verify(cert
, pkey
);
409 debug("Can't determine if we were signed by CA %s: %s\n",
410 cacert
.get_subject(), wvssl_errstr());
413 bool issigned
= (result
> 0);
415 debug("Certificate was%s signed by CA %s.\n", issigned
? "" : " NOT",
416 cacert
.get_subject());
422 bool WvX509::issuedbyca(WvX509
&cacert
) const
424 if (!cert
|| !cacert
.cert
)
426 debug(WvLog::Warning
, "Tried to determine if certificate was issued "
427 "by CA, but either client or CA certificate (or both) are "
432 int ret
= X509_check_issued(cacert
.cert
, cert
);
433 debug("issuedbyca: %s==X509_V_OK(%s)\n", ret
, X509_V_OK
);
434 if (ret
!= X509_V_OK
)
441 WvString
WvX509::encode(const DumpMode mode
) const
444 encode(mode
, retval
);
445 return retval
.getstr();
449 void WvX509::encode(const DumpMode mode
, WvBuf
&buf
) const
451 if (mode
== CertFileDER
|| mode
== CertFilePEM
)
452 return; // file modes are no ops with encode
456 debug(WvLog::Warning
, "Tried to encode certificate, but certificate "
461 debug("Encoding X509 certificate.\n");
466 unsigned char *keybuf
, *iend
;
469 size
= i2d_X509(cert
, NULL
);
470 iend
= keybuf
= new unsigned char[size
];
471 i2d_X509(cert
, &iend
);
473 enccert
.setsize(size
* 2 +1);
474 ::hexify(enccert
.edit(), keybuf
, size
);
481 BIO
*bufbio
= BIO_new(BIO_s_mem());
485 PEM_write_bio_X509(bufbio
, cert
);
486 else if (mode
== CertDER
)
487 i2d_X509_bio(bufbio
, cert
);
489 debug(WvLog::Warning
, "Tried to encode certificate with unknown "
492 BIO_get_mem_ptr(bufbio
, &bm
);
493 buf
.put(bm
->data
, bm
->length
);
499 void WvX509::decode(const DumpMode mode
, WvStringParm str
)
503 debug("Replacing an already existant X509 certificate.\n");
508 if (mode
== CertFileDER
)
510 BIO
*bio
= BIO_new(BIO_s_file());
512 if (BIO_read_filename(bio
, str
.cstr()) <= 0)
514 debug(WvLog::Warning
, "Open '%s': %s\n", str
, wvssl_errstr());
519 if (!(cert
= d2i_X509_bio(bio
, NULL
)))
520 debug(WvLog::Warning
, "Import DER from '%s': %s\n",
521 str
, wvssl_errstr());
526 else if (mode
== CertFilePEM
)
528 FILE *fp
= fopen(str
, "rb");
532 debug("Open '%s': %s\n", str
, strerror(errnum
));
536 if (!(cert
= PEM_read_X509(fp
, NULL
, NULL
, NULL
)))
537 debug(WvLog::Warning
, "Import PEM from '%s': %s\n",
538 str
, wvssl_errstr());
543 else if (mode
== CertHex
)
545 int hexbytes
= str
.len();
546 int bufsize
= hexbytes
/2;
547 unsigned char *certbuf
= new unsigned char[bufsize
];
548 unsigned char *cp
= certbuf
;
551 ::unhexify(certbuf
, str
);
552 tmpcert
= cert
= X509_new();
553 cert
= wv_d2i_X509(&tmpcert
, &cp
, bufsize
);
558 // we use the buffer decode functions for everything else
565 void WvX509::decode(const DumpMode mode
, WvBuf
&encoded
)
569 debug("Replacing an already existant X509 certificate.\n");
574 if (mode
== CertHex
|| mode
== CertFileDER
|| mode
== CertFilePEM
)
575 decode(mode
, encoded
.getstr());
578 BIO
*membuf
= BIO_new(BIO_s_mem());
579 BIO_write(membuf
, encoded
.get(encoded
.used()), encoded
.used());
582 cert
= PEM_read_bio_X509(membuf
, NULL
, NULL
, NULL
);
583 else if (mode
== CertDER
)
584 cert
= d2i_X509_bio(membuf
, NULL
);
586 debug(WvLog::Warning
, "Tried to decode certificate with unknown "
589 BIO_free_all(membuf
);
594 WvString
WvX509::get_issuer() const
596 CHECK_CERT_EXISTS_GET("issuer", WvString::null
);
598 char *name
= X509_NAME_oneline(X509_get_issuer_name(cert
),0,0);
599 WvString
retval(name
);
605 void WvX509::set_issuer(WvStringParm issuer
)
607 CHECK_CERT_EXISTS_SET("issuer");
609 X509_NAME
*name
= X509_get_issuer_name(cert
);
610 set_name_entry(name
, issuer
);
611 X509_set_issuer_name(cert
, name
);
615 void WvX509::set_issuer(const WvX509
&cacert
)
617 CHECK_CERT_EXISTS_SET("issuer");
619 X509_NAME
*casubj
= X509_get_subject_name(cacert
.cert
);
620 X509_set_issuer_name(cert
, casubj
);
624 WvString
WvX509::get_subject() const
626 CHECK_CERT_EXISTS_GET("subject", WvString::null
);
628 char *name
= X509_NAME_oneline(X509_get_subject_name(cert
), 0, 0);
629 WvString
retval(name
);
635 void WvX509::set_subject(WvStringParm subject
)
637 CHECK_CERT_EXISTS_SET("subject");
639 X509_NAME
*name
= X509_get_subject_name(cert
);
640 set_name_entry(name
, subject
);
641 X509_set_subject_name(cert
, name
);
645 void WvX509::set_subject(X509_NAME
*name
)
647 CHECK_CERT_EXISTS_SET("subject");
649 X509_set_subject_name(cert
, name
);
653 void WvX509::set_pubkey(WvRSAKey
&_rsa
)
655 CHECK_CERT_EXISTS_SET("pubkey");
657 EVP_PKEY
*pk
= EVP_PKEY_new();
660 // Assign RSA Key from WvRSAKey into stupid package that OpenSSL needs
661 if (!EVP_PKEY_set1_RSA(pk
, _rsa
.rsa
))
663 debug("Error adding RSA keys to certificate.\n");
667 X509_set_pubkey(cert
, pk
);
674 void WvX509::set_nsserver(WvStringParm servername
)
676 CHECK_CERT_EXISTS_SET("nsserver");
680 // FQDN cannot have a = in it, therefore it
681 // must be a distinguished name :)
682 if (strchr(servername
, '='))
683 fqdn
= set_name_entry(NULL
, servername
);
688 fqdn
= "null.noname.null";
690 debug("Setting Netscape SSL server name extension to '%s'.\n", fqdn
);
692 // Add in the netscape-specific server extension
693 set_extension(NID_netscape_cert_type
, "server");
694 set_extension(NID_netscape_ssl_server_name
, fqdn
);
698 WvString
WvX509::get_nsserver() const
700 return get_extension(NID_netscape_ssl_server_name
);
704 WvString
WvX509::get_serial(bool hex
) const
706 CHECK_CERT_EXISTS_GET("serial", WvString::null
);
708 BIGNUM
*bn
= BN_new();
709 bn
= ASN1_INTEGER_to_BN(X509_get_serialNumber(cert
), bn
);
715 WvString
ret("%s", c
);
722 void WvX509::set_version()
724 CHECK_CERT_EXISTS_SET("version");
726 X509_set_version(cert
, 0x2);
730 void WvX509::set_serial(long serial
)
732 CHECK_CERT_EXISTS_SET("serial");
734 ASN1_INTEGER_set(X509_get_serialNumber(cert
), serial
);
738 WvString
WvX509::get_crl_dp() const
740 return get_extension(NID_crl_distribution_points
);
744 void WvX509::set_lifetime(long seconds
)
746 CHECK_CERT_EXISTS_SET("lifetime");
748 // Set the NotBefore time to now.
749 X509_gmtime_adj(X509_get_notBefore(cert
), 0);
751 // Now + 10 years... should be shorter, but since we don't currently
752 // have a set of routines to refresh the certificates, make it
754 X509_gmtime_adj(X509_get_notAfter(cert
), seconds
);
758 void WvX509::set_key_usage(WvStringParm values
)
760 set_extension(NID_key_usage
, values
);
764 WvString
WvX509::get_key_usage() const
766 return get_extension(NID_key_usage
);
770 void WvX509::set_ext_key_usage(WvStringParm values
)
772 set_extension(NID_ext_key_usage
, values
);
776 WvString
WvX509::get_ext_key_usage() const
778 return get_extension(NID_ext_key_usage
);
782 WvString
WvX509::get_altsubject() const
784 return get_extension(NID_subject_alt_name
);
788 bool WvX509::get_basic_constraints(bool &ca
, int &pathlen
) const
790 CHECK_CERT_EXISTS_GET("basic constraints", false);
792 BASIC_CONSTRAINTS
*constraints
= NULL
;
795 constraints
= static_cast<BASIC_CONSTRAINTS
*>
796 (X509_get_ext_d2i(cert
, NID_basic_constraints
, &i
, NULL
));
799 ca
= constraints
->ca
;
800 if (constraints
->pathlen
)
802 if ((constraints
->pathlen
->type
== V_ASN1_NEG_INTEGER
) || !ca
)
804 debug("Path length type not valid when getting basic "
806 BASIC_CONSTRAINTS_free(constraints
);
811 pathlen
= ASN1_INTEGER_get(constraints
->pathlen
);
816 BASIC_CONSTRAINTS_free(constraints
);
820 debug("Basic constraints extension not present.\n");
825 void WvX509::set_basic_constraints(bool ca
, int pathlen
)
827 CHECK_CERT_EXISTS_SET("basic constraints");
829 BASIC_CONSTRAINTS
*constraints
= BASIC_CONSTRAINTS_new();
831 constraints
->ca
= static_cast<int>(ca
);
834 ASN1_INTEGER
*i
= ASN1_INTEGER_new();
835 ASN1_INTEGER_set(i
, pathlen
);
836 constraints
->pathlen
= i
;
839 X509_EXTENSION
*ex
= X509V3_EXT_i2d(NID_basic_constraints
, 0,
841 while (int idx
= X509_get_ext_by_NID(cert
, NID_basic_constraints
, 0) >= 0)
843 debug("Found extension at idx %s\n", idx
);
844 X509_EXTENSION
*tmpex
= X509_delete_ext(cert
, idx
);
845 X509_EXTENSION_free(tmpex
);
848 X509_add_ext(cert
, ex
, NID_basic_constraints
);
849 X509_EXTENSION_free(ex
);
850 BASIC_CONSTRAINTS_free(constraints
);
855 * These functions are optional to the API. If OpenSSL doesn't support them,
856 * we simply won't include them here, and apps that need them won't compile.
858 #ifdef HAVE_OPENSSL_POLICY_MAPPING
860 bool WvX509::get_policy_constraints(int &require_explicit_policy
,
861 int &inhibit_policy_mapping
) const
863 CHECK_CERT_EXISTS_GET("policy constraints", false);
865 POLICY_CONSTRAINTS
*constraints
= NULL
;
868 constraints
= static_cast<POLICY_CONSTRAINTS
*>(X509_get_ext_d2i(
869 cert
, NID_policy_constraints
,
873 if (constraints
->requireExplicitPolicy
)
874 require_explicit_policy
= ASN1_INTEGER_get(
875 constraints
->requireExplicitPolicy
);
877 require_explicit_policy
= (-1);
879 if (constraints
->inhibitPolicyMapping
)
880 inhibit_policy_mapping
= ASN1_INTEGER_get(
881 constraints
->inhibitPolicyMapping
);
883 inhibit_policy_mapping
= (-1);
884 POLICY_CONSTRAINTS_free(constraints
);
892 void WvX509::set_policy_constraints(int require_explicit_policy
,
893 int inhibit_policy_mapping
)
895 CHECK_CERT_EXISTS_SET("policy constraints");
897 POLICY_CONSTRAINTS
*constraints
= POLICY_CONSTRAINTS_new();
899 ASN1_INTEGER
*i
= ASN1_INTEGER_new();
900 ASN1_INTEGER_set(i
, require_explicit_policy
);
901 constraints
->requireExplicitPolicy
= i
;
902 i
= ASN1_INTEGER_new();
903 ASN1_INTEGER_set(i
, inhibit_policy_mapping
);
904 constraints
->inhibitPolicyMapping
= i
;
906 X509_EXTENSION
*ex
= X509V3_EXT_i2d(NID_policy_constraints
, 0,
908 X509_add_ext(cert
, ex
, -1);
909 X509_EXTENSION_free(ex
);
910 POLICY_CONSTRAINTS_free(constraints
);
914 bool WvX509::get_policy_mapping(PolicyMapList
&list
) const
916 CHECK_CERT_EXISTS_GET("policy mapping", false);
918 POLICY_MAPPINGS
*mappings
= NULL
;
919 POLICY_MAPPING
*map
= NULL
;
922 mappings
= static_cast<POLICY_MAPPINGS
*>(X509_get_ext_d2i(
923 cert
, NID_policy_mappings
,
928 const int POLICYID_MAXLEN
= 80;
931 for(int j
= 0; j
< sk_POLICY_MAPPING_num(mappings
); j
++)
933 map
= sk_POLICY_MAPPING_value(mappings
, j
);
934 OBJ_obj2txt(tmp1
, POLICYID_MAXLEN
, map
->issuerDomainPolicy
, true);
935 OBJ_obj2txt(tmp2
, POLICYID_MAXLEN
, map
->subjectDomainPolicy
, true);
936 list
.append(new PolicyMap(tmp1
, tmp2
), true);
939 sk_POLICY_MAPPING_pop_free(mappings
, POLICY_MAPPING_free
);
945 void WvX509::set_policy_mapping(PolicyMapList
&list
)
947 CHECK_CERT_EXISTS_SET("policy mapping");
949 POLICY_MAPPINGS
*maps
= sk_POLICY_MAPPING_new_null();
951 PolicyMapList::Iter
i(list
);
952 for (i
.rewind(); i
.next();)
954 POLICY_MAPPING
*map
= POLICY_MAPPING_new();
955 map
->issuerDomainPolicy
= OBJ_txt2obj(i().issuer_domain
.cstr(), 0);
956 map
->subjectDomainPolicy
= OBJ_txt2obj(i().subject_domain
.cstr(), 0);
957 sk_POLICY_MAPPING_push(maps
, map
);
961 X509_EXTENSION
*ex
= X509V3_EXT_i2d(NID_policy_mappings
, 0, maps
);
962 X509_add_ext(cert
, ex
, -1);
963 X509_EXTENSION_free(ex
);
964 sk_POLICY_MAPPING_pop_free(maps
, POLICY_MAPPING_free
);
967 #endif // HAVE_OPENSSL_POLICY_MAPPING
970 static void add_aia(WvStringParm type
, WvString identifier
,
971 AUTHORITY_INFO_ACCESS
*ainfo
)
973 ACCESS_DESCRIPTION
*acc
= ACCESS_DESCRIPTION_new();
974 sk_ACCESS_DESCRIPTION_push(ainfo
, acc
);
975 acc
->method
= OBJ_txt2obj(type
.cstr(), 0);
976 acc
->location
->type
= GEN_URI
;
977 acc
->location
->d
.ia5
= M_ASN1_IA5STRING_new();
978 unsigned char *cident
979 = reinterpret_cast<unsigned char *>(identifier
.edit());
980 ASN1_STRING_set(acc
->location
->d
.ia5
, cident
, identifier
.len());
984 void WvX509::set_aia(WvStringList
&ca_urls
,
985 WvStringList
&responders
)
987 CHECK_CERT_EXISTS_SET("aia");
989 AUTHORITY_INFO_ACCESS
*ainfo
= sk_ACCESS_DESCRIPTION_new_null();
991 WvStringList::Iter
i(ca_urls
);
992 for (i
.rewind(); i
.next();)
993 add_aia("caIssuers", i(), ainfo
);
995 WvStringList::Iter
j(responders
);
996 for (j
.rewind(); j
.next();)
997 add_aia("OCSP", j(), ainfo
);
999 X509_EXTENSION
*ex
= X509V3_EXT_i2d(NID_info_access
, 0, ainfo
);
1000 X509_add_ext(cert
, ex
, -1);
1001 X509_EXTENSION_free(ex
);
1002 sk_ACCESS_DESCRIPTION_pop_free(ainfo
, ACCESS_DESCRIPTION_free
);
1006 WvString
WvX509::get_aia() const
1008 return get_extension(NID_info_access
);
1012 static void parse_stack(WvStringParm ext
, WvStringList
&list
,
1013 WvStringParm prefix
)
1016 stack
.split(ext
, ";\n");
1017 WvStringList::Iter
i(stack
);
1018 for (i
.rewind();i
.next();)
1020 WvString
stack_entry(*i
);
1021 if (strstr(stack_entry
, prefix
))
1023 WvString
uri(stack_entry
.edit() + prefix
.len());
1030 void WvX509::get_ocsp(WvStringList
&responders
) const
1032 parse_stack(get_aia(), responders
, "OCSP - URI:");
1036 void WvX509::get_ca_urls(WvStringList
&urls
) const
1038 parse_stack(get_aia(), urls
, "CA Issuers - URI:");
1042 void WvX509::get_crl_urls(WvStringList
&urls
) const
1044 parse_stack(get_crl_dp(), urls
, "URI:");
1048 void WvX509::set_crl_urls(WvStringList
&urls
)
1050 CHECK_CERT_EXISTS_SET("CRL urls");
1052 STACK_OF(DIST_POINT
) *crldp
= sk_DIST_POINT_new_null();
1053 WvStringList::Iter
i(urls
);
1054 for (i
.rewind(); i
.next();)
1056 DIST_POINT
*point
= DIST_POINT_new();
1057 sk_DIST_POINT_push(crldp
, point
);
1059 GENERAL_NAMES
*uris
= GENERAL_NAMES_new();
1060 GENERAL_NAME
*uri
= GENERAL_NAME_new();
1061 uri
->type
= GEN_URI
;
1062 uri
->d
.ia5
= M_ASN1_IA5STRING_new();
1063 unsigned char *cident
1064 = reinterpret_cast<unsigned char *>(i().edit());
1065 ASN1_STRING_set(uri
->d
.ia5
, cident
, i().len());
1066 sk_GENERAL_NAME_push(uris
, uri
);
1068 point
->distpoint
= DIST_POINT_NAME_new();
1069 point
->distpoint
->name
.fullname
= uris
;
1070 point
->distpoint
->type
= 0;
1073 X509_EXTENSION
*ex
= X509V3_EXT_i2d(NID_crl_distribution_points
, 0, crldp
);
1074 X509_add_ext(cert
, ex
, -1);
1075 X509_EXTENSION_free(ex
);
1076 sk_DIST_POINT_pop_free(crldp
, DIST_POINT_free
);
1080 bool WvX509::get_policies(WvStringList
&policy_oids
) const
1082 CHECK_CERT_EXISTS_GET("policies", false);
1085 CERTIFICATEPOLICIES
* policies
= static_cast<CERTIFICATEPOLICIES
*>(
1086 X509_get_ext_d2i(cert
, NID_certificate_policies
, &critical
, NULL
));
1089 for (int i
= 0; i
< sk_POLICYINFO_num(policies
); i
++)
1091 POLICYINFO
* policy
= sk_POLICYINFO_value(policies
, i
);
1092 const int POLICYID_MAXLEN
= 80;
1094 char policyid
[POLICYID_MAXLEN
];
1095 OBJ_obj2txt(policyid
, POLICYID_MAXLEN
, policy
->policyid
,
1096 true); // don't substitute human-readable names
1097 policy_oids
.append(policyid
);
1100 sk_POLICYINFO_pop_free(policies
, POLICYINFO_free
);
1108 void WvX509::set_policies(WvStringList
&policy_oids
)
1110 CHECK_CERT_EXISTS_SET("policies");
1112 STACK_OF(POLICYINFO
) *sk_pinfo
= sk_POLICYINFO_new_null();
1114 WvStringList::Iter
i(policy_oids
);
1115 for (i
.rewind(); i
.next();)
1117 ASN1_OBJECT
*pobj
= OBJ_txt2obj(i(), 0);
1118 POLICYINFO
*pol
= POLICYINFO_new();
1119 pol
->policyid
= pobj
;
1120 sk_POLICYINFO_push(sk_pinfo
, pol
);
1124 // this code would let you set URL information to a policy
1126 POLICYQUALINFO
*qual
= NULL
;
1130 pol
->qualifiers
= sk_POLICYQUALINFO_new_null();
1131 qual
= POLICYQUALINFO_new();
1132 qual
->pqualid
= OBJ_nid2obj(NID_id_qt_cps
);
1133 qual
->d
.cpsouri
= M_ASN1_IA5STRING_new();
1134 ASN1_STRING_set(qual
->d
.cpsuri
, url
.edit(), url
.len());
1135 sk_POLICYQUALINFO_push(pol
->qualifiers
, qual
);
1139 X509_EXTENSION
*ex
= X509V3_EXT_i2d(NID_certificate_policies
, 0,
1141 X509_add_ext(cert
, ex
, -1);
1142 X509_EXTENSION_free(ex
);
1143 sk_POLICYINFO_pop_free(sk_pinfo
, POLICYINFO_free
);
1147 WvString
WvX509::get_extension(int nid
) const
1149 CHECK_CERT_EXISTS_GET("extension", WvString::null
);
1151 WvString retval
= WvString::null
;
1153 int index
= X509_get_ext_by_NID(cert
, nid
, -1);
1156 X509_EXTENSION
*ext
= X509_get_ext(cert
, index
);
1160 X509V3_EXT_METHOD
*method
= X509V3_EXT_get(ext
);
1164 buf
.put(ext
->value
->data
, ext
->value
->length
);
1165 retval
= buf
.getstr();
1169 void *ext_data
= NULL
;
1170 // we NEED to use a temporary pointer for ext_value_data,
1171 // as openssl's ASN1_item_d2i will muck around with it,
1172 // even though it's const (at least as of version 0.9.8e).
1174 #if OPENSSL_VERSION_NUMBER >= 0x0090800fL
1175 const unsigned char * ext_value_data
= ext
->value
->data
;
1177 unsigned char *ext_value_data
= ext
->value
->data
;
1181 ext_data
= ASN1_item_d2i(NULL
, &ext_value_data
,
1183 ASN1_ITEM_ptr(method
->it
));
1184 TRACE("Applied generic conversion!\n");
1188 ext_data
= method
->d2i(NULL
, &ext_value_data
,
1189 ext
->value
->length
);
1190 TRACE("Applied method specific conversion!\n");
1195 TRACE("String Extension!\n");
1196 char *s
= method
->i2s(method
, ext_data
);
1200 else if (method
->i2v
)
1202 TRACE("Stack Extension!\n");
1203 CONF_VALUE
*val
= NULL
;
1204 STACK_OF(CONF_VALUE
) *svals
= NULL
;
1205 svals
= method
->i2v(method
, ext_data
, NULL
);
1206 if (!sk_CONF_VALUE_num(svals
))
1211 for(int i
= 0; i
< sk_CONF_VALUE_num(svals
); i
++)
1213 val
= sk_CONF_VALUE_value(svals
, i
);
1215 list
.append(WvString(val
->value
));
1216 else if (!val
->value
)
1217 list
.append(WvString(val
->name
));
1220 WvString
pair("%s:%s", val
->name
, val
->value
);
1224 retval
= list
.join(";\n");
1226 sk_CONF_VALUE_pop_free(svals
, X509V3_conf_free
);
1228 else if (method
->i2r
)
1230 TRACE("Raw Extension!\n");
1232 BIO
*bufbio
= BIO_new(BIO_s_mem());
1234 method
->i2r(method
, ext_data
, bufbio
, 0);
1235 BIO_get_mem_ptr(bufbio
, &bm
);
1236 retvalbuf
.put(bm
->data
, bm
->length
);
1238 retval
= retvalbuf
.getstr();
1242 ASN1_item_free((ASN1_VALUE
*)ext_data
,
1243 ASN1_ITEM_ptr(method
->it
));
1245 method
->ext_free(ext_data
);
1252 TRACE("Extension not present!\n");
1256 TRACE("Returning: %s\n", retval
);
1262 void WvX509::set_extension(int nid
, WvStringParm _values
)
1264 CHECK_CERT_EXISTS_SET("extension");
1266 // first we check to see if the extension already exists, if so we need to
1268 int index
= X509_get_ext_by_NID(cert
, nid
, -1);
1271 X509_EXTENSION
*ex
= X509_delete_ext(cert
, index
);
1272 X509_EXTENSION_free(ex
);
1275 // now set the extension
1276 WvString
values(_values
);
1277 X509_EXTENSION
*ex
= NULL
;
1278 ex
= X509V3_EXT_conf_nid(NULL
, NULL
, nid
, values
.edit());
1279 X509_add_ext(cert
, ex
, -1);
1280 X509_EXTENSION_free(ex
);
1284 bool WvX509::isok() const
1290 bool WvX509::operator! () const
1296 WvString
WvX509::errstr() const
1299 return "No certificate.";
1301 return WvString::empty
;
1305 bool WvX509::verify(WvStringParm original
, WvStringParm signature
) const
1308 buf
.putstr(original
);
1309 return verify(buf
, signature
);
1313 bool WvX509::verify(WvBuf
&original
, WvStringParm signature
) const
1315 unsigned char sig_buf
[4096];
1316 size_t sig_size
= sizeof(sig_buf
);
1317 WvBase64Decoder().flushstrmem(signature
, sig_buf
, &sig_size
, true);
1319 EVP_PKEY
*pk
= X509_get_pubkey(cert
);
1323 /* Verify the signature */
1325 EVP_VerifyInit(&sig_ctx
, EVP_sha1());
1326 EVP_VerifyUpdate(&sig_ctx
, original
.peek(0, original
.used()),
1328 int sig_err
= EVP_VerifyFinal(&sig_ctx
, sig_buf
, sig_size
, pk
);
1330 EVP_MD_CTX_cleanup(&sig_ctx
); // Again, not my fault...
1333 debug("Verify failed!\n");
1341 static time_t ASN1_TIME_to_time_t(ASN1_TIME
*t
)
1346 memset(&d
,'\0',sizeof(d
));
1347 memset(&newtime
,'\0',sizeof newtime
);
1349 if (t
->type
== V_ASN1_GENERALIZEDTIME
)
1351 // For time values >= 2050, OpenSSL uses
1352 // ASN1_GENERALIZEDTIME - which we'll worry about
1357 p
= (char *)t
->data
;
1358 sscanf(p
,"%2s%2s%2s%2s%2s%2sZ", d
, &d
[3], &d
[6], &d
[9], &d
[12], &d
[15]);
1360 int year
= strtol(d
, (char **)NULL
, 10);
1366 newtime
.tm_year
= year
;
1367 newtime
.tm_mon
= strtol(&d
[3], (char **)NULL
, 10) - 1;
1368 newtime
.tm_mday
= strtol(&d
[6], (char **)NULL
, 10);
1369 newtime
.tm_hour
= strtol(&d
[9], (char **)NULL
, 10);
1370 newtime
.tm_min
= strtol(&d
[12], (char **)NULL
, 10);
1371 newtime
.tm_sec
= strtol(&d
[15], (char **)NULL
, 10);
1373 return mktime(&newtime
);
1377 time_t WvX509::get_notvalid_before() const
1379 CHECK_CERT_EXISTS_GET("not valid before", 0);
1381 return ASN1_TIME_to_time_t(X509_get_notBefore(cert
));
1385 time_t WvX509::get_notvalid_after() const
1387 CHECK_CERT_EXISTS_GET("not valid after", 0);
1389 return ASN1_TIME_to_time_t(X509_get_notAfter(cert
));
1393 WvString
WvX509::get_ski() const
1395 CHECK_CERT_EXISTS_GET("ski", WvString::null
);
1397 return get_extension(NID_subject_key_identifier
);
1401 WvString
WvX509::get_aki() const
1403 CHECK_CERT_EXISTS_GET("aki", WvString::null
);
1405 WvStringList aki_list
;
1406 parse_stack(get_extension(NID_authority_key_identifier
), aki_list
,
1408 if (aki_list
.count())
1409 return aki_list
.popstr();
1411 return WvString::null
;
1415 WvString
WvX509::get_fingerprint(const FprintMode mode
) const
1417 CHECK_CERT_EXISTS_GET("fingerprint", WvString::null
);
1419 /* Default to SHA-1 because OpenSSL does too */
1420 const EVP_MD
*digest
= EVP_sha1();
1421 if (mode
== FingerMD5
)
1424 unsigned char md
[EVP_MAX_MD_SIZE
];
1426 if (!X509_digest(cert
, digest
, md
, &n
))
1429 debug("get_fingerprint: Out of memory\n");
1430 return WvString::null
;
1437 sprintf(buf
, "%02X", md
[i
]);
1439 } while (++i
< n
&& (store
.putch(':'), 1));
1441 return store
.getstr();
1445 void WvX509::set_ski()
1447 CHECK_CERT_EXISTS_SET("ski");
1449 ASN1_OCTET_STRING
*oct
= M_ASN1_OCTET_STRING_new();
1450 ASN1_BIT_STRING
*pk
= cert
->cert_info
->key
->public_key
;
1451 unsigned char pkey_dig
[EVP_MAX_MD_SIZE
];
1452 unsigned int diglen
;
1454 EVP_Digest(pk
->data
, pk
->length
, pkey_dig
, &diglen
, EVP_sha1(), NULL
);
1456 M_ASN1_OCTET_STRING_set(oct
, pkey_dig
, diglen
);
1457 X509_EXTENSION
*ext
= X509V3_EXT_i2d(NID_subject_key_identifier
, 0,
1459 X509_add_ext(cert
, ext
, -1);
1460 X509_EXTENSION_free(ext
);
1461 M_ASN1_OCTET_STRING_free(oct
);
1465 void WvX509::set_aki(const WvX509
&cacert
)
1467 CHECK_CERT_EXISTS_SET("aki");
1469 // can't set a meaningful AKI for subordinate certification without the
1470 // parent having an SKI
1471 ASN1_OCTET_STRING
*ikeyid
= NULL
;
1472 X509_EXTENSION
*ext
;
1473 int i
= X509_get_ext_by_NID(cacert
.cert
, NID_subject_key_identifier
, -1);
1474 if ((i
>= 0) && (ext
= X509_get_ext(cacert
.cert
, i
)))
1475 ikeyid
= static_cast<ASN1_OCTET_STRING
*>(X509V3_EXT_d2i(ext
));
1480 AUTHORITY_KEYID
*akeyid
= AUTHORITY_KEYID_new();
1481 akeyid
->issuer
= NULL
;
1482 akeyid
->serial
= NULL
;
1483 akeyid
->keyid
= ikeyid
;
1484 ext
= X509V3_EXT_i2d(NID_authority_key_identifier
, 0, akeyid
);
1485 X509_add_ext(cert
, ext
, -1);
1486 X509_EXTENSION_free(ext
);
1487 AUTHORITY_KEYID_free(akeyid
);