int ne_ssl_clicert_decrypt(ne_ssl_client_cert *cc, const char *password) { int ret; gnutls_x509_crt cert = NULL; gnutls_x509_privkey pkey = NULL; if (gnutls_pkcs12_verify_mac(cc->p12, password) != 0) { return -1; } ret = pkcs12_parse(cc->p12, &pkey, &cert, NULL, password); if (ret < 0) return ret; if (!cert || (!pkey && !cc->keyless)) { if (cert) gnutls_x509_crt_deinit(cert); if (pkey) gnutls_x509_privkey_deinit(pkey); return -1; } gnutls_pkcs12_deinit(cc->p12); populate_cert(&cc->cert, cert); cc->pkey = pkey; cc->decrypted = 1; cc->p12 = NULL; return 0; }
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; } }
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(); }
/** * 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; }
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; }