[pubcookie-dev] RFC: pubcookie and SSL certificate chains

Paul Fardy paul.fardy at utoronto.ca
Wed Jul 19 09:52:42 PDT 2006


It seems it's uncommon to use chained certificates, but we at UT get  
chained certificates from the Comodo Group. The Comodo Group used  
chained certificates and now uses a cross-signed root certificate.  
[Cross-signed root: will work most of the time, but it might not be  
trusted by all of your clients, so a chained certificate is also used  
that's signed by another root CA that, hopefully, those wary clients  
will trust.]

I'd found that using SSL_CTX_use_certificate_chain_file() and adding  
an "ssl_cert_chain_file" parameter to the config was the shortest  
path to a patch. But it didn't feel like the right solution for two  
reasons:

1) It completely supersedes the ssl_cert_file and there were (only a  
few) sanity checks for that file. So the file has to exist or the  
sanity checks need to patched and tracking references to  
ssl_cert_file just seems to urge a refactoring that I was reluctant  
to implement.

2) Because it supersedes the ssl_cert_file, it's not consistent with  
the way Apache handles certificates. The OpenSSL docs advocate  
use_certificate_chain_file(), but Apache is the de facto standard:  
people like to have the Pubcookie configs match the Apache ssl.conf.

I've been patching use_certificate_file() into my pubcookie library  
with each release (it's just a few lines), but it would obviously be  
better if the distro had a good solution.

I'm proposing two new parameters:

	ssl_cert_chain_file: <path-name>
	ssl_cert_chain_compatibility: [apache|ssl-lib]

and compatibility should probably default to "apache", though  
ssl_cert_chain_file is, for now, UT-only and "ssl-lib" would be  
backward compatible.

Ultimately, Pubcookie needs to support certificate chains. I think  
Apache-style is also necessary. The "ssl-lib" compatibility could be  
dismissed as my problem.

Here's a patch for the keyclient. I'll be patching keyserver.c, too.  
But I figured I see where the wind's blowing...

Note: the patch also includes a patch to the SSL error callback that  
provides details that have been helpful, particularly when dealing  
with chained certificates.

Thanks for your time,

Paul

-------------- next part --------------
--- keyclient.c	2006/06/13 17:13:39	1.1
+++ keyclient.c	2006/07/19 16:06:36
@@ -169,8 +169,13 @@
     err = X509_STORE_CTX_get_error (ctx);
 
     if (!ok) {
-        fprintf (stderr, "verify error:num=%d:%s\n", err,
+        char subject[1024], issuer[1024];
+        fprintf (stderr, "verify error: SSL err %d: %s\n", err,
                  X509_verify_cert_error_string (err));
+        X509_NAME_oneline(X509_get_subject_name(err_cert), subject, sizeof subject);
+        X509_NAME_oneline(X509_get_issuer_name(err_cert), issuer, sizeof issuer);
+        fprintf (stderr, "verify error: detail: subject=%s\n", subject);
+        fprintf (stderr, "verify error: detail: issuer=%s\n", issuer);
 
         /* we want to ignore any key usage problems but no other faults */
         switch (ctx->error) {
@@ -206,6 +211,8 @@
     char peer_cn[257];
     const char *keyfile = NULL;
     const char *certfile = NULL;
+    const char *certchainfile = NULL;
+    const char *certchaincompat = NULL;
     const char *cafile = NULL;
     const char *cadir = NULL;
     int done = 0;
@@ -349,6 +356,12 @@
     if (!certfile)
         certfile =
             libpbc_config_getstring (p, "ssl_cert_file", "server.pem");
+    if (!certchainfile)
+        certchainfile =
+            libpbc_config_getstring (p, "ssl_cert_chain_file", NULL);
+    if (!certchaincompat)
+        certchaincompat =
+            libpbc_config_getstring (p, "ssl_cert_chain_compatibility", "apache");
     if (!cafile)
         cafile = libpbc_config_getstring (p, "ssl_ca_file", NULL);
     if (!cadir)
@@ -383,7 +396,44 @@
     ctx = SSL_CTX_new (TLSv1_client_method ());
 
     /* setup the correct certificate */
-    if (!SSL_CTX_use_certificate_file (ctx, certfile, filetype)) {
+    if (certchainfile) {
+        if (strcmp(certchaincompat, "ssl-lib") == 0) {
+            if (!SSL_CTX_use_certificate_chain_file (ctx, certchainfile)) {
+                fprintf (stderr, "SSL_CTX_use_certificate_chain_file: %s\n", certchainfile);
+                ERR_print_errors_fp (stderr);
+                exit (1);
+            }
+        } else if (strcmp(certchaincompat, "apache") == 0) {
+            X509 *crt;
+            FILE *fp;
+
+            if (!SSL_CTX_use_certificate_file (ctx, certfile, filetype)) {
+                fprintf (stderr, "SSL_CTX_use_certificate_file:\n");
+                ERR_print_errors_fp (stderr);
+                exit (1);
+            }
+
+            fp = fopen(certchainfile, "r");
+            if (!fp) {
+			 fprintf(stderr, "cannot read ssl_cert_chain_file \"%s\": %s\n", certchainfile, strerror(errno));
+                exit(1);
+            }
+
+            while (crt = PEM_read_X509(fp, NULL, NULL, NULL)) {
+                if (!SSL_CTX_add_extra_chain_cert(ctx, crt)) {
+                    char subject[1024], issuer[1024];
+                    fprintf (stderr, "Certificate Chain File: %s (using Apache compatibility)\n", certchainfile);
+                    fprintf (stderr, "Failed to add certificate to certificate chain:\n", certchainfile);
+                    ERR_print_errors_fp (stderr);
+                    X509_NAME_oneline(X509_get_subject_name(crt), subject, sizeof subject);
+                    X509_NAME_oneline(X509_get_issuer_name(crt), issuer, sizeof issuer);
+                    fprintf (stderr, "  detail: subject=%s\n", subject);
+                    fprintf (stderr, "  detail: issuer=%s\n", subject);
+                    exit (1);
+                }
+           }
+        }
+    } else if (!SSL_CTX_use_certificate_file (ctx, certfile, filetype)) {
         fprintf (stderr, "SSL_CTX_use_certificate_file:\n");
         ERR_print_errors_fp (stderr);
         exit (1);


More information about the pubcookie-dev mailing list