From: Nikos Mavrogiannopoulos Date: Sun, 3 Jun 2012 10:15:15 +0000 (+0200) Subject: gnutls_certificate_set_x509_simple_pkcs12_file() now imports certificate chain if... X-Git-Tag: gnutls_3_0_21~84 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=7338cd1084f805513d3fbb0ffb1517f3aea847a7;p=thirdparty%2Fgnutls.git gnutls_certificate_set_x509_simple_pkcs12_file() now imports certificate chain if it is present. gnutls_pkcs12_parse() was renamed to gnutls_pkcs12_simple_parse() --- diff --git a/lib/gnutls_x509.c b/lib/gnutls_x509.c index 9e73d37aff..b308d6ba23 100644 --- a/lib/gnutls_x509.c +++ b/lib/gnutls_x509.c @@ -1832,396 +1832,6 @@ gnutls_certificate_set_x509_crl_file (gnutls_certificate_credentials_t res, #include -/** - * 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: @@ -2277,6 +1887,8 @@ int return ret; } +#define MAX_CERT_LIST 16 + /** * gnutls_certificate_set_x509_simple_pkcs12_mem: * @res: is a #gnutls_certificate_credentials_t structure. @@ -2316,7 +1928,9 @@ int 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); @@ -2345,7 +1959,8 @@ int } } - 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) { @@ -2355,7 +1970,40 @@ int 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;isize, vdata->data, s); + ret = rsa_pkcs1_sign_tr(&pub, &priv, NULL, rnd_func, + vdata->size, vdata->data, s); if (ret == 0) { gnutls_assert(); diff --git a/lib/x509/pkcs12.c b/lib/x509/pkcs12.c index f26fd4fa35..40b3a8a2d9 100644 --- a/lib/x509/pkcs12.c +++ b/lib/x509/pkcs12.c @@ -1328,3 +1328,391 @@ cleanup: } +/** + * 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; +}