gboolean crypto_verify_pkcs12 (const guint8 *data, gsize data_len, const char *password, GError **error) { gnutls_pkcs12_t p12; gnutls_datum_t dt; gboolean success = FALSE; int err; g_return_val_if_fail (data != NULL, FALSE); if (!crypto_init (error)) return FALSE; dt.data = (unsigned char *) data; dt.size = data_len; err = gnutls_pkcs12_init (&p12); if (err < 0) { g_set_error (error, NM_CRYPTO_ERROR, NM_CRYPTO_ERROR_FAILED, _("Couldn't initialize PKCS#12 decoder: %s"), gnutls_strerror (err)); return FALSE; } /* DER first */ err = gnutls_pkcs12_import (p12, &dt, GNUTLS_X509_FMT_DER, 0); if (err < 0) { /* PEM next */ err = gnutls_pkcs12_import (p12, &dt, GNUTLS_X509_FMT_PEM, 0); if (err < 0) { g_set_error (error, NM_CRYPTO_ERROR, NM_CRYPTO_ERROR_INVALID_DATA, _("Couldn't decode PKCS#12 file: %s"), gnutls_strerror (err)); goto out; } } err = gnutls_pkcs12_verify_mac (p12, password); if (err == GNUTLS_E_SUCCESS) success = TRUE; else { g_set_error (error, NM_CRYPTO_ERROR, NM_CRYPTO_ERROR_DECRYPTION_FAILED, _("Couldn't verify PKCS#12 file: %s"), gnutls_strerror (err)); } out: gnutls_pkcs12_deinit (p12); return success; }
ne_ssl_client_cert *ne_ssl_clicert_read(const char *filename) { int ret; gnutls_datum data; gnutls_pkcs12 p12; ne_ssl_client_cert *cc; char *friendly_name = NULL; gnutls_x509_crt cert = NULL; gnutls_x509_privkey pkey = NULL; if (read_to_datum(filename, &data)) return NULL; if (gnutls_pkcs12_init(&p12) != 0) { return NULL; } ret = gnutls_pkcs12_import(p12, &data, GNUTLS_X509_FMT_DER, 0); ne_free(data.data); if (ret < 0) { gnutls_pkcs12_deinit(p12); return NULL; } if (gnutls_pkcs12_verify_mac(p12, "") == 0) { if (pkcs12_parse(p12, &pkey, &cert, &friendly_name, "") != 0 || !cert || !pkey) { gnutls_pkcs12_deinit(p12); return NULL; } cc = ne_calloc(sizeof *cc); cc->pkey = pkey; cc->decrypted = 1; cc->friendly_name = friendly_name; populate_cert(&cc->cert, cert); gnutls_pkcs12_deinit(p12); cc->p12 = NULL; return cc; } else { /* TODO: calling pkcs12_parse() here to find the friendly_name * seems to break horribly. */ cc = ne_calloc(sizeof *cc); cc->p12 = p12; return cc; } }
static int import_pkcs12_privkey (gnutls_x509_privkey_t key, const gnutls_datum_t * data, gnutls_x509_crt_fmt_t format, const char* password, unsigned int flags) { int ret; gnutls_pkcs12_t p12; gnutls_x509_privkey_t newkey; ret = gnutls_pkcs12_init(&p12); if (ret < 0) return gnutls_assert_val(ret); ret = gnutls_pkcs12_import(p12, data, format, flags); if (ret < 0) { gnutls_assert(); goto fail; } ret = gnutls_pkcs12_simple_parse (p12, password, &newkey, NULL, NULL, NULL, NULL, NULL, 0); if (ret < 0) { gnutls_assert(); goto fail; } ret = gnutls_x509_privkey_cpy (key, newkey); gnutls_x509_privkey_deinit (newkey); if (ret < 0) { gnutls_assert(); goto fail; } ret = 0; fail: gnutls_pkcs12_deinit(p12); return ret; }
void doit(void) { gnutls_pkcs12_t pkcs12; gnutls_x509_crt_t client; gnutls_x509_crt_t ca; gnutls_pkcs12_bag_t bag; unsigned char key_id_buf[20]; gnutls_datum_t key_id; int ret, indx; char outbuf[10240]; size_t size; unsigned tests, i; ret = global_init(); if (ret < 0) { fprintf(stderr, "global_init %d", ret); exit(1); } gnutls_global_set_log_function(tls_log_func); if (debug) gnutls_global_set_log_level(4711); /* Read certs. */ ret = gnutls_x509_crt_init(&client); if (ret < 0) { fprintf(stderr, "crt_init: %d", ret); exit(1); } ret = gnutls_x509_crt_import(client, &client_dat, GNUTLS_X509_FMT_PEM); if (ret < 0) { fprintf(stderr, "crt_import: %d", ret); exit(1); } ret = gnutls_x509_crt_init(&ca); if (ret < 0) { fprintf(stderr, "ca_init: %d", ret); exit(1); } ret = gnutls_x509_crt_import(ca, &ca_dat, GNUTLS_X509_FMT_PEM); if (ret < 0) { fprintf(stderr, "ca_import: %d", ret); exit(1); } /* Create PKCS#12 structure. */ ret = gnutls_pkcs12_init(&pkcs12); if (ret < 0) { fprintf(stderr, "pkcs12_init: %d", ret); exit(1); } /* Generate and add PKCS#12 cert bags. */ #ifndef ENABLE_FIPS140 tests = 2; /* include RC2 */ #else tests = 1; #endif for (i = 0; i < tests; i++) { ret = gnutls_pkcs12_bag_init(&bag); if (ret < 0) { fprintf(stderr, "bag_init: %s (%d)\n", gnutls_strerror(ret), ret); exit(1); } ret = gnutls_pkcs12_bag_set_crt(bag, i == 0 ? client : ca); if (ret < 0) { fprintf(stderr, "set_crt: %s (%d)\n", gnutls_strerror(ret), ret); exit(1); } indx = ret; ret = gnutls_pkcs12_bag_set_friendly_name(bag, indx, i == 0 ? "client" : "ca"); if (ret < 0) { fprintf(stderr, "set_friendly_name: %s (%d)\n", gnutls_strerror(ret), ret); exit(1); } size = sizeof(key_id_buf); ret = gnutls_x509_crt_get_key_id(i == 0 ? client : ca, 0, key_id_buf, &size); if (ret < 0) { fprintf(stderr, "get_key_id: %s (%d)\n", gnutls_strerror(ret), ret); exit(1); } key_id.data = key_id_buf; key_id.size = size; ret = gnutls_pkcs12_bag_set_key_id(bag, indx, &key_id); if (ret < 0) { fprintf(stderr, "bag_set_key_id: %s (%d)\n", gnutls_strerror(ret), ret); exit(1); } ret = gnutls_pkcs12_bag_encrypt(bag, "pass", i == 0 ? GNUTLS_PKCS8_USE_PKCS12_3DES : GNUTLS_PKCS_USE_PKCS12_RC2_40); if (ret < 0) { fprintf(stderr, "bag_encrypt: %d: %s", ret, i == 0 ? "3DES" : "RC2-40"); exit(1); } ret = gnutls_pkcs12_set_bag(pkcs12, bag); if (ret < 0) { fprintf(stderr, "set_bag: %s (%d)\n", gnutls_strerror(ret), ret); exit(1); } gnutls_pkcs12_bag_deinit(bag); } /* MAC the structure, export and print. */ ret = gnutls_pkcs12_generate_mac2(pkcs12, GNUTLS_MAC_SHA1, "pass"); if (ret < 0) { fprintf(stderr, "generate_mac: %s (%d)\n", gnutls_strerror(ret), ret); exit(1); } ret = gnutls_pkcs12_verify_mac(pkcs12, "pass"); if (ret < 0) { fprintf(stderr, "verify_mac: %s (%d)\n", gnutls_strerror(ret), ret); exit(1); } ret = gnutls_pkcs12_generate_mac2(pkcs12, GNUTLS_MAC_SHA256, "passwd"); if (ret < 0) { fprintf(stderr, "generate_mac2: %s (%d)\n", gnutls_strerror(ret), ret); exit(1); } ret = gnutls_pkcs12_verify_mac(pkcs12, "passwd"); if (ret < 0) { fprintf(stderr, "verify_mac2: %s (%d)\n", gnutls_strerror(ret), ret); exit(1); } size = sizeof(outbuf); ret = gnutls_pkcs12_export(pkcs12, GNUTLS_X509_FMT_PEM, outbuf, &size); if (ret < 0) { fprintf(stderr, "pkcs12_export: %s (%d)\n", gnutls_strerror(ret), ret); exit(1); } if (debug) fwrite(outbuf, size, 1, stdout); /* Cleanup. */ gnutls_pkcs12_deinit(pkcs12); gnutls_x509_crt_deinit(client); gnutls_x509_crt_deinit(ca); gnutls_global_deinit(); }
void doit(void) { #ifdef ENABLE_NON_SUITEB_CURVES const char *filename, *password = "******"; gnutls_pkcs12_t pkcs12; gnutls_datum_t data; gnutls_x509_crt_t *chain, *extras; unsigned int chain_size = 0, extras_size = 0, i; gnutls_x509_privkey_t pkey; int ret; ret = global_init(); if (ret < 0) fail("global_init failed %d\n", ret); gnutls_global_set_log_function(tls_log_func); if (debug) gnutls_global_set_log_level(2); ret = gnutls_pkcs12_init(&pkcs12); if (ret < 0) fail("initialization failed: %s\n", gnutls_strerror(ret)); filename = getenv("PKCS12_MANY_CERTS_FILE"); if (!filename) filename = "pkcs12-decode/pkcs12_5certs.p12"; if (debug) success ("Reading PKCS#12 blob from `%s' using password `%s'.\n", filename, password); ret = gnutls_load_file(filename, &data); if (ret < 0) fail("cannot open file"); ret = gnutls_pkcs12_import(pkcs12, &data, GNUTLS_X509_FMT_DER, 0); if (ret < 0) fail("pkcs12_import failed %d: %s\n", ret, gnutls_strerror(ret)); if (debug) success("Read file OK\n"); ret = gnutls_pkcs12_simple_parse(pkcs12, password, &pkey, &chain, &chain_size, &extras, &extras_size, NULL, 0); if (ret < 0) fail("pkcs12_simple_parse failed %d: %s\n", ret, gnutls_strerror(ret)); if (chain_size != 1) fail("chain size (%u) should have been 1\n", chain_size); if (extras_size != 4) fail("extras size (%u) should have been 4\n", extras_size); if (debug) { char dn[512]; size_t dn_size; dn_size = sizeof(dn); ret = gnutls_x509_crt_get_dn(chain[0], dn, &dn_size); if (ret < 0) fail("crt_get_dn failed %d: %s\n", ret, gnutls_strerror(ret)); success("dn: %s\n", dn); dn_size = sizeof(dn); ret = gnutls_x509_crt_get_issuer_dn(chain[0], dn, &dn_size); if (ret < 0) fail("crt_get_dn failed %d: %s\n", ret, gnutls_strerror(ret)); success("issuer dn: %s\n", dn); } gnutls_pkcs12_deinit(pkcs12); gnutls_x509_privkey_deinit(pkey); for (i = 0; i < chain_size; i++) gnutls_x509_crt_deinit(chain[i]); gnutls_free(chain); for (i = 0; i < extras_size; i++) gnutls_x509_crt_deinit(extras[i]); gnutls_free(extras); /* Try gnutls_x509_privkey_import2() */ ret = gnutls_x509_privkey_init(&pkey); if (ret < 0) fail("gnutls_x509_privkey_init failed %d: %s\n", ret, gnutls_strerror(ret)); ret = gnutls_x509_privkey_import2(pkey, &data, GNUTLS_X509_FMT_DER, password, 0); if (ret < 0) fail("gnutls_x509_privkey_import2 failed %d: %s\n", ret, gnutls_strerror(ret)); gnutls_x509_privkey_deinit(pkey); free(data.data); gnutls_global_deinit(); #else exit(77); #endif }
/** * ntfs_pkcs12_extract_rsa_key */ static ntfs_rsa_private_key ntfs_pkcs12_extract_rsa_key(u8 *pfx, int pfx_size, char *password, char *thumbprint, int thumbprint_size, NTFS_DF_TYPES *df_type) { int err, bag_index, flags; gnutls_datum_t dpfx, dkey; gnutls_pkcs12_t pkcs12 = NULL; gnutls_pkcs12_bag_t bag = NULL; gnutls_x509_privkey_t pkey = NULL; gnutls_x509_crt_t crt = NULL; ntfs_rsa_private_key rsa_key = NULL; char purpose_oid[100]; size_t purpose_oid_size = sizeof(purpose_oid); size_t tp_size = thumbprint_size; BOOL have_thumbprint = FALSE; *df_type = DF_TYPE_UNKNOWN; /* Create a pkcs12 structure. */ err = gnutls_pkcs12_init(&pkcs12); if (err) { ntfs_log_error("Failed to initialize PKCS#12 structure: %s\n", gnutls_strerror(err)); return NULL; } /* Convert the PFX file (DER format) to native pkcs12 format. */ dpfx.data = pfx; dpfx.size = pfx_size; err = gnutls_pkcs12_import(pkcs12, &dpfx, GNUTLS_X509_FMT_DER, 0); if (err) { ntfs_log_error("Failed to convert the PFX file from DER to " "native PKCS#12 format: %s\n", gnutls_strerror(err)); goto err; } /* * Verify that the password is correct and that the key file has not * been tampered with. Note if the password has zero length and the * verification fails, retry with password set to NULL. This is needed * to get passwordless .pfx files generated with Windows XP SP1 (and * probably earlier versions of Windows) to work. */ retry_verify: err = gnutls_pkcs12_verify_mac(pkcs12, password); if (err) { if (err == GNUTLS_E_MAC_VERIFY_FAILED && password && !strlen(password)) { password = NULL; goto retry_verify; } ntfs_log_error("Failed to verify the MAC: %s Is the " "password correct?\n", gnutls_strerror(err)); goto err; } for (bag_index = 0; ; bag_index++) { err = gnutls_pkcs12_bag_init(&bag); if (err) { ntfs_log_error("Failed to initialize PKCS#12 Bag " "structure: %s\n", gnutls_strerror(err)); goto err; } err = gnutls_pkcs12_get_bag(pkcs12, bag_index, bag); if (err) { if (err == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { err = 0; break; } ntfs_log_error("Failed to obtain Bag from PKCS#12 " "structure: %s\n", gnutls_strerror(err)); goto err; } check_again: err = gnutls_pkcs12_bag_get_count(bag); if (err < 0) { ntfs_log_error("Failed to obtain Bag count: %s\n", gnutls_strerror(err)); goto err; } err = gnutls_pkcs12_bag_get_type(bag, 0); if (err < 0) { ntfs_log_error("Failed to determine Bag type: %s\n", gnutls_strerror(err)); goto err; } flags = 0; switch (err) { case GNUTLS_BAG_PKCS8_KEY: flags = GNUTLS_PKCS_PLAIN; case GNUTLS_BAG_PKCS8_ENCRYPTED_KEY: err = gnutls_pkcs12_bag_get_data(bag, 0, &dkey); if (err < 0) { ntfs_log_error("Failed to obtain Bag data: " "%s\n", gnutls_strerror(err)); goto err; } err = gnutls_x509_privkey_init(&pkey); if (err) { ntfs_log_error("Failed to initialized " "private key structure: %s\n", gnutls_strerror(err)); goto err; } /* Decrypt the private key into GNU TLS format. */ err = gnutls_x509_privkey_import_pkcs8(pkey, &dkey, GNUTLS_X509_FMT_DER, password, flags); if (err) { ntfs_log_error("Failed to convert private " "key from DER to GNU TLS " "format: %s\n", gnutls_strerror(err)); goto err; } #if 0 /* * Export the key again, but unencrypted, and output it * to stderr. Note the output has an RSA header so to * compare to openssl pkcs12 -nodes -in myfile.pfx * output need to ignore the part of the key between * the first "MII..." up to the second "MII...". The * actual RSA private key begins at the second "MII..." * and in my testing at least was identical to openssl * output and was also identical both on big and little * endian so gnutls should be endianness safe. */ char *buf = malloc(8192); size_t bufsize = 8192; err = gnutls_x509_privkey_export_pkcs8(pkey, GNUTLS_X509_FMT_PEM, "", GNUTLS_PKCS_PLAIN, buf, &bufsize); if (err) { ntfs_log_error("eek1\n"); exit(1); } ntfs_log_error("%s\n", buf); free(buf); #endif /* Convert the private key to our internal format. */ rsa_key = ntfs_rsa_private_key_import_from_gnutls(pkey); if (!rsa_key) goto err; break; case GNUTLS_BAG_ENCRYPTED: err = gnutls_pkcs12_bag_decrypt(bag, password); if (err) { ntfs_log_error("Failed to decrypt Bag: %s\n", gnutls_strerror(err)); goto err; } goto check_again; case GNUTLS_BAG_CERTIFICATE: err = gnutls_pkcs12_bag_get_data(bag, 0, &dkey); if (err < 0) { ntfs_log_error("Failed to obtain Bag data: " "%s\n", gnutls_strerror(err)); goto err; } err = gnutls_x509_crt_init(&crt); if (err) { ntfs_log_error("Failed to initialize " "certificate structure: %s\n", gnutls_strerror(err)); goto err; } err = gnutls_x509_crt_import(crt, &dkey, GNUTLS_X509_FMT_DER); if (err) { ntfs_log_error("Failed to convert certificate " "from DER to GNU TLS format: " "%s\n", gnutls_strerror(err)); goto err; } err = gnutls_x509_crt_get_key_purpose_oid(crt, 0, purpose_oid, &purpose_oid_size, NULL); if (err) { ntfs_log_error("Failed to get key purpose " "OID: %s\n", gnutls_strerror(err)); goto err; } purpose_oid[purpose_oid_size - 1] = '\0'; if (!strcmp(purpose_oid, NTFS_EFS_CERT_PURPOSE_OID_DRF)) *df_type = DF_TYPE_DRF; else if (!strcmp(purpose_oid, NTFS_EFS_CERT_PURPOSE_OID_DDF)) *df_type = DF_TYPE_DDF; else { ntfs_log_error("Certificate has unknown " "purpose OID %s.\n", purpose_oid); err = EINVAL; goto err; } /* Return the thumbprint to the caller. */ err = gnutls_x509_crt_get_fingerprint(crt, GNUTLS_DIG_SHA1, thumbprint, &tp_size); if (err) { ntfs_log_error("Failed to get thumbprint: " "%s\n", gnutls_strerror(err)); goto err; } if (tp_size != NTFS_SHA1_THUMBPRINT_SIZE) { ntfs_log_error("Invalid thumbprint size %zd. " "Should be %d.\n", tp_size, thumbprint_size); err = EINVAL; goto err; } have_thumbprint = TRUE; gnutls_x509_crt_deinit(crt); crt = NULL; break; default: /* We do not care about other types. */ break; } gnutls_pkcs12_bag_deinit(bag); } err: if (rsa_key && (err || *df_type == DF_TYPE_UNKNOWN || !have_thumbprint)) { if (!err) ntfs_log_error("Key type or thumbprint not found, " "aborting.\n"); ntfs_rsa_private_key_release(rsa_key); rsa_key = NULL; } if (crt) gnutls_x509_crt_deinit(crt); if (pkey) gnutls_x509_privkey_deinit(pkey); if (bag) gnutls_pkcs12_bag_deinit(bag); if (pkcs12) gnutls_pkcs12_deinit(pkcs12); return rsa_key; }
/* This function will write a pkcs12 structure into a file. * cert: is a DER encoded certificate * pkcs8_key: is a PKCS #8 encrypted key (note that this must be * encrypted using a PKCS #12 cipher, or some browsers will crash) * password: is the password used to encrypt the PKCS #12 packet. */ int write_pkcs12 (const gnutls_datum_t * cert, const gnutls_datum_t * pkcs8_key, const char *password) { gnutls_pkcs12_t pkcs12; int ret, bag_index; gnutls_pkcs12_bag_t bag, key_bag; char pkcs12_struct[10 * 1024]; size_t pkcs12_struct_size; FILE *fd; /* A good idea might be to use gnutls_x509_privkey_get_key_id() * to obtain a unique ID. */ gnutls_datum_t key_id = { "\x00\x00\x07", 3 }; gnutls_global_init (); /* Firstly we create two helper bags, which hold the certificate, * and the (encrypted) key. */ gnutls_pkcs12_bag_init (&bag); gnutls_pkcs12_bag_init (&key_bag); ret = gnutls_pkcs12_bag_set_data (bag, GNUTLS_BAG_CERTIFICATE, cert); if (ret < 0) { fprintf (stderr, "ret: %s\n", gnutls_strerror (ret)); return 1; } /* ret now holds the bag's index. */ bag_index = ret; /* Associate a friendly name with the given certificate. Used * by browsers. */ gnutls_pkcs12_bag_set_friendly_name (bag, bag_index, "My name"); /* Associate the certificate with the key using a unique key * ID. */ gnutls_pkcs12_bag_set_key_id (bag, bag_index, &key_id); /* use weak encryption for the certificate. */ gnutls_pkcs12_bag_encrypt (bag, password, GNUTLS_PKCS_USE_PKCS12_RC2_40); /* Now the key. */ ret = gnutls_pkcs12_bag_set_data (key_bag, GNUTLS_BAG_PKCS8_ENCRYPTED_KEY, pkcs8_key); if (ret < 0) { fprintf (stderr, "ret: %s\n", gnutls_strerror (ret)); return 1; } /* Note that since the PKCS #8 key is already encrypted we don't * bother encrypting that bag. */ bag_index = ret; gnutls_pkcs12_bag_set_friendly_name (key_bag, bag_index, "My name"); gnutls_pkcs12_bag_set_key_id (key_bag, bag_index, &key_id); /* The bags were filled. Now create the PKCS #12 structure. */ gnutls_pkcs12_init (&pkcs12); /* Insert the two bags in the PKCS #12 structure. */ gnutls_pkcs12_set_bag (pkcs12, bag); gnutls_pkcs12_set_bag (pkcs12, key_bag); /* Generate a message authentication code for the PKCS #12 * structure. */ gnutls_pkcs12_generate_mac (pkcs12, password); pkcs12_struct_size = sizeof (pkcs12_struct); ret = gnutls_pkcs12_export (pkcs12, GNUTLS_X509_FMT_DER, pkcs12_struct, &pkcs12_struct_size); if (ret < 0) { fprintf (stderr, "ret: %s\n", gnutls_strerror (ret)); return 1; } fd = fopen (OUTFILE, "w"); if (fd == NULL) { fprintf (stderr, "cannot open file\n"); return 1; } fwrite (pkcs12_struct, 1, pkcs12_struct_size, fd); fclose (fd); gnutls_pkcs12_bag_deinit (bag); gnutls_pkcs12_bag_deinit (key_bag); gnutls_pkcs12_deinit (pkcs12); return 0; }
bool load_p12(gnutls_certificate_credentials_t xcred, const char *const p12_file, const char *const password) { FILE*p12file; gnutls_datum_t p12blob; gnutls_pkcs12_t p12 = NULL; gnutls_x509_privkey_t key = NULL; gnutls_x509_crt_t certs[MAX_CERTS]; int certs_nr; int id_cert; uint8_t key_id[20]; size_t key_id_size; bool is_success = false; int ret; int i; assert(p12_file != NULL); /* Read file */ p12file = fopen(p12_file, "rb"); if(p12file == NULL) { trace(LOG_ERR, "failed to open PKCS#12 file '%s': %s (%d)", p12_file, strerror(errno), errno); ret = GNUTLS_E_FILE_ERROR; goto error; } p12blob.data = malloc(32768 * sizeof(char)); p12blob.size = fread((void*) p12blob.data, sizeof(char), 32768, p12file); fclose(p12file); /* Init structure and import P12 */ ret = gnutls_pkcs12_init(&p12); if(ret < 0) { trace(LOG_ERR, "failed to init PKCS#12 object (%d)", ret); goto free_blob; } ret = gnutls_pkcs12_import(p12, &p12blob, GNUTLS_X509_FMT_DER, 0); if(ret < 0) { trace(LOG_ERR, "failed to import PKCS#12 data (%d)", ret); goto deinit_pkcs12; } if(password) { ret = gnutls_pkcs12_verify_mac(p12, password); if(ret < 0) { trace(LOG_ERR, "failed to verify PKCS#12 MAC (%d)", ret); goto deinit_pkcs12; } } /* extract the private key and the certificates from PKCS#12 */ if(!tls_parse_pkcs12(&p12, password, &key, certs, &certs_nr)) { trace(LOG_ERR, "failed to parse PKCS#12 file '%s'", p12_file); goto deinit_pkcs12; } if(certs_nr < 2) { trace(LOG_ERR, "too few certificates in PKCS#12 file '%s'", p12_file); goto free_certs_key; } /* get the ID of private key */ key_id_size = sizeof(key_id); ret = gnutls_x509_privkey_get_key_id(key, 0, key_id, &key_id_size); if(ret < 0) { trace(LOG_ERR, "failed to get key ID"); goto free_certs_key; } id_cert = -1; for(i = 0; i < certs_nr; i++) { uint8_t cert_id[20]; size_t cert_id_size; cert_id_size = sizeof(cert_id); ret = gnutls_x509_crt_get_key_id(certs[i], 0, cert_id, &cert_id_size); if(ret < 0) { trace(LOG_ERR, "failed to get key ID for certificate #%d (%d)", i, ret); goto free_certs_key; } if(key_id_size == cert_id_size && memcmp(cert_id, key_id, cert_id_size) == 0) { /* it's the key certificate ! */ if(id_cert != -1) { ret = GNUTLS_E_INTERRUPTED; trace(LOG_ERR, "Duplicate key certificate !\n"); goto free_certs_key; } id_cert = i; } } if(id_cert == -1) { ret = GNUTLS_E_INTERRUPTED; trace(LOG_ERR, "Unable to find key certificate !\n"); goto free_certs_key; } /* Now have fun with the key and certs ! */ ret = gnutls_certificate_set_x509_key(xcred, &certs[id_cert], 1, key); if(ret < 0) { trace(LOG_ERR, "failed to set key for main certificate"); goto free_certs_key; } for(i = 0; i < certs_nr; i++) { if(i != id_cert) { ret = gnutls_certificate_set_x509_trust(xcred, &certs[i], 1); if(ret < 0) { trace(LOG_ERR, "failed to trust certificate #%d", i + 1); goto free_certs_key; } } } is_success = true; free_certs_key: gnutls_x509_privkey_deinit(key); for(i = 0; i < certs_nr; i++) { gnutls_x509_crt_deinit(certs[i]); } deinit_pkcs12: gnutls_pkcs12_deinit(p12); free_blob: free(p12blob.data); error: return is_success; }
/** * gnutls_certificate_set_x509_simple_pkcs12_mem: * @res: is a #gnutls_certificate_credentials_t type. * @p12blob: the 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 type. This function may * be called more than once (in case multiple keys/certificates exist * for the server). * * Encrypted PKCS#12 bags and PKCS#8 private keys are supported. However, * only password based security, and the same password for all * operations, are supported. * * PKCS#12 file may contain many keys and/or certificates, and this * function will try to auto-detect based on the key ID the certificate * and key pair to use. If the PKCS#12 file contain the issuer of * the selected certificate, it will be appended to the certificate * to form a chain. * * If more than one private keys are stored in the PKCS#12 file, * then only one key will be read (and it is undefined which one). * * 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. * * Note that, this function by default returns zero on success and a negative value on error. * Since 3.5.6, when the flag %GNUTLS_CERTIFICATE_API_V2 is set using gnutls_certificate_set_flags() * it returns an index (greater or equal to zero). That index can be used to other functions to refer to the added key-pair. * * Returns: On success this functions returns zero, and otherwise a negative value on error (see above for modifying that behavior). * * Since: 2.8.0 **/ int gnutls_certificate_set_x509_simple_pkcs12_mem (gnutls_certificate_credentials_t res, const gnutls_datum_t * p12blob, gnutls_x509_crt_fmt_t type, const char *password) { gnutls_pkcs12_t p12; gnutls_x509_privkey_t key = NULL; gnutls_x509_crt_t *chain = NULL; gnutls_x509_crl_t crl = NULL; unsigned int chain_size = 0, i; int ret, idx; ret = gnutls_pkcs12_init(&p12); if (ret < 0) { gnutls_assert(); return ret; } ret = gnutls_pkcs12_import(p12, p12blob, type, 0); 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 = gnutls_pkcs12_simple_parse(p12, password, &key, &chain, &chain_size, NULL, NULL, &crl, 0); gnutls_pkcs12_deinit(p12); if (ret < 0) { gnutls_assert(); return ret; } if (key && chain) { ret = gnutls_certificate_set_x509_key(res, chain, chain_size, key); if (ret < 0) { gnutls_assert(); goto done; } idx = ret; } else { gnutls_assert(); ret = GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; goto done; } if (crl) { ret = gnutls_certificate_set_x509_crl(res, &crl, 1); if (ret < 0) { gnutls_assert(); goto done; } } if (res->flags & GNUTLS_CERTIFICATE_API_V2) ret = idx; else ret = 0; done: if (chain) { for (i = 0; i < chain_size; i++) gnutls_x509_crt_deinit(chain[i]); gnutls_free(chain); } if (key) gnutls_x509_privkey_deinit(key); if (crl) gnutls_x509_crl_deinit(crl); return ret; }
/** * gnutls_system_key_add_x509: * @crt: the certificate to be added * @privkey: the key to be added * @label: the friendly name to describe the key * @cert_url: if non-NULL it will contain an allocated value with the certificate URL * @key_url: if non-NULL it will contain an allocated value with the key URL * * This function will added the given key and certificate pair, * to the system list. * * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a * negative error value. * * Since: 3.4.0 **/ int gnutls_system_key_add_x509(gnutls_x509_crt_t crt, gnutls_x509_privkey_t privkey, const char *label, char **cert_url, char **key_url) { HCERTSTORE store = NULL; CRYPT_DATA_BLOB pfx; gnutls_datum_t _pfx = { NULL, 0 }; gnutls_pkcs12_t p12 = NULL; gnutls_pkcs12_bag_t bag1 = NULL, bag2 = NULL; uint8_t id[MAX_WID_SIZE]; size_t id_size; gnutls_datum_t kid; int ret; if (ncrypt_init == 0) return gnutls_assert_val(GNUTLS_E_UNIMPLEMENTED_FEATURE); if (label == NULL) return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); id_size = sizeof(id); ret = gnutls_x509_crt_get_key_id(crt, 0, id, &id_size); if (ret < 0) return gnutls_assert_val(ret); kid.data = id; kid.size = id_size; /* the idea: import the cert and private key into PKCS #12 * format, export it into pfx, and import it into store */ ret = gnutls_pkcs12_init(&p12); if (ret < 0) return gnutls_assert_val(ret); ret = gnutls_pkcs12_bag_init(&bag1); if (ret < 0) { gnutls_assert(); goto cleanup; } ret = gnutls_pkcs12_bag_set_crt(bag1, crt); if (ret < 0) { gnutls_assert(); goto cleanup; } ret = gnutls_pkcs12_bag_set_key_id(bag1, 0, &kid); if (ret < 0) { gnutls_assert(); goto cleanup; } if (label) gnutls_pkcs12_bag_set_friendly_name(bag1, 0, label); ret = gnutls_pkcs12_bag_init(&bag2); if (ret < 0) { gnutls_assert(); goto cleanup; } ret = gnutls_pkcs12_bag_set_privkey(bag2, privkey, NULL, 0); if (ret < 0) { gnutls_assert(); goto cleanup; } ret = gnutls_pkcs12_bag_set_key_id(bag2, 0, &kid); if (ret < 0) { gnutls_assert(); goto cleanup; } if (label) gnutls_pkcs12_bag_set_friendly_name(bag2, 0, label); ret = gnutls_pkcs12_set_bag(p12, bag1); if (ret < 0) { gnutls_assert(); goto cleanup; } ret = gnutls_pkcs12_set_bag(p12, bag2); if (ret < 0) { gnutls_assert(); goto cleanup; } ret = gnutls_pkcs12_generate_mac(p12, "123456"); if (ret < 0) { gnutls_assert(); goto cleanup; } ret = gnutls_pkcs12_export2(p12, GNUTLS_X509_FMT_DER, &_pfx); if (ret < 0) { gnutls_assert(); goto cleanup; } pfx.cbData = _pfx.size; pfx.pbData = _pfx.data; store = PFXImportCertStore(&pfx, L"123456", 0); if (store == NULL) { gnutls_assert(); ret = GNUTLS_E_INVALID_REQUEST; goto cleanup; } if (cert_url || key_url) { unsigned char sha[20]; CRYPT_HASH_BLOB blob; const CERT_CONTEXT *cert = NULL; gnutls_datum_t data; ret = gnutls_x509_crt_export2(crt, GNUTLS_X509_FMT_DER, &data); if (ret < 0) { gnutls_assert(); goto cleanup; } ret = gnutls_hash_fast(GNUTLS_DIG_SHA1, data.data, data.size, sha); gnutls_free(data.data); if (ret < 0) { gnutls_assert(); goto cleanup; } blob.cbData = sizeof(sha); blob.pbData = sha; cert = CertFindCertificateInStore(store, X509_ASN_ENCODING, 0, CERT_FIND_SHA1_HASH, &blob, NULL); if (cert == NULL) { gnutls_assert(); ret = GNUTLS_E_KEY_IMPORT_FAILED; goto cleanup; } ret = get_win_urls(cert, cert_url, key_url, NULL, NULL); if (ret < 0) { gnutls_assert(); goto cleanup; } } ret = 0; cleanup: if (p12 != NULL) gnutls_pkcs12_deinit(p12); if (bag1 != NULL) gnutls_pkcs12_bag_deinit(bag1); if (bag2 != NULL) gnutls_pkcs12_bag_deinit(bag2); if (store != NULL) CertCloseStore(store, 0); gnutls_free(_pfx.data); return ret; }