[glib-networking/pgriffis/gtlscertificate-password] Add support for PKCS #12 encrypted files
- From: Patrick Griffis <pgriffis src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [glib-networking/pgriffis/gtlscertificate-password] Add support for PKCS #12 encrypted files
- Date: Wed, 1 Sep 2021 20:30:48 +0000 (UTC)
commit a6434ed42c153ad967b9a6ff9b2e04a3bf377d1d
Author: Patrick Griffis <pgriffis igalia com>
Date: Wed Sep 1 15:22:34 2021 -0500
Add support for PKCS #12 encrypted files
tls/gnutls/gtlscertificate-gnutls.c | 111 +++++++++++++++++++++
tls/tests/certificate.c | 98 ++++++++++++++++++
tls/tests/files/client-and-key-password-enckey.p12 | Bin 0 -> 2644 bytes
tls/tests/files/client-and-key-password.p12 | Bin 0 -> 2552 bytes
tls/tests/files/client-and-key.p12 | Bin 0 -> 2548 bytes
tls/tests/files/create-files.sh | 9 ++
6 files changed, 218 insertions(+)
---
diff --git a/tls/gnutls/gtlscertificate-gnutls.c b/tls/gnutls/gtlscertificate-gnutls.c
index 11f5c5c7..da143ce5 100644
--- a/tls/gnutls/gtlscertificate-gnutls.c
+++ b/tls/gnutls/gtlscertificate-gnutls.c
@@ -26,6 +26,7 @@
#include <gnutls/gnutls.h>
#include <gnutls/x509.h>
+#include <gnutls/pkcs12.h>
#include <string.h>
#include "gtlscertificate-gnutls.h"
@@ -48,6 +49,8 @@ enum
PROP_ISSUER_NAME,
PROP_DNS_NAMES,
PROP_IP_ADDRESSES,
+ PROP_PKCS12_BYTES,
+ PROP_PASSWORD,
};
struct _GTlsCertificateGnutls
@@ -61,8 +64,11 @@ struct _GTlsCertificateGnutls
gchar *private_key_pkcs11_uri;
GTlsCertificateGnutls *issuer;
+ GBytes *pkcs12_bytes;
+ char *password;
GError *construct_error;
+ gboolean construct_needs_pkcs12_password;
guint have_cert : 1;
guint have_key : 1;
@@ -191,6 +197,95 @@ err:
gnutls_x509_privkey_deinit (x509_privkey);
}
+static void
+maybe_import_pkcs12 (GTlsCertificateGnutls *gnutls)
+{
+ gnutls_pkcs12_t p12 = NULL;
+ gnutls_x509_privkey_t x509_key = NULL;
+ gnutls_x509_crt_t *chain = NULL;
+ guint chain_len;
+ int status;
+ gnutls_datum_t p12_data;
+
+ /* If password is set first. */
+ if (!gnutls->pkcs12_bytes)
+ return;
+
+ p12_data.data = (guint8*)g_bytes_get_data (gnutls->pkcs12_bytes, NULL);
+ p12_data.size = g_bytes_get_size (gnutls->pkcs12_bytes);
+
+ status = gnutls_pkcs12_init (&p12);
+ if (status != GNUTLS_E_SUCCESS)
+ goto import_failed;
+
+ /* Try both PEM and DER. */
+ status = gnutls_pkcs12_import (p12, &p12_data, GNUTLS_X509_FMT_PEM, 0);
+ if (status != GNUTLS_E_SUCCESS)
+ {
+ status = gnutls_pkcs12_import (p12, &p12_data, GNUTLS_X509_FMT_DER, 0);
+ if (status != GNUTLS_E_SUCCESS)
+ goto import_failed;
+ }
+
+ if (gnutls->password)
+ {
+ status = gnutls_pkcs12_verify_mac (p12, gnutls->password);
+ if (status != GNUTLS_E_SUCCESS)
+ goto import_failed;
+ }
+
+ status = gnutls_pkcs12_simple_parse (p12,
+ gnutls->password ? gnutls->password : "",
+ &x509_key,
+ &chain, &chain_len,
+ NULL, NULL,
+ NULL, 0);
+ if (status == GNUTLS_E_DECRYPTION_FAILED)
+ gnutls->construct_needs_pkcs12_password = TRUE;
+ if (status != GNUTLS_E_SUCCESS)
+ goto import_failed;
+
+ /* Clear previous failure now that we have the password. */
+ if (gnutls->construct_needs_pkcs12_password)
+ g_clear_error (&gnutls->construct_error);
+
+ if (chain)
+ {
+ gnutls->cert = chain[0];
+ gnutls->have_cert = TRUE;
+ // TODO: Rest of chain as issuer
+ }
+
+ if (x509_key)
+ {
+ gnutls_privkey_t key;
+
+ status = gnutls_privkey_init (&key);
+ if (status != GNUTLS_E_SUCCESS)
+ goto import_failed;
+
+ status = gnutls_privkey_import_x509 (key, x509_key, GNUTLS_PRIVKEY_IMPORT_COPY);
+ if (status != GNUTLS_E_SUCCESS)
+ goto import_failed;
+
+ gnutls_x509_privkey_deinit (x509_key);
+
+ gnutls->key = key;
+ gnutls->have_key = TRUE;
+ }
+
+ gnutls_pkcs12_deinit (p12);
+ return;
+
+import_failed:
+ g_clear_error (&gnutls->construct_error);
+ g_set_error (&gnutls->construct_error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
+ _("Failed to import PKCS #12: %s"), gnutls_strerror (status));
+
+ g_clear_pointer (&p12, gnutls_pkcs12_deinit);
+ g_clear_pointer (&x509_key, gnutls_x509_privkey_deinit);
+}
+
static void
g_tls_certificate_gnutls_get_property (GObject *object,
guint prop_id,
@@ -209,6 +304,10 @@ g_tls_certificate_gnutls_get_property (GObject *object,
switch (prop_id)
{
+ case PROP_PKCS12_BYTES:
+ g_value_set_boxed (value, gnutls->pkcs12_bytes);
+ break;
+
case PROP_CERTIFICATE:
size = 0;
status = gnutls_x509_crt_export (gnutls->cert,
@@ -343,6 +442,16 @@ g_tls_certificate_gnutls_set_property (GObject *object,
switch (prop_id)
{
+ case PROP_PASSWORD:
+ gnutls->password = g_value_dup_string (value);
+ maybe_import_pkcs12 (gnutls);
+ break;
+
+ case PROP_PKCS12_BYTES:
+ gnutls->pkcs12_bytes = g_value_dup_boxed (value);
+ maybe_import_pkcs12 (gnutls);
+ break;
+
case PROP_CERTIFICATE:
bytes = g_value_get_boxed (value);
if (!bytes)
@@ -592,6 +701,8 @@ g_tls_certificate_gnutls_class_init (GTlsCertificateGnutlsClass *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
diff --git a/tls/tests/certificate.c b/tls/tests/certificate.c
index c0100d30..5c9b5627 100644
--- a/tls/tests/certificate.c
+++ b/tls/tests/certificate.c
@@ -774,6 +774,101 @@ test_certificate_ip_addresses (void)
g_object_unref (cert);
}
+static GBytes *
+load_bytes_for_test_file (const char *filename)
+{
+ GFile *file = g_file_new_for_path (tls_test_file_path (filename));
+ GBytes *bytes = g_file_load_bytes (file, NULL, NULL, NULL);
+
+ g_assert_nonnull (bytes);
+ g_object_unref (file);
+ return bytes;
+}
+
+static void
+assert_cert_contains_cert_and_key (GTlsCertificate *certificate)
+{
+ char *cert_pem, *key_pem;
+
+ g_object_get (certificate,
+ "certificate-pem", &cert_pem,
+ "private-key-pem", &key_pem,
+ NULL);
+
+ g_assert_nonnull (cert_pem);
+ g_assert_nonnull (key_pem);
+
+ g_free (cert_pem);
+ g_free (key_pem);
+}
+
+static void
+assert_equals_original_cert (GTlsCertificate *cert)
+{
+ GTlsCertificate *original_cert = g_tls_certificate_new_from_file (tls_test_file_path
("client-and-key.pem"), NULL);
+ g_assert_nonnull (original_cert);
+ g_assert_true (g_tls_certificate_is_same (original_cert, cert));
+ g_object_unref (original_cert);
+}
+
+static void
+test_certificate_pkcs12_basic (void)
+{
+ GTlsCertificate *cert;
+ GBytes *pkcs12_data;
+ GError *error = NULL;
+
+ pkcs12_data = load_bytes_for_test_file ("client-and-key.p12");
+ cert = g_tls_certificate_new_from_pkcs12 (pkcs12_data, NULL, &error);
+
+ g_assert_no_error (error);
+ g_assert_nonnull (cert);
+ assert_cert_contains_cert_and_key (cert);
+ assert_equals_original_cert (cert);
+
+ g_bytes_unref (pkcs12_data);
+ g_object_unref (cert);
+}
+
+static void
+test_certificate_pkcs12_password (void)
+{
+ GTlsCertificate *cert;
+ GBytes *pkcs12_data, *pkcs12_enc_data;
+ GError *error = NULL;
+
+ pkcs12_data = load_bytes_for_test_file ("client-and-key-password.p12");
+ pkcs12_enc_data = load_bytes_for_test_file ("client-and-key-password-enckey.p12");
+
+ /* Without a password it fails. */
+ cert = g_tls_certificate_new_from_pkcs12 (pkcs12_data, NULL, &error);
+ g_assert_error (error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE);
+ g_clear_error (&error);
+
+ /* With the wrong password it fails. */
+ cert = g_tls_certificate_new_from_pkcs12 (pkcs12_data, "oajfo", &error);
+ g_assert_error (error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE);
+ g_clear_error (&error);
+
+ /* With the correct password it succeeds. */
+ cert = g_tls_certificate_new_from_pkcs12 (pkcs12_data, "1234", &error);
+ g_assert_no_error (error);
+ g_assert_nonnull (cert);
+ assert_cert_contains_cert_and_key (cert);
+ assert_equals_original_cert (cert);
+ g_object_unref (cert);
+
+ /* With the same password for the key it succeeds. */
+ cert = g_tls_certificate_new_from_pkcs12 (pkcs12_enc_data, "1234", &error);
+ g_assert_no_error (error);
+ g_assert_nonnull (cert);
+ assert_cert_contains_cert_and_key (cert);
+ assert_equals_original_cert (cert);
+
+ g_bytes_unref (pkcs12_data);
+ g_object_unref (cert);
+}
+
int
main (int argc,
char *argv[])
@@ -843,5 +938,8 @@ main (int argc,
g_test_add_func ("/tls/" BACKEND "/certificate/dns-names", test_certificate_dns_names);
g_test_add_func ("/tls/" BACKEND "/certificate/ip-addresses", test_certificate_ip_addresses);
+ g_test_add_func ("/tls/" BACKEND "/certificate/pkcs12/basic", test_certificate_pkcs12_basic);
+ g_test_add_func ("/tls/" BACKEND "/certificate/pkcs12/password", test_certificate_pkcs12_password);
+
return g_test_run();
}
diff --git a/tls/tests/files/client-and-key-password-enckey.p12
b/tls/tests/files/client-and-key-password-enckey.p12
new file mode 100644
index 00000000..feb8c9aa
Binary files /dev/null and b/tls/tests/files/client-and-key-password-enckey.p12 differ
diff --git a/tls/tests/files/client-and-key-password.p12 b/tls/tests/files/client-and-key-password.p12
new file mode 100644
index 00000000..a11df4f4
Binary files /dev/null and b/tls/tests/files/client-and-key-password.p12 differ
diff --git a/tls/tests/files/client-and-key.p12 b/tls/tests/files/client-and-key.p12
new file mode 100644
index 00000000..ba4ec961
Binary files /dev/null and b/tls/tests/files/client-and-key.p12 differ
diff --git a/tls/tests/files/create-files.sh b/tls/tests/files/create-files.sh
index b699e7c8..fd8601c2 100755
--- a/tls/tests/files/create-files.sh
+++ b/tls/tests/files/create-files.sh
@@ -212,6 +212,15 @@ msg "Updating test expectations"
./update-test-database.py ca.pem ../file-database.h
./update-certificate-test.py server.pem ../certificate.h
+#######################################################################
+### Generate PKCS #12 format copies for testing
+#######################################################################
+
+msg "Generating PKCS #12 files"
+openssl pkcs12 -in client-and-key.pem -export -out client-and-key.p12 -nodes --passout pass: -name "No
password"
+openssl pkcs12 -in client-and-key.pem -export -out client-and-key-password.p12 -nodes --passout pass:1234
-name "With Password"
+openssl pkcs12 -in client-and-key.pem -export -out client-and-key-password-enckey.p12 --passout pass:1234
-name "With Password and encrypted privkey"
+
#######################################################################
### Cleanup
#######################################################################
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]