diff options
author | Simon Josefsson <simon@josefsson.org> | 2005-12-08 14:32:55 +0000 |
---|---|---|
committer | Simon Josefsson <simon@josefsson.org> | 2005-12-08 14:32:55 +0000 |
commit | b3eb305de94fc79f9c69a91cc1f45ac86c04ec5c (patch) | |
tree | c9208b3c5a5bd114f0f7e4a18b9bbef41362967c | |
parent | e3a683c3eee3fe7e435d54a5c37de067da00c661 (diff) | |
download | gnutls-b3eb305de94fc79f9c69a91cc1f45ac86c04ec5c.tar.gz |
Support reading X.509 credentials from PKCS#12 files.
-rw-r--r-- | NEWS | 16 | ||||
-rw-r--r-- | doc/TODO | 3 | ||||
-rw-r--r-- | includes/gnutls/gnutls.h.in | 5 | ||||
-rw-r--r-- | lib/gnutls_x509.c | 278 | ||||
-rw-r--r-- | tests/Makefile.am | 6 | ||||
-rw-r--r-- | tests/set_pkcs12_cred.c | 39 |
6 files changed, 343 insertions, 4 deletions
@@ -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. @@ -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; +} |