[glib-networking] tls: expose cert details on GTlsCertificate



commit 588d8fb28c176cabaf587a5bb141caed1c31bc65
Author: Ross A. Wollman <ross wollman gmail com>
Date:   Wed May 26 08:14:06 2021 +0000

    tls: expose cert details on GTlsCertificate
    
    This changeset exposes
    
    * `not-valid-before`
    * `not-valid-after`
    * `subject-name`
    * `issuer-name`
    
    For more information, see related glib MR:
    https://gitlab.gnome.org/GNOME/glib/-/merge_requests/2113

 tls/gnutls/gtlscertificate-gnutls.c   | 49 ++++++++++++++++++++
 tls/openssl/gtlscertificate-openssl.c | 53 ++++++++++++++++++++-
 tls/tests/certificate.c               | 87 +++++++++++++++++++++++++++++++++++
 tls/tests/files/create-files.sh       | 11 +++++
 4 files changed, 199 insertions(+), 1 deletion(-)
---
diff --git a/tls/gnutls/gtlscertificate-gnutls.c b/tls/gnutls/gtlscertificate-gnutls.c
index 62d586a..4c27587 100644
--- a/tls/gnutls/gtlscertificate-gnutls.c
+++ b/tls/gnutls/gtlscertificate-gnutls.c
@@ -42,6 +42,10 @@ enum
   PROP_ISSUER,
   PROP_PKCS11_URI,
   PROP_PRIVATE_KEY_PKCS11_URI,
+  PROP_NOT_VALID_BEFORE,
+  PROP_NOT_VALID_AFTER,
+  PROP_SUBJECT_NAME,
+  PROP_ISSUER_NAME,
 };
 
 struct _GTlsCertificateGnutls
@@ -97,6 +101,9 @@ g_tls_certificate_gnutls_get_property (GObject    *object,
   char *certificate_pem;
   int status;
   size_t size;
+  gnutls_x509_dn_t dn;
+  gnutls_datum_t data;
+  time_t time;
 
   switch (prop_id)
     {
@@ -157,6 +164,44 @@ g_tls_certificate_gnutls_get_property (GObject    *object,
       g_value_set_string (value, gnutls->private_key_pkcs11_uri);
       break;
 
+    case PROP_NOT_VALID_BEFORE:
+      time = gnutls_x509_crt_get_activation_time (gnutls->cert);
+      if (time != (time_t)-1)
+        g_value_take_boxed (value, g_date_time_new_from_unix_utc (time));
+      break;
+
+    case PROP_NOT_VALID_AFTER:
+      time = gnutls_x509_crt_get_expiration_time (gnutls->cert);
+      if (time != (time_t)-1)
+        g_value_take_boxed (value, g_date_time_new_from_unix_utc (time));
+      break;
+
+    case PROP_SUBJECT_NAME:
+      status = gnutls_x509_crt_get_subject (gnutls->cert, &dn);
+      if (status != GNUTLS_E_SUCCESS)
+        return;
+
+      status = gnutls_x509_dn_get_str (dn, &data);
+      if (status != GNUTLS_E_SUCCESS)
+        return;
+
+      g_value_take_string (value, g_strndup ((gchar *)data.data, data.size));
+      gnutls_free (data.data);
+      break;
+
+    case PROP_ISSUER_NAME:
+      status = gnutls_x509_crt_get_issuer (gnutls->cert, &dn);
+      if (status != GNUTLS_E_SUCCESS)
+        return;
+
+      status = gnutls_x509_dn_get_str (dn, &data);
+      if (status != GNUTLS_E_SUCCESS)
+        return;
+
+      g_value_take_string (value, g_strndup ((gchar *)data.data, data.size));
+      gnutls_free (data.data);
+      break;
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
     }
@@ -411,6 +456,10 @@ g_tls_certificate_gnutls_class_init (GTlsCertificateGnutlsClass *klass)
   g_object_class_override_property (gobject_class, PROP_ISSUER, "issuer");
   g_object_class_override_property (gobject_class, PROP_PKCS11_URI, "pkcs11-uri");
   g_object_class_override_property (gobject_class, PROP_PRIVATE_KEY_PKCS11_URI, "private-key-pkcs11-uri");
+  g_object_class_override_property (gobject_class, PROP_NOT_VALID_BEFORE, "not-valid-before");
+  g_object_class_override_property (gobject_class, PROP_NOT_VALID_AFTER, "not-valid-after");
+  g_object_class_override_property (gobject_class, PROP_SUBJECT_NAME, "subject-name");
+  g_object_class_override_property (gobject_class, PROP_ISSUER_NAME, "issuer-name");
 }
 
 static void
diff --git a/tls/openssl/gtlscertificate-openssl.c b/tls/openssl/gtlscertificate-openssl.c
index 2fa9695..dd9664c 100644
--- a/tls/openssl/gtlscertificate-openssl.c
+++ b/tls/openssl/gtlscertificate-openssl.c
@@ -55,7 +55,11 @@ enum
   PROP_CERTIFICATE_PEM,
   PROP_PRIVATE_KEY,
   PROP_PRIVATE_KEY_PEM,
-  PROP_ISSUER
+  PROP_ISSUER,
+  PROP_NOT_VALID_BEFORE,
+  PROP_NOT_VALID_AFTER,
+  PROP_SUBJECT_NAME,
+  PROP_ISSUER_NAME,
 };
 
 static void     g_tls_certificate_openssl_initable_iface_init (GInitableIface  *iface);
@@ -94,6 +98,13 @@ g_tls_certificate_openssl_get_property (GObject    *object,
   char *certificate_pem;
   int size;
 
+  const ASN1_TIME *time_asn1;
+  struct tm time_tm;
+  GDateTime *time;
+  GTimeZone *tz;
+  X509_NAME *name;
+  const char *name_string;
+
   switch (prop_id)
     {
     case PROP_CERTIFICATE:
@@ -134,6 +145,42 @@ g_tls_certificate_openssl_get_property (GObject    *object,
       g_value_set_object (value, openssl->issuer);
       break;
 
+    case PROP_NOT_VALID_BEFORE:
+      time_asn1 = X509_get0_notBefore (openssl->cert);
+      ASN1_TIME_to_tm (time_asn1, &time_tm);
+      tz = g_time_zone_new_utc ();
+      time = g_date_time_new (tz, time_tm.tm_year + 1900, time_tm.tm_mon + 1, time_tm.tm_mday, 
time_tm.tm_hour, time_tm.tm_min, time_tm.tm_sec);
+      g_value_take_boxed (value, time);
+      g_time_zone_unref (tz);
+      break;
+
+    case PROP_NOT_VALID_AFTER:
+      time_asn1 = X509_get0_notAfter (openssl->cert);
+      ASN1_TIME_to_tm (time_asn1, &time_tm);
+      tz = g_time_zone_new_utc ();
+      time = g_date_time_new (tz, time_tm.tm_year + 1900, time_tm.tm_mon + 1, time_tm.tm_mday, 
time_tm.tm_hour, time_tm.tm_min, time_tm.tm_sec);
+      g_value_take_boxed (value, time);
+      g_time_zone_unref (tz);
+      break;
+
+    case PROP_SUBJECT_NAME:
+      bio = BIO_new (BIO_s_mem ());
+      name = X509_get_subject_name (openssl->cert);
+      X509_NAME_print_ex (bio, name, 0, XN_FLAG_SEP_COMMA_PLUS);
+      BIO_get_mem_data (bio, (char **)&name_string);
+      g_value_set_string (value, name_string);
+      BIO_free_all (bio);
+      break;
+
+    case PROP_ISSUER_NAME:
+      bio = BIO_new (BIO_s_mem ());
+      name = X509_get_issuer_name (openssl->cert);
+      X509_NAME_print_ex (bio, name, 0, XN_FLAG_SEP_COMMA_PLUS);
+      BIO_get_mem_data (bio, &name_string);
+      g_value_set_string (value, name_string);
+      BIO_free_all (bio);
+      break;
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
     }
@@ -359,6 +406,10 @@ g_tls_certificate_openssl_class_init (GTlsCertificateOpensslClass *klass)
   g_object_class_override_property (gobject_class, PROP_PRIVATE_KEY, "private-key");
   g_object_class_override_property (gobject_class, PROP_PRIVATE_KEY_PEM, "private-key-pem");
   g_object_class_override_property (gobject_class, PROP_ISSUER, "issuer");
+  g_object_class_override_property (gobject_class, PROP_NOT_VALID_BEFORE, "not-valid-before");
+  g_object_class_override_property (gobject_class, PROP_NOT_VALID_AFTER, "not-valid-after");
+  g_object_class_override_property (gobject_class, PROP_SUBJECT_NAME, "subject-name");
+  g_object_class_override_property (gobject_class, PROP_ISSUER_NAME, "issuer-name");
 }
 
 static void
diff --git a/tls/tests/certificate.c b/tls/tests/certificate.c
index 921f29e..50b7dc3 100644
--- a/tls/tests/certificate.c
+++ b/tls/tests/certificate.c
@@ -576,6 +576,88 @@ test_certificate_is_same (void)
   g_object_unref (three);
 }
 
+static void
+test_certificate_not_valid_before (void)
+{
+  const gchar *EXPECTED_NOT_VALID_BEFORE = "2020-10-12T17:49:44Z";
+
+  GTlsCertificate *cert;
+  GError *error = NULL;
+  GDateTime *actual;
+  gchar *actual_str;
+
+  cert = g_tls_certificate_new_from_file (tls_test_file_path ("server.pem"), &error);
+  g_assert_no_error (error);
+
+  actual = g_tls_certificate_get_not_valid_before (cert);
+  g_assert_nonnull (actual);
+  actual_str = g_date_time_format_iso8601 (actual);
+  g_assert_cmpstr (actual_str, ==, EXPECTED_NOT_VALID_BEFORE);
+  g_free (actual_str);
+  g_date_time_unref (actual);
+  g_object_unref (cert);
+}
+
+static void
+test_certificate_not_valid_after (void)
+{
+  const gchar *EXPECTED_NOT_VALID_AFTER = "2045-10-06T17:49:44Z";
+
+  GTlsCertificate *cert;
+  GError *error = NULL;
+  GDateTime *actual;
+  gchar *actual_str;
+
+  cert = g_tls_certificate_new_from_file (tls_test_file_path ("server.pem"), &error);
+  g_assert_no_error (error);
+
+  actual = g_tls_certificate_get_not_valid_after (cert);
+  g_assert_nonnull (actual);
+  actual_str = g_date_time_format_iso8601 (actual);
+  g_assert_cmpstr (actual_str, ==, EXPECTED_NOT_VALID_AFTER);
+  g_free (actual_str);
+  g_date_time_unref (actual);
+  g_object_unref (cert);
+}
+
+static void
+test_certificate_subject_name (void)
+{
+  const gchar *EXPECTED_SUBJECT_NAME = "DC=COM,DC=EXAMPLE,CN=server.example.com";
+
+  GTlsCertificate *cert;
+  GError *error = NULL;
+  gchar *actual;
+
+  cert = g_tls_certificate_new_from_file (tls_test_file_path ("server.pem"), &error);
+  g_assert_no_error (error);
+
+  actual = g_tls_certificate_get_subject_name (cert);
+  g_assert_nonnull (actual);
+  g_assert_cmpstr (actual, ==, EXPECTED_SUBJECT_NAME);
+  g_free (actual);
+  g_object_unref (cert);
+}
+
+static void
+test_certificate_issuer_name (void)
+{
+  GTlsCertificate *cert;
+  GError *error = NULL;
+  gchar *actual;
+
+  cert = g_tls_certificate_new_from_file (tls_test_file_path ("server.pem"), &error);
+  g_assert_no_error (error);
+
+  actual = g_tls_certificate_get_issuer_name (cert);
+  g_assert_nonnull (actual);
+  // For GnuTLS the full string includes ",EMAIL=ca example com" at the end while
+  // OpenSSL includes ",emailAddress=ca example com" at the end
+  g_assert (strstr (actual, "DC=COM,DC=EXAMPLE,OU=Certificate Authority,CN=ca.example.com"));
+  g_free (actual);
+  g_object_unref (cert);
+}
+
 int
 main (int   argc,
       char *argv[])
@@ -634,5 +716,10 @@ main (int   argc,
 
   g_test_add_func ("/tls/" BACKEND "/certificate/is-same", test_certificate_is_same);
 
+  g_test_add_func ("/tls/" BACKEND "/certificate/not-valid-before", test_certificate_not_valid_before);
+  g_test_add_func ("/tls/" BACKEND "/certificate/not-valid-after", test_certificate_not_valid_after);
+  g_test_add_func ("/tls/" BACKEND "/certificate/subject-name", test_certificate_subject_name);
+  g_test_add_func ("/tls/" BACKEND "/certificate/issuer-name", test_certificate_issuer_name);
+
   return g_test_run();
 }
diff --git a/tls/tests/files/create-files.sh b/tls/tests/files/create-files.sh
index b4693c8..4eeac0f 100755
--- a/tls/tests/files/create-files.sh
+++ b/tls/tests/files/create-files.sh
@@ -17,6 +17,17 @@ echo "                   *** IMPORTANT ***"
 echo
 echo "This script depends on datefudge, openssl, and python3's cryptography module."
 echo
+echo "A few manual changes need to be made."
+echo "certificate.c:test_certificate_not_valid_before"
+echo "and certificate.c:test_certificate_not_valid_after have"
+echo "EXPECTED_NOT_VALID_BEFORE and EXPECTED_NOT_VALID_AFTER"
+echo "that needs to be changed to match corresponding validity dates"
+echo "that are part of the new certificate named server.pem."
+echo "One way to obtain the new values is to inspect the output of:"
+echo "$ openssl x509 -inform pem -in ./tls/tests/files/server.pem -noout -text"
+echo
+echo "                   *** IMPORTANT ***"
+echo
 
 read -p "Press [Enter] key to continue..."
 


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