summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon Josefsson <simon@josefsson.org>2005-12-08 14:32:55 +0000
committerSimon Josefsson <simon@josefsson.org>2005-12-08 14:32:55 +0000
commitb3eb305de94fc79f9c69a91cc1f45ac86c04ec5c (patch)
treec9208b3c5a5bd114f0f7e4a18b9bbef41362967c
parente3a683c3eee3fe7e435d54a5c37de067da00c661 (diff)
downloadgnutls-b3eb305de94fc79f9c69a91cc1f45ac86c04ec5c.tar.gz
Support reading X.509 credentials from PKCS#12 files.
-rw-r--r--NEWS16
-rw-r--r--doc/TODO3
-rw-r--r--includes/gnutls/gnutls.h.in5
-rw-r--r--lib/gnutls_x509.c278
-rw-r--r--tests/Makefile.am6
-rw-r--r--tests/set_pkcs12_cred.c39
6 files changed, 343 insertions, 4 deletions
diff --git a/NEWS b/NEWS
index d0337e17e3..61f7cae73f 100644
--- a/NEWS
+++ b/NEWS
@@ -13,6 +13,22 @@ Otto Maddox <ottomaddox@fastmail.fm> and Nozomu Ando <nand@mac.com>.
** Corrected a bug in certtool for 64 bit machines. Reported
by Max Kellermann <max@duempel.org>.
+** New function to set a X.509 private key and certificate pairs, and/or
+CRLs, from an PKCS#12 file, suggested by Emile van Bergen
+<emile@e-advies.nl>.
+
+The integrity of the PKCS#12 file is protected through a password
+based MAC; public-key based signatures for integrity protection are
+not supported. PKCS#12 bags may be encrypted using password derived
+symmetric keys, public-key based encryption is not supported. The
+PKCS#8 keys may be encrypted using passwords. The API use the same
+password for all operations, it is believed that any more flexibility
+just create too much complexity.
+
+** API and ABI modifications:
+New function to set X.509 credentials from a PKCS#12 file:
+ gnutls_certificate_set_x509_simple_pkcs12_file
+
* Version 1.3.0 (2005-11-15)
** Support for TLS Pre-Shared Key (TLS-PSK) ciphersuites have been added.
diff --git a/doc/TODO b/doc/TODO
index 0f80b7446f..ee3dda974d 100644
--- a/doc/TODO
+++ b/doc/TODO
@@ -9,9 +9,6 @@ Current list:
* Audit the code
* Parse passwordless PKCS#8 properly.
+ Support PKCS#8 AES and DES-MD5 encrypted keys.
-+ Support setting key/cert via PKCS#12 blob containing PKCS#8 key and
- a certificate in a X.509 credential, i.e.,
- gnutls_certificate_set_x509_pkcs12_file().
- Support OpenSSL encrypted PKCS#1 RSA keys, for compatibility (new
applications should use PKCS#8 instead).
- Allow verifying of certificates on their reception.
diff --git a/includes/gnutls/gnutls.h.in b/includes/gnutls/gnutls.h.in
index 8b60c19655..e1d33b9fdd 100644
--- a/includes/gnutls/gnutls.h.in
+++ b/includes/gnutls/gnutls.h.in
@@ -419,6 +419,11 @@ int gnutls_certificate_set_x509_key_mem(gnutls_certificate_credentials_t res,
const gnutls_datum_t* CERT, const gnutls_datum_t* KEY,
gnutls_x509_crt_fmt_t type);
+extern int
+gnutls_certificate_set_x509_simple_pkcs12_file (gnutls_certificate_credentials_t res,
+ const char *pkcs12file,
+ gnutls_x509_crt_fmt_t type,
+ const char *password);
/* New functions to allow setting already parsed X.509 stuff.
*/
diff --git a/lib/gnutls_x509.c b/lib/gnutls_x509.c
index 34cc5bb67a..cc62f328d3 100644
--- a/lib/gnutls_x509.c
+++ b/lib/gnutls_x509.c
@@ -1804,6 +1804,284 @@ gnutls_certificate_set_x509_crl_file (gnutls_certificate_credentials_t
return ret;
}
+#include <gnutls/pkcs12.h>
+
+static int
+parse_pkcs12 (gnutls_certificate_credentials_t res,
+ gnutls_pkcs12_t p12,
+ const char *password,
+ gnutls_x509_privkey *key,
+ gnutls_x509_crt_t *cert,
+ gnutls_x509_crl_t *crl)
+{
+ gnutls_pkcs12_bag bag = NULL;
+ int index = 0;
+ int ret;
+
+ for (;;)
+ {
+ int elements_in_bag;
+ int i;
+
+ ret = gnutls_pkcs12_bag_init (&bag);
+ if (ret < 0)
+ {
+ bag = NULL;
+ gnutls_assert ();
+ goto done;
+ }
+
+ ret = gnutls_pkcs12_get_bag (p12, index, bag);
+ if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
+ break;
+ if (ret < 0)
+ {
+ gnutls_assert ();
+ goto done;
+ }
+
+ ret = gnutls_pkcs12_bag_get_type (bag, 0);
+ if (ret < 0)
+ {
+ gnutls_assert ();
+ goto done;
+ }
+
+ if (ret == GNUTLS_BAG_ENCRYPTED)
+ {
+ ret = gnutls_pkcs12_bag_decrypt (bag, password);
+ if (ret < 0)
+ {
+ gnutls_assert ();
+ goto done;
+ }
+ }
+
+ elements_in_bag = gnutls_pkcs12_bag_get_count (bag);
+ if (elements_in_bag < 0)
+ {
+ gnutls_assert ();
+ goto done;
+ }
+
+ for (i = 0; i < elements_in_bag; i++)
+ {
+ int type;
+ gnutls_datum data;
+
+ type = gnutls_pkcs12_bag_get_type (bag, i);
+ if (type < 0)
+ {
+ gnutls_assert ();
+ goto done;
+ }
+
+ ret = gnutls_pkcs12_bag_get_data (bag, i, &data);
+ if (ret < 0)
+ {
+ gnutls_assert ();
+ goto done;
+ }
+
+ switch (type)
+ {
+ case GNUTLS_BAG_PKCS8_ENCRYPTED_KEY:
+ case GNUTLS_BAG_PKCS8_KEY:
+ ret = gnutls_x509_privkey_init (key);
+ if (ret < 0)
+ {
+ gnutls_assert ();
+ goto done;
+ }
+
+ ret = gnutls_x509_privkey_import_pkcs8
+ (*key, &data, GNUTLS_X509_FMT_DER, password,
+ type == GNUTLS_BAG_PKCS8_KEY ? GNUTLS_PKCS_PLAIN : 0);
+ if (ret < 0)
+ {
+ gnutls_assert ();
+ goto done;
+ }
+ break;
+
+ case GNUTLS_BAG_CERTIFICATE:
+ ret = gnutls_x509_crt_init (cert);
+ if (ret < 0)
+ {
+ gnutls_assert ();
+ goto done;
+ }
+
+ ret = gnutls_x509_crt_import (*cert, &data, GNUTLS_X509_FMT_DER);
+ if (ret < 0)
+ {
+ gnutls_assert ();
+ goto done;
+ }
+ break;
+
+ case GNUTLS_BAG_CRL:
+ ret = gnutls_x509_crl_init (crl);
+ if (ret < 0)
+ {
+ gnutls_assert ();
+ goto done;
+ }
+
+ ret = gnutls_x509_crl_import (*crl, &data, GNUTLS_X509_FMT_DER);
+ if (ret < 0)
+ {
+ gnutls_assert ();
+ goto done;
+ }
+ break;
+
+ case GNUTLS_BAG_ENCRYPTED:
+ /* XXX Bother to recurse one level down? Unlikely to
+ use the same password anyway. */
+ case GNUTLS_BAG_EMPTY:
+ default:
+ break;
+ }
+ }
+
+ index++;
+ }
+
+ ret = 0;
+
+ done:
+ if (bag)
+ gnutls_pkcs12_bag_deinit (bag);
+
+ return ret;
+}
+
+/**
+ * gnutls_certificate_set_x509_simple_pkcs12_file:
+ * @res: is an #gnutls_certificate_credentials_t structure.
+ * @pkcs12file: filename of file containing PKCS#12 blob.
+ * @type: is PEM or DER of the @pkcs12file.
+ * @password: optional password used to decrypt PKCS#12 file, bags and keys.
+ *
+ * This function sets a certificate/private key pair and/or a CRL in
+ * the gnutls_certificate_credentials_t structure. This function may
+ * be called more than once (in case multiple keys/certificates exist
+ * for the server).
+ *
+ * MAC:ed PKCS#12 files are supported. Encrypted PKCS#12 bags are
+ * supported. Encrypted PKCS#8 private keys are supported. However,
+ * only password based security, and the same password for all
+ * operations, are supported.
+ *
+ * The private keys may be RSA PKCS#1 or DSA private keys encoded in
+ * the OpenSSL way.
+ *
+ * PKCS#12 file may contain many keys and/or certificates, and there
+ * is no way to identify which key/certificate pair you want. You
+ * should make sure the PKCS#12 file only contain one key/certificate
+ * pair and/or one CRL.
+ *
+ * It is believed that the limitations of this function is acceptable
+ * for most usage, and that any more flexibility would introduce
+ * complexity that would make it harder to use this functionality at
+ * all.
+ *
+ * Return value: Returns 0 on success, or an error code.
+ **/
+int
+gnutls_certificate_set_x509_simple_pkcs12_file (gnutls_certificate_credentials_t res,
+ const char *pkcs12file,
+ gnutls_x509_crt_fmt_t type,
+ const char *password)
+{
+ gnutls_pkcs12_t p12;
+ gnutls_datum_t p12blob;
+ gnutls_x509_privkey key = NULL;
+ gnutls_x509_crt_t cert = NULL;
+ gnutls_x509_crl_t crl = NULL;
+ int ret;
+ strfile x;
+
+ ret = gnutls_pkcs12_init (&p12);
+ if (ret < 0)
+ {
+ gnutls_assert ();
+ return ret;
+ }
+
+ x = _gnutls_file_to_str (pkcs12file);
+ if (x.data == NULL)
+ {
+ gnutls_assert ();
+ gnutls_pkcs12_deinit (p12);
+ return GNUTLS_E_FILE_ERROR;
+ }
+
+ p12blob.data = x.data;
+ p12blob.size = x.size;
+
+ ret = gnutls_pkcs12_import (p12, &p12blob, type, 0);
+ _gnutls_strfile_free (&x);
+ if (ret < 0)
+ {
+ gnutls_assert ();
+ gnutls_pkcs12_deinit (p12);
+ return ret;
+ }
+
+ if (password)
+ {
+ ret = gnutls_pkcs12_verify_mac (p12, password);
+ if (ret < 0)
+ {
+ gnutls_assert ();
+ gnutls_pkcs12_deinit (p12);
+ return ret;
+ }
+ }
+
+ ret = parse_pkcs12 (res, p12, password, &key, &cert, &crl);
+ gnutls_pkcs12_deinit (p12);
+ if (ret < 0)
+ {
+ gnutls_assert ();
+ return ret;
+ }
+
+ if (key && cert)
+ {
+ ret = gnutls_certificate_set_x509_key (res, &cert, 1, key);
+ if (ret < 0)
+ {
+ gnutls_assert ();
+ goto done;
+ }
+ }
+
+ if (crl)
+ {
+ ret = gnutls_certificate_set_x509_crl (res, &crl, 1);
+ if (ret < 0)
+ {
+ gnutls_assert ();
+ goto done;
+ }
+ }
+
+ ret = 0;
+
+ done:
+ if (cert)
+ gnutls_x509_crt_deinit (cert);
+ if (key)
+ gnutls_x509_privkey_deinit (key);
+ if (crl)
+ gnutls_x509_crl_deinit (crl);
+
+ return ret;
+}
+
/**
* gnutls_certificate_free_crls - Used to free all the CRLs from a gnutls_certificate_credentials_t structure
diff --git a/tests/Makefile.am b/tests/Makefile.am
index ab21e593e4..e30e91a530 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -30,7 +30,11 @@ EXTRA_DIST = client.p12 noclient.p12 unclient.p12
openssl_LDADD = $(LDADD) ../libextra/libgnutls-openssl.la
-ctests = simple anonself pskself openssl gc dhepskself
+ctests = simple anonself pskself openssl gc dhepskself set_pkcs12_cred
TESTS = pkcs12_neon $(ctests)
check_PROGRAMS = $(ctests)
dist_check_SCRIPTS = pkcs12_neon
+
+TESTS_ENVIRONMENT = \
+ PKCS12FILE=$(srcdir)/client.p12 \
+ PKCS12PASSWORD=foobar
diff --git a/tests/set_pkcs12_cred.c b/tests/set_pkcs12_cred.c
new file mode 100644
index 0000000000..a67e771384
--- /dev/null
+++ b/tests/set_pkcs12_cred.c
@@ -0,0 +1,39 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <gnutls/gnutls.h>
+
+int
+main (int argc, char *argv[])
+{
+ gnutls_certificate_credentials_t x509cred;
+ char *file, *password;
+ int ret;
+
+ ret = gnutls_global_init ();
+ if (ret < 0)
+ return 1;
+
+ ret = gnutls_certificate_allocate_credentials (&x509cred);
+ if (ret < 0)
+ return 1;
+
+ file = getenv ("PKCS12FILE");
+ password = getenv ("PKCS12PASSWORD");
+
+ printf ("Reading PKCS#12 blob from `%s' using password `%s'.\n",
+ file, password);
+ ret = gnutls_certificate_set_x509_simple_pkcs12_file (x509cred,
+ file,
+ GNUTLS_X509_FMT_DER,
+ password);
+ if (ret < 0)
+ {
+ printf ("x509_pkcs12 (%d): %s\n", ret, gnutls_strerror (ret));
+ }
+
+ gnutls_certificate_free_credentials (x509cred);
+
+ gnutls_global_deinit ();
+
+ return 0;
+}