[glib-networking/pgriffis/gtlscertificate-password: 5/5] openssl: Add PKCS #12 support




commit af79107eff6e6e47a3caf6485a721017d8f97073
Author: Patrick Griffis <pgriffis igalia com>
Date:   Thu Sep 2 14:36:46 2021 -0500

    openssl: Add PKCS #12 support

 tls/openssl/gtlscertificate-openssl.c | 105 ++++++++++++++++++++++++++++++++++
 1 file changed, 105 insertions(+)
---
diff --git a/tls/openssl/gtlscertificate-openssl.c b/tls/openssl/gtlscertificate-openssl.c
index 01ed1773..9df06274 100644
--- a/tls/openssl/gtlscertificate-openssl.c
+++ b/tls/openssl/gtlscertificate-openssl.c
@@ -27,6 +27,7 @@
 
 #include <string.h>
 #include "openssl-include.h"
+#include <openssl/pkcs12.h>
 
 #include "gtlscertificate-openssl.h"
 #include <glib/gi18n-lib.h>
@@ -38,9 +39,13 @@ struct _GTlsCertificateOpenssl
   X509 *cert;
   EVP_PKEY *key;
 
+  GBytes *pkcs12_bytes;
+  char *password;
+
   GTlsCertificateOpenssl *issuer;
 
   GError *construct_error;
+  gboolean construct_needs_pkcs12_password;
 
   guint have_cert : 1;
   guint have_key  : 1;
@@ -61,6 +66,8 @@ enum
   PROP_ISSUER_NAME,
   PROP_DNS_NAMES,
   PROP_IP_ADDRESSES,
+  PROP_PKCS12_BYTES,
+  PROP_PASSWORD,
 };
 
 static void     g_tls_certificate_openssl_initable_iface_init (GInitableIface  *iface);
@@ -205,6 +212,78 @@ out:
   return result;
 }
 
+static void
+maybe_import_pkcs12 (GTlsCertificateOpenssl *openssl)
+{
+  PKCS12 *p12 = NULL;
+  X509 *cert = NULL;
+  STACK_OF(X509) *ca = NULL;
+  EVP_PKEY *key = NULL;
+  BIO *bio = NULL;
+  gsize bytes_size;
+  const guint8 *bytes;
+  int status;
+  char error_buffer[256] = { 0 };
+
+  /* If password is set first. */
+  if (!openssl->pkcs12_bytes)
+    return;
+
+  bytes = g_bytes_get_data (openssl->pkcs12_bytes, &bytes_size);
+
+  bio = BIO_new (BIO_s_mem ());
+  status = BIO_write (bio, bytes, bytes_size);
+  if (status <= 0)
+    goto import_failed;
+  g_assert (status == bytes_size);
+
+  p12 = d2i_PKCS12_bio (bio, NULL);
+  if (p12 == NULL)
+    goto import_failed;
+
+  status = PKCS12_parse (p12, openssl->password, &key, &cert, &ca);
+  g_clear_pointer (&bio, BIO_free_all);
+
+  if (status != 1)
+    {
+      if (ERR_GET_REASON (ERR_peek_last_error ()) == PKCS12_R_MAC_VERIFY_FAILURE)
+        openssl->construct_needs_pkcs12_password = TRUE;
+      goto import_failed;
+    }
+
+  /* Clear previous failure now that we have the password. */
+  if (openssl->construct_needs_pkcs12_password)
+    g_clear_error (&openssl->construct_error);
+
+  // TODO: Handle chain
+  if (cert)
+    {
+      openssl->cert = g_steal_pointer (&cert);
+      openssl->have_cert = TRUE;
+    }
+
+  if (key)
+    {
+      openssl->key = g_steal_pointer (&key);
+      openssl->have_key = TRUE;
+    }
+
+  g_clear_pointer (&p12, PKCS12_free);
+  return;
+
+import_failed:
+  g_clear_error (&openssl->construct_error);
+
+  if (!error_buffer[0])
+    ERR_error_string_n (ERR_get_error (), error_buffer, sizeof (error_buffer));
+
+  g_set_error (&openssl->construct_error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
+              _("Failed to import PKCS #12: %s"), error_buffer);
+
+  g_clear_pointer (&p12, PKCS12_free);
+  g_clear_pointer (&bio, BIO_free_all);
+}
+
 static void
 g_tls_certificate_openssl_get_property (GObject    *object,
                                         guint       prop_id,
@@ -228,6 +307,10 @@ g_tls_certificate_openssl_get_property (GObject    *object,
 
   switch (prop_id)
     {
+    case PROP_PKCS12_BYTES:
+      g_value_set_boxed (value, openssl->pkcs12_bytes);
+      break;
+
     case PROP_CERTIFICATE:
       /* NOTE: we do the two calls to avoid openssl allocating the buffer for us */
       size = i2d_X509 (openssl->cert, NULL);
@@ -344,6 +427,26 @@ g_tls_certificate_openssl_set_property (GObject      *object,
 
   switch (prop_id)
     {
+    case PROP_PASSWORD:
+      openssl->password = g_value_dup_string (value);
+      if (openssl->password)
+        {
+          g_return_if_fail (openssl->have_cert == FALSE);
+          g_return_if_fail (openssl->have_key == FALSE);
+          maybe_import_pkcs12 (openssl);
+        }
+      break;
+
+    case PROP_PKCS12_BYTES:
+      openssl->pkcs12_bytes = g_value_dup_boxed (value);
+      if (openssl->pkcs12_bytes)
+        {
+          g_return_if_fail (openssl->have_cert == FALSE);
+          g_return_if_fail (openssl->have_key == FALSE);
+          maybe_import_pkcs12 (openssl);
+        }
+      break;
+
     case PROP_CERTIFICATE:
       bytes = g_value_get_boxed (value);
       if (!bytes)
@@ -556,6 +659,8 @@ g_tls_certificate_openssl_class_init (GTlsCertificateOpensslClass *klass)
   g_object_class_override_property (gobject_class, PROP_ISSUER_NAME, "issuer-name");
   g_object_class_override_property (gobject_class, PROP_DNS_NAMES, "dns-names");
   g_object_class_override_property (gobject_class, PROP_IP_ADDRESSES, "ip-addresses");
+  g_object_class_override_property (gobject_class, PROP_PKCS12_BYTES, "pkcs12-bytes");
+  g_object_class_override_property (gobject_class, PROP_PASSWORD, "password");
 }
 
 static void


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]