static char *find_friendly_name(PKCS12 *p12) { STACK_OF(PKCS7) *safes; int n, m; char *name = NULL; PKCS7 *safe; STACK_OF(PKCS12_SAFEBAG) *bags; PKCS12_SAFEBAG *bag; if ((safes = PKCS12_unpack_authsafes(p12)) == NULL) return NULL; for (n = 0; n < sk_PKCS7_num(safes) && name == NULL; n++) { safe = sk_PKCS7_value(safes, n); if (OBJ_obj2nid(safe->type) != NID_pkcs7_data || (bags = PKCS12_unpack_p7data(safe)) == NULL) continue; for (m = 0; m < sk_PKCS12_SAFEBAG_num(bags) && name == NULL; m++) { bag = sk_PKCS12_SAFEBAG_value(bags, m); name = PKCS12_get_friendlyname(bag); } sk_PKCS12_SAFEBAG_pop_free(bags, PKCS12_SAFEBAG_free); } sk_PKCS7_pop_free(safes, PKCS7_free); return name; }
static EVP_PKEY *getPrivateKey(PKCS12 *p12, X509 *x509, const char* pass) { // Extract all PKCS7 safes STACK_OF(PKCS7) *pkcs7s = PKCS12_unpack_authsafes(p12); if (!pkcs7s) { certutil_updateErrorString(); return NULL; } // For each PKCS7 safe int nump = sk_PKCS7_num(pkcs7s); for (int p = 0; p < nump; p++) { PKCS7 *p7 = sk_PKCS7_value(pkcs7s, p); if (!p7) continue; STACK_OF(PKCS12_SAFEBAG) *safebags = PKCS12_unpack_p7data(p7); if (!safebags) continue; // For each PKCS12 safebag int numb = sk_PKCS12_SAFEBAG_num(safebags); for (int i = 0; i < numb; i++) { PKCS12_SAFEBAG *bag = sk_PKCS12_SAFEBAG_value(safebags, i); if (!bag) continue; switch (M_PKCS12_bag_type(bag)) { case NID_pkcs8ShroudedKeyBag:; // Encrypted key PKCS8_PRIV_KEY_INFO *p8 = PKCS12_decrypt_skey(bag, pass, strlen(pass)); if (p8) { EVP_PKEY *pk = EVP_PKCS82PKEY(p8); PKCS8_PRIV_KEY_INFO_free(p8); if (!pk) break; // out of switch if (X509_check_private_key(x509, pk) > 0) { sk_PKCS12_SAFEBAG_pop_free(safebags, PKCS12_SAFEBAG_free); sk_PKCS7_pop_free(pkcs7s, PKCS7_free); return pk; } EVP_PKEY_free(pk); } break; } } sk_PKCS12_SAFEBAG_pop_free(safebags, PKCS12_SAFEBAG_free); } sk_PKCS7_pop_free(pkcs7s, PKCS7_free); return NULL; }
/** * Returns a list of all x509 certificates in a PKCS12 object. */ static STACK_OF(X509) *pkcs12_listCerts(PKCS12 *p12) { STACK_OF(X509) *x509s = sk_X509_new_null(); if (!x509s) return NULL; // Extract all PKCS7 safes STACK_OF(PKCS7) *pkcs7s = PKCS12_unpack_authsafes(p12); if (!pkcs7s) { certutil_updateErrorString(); sk_X509_free(x509s); return NULL; } // For each PKCS7 safe int nump = sk_PKCS7_num(pkcs7s); for (int p = 0; p < nump; p++) { PKCS7 *p7 = sk_PKCS7_value(pkcs7s, p); if (!p7) continue; STACK_OF(PKCS12_SAFEBAG) *safebags = PKCS12_unpack_p7data(p7); if (!safebags) { certutil_updateErrorString(); continue; } // For each PKCS12 safebag int numb = sk_PKCS12_SAFEBAG_num(safebags); for (int i = 0; i < numb; i++) { PKCS12_SAFEBAG *bag = sk_PKCS12_SAFEBAG_value(safebags, i); if (!bag) continue; if (M_PKCS12_bag_type(bag) == NID_certBag) { // Extract x509 cert X509 *x509 = PKCS12_certbag2x509(bag); if (x509 == NULL) { certutil_updateErrorString(); } else { sk_X509_push(x509s, x509); } } } sk_PKCS12_SAFEBAG_pop_free(safebags, PKCS12_SAFEBAG_free); } sk_PKCS7_pop_free(pkcs7s, PKCS7_free); return x509s; }
static TokenError saveKeys(const CertReq *reqs, const char *hostname, const char *password, FILE *file) { TokenError error = TokenError_Unknown; PKCS12 *p12 = NULL; // Add PKCS7 safes with the keys STACK_OF(PKCS7) *authsafes = NULL; uint32_t localKeyId = 0; size_t error_count = 0; while (reqs) { STACK_OF(PKCS12_SAFEBAG) *bags = NULL; X509 *cert = NULL; ASN1_OBJECT *objOwningHost = NULL; uint32_t keyid = htonl(localKeyId++); bool success = false; // Add private key PKCS12_SAFEBAG *bag = PKCS12_add_key(&bags, reqs->privkey, opensslKeyUsages[reqs->pkcs10->keyUsage], ENC_ITER, ENC_NID, (char*)password); if (!bag) goto loop_end; // Add name and localKeyId to the key bag // TODO extract name from subject DN char *name = "names are not implemented yet"; if (!X509at_add1_attr_by_NID(&bag->attrib, NID_friendlyName, MBSTRING_UTF8, (unsigned char*)name, strlen(name)) || !PKCS12_add_localkeyid(bag, (unsigned char*)&keyid, sizeof(keyid))) goto loop_end; // Add a certificate so we can find the key by the subject name cert = X509_REQ_to_X509(reqs->x509, 3650, reqs->privkey); if (!cert || !X509_keyid_set1(cert, (unsigned char*)&keyid, sizeof(keyid))) goto loop_end; if (!X509_add_ext(cert, makeKeyUsageExt(reqs->pkcs10->keyUsage), -1)) goto loop_end; if (!PKCS12_add_cert(&bags, cert)) goto loop_end; // Add hostname (FriBID extension) so we can do same-origin checks // TODO maybe we should use document.domain instead of document.location.hostname? objOwningHost = OBJ_txt2obj(OID_OWNING_HOST, 1); if (!objOwningHost) goto loop_end; bag = sk_PKCS12_SAFEBAG_value(bags, sk_PKCS12_SAFEBAG_num(bags)-1); if (!X509at_add1_attr_by_OBJ(&bag->attrib, objOwningHost, MBSTRING_UTF8, (unsigned char*)hostname, strlen(hostname))) goto loop_end; // Add a new authsafe if (!PKCS12_add_safe(&authsafes, bags, -1, 0, NULL)) goto loop_end; // Success! success = true; loop_end: if (!success) { error_count--; certutil_updateErrorString(); } ASN1_OBJECT_free(objOwningHost); X509_free(cert); sk_PKCS12_SAFEBAG_pop_free(bags, PKCS12_SAFEBAG_free); reqs = reqs->next; } if (error_count != 0) goto end; // Create the PKCS12 wrapper p12 = PKCS12_add_safes(authsafes, 0); if (!p12) { certutil_updateErrorString(); goto end; } PKCS12_set_mac(p12, (char*)password, -1, NULL, 0, MAC_ITER, NULL); // Save file if (i2d_PKCS12_fp(file, p12)) { error = TokenError_Success; } end: sk_PKCS7_pop_free(authsafes, PKCS7_free); PKCS12_free(p12); return error; }