#include <gnutls/pkcs12.h>
-/**
- * gnutls_pkcs12_parse:
- * @res: is a #gnutls_certificate_credentials_t structure.
- * @p12: the PKCS#12 blob.
- * @password: optional password used to decrypt PKCS#12 blob, bags and keys.
- * @key: a structure to store the parsed private key.
- * @cert: a structure to store the parsed certificate.
- * @extra_certs_ret: optional pointer to receive an array of additional
- * certificates found in the PKCS#12 blob.
- * @extra_certs_ret_len: will be updated with the number of additional
- * certs.
- * @crl: a structure to store the parsed CRL.
- *
- * This function parses a PKCS#12 blob in @p12blob and extracts the
- * private key, the corresponding certificate, and any additional
- * certificates and a CRL.
- *
- * The @extra_certs_ret and @extra_certs_ret_len parameters are optional
- * and both may be set to %NULL. If either is non-%NULL, then both must
- * be.
- *
- * 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.
- *
- * Returns: %GNUTLS_E_SUCCESS on success, or an error code.
- *
- * Since: 2.8.0
- **/
-int
-gnutls_pkcs12_parse (gnutls_certificate_credentials_t res,
- gnutls_pkcs12_t p12,
- const char *password,
- gnutls_x509_privkey_t * key,
- gnutls_x509_crt_t * cert,
- gnutls_x509_crt_t ** extra_certs_ret,
- unsigned int * extra_certs_ret_len,
- gnutls_x509_crl_t * crl)
-{
- gnutls_pkcs12_bag_t bag = NULL;
- gnutls_x509_crt_t *extra_certs = NULL;
- unsigned int extra_certs_len = 0;
- int idx = 0;
- int ret;
- size_t cert_id_size = 0;
- size_t key_id_size = 0;
- uint8_t cert_id[20];
- uint8_t key_id[20];
- int privkey_ok = 0;
-
- *cert = NULL;
- *key = NULL;
- *crl = NULL;
-
- /* find the first private key */
- 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, idx, 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_t 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:
- if (*key != NULL) /* too simple to continue */
- {
- gnutls_assert ();
- break;
- }
-
- 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 ();
- gnutls_x509_privkey_deinit (*key);
- goto done;
- }
-
- key_id_size = sizeof (key_id);
- ret =
- gnutls_x509_privkey_get_key_id (*key, 0, key_id,
- &key_id_size);
- if (ret < 0)
- {
- gnutls_assert ();
- gnutls_x509_privkey_deinit (*key);
- goto done;
- }
-
- privkey_ok = 1; /* break */
- break;
- default:
- break;
- }
- }
-
- idx++;
- gnutls_pkcs12_bag_deinit (bag);
-
- if (privkey_ok != 0) /* private key was found */
- break;
- }
-
- if (privkey_ok == 0) /* no private key */
- {
- gnutls_assert ();
- return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
- }
-
- /* now find the corresponding certificate
- */
- idx = 0;
- bag = NULL;
- 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, idx, 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_t data;
- gnutls_x509_crt_t this_cert;
-
- 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_CERTIFICATE:
- ret = gnutls_x509_crt_init (&this_cert);
- if (ret < 0)
- {
- gnutls_assert ();
- goto done;
- }
-
- ret =
- gnutls_x509_crt_import (this_cert, &data, GNUTLS_X509_FMT_DER);
- if (ret < 0)
- {
- gnutls_assert ();
- gnutls_x509_crt_deinit (this_cert);
- goto done;
- }
-
- /* check if the key id match */
- cert_id_size = sizeof (cert_id);
- ret =
- gnutls_x509_crt_get_key_id (this_cert, 0, cert_id, &cert_id_size);
- if (ret < 0)
- {
- gnutls_assert ();
- gnutls_x509_crt_deinit (this_cert);
- goto done;
- }
-
- if (memcmp (cert_id, key_id, cert_id_size) != 0)
- { /* they don't match - skip the certificate */
- if (extra_certs_ret)
- {
- extra_certs = gnutls_realloc (extra_certs,
- sizeof(extra_certs[0]) *
- ++extra_certs_len);
- if (!extra_certs)
- {
- gnutls_assert ();
- ret = GNUTLS_E_MEMORY_ERROR;
- goto done;
- }
- extra_certs[extra_certs_len - 1] = this_cert;
- this_cert = NULL;
- }
- else
- {
- gnutls_x509_crt_deinit (this_cert);
- }
- break;
- }
- else
- {
- if (*cert != NULL) /* no need to set it again */
- {
- gnutls_assert ();
- break;
- }
- *cert = this_cert;
- this_cert = NULL;
- }
- break;
-
- case GNUTLS_BAG_CRL:
- if (*crl != NULL)
- {
- gnutls_assert ();
- break;
- }
-
- 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 ();
- gnutls_x509_crl_deinit (*crl);
- 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;
- }
- }
-
- idx++;
- gnutls_pkcs12_bag_deinit (bag);
- }
-
- ret = 0;
-
-done:
- if (bag)
- gnutls_pkcs12_bag_deinit (bag);
-
- if (ret)
- {
- if (*key)
- gnutls_x509_privkey_deinit(*key);
- if (*cert)
- gnutls_x509_crt_deinit(*cert);
- if (extra_certs_len)
- {
- int i;
- for (i = 0; i < extra_certs_len; i++)
- gnutls_x509_crt_deinit(extra_certs[i]);
- gnutls_free(extra_certs);
- }
- }
- else if (extra_certs_ret)
- {
- *extra_certs_ret = extra_certs;
- *extra_certs_ret_len = extra_certs_len;
- }
-
- return ret;
-}
/**
* gnutls_certificate_set_x509_simple_pkcs12_file:
return ret;
}
+#define MAX_CERT_LIST 16
+
/**
* gnutls_certificate_set_x509_simple_pkcs12_mem:
* @res: is a #gnutls_certificate_credentials_t structure.
gnutls_pkcs12_t p12;
gnutls_x509_privkey_t key = NULL;
gnutls_x509_crt_t cert = NULL;
+ gnutls_x509_crt_t *extra_certs = NULL;
gnutls_x509_crl_t crl = NULL;
+ unsigned int extra_certs_size = 0, i;
int ret;
ret = gnutls_pkcs12_init (&p12);
}
}
- ret = gnutls_pkcs12_parse (res, p12, password, &key, &cert, NULL, NULL, &crl);
+ ret = gnutls_pkcs12_simple_parse (p12, password, &key, &cert,
+ &extra_certs, &extra_certs_size, &crl);
gnutls_pkcs12_deinit (p12);
if (ret < 0)
{
if (key && cert)
{
- ret = gnutls_certificate_set_x509_key (res, &cert, 1, key);
+ gnutls_x509_crt_t chain[MAX_CERT_LIST];
+ unsigned int chain_size = 1, j;
+ unsigned int done = 0;
+
+ j = 0;
+ chain[j] = cert;
+
+ if (extra_certs_size > 0 && extra_certs_size < MAX_CERT_LIST-1)
+ {
+ do
+ {
+ for (i=0;i<extra_certs_size;i++)
+ {
+ if (gnutls_x509_crt_check_issuer(chain[j], extra_certs[i]) != 0 &&
+ gnutls_x509_crt_check_issuer(extra_certs[i], chain[j]) == 0)
+ {
+ if (gnutls_x509_crt_check_issuer(extra_certs[i], extra_certs[i]) != 0)
+ { /* we found a self-signed one. We are done. */
+ done = 1;
+ break;
+ }
+ chain[++j] = extra_certs[i];
+ chain_size++;
+ break;
+ }
+
+ if (i==extra_certs_size - 1)
+ done = 1;
+ }
+ }
+ while(done == 0);
+ }
+
+ ret = gnutls_certificate_set_x509_key (res, chain, chain_size, key);
if (ret < 0)
{
gnutls_assert ();
ret = 0;
done:
+ if (extra_certs)
+ {
+ for (i=0;i<extra_certs_size;i++)
+ gnutls_x509_crt_deinit (extra_certs[i]);
+ gnutls_free(extra_certs);
+ }
if (cert)
gnutls_x509_crt_deinit (cert);
if (key)
}
+/**
+ * gnutls_pkcs12_simple_parse:
+ * @p12: the PKCS#12 blob.
+ * @password: optional password used to decrypt PKCS#12 blob, bags and keys.
+ * @key: a structure to store the parsed private key.
+ * @cert: a structure to store the parsed certificate.
+ * @extra_certs_ret: optional pointer to receive an array of additional
+ * certificates found in the PKCS#12 blob.
+ * @extra_certs_ret_len: will be updated with the number of additional
+ * certs.
+ * @crl: a structure to store the parsed CRL.
+ *
+ * This function parses a PKCS#12 blob in @p12blob and extracts the
+ * private key, the corresponding certificate, and any additional
+ * certificates and a CRL.
+ *
+ * The @extra_certs_ret and @extra_certs_ret_len parameters are optional
+ * and both may be set to %NULL. If either is non-%NULL, then both must
+ * be.
+ *
+ * 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.
+ *
+ * Returns: %GNUTLS_E_SUCCESS on success, or an error code.
+ *
+ * Since: 3.1
+ **/
+int
+gnutls_pkcs12_simple_parse (gnutls_pkcs12_t p12,
+ const char *password,
+ gnutls_x509_privkey_t * key,
+ gnutls_x509_crt_t * cert,
+ gnutls_x509_crt_t ** extra_certs_ret,
+ unsigned int * extra_certs_ret_len,
+ gnutls_x509_crl_t * crl)
+{
+ gnutls_pkcs12_bag_t bag = NULL;
+ gnutls_x509_crt_t *extra_certs = NULL;
+ unsigned int extra_certs_len = 0;
+ int idx = 0;
+ int ret;
+ size_t cert_id_size = 0;
+ size_t key_id_size = 0;
+ uint8_t cert_id[20];
+ uint8_t key_id[20];
+ int privkey_ok = 0;
+
+ *cert = NULL;
+ *key = NULL;
+ *crl = NULL;
+
+ /* find the first private key */
+ 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, idx, 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_t 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:
+ if (*key != NULL) /* too simple to continue */
+ {
+ gnutls_assert ();
+ break;
+ }
+
+ 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 ();
+ gnutls_x509_privkey_deinit (*key);
+ goto done;
+ }
+
+ key_id_size = sizeof (key_id);
+ ret =
+ gnutls_x509_privkey_get_key_id (*key, 0, key_id,
+ &key_id_size);
+ if (ret < 0)
+ {
+ gnutls_assert ();
+ gnutls_x509_privkey_deinit (*key);
+ goto done;
+ }
+
+ privkey_ok = 1; /* break */
+ break;
+ default:
+ break;
+ }
+ }
+
+ idx++;
+ gnutls_pkcs12_bag_deinit (bag);
+
+ if (privkey_ok != 0) /* private key was found */
+ break;
+ }
+
+ if (privkey_ok == 0) /* no private key */
+ {
+ gnutls_assert ();
+ return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
+ }
+
+ /* now find the corresponding certificate
+ */
+ idx = 0;
+ bag = NULL;
+ 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, idx, 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_t data;
+ gnutls_x509_crt_t this_cert;
+
+ 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_CERTIFICATE:
+ ret = gnutls_x509_crt_init (&this_cert);
+ if (ret < 0)
+ {
+ gnutls_assert ();
+ goto done;
+ }
+
+ ret =
+ gnutls_x509_crt_import (this_cert, &data, GNUTLS_X509_FMT_DER);
+ if (ret < 0)
+ {
+ gnutls_assert ();
+ gnutls_x509_crt_deinit (this_cert);
+ goto done;
+ }
+
+ /* check if the key id match */
+ cert_id_size = sizeof (cert_id);
+ ret =
+ gnutls_x509_crt_get_key_id (this_cert, 0, cert_id, &cert_id_size);
+ if (ret < 0)
+ {
+ gnutls_assert ();
+ gnutls_x509_crt_deinit (this_cert);
+ goto done;
+ }
+
+ if (memcmp (cert_id, key_id, cert_id_size) != 0)
+ { /* they don't match - skip the certificate */
+ if (extra_certs_ret)
+ {
+ extra_certs = gnutls_realloc (extra_certs,
+ sizeof(extra_certs[0]) *
+ ++extra_certs_len);
+ if (!extra_certs)
+ {
+ gnutls_assert ();
+ ret = GNUTLS_E_MEMORY_ERROR;
+ goto done;
+ }
+ extra_certs[extra_certs_len - 1] = this_cert;
+ this_cert = NULL;
+ }
+ else
+ {
+ gnutls_x509_crt_deinit (this_cert);
+ }
+ break;
+ }
+ else
+ {
+ if (*cert != NULL) /* no need to set it again */
+ {
+ gnutls_assert ();
+ break;
+ }
+ *cert = this_cert;
+ this_cert = NULL;
+ }
+ break;
+
+ case GNUTLS_BAG_CRL:
+ if (*crl != NULL)
+ {
+ gnutls_assert ();
+ break;
+ }
+
+ 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 ();
+ gnutls_x509_crl_deinit (*crl);
+ 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;
+ }
+ }
+
+ idx++;
+ gnutls_pkcs12_bag_deinit (bag);
+ }
+
+ ret = 0;
+
+done:
+ if (bag)
+ gnutls_pkcs12_bag_deinit (bag);
+
+ if (ret)
+ {
+ if (*key)
+ gnutls_x509_privkey_deinit(*key);
+ if (*cert)
+ gnutls_x509_crt_deinit(*cert);
+ if (extra_certs_len)
+ {
+ unsigned int i;
+ for (i = 0; i < extra_certs_len; i++)
+ gnutls_x509_crt_deinit(extra_certs[i]);
+ gnutls_free(extra_certs);
+ }
+ }
+ else if (extra_certs_ret)
+ {
+ *extra_certs_ret = extra_certs;
+ *extra_certs_ret_len = extra_certs_len;
+ }
+
+ return ret;
+}