// NSS exports private EC keys without the CKA_EC_POINT attribute, i.e. the // public value. To properly export the private key to JWK or PKCS #8 we need // the public key data though and so we use this method to augment a private // key with data from the given public key. nsresult CryptoKey::AddPublicKeyData(SECKEYPublicKey* aPublicKey) { // This should be a private key. MOZ_ASSERT(GetKeyType() == PRIVATE); // There should be a private NSS key with type 'EC'. MOZ_ASSERT(mPrivateKey && mPrivateKey->keyType == ecKey); // The given public key should have the same key type. MOZ_ASSERT(aPublicKey->keyType == mPrivateKey->keyType); nsNSSShutDownPreventionLock locker; // Read EC params. ScopedAutoSECItem params; SECStatus rv = PK11_ReadRawAttribute(PK11_TypePrivKey, mPrivateKey.get(), CKA_EC_PARAMS, ¶ms); if (rv != SECSuccess) { return NS_ERROR_DOM_OPERATION_ERR; } // Read private value. ScopedAutoSECItem value; rv = PK11_ReadRawAttribute(PK11_TypePrivKey, mPrivateKey.get(), CKA_VALUE, &value); if (rv != SECSuccess) { return NS_ERROR_DOM_OPERATION_ERR; } SECItem* point = &aPublicKey->u.ec.publicValue; CK_OBJECT_CLASS privateKeyValue = CKO_PRIVATE_KEY; CK_BBOOL falseValue = CK_FALSE; CK_KEY_TYPE ecValue = CKK_EC; CK_ATTRIBUTE keyTemplate[9] = { { CKA_CLASS, &privateKeyValue, sizeof(privateKeyValue) }, { CKA_KEY_TYPE, &ecValue, sizeof(ecValue) }, { CKA_TOKEN, &falseValue, sizeof(falseValue) }, { CKA_SENSITIVE, &falseValue, sizeof(falseValue) }, { CKA_PRIVATE, &falseValue, sizeof(falseValue) }, // PrivateKeyFromPrivateKeyTemplate sets the ID. { CKA_ID, nullptr, 0 }, { CKA_EC_PARAMS, params.data, params.len }, { CKA_EC_POINT, point->data, point->len }, { CKA_VALUE, value.data, value.len }, }; mPrivateKey = PrivateKeyFromPrivateKeyTemplate(keyTemplate, ArrayLength(keyTemplate)); NS_ENSURE_TRUE(mPrivateKey, NS_ERROR_DOM_OPERATION_ERR); return NS_OK; }
bool ReadAndEncodeAttribute(SECKEYPrivateKey* aKey, CK_ATTRIBUTE_TYPE aAttribute, Optional<nsString>& aDst) { ScopedSECItem item(::SECITEM_AllocItem(nullptr, nullptr, 0)); if (!item) { return false; } if (PK11_ReadRawAttribute(PK11_TypePrivKey, aKey, aAttribute, item) != SECSuccess) { return false; } CryptoBuffer buffer; if (!buffer.Assign(item)) { return false; } if (NS_FAILED(buffer.ToJwkBase64(aDst.Value()))) { return false; } return true; }
/* Create an ECDHE key pair for a given curve */ SECStatus ssl_CreateECDHEphemeralKeyPair(const sslSocket *ss, const sslNamedGroupDef *ecGroup, sslEphemeralKeyPair **keyPair) { SECKEYPrivateKey *privKey = NULL; SECKEYPublicKey *pubKey = NULL; SECKEYECParams ecParams = { siBuffer, NULL, 0 }; sslEphemeralKeyPair *pair; if (ssl_NamedGroup2ECParams(NULL, ecGroup, &ecParams) != SECSuccess) { return SECFailure; } privKey = SECKEY_CreateECPrivateKey(&ecParams, &pubKey, ss->pkcs11PinArg); SECITEM_FreeItem(&ecParams, PR_FALSE); if (!privKey || !pubKey || !(pair = ssl_NewEphemeralKeyPair(ecGroup, privKey, pubKey))) { if (privKey) { SECKEY_DestroyPrivateKey(privKey); } if (pubKey) { SECKEY_DestroyPublicKey(pubKey); } ssl_MapLowLevelError(SEC_ERROR_KEYGEN_FAIL); return SECFailure; } *keyPair = pair; SSL_TRC(50, ("%d: SSL[%d]: Create ECDH ephemeral key %d", SSL_GETPID(), ss ? ss->fd : NULL, ecGroup->name)); PRINT_BUF(50, (ss, "Public Key", pubKey->u.ec.publicValue.data, pubKey->u.ec.publicValue.len)); #ifdef TRACE if (ssl_trace >= 50) { SECItem d = { siBuffer, NULL, 0 }; SECStatus rv = PK11_ReadRawAttribute(PK11_TypePrivKey, privKey, CKA_VALUE, &d); if (rv == SECSuccess) { PRINT_BUF(50, (ss, "Private Key", d.data, d.len)); SECITEM_FreeItem(&d, PR_FALSE); } else { SSL_TRC(50, ("Error extracting private key")); } } #endif return SECSuccess; }
static bool isExtractable(SECKEYPrivateKey *privKey) { SECItem value; bool isExtractable = false; SECStatus rv; rv=PK11_ReadRawAttribute(PK11_TypePrivKey, privKey, CKA_EXTRACTABLE, &value); if (rv != SECSuccess) { return false; } if ((value.len == 1) && (value.data != NULL)) { isExtractable = !!(*(CK_BBOOL*)value.data); } SECITEM_FreeItem(&value, false); return isExtractable; }
// Attempt to read the CKA_EXTRACTABLE attribute on a private key inside // a token. On success, store the attribute in |extractable| and return // SECSuccess. SECStatus isExtractable(SECKEYPrivateKey *privKey, PRBool *extractable) { SECItem value; SECStatus rv; rv=PK11_ReadRawAttribute(PK11_TypePrivKey, privKey, CKA_EXTRACTABLE, &value); if (rv != SECSuccess) return rv; if ((value.len == 1) && (value.data != NULL)) *extractable = !!(*(CK_BBOOL*)value.data); else rv = SECFailure; SECITEM_FreeItem(&value, PR_FALSE); return rv; }
static PRBool isExtractable(SECKEYPrivateKey *privKey) { SECItem value; PRBool isExtractable = PR_FALSE; SECStatus rv; rv=PK11_ReadRawAttribute(PK11_TypePrivKey, privKey, CKA_EXTRACTABLE, &value); if (rv != SECSuccess) { return PR_FALSE; } if ((value.len == 1) && (value.data != NULL)) { isExtractable = *(CK_BBOOL*)value.data; } SECITEM_FreeItem(&value, PR_FALSE); return isExtractable; }
/** * * Callback to pick the SSL client certificate. */ static SECStatus SelectClientCert(void *arg, PRFileDesc *sock, struct CERTDistNamesStr *caNames, struct CERTCertificateStr **pRetCert, struct SECKEYPrivateKeyStr **pRetKey) { struct ssl_connect_data *connssl = (struct ssl_connect_data *)arg; struct SessionHandle *data = connssl->data; const char *nickname = connssl->client_nickname; if(connssl->obj_clicert) { /* use the cert/key provided by PEM reader */ static const char pem_slotname[] = "PEM Token #1"; SECItem cert_der = { 0, NULL, 0 }; void *proto_win = SSL_RevealPinArg(sock); struct CERTCertificateStr *cert; struct SECKEYPrivateKeyStr *key; PK11SlotInfo *slot = PK11_FindSlotByName(pem_slotname); if(NULL == slot) { failf(data, "NSS: PK11 slot not found: %s", pem_slotname); return SECFailure; } if(PK11_ReadRawAttribute(PK11_TypeGeneric, connssl->obj_clicert, CKA_VALUE, &cert_der) != SECSuccess) { failf(data, "NSS: CKA_VALUE not found in PK11 generic object"); PK11_FreeSlot(slot); return SECFailure; } cert = PK11_FindCertFromDERCertItem(slot, &cert_der, proto_win); SECITEM_FreeItem(&cert_der, PR_FALSE); if(NULL == cert) { failf(data, "NSS: client certificate from file not found"); PK11_FreeSlot(slot); return SECFailure; } key = PK11_FindPrivateKeyFromCert(slot, cert, NULL); PK11_FreeSlot(slot); if(NULL == key) { failf(data, "NSS: private key from file not found"); CERT_DestroyCertificate(cert); return SECFailure; } infof(data, "NSS: client certificate from file\n"); display_cert_info(data, cert); *pRetCert = cert; *pRetKey = key; return SECSuccess; } /* use the default NSS hook */ if(SECSuccess != NSS_GetClientAuthData((void *)nickname, sock, caNames, pRetCert, pRetKey) || NULL == *pRetCert) { if(NULL == nickname) failf(data, "NSS: client certificate not found (nickname not " "specified)"); else failf(data, "NSS: client certificate not found: %s", nickname); return SECFailure; } /* get certificate nickname if any */ nickname = (*pRetCert)->nickname; if(NULL == nickname) nickname = "[unknown]"; if(NULL == *pRetKey) { failf(data, "NSS: private key not found for certificate: %s", nickname); return SECFailure; } infof(data, "NSS: using client certificate: %s\n", nickname); display_cert_info(data, *pRetCert); return SECSuccess; }
nsresult CryptoKey::PrivateKeyToJwk(SECKEYPrivateKey* aPrivKey, JsonWebKey& aRetVal, const nsNSSShutDownPreventionLock& /*proofOfLock*/) { switch (aPrivKey->keyType) { case rsaKey: { aRetVal.mN.Construct(); aRetVal.mE.Construct(); aRetVal.mD.Construct(); aRetVal.mP.Construct(); aRetVal.mQ.Construct(); aRetVal.mDp.Construct(); aRetVal.mDq.Construct(); aRetVal.mQi.Construct(); if (!ReadAndEncodeAttribute(aPrivKey, CKA_MODULUS, aRetVal.mN) || !ReadAndEncodeAttribute(aPrivKey, CKA_PUBLIC_EXPONENT, aRetVal.mE) || !ReadAndEncodeAttribute(aPrivKey, CKA_PRIVATE_EXPONENT, aRetVal.mD) || !ReadAndEncodeAttribute(aPrivKey, CKA_PRIME_1, aRetVal.mP) || !ReadAndEncodeAttribute(aPrivKey, CKA_PRIME_2, aRetVal.mQ) || !ReadAndEncodeAttribute(aPrivKey, CKA_EXPONENT_1, aRetVal.mDp) || !ReadAndEncodeAttribute(aPrivKey, CKA_EXPONENT_2, aRetVal.mDq) || !ReadAndEncodeAttribute(aPrivKey, CKA_COEFFICIENT, aRetVal.mQi)) { return NS_ERROR_DOM_OPERATION_ERR; } aRetVal.mKty = NS_LITERAL_STRING(JWK_TYPE_RSA); return NS_OK; } case ecKey: { // Read EC params. ScopedSECItem params(::SECITEM_AllocItem(nullptr, nullptr, 0)); SECStatus rv = PK11_ReadRawAttribute(PK11_TypePrivKey, aPrivKey, CKA_EC_PARAMS, params); if (rv != SECSuccess) { return NS_ERROR_DOM_OPERATION_ERR; } // Read public point Q. ScopedSECItem ecPoint(::SECITEM_AllocItem(nullptr, nullptr, 0)); rv = PK11_ReadRawAttribute(PK11_TypePrivKey, aPrivKey, CKA_EC_POINT, ecPoint); if (rv != SECSuccess) { return NS_ERROR_DOM_OPERATION_ERR; } if (!ECKeyToJwk(PK11_TypePrivKey, aPrivKey, params, ecPoint, aRetVal)) { return NS_ERROR_DOM_OPERATION_ERR; } aRetVal.mD.Construct(); // Read private value. if (!ReadAndEncodeAttribute(aPrivKey, CKA_VALUE, aRetVal.mD)) { return NS_ERROR_DOM_OPERATION_ERR; } return NS_OK; } default: return NS_ERROR_DOM_NOT_SUPPORTED_ERR; } }
/* * 'clone' a physical card as a virtual card */ static VCard * vcard_emul_mirror_card(VReader *vreader) { /* * lookup certs using the C_FindObjects. The Stan Cert handle won't give * us the real certs until we log in. */ PK11GenericObject *firstObj, *thisObj; int cert_count; unsigned char **certs; int *cert_len; VCardKey **keys; PK11SlotInfo *slot; PRBool ret; VCard *card; slot = vcard_emul_reader_get_slot(vreader); if (slot == NULL) { return NULL; } firstObj = PK11_FindGenericObjects(slot, CKO_CERTIFICATE); if (firstObj == NULL) { return NULL; } /* count the certs */ cert_count = 0; for (thisObj = firstObj; thisObj; thisObj = PK11_GetNextGenericObject(thisObj)) { cert_count++; } if (cert_count == 0) { PK11_DestroyGenericObjects(firstObj); return NULL; } /* allocate the arrays */ ret = vcard_emul_alloc_arrays(&certs, &cert_len, &keys, cert_count); if (ret == PR_FALSE) { return NULL; } /* fill in the arrays */ cert_count = 0; for (thisObj = firstObj; thisObj; thisObj = PK11_GetNextGenericObject(thisObj)) { SECItem derCert; CERTCertificate *cert; SECStatus rv; rv = PK11_ReadRawAttribute(PK11_TypeGeneric, thisObj, CKA_VALUE, &derCert); if (rv != SECSuccess) { continue; } /* create floating temp cert. This gives us a cert structure even if * the token isn't logged in */ cert = CERT_NewTempCertificate(CERT_GetDefaultCertDB(), &derCert, NULL, PR_FALSE, PR_TRUE); SECITEM_FreeItem(&derCert, PR_FALSE); if (cert == NULL) { continue; } certs[cert_count] = cert->derCert.data; cert_len[cert_count] = cert->derCert.len; keys[cert_count] = vcard_emul_make_key(slot, cert); cert_count++; CERT_DestroyCertificate(cert); /* key obj still has a reference */ } /* now create the card */ card = vcard_emul_make_card(vreader, certs, cert_len, keys, cert_count); g_free(certs); g_free(cert_len); g_free(keys); return card; }
// NSS exports private EC keys without the CKA_EC_POINT attribute, i.e. the // public value. To properly export the private key to JWK or PKCS #8 we need // the public key data though and so we use this method to augment a private // key with data from the given public key. nsresult CryptoKey::AddPublicKeyData(SECKEYPublicKey* aPublicKey) { // This should be a private key. MOZ_ASSERT(GetKeyType() == PRIVATE); // There should be a private NSS key with type 'EC'. MOZ_ASSERT(mPrivateKey && mPrivateKey->keyType == ecKey); // The given public key should have the same key type. MOZ_ASSERT(aPublicKey->keyType == mPrivateKey->keyType); nsNSSShutDownPreventionLock locker; ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); if (!slot) { return NS_ERROR_DOM_OPERATION_ERR; } // Generate a random 160-bit object ID. ScopedSECItem objID(::SECITEM_AllocItem(nullptr, nullptr, 20)); SECStatus rv = PK11_GenerateRandomOnSlot(slot, objID->data, objID->len); if (rv != SECSuccess) { return NS_ERROR_DOM_OPERATION_ERR; } // Read EC params. ScopedSECItem params(::SECITEM_AllocItem(nullptr, nullptr, 0)); rv = PK11_ReadRawAttribute(PK11_TypePrivKey, mPrivateKey, CKA_EC_PARAMS, params); if (rv != SECSuccess) { return NS_ERROR_DOM_OPERATION_ERR; } // Read private value. ScopedSECItem value(::SECITEM_AllocItem(nullptr, nullptr, 0)); rv = PK11_ReadRawAttribute(PK11_TypePrivKey, mPrivateKey, CKA_VALUE, value); if (rv != SECSuccess) { return NS_ERROR_DOM_OPERATION_ERR; } SECItem* point = &aPublicKey->u.ec.publicValue; CK_OBJECT_CLASS privateKeyValue = CKO_PRIVATE_KEY; CK_BBOOL falseValue = CK_FALSE; CK_KEY_TYPE ecValue = CKK_EC; CK_ATTRIBUTE keyTemplate[9] = { { CKA_CLASS, &privateKeyValue, sizeof(privateKeyValue) }, { CKA_KEY_TYPE, &ecValue, sizeof(ecValue) }, { CKA_TOKEN, &falseValue, sizeof(falseValue) }, { CKA_SENSITIVE, &falseValue, sizeof(falseValue) }, { CKA_PRIVATE, &falseValue, sizeof(falseValue) }, { CKA_ID, objID->data, objID->len }, { CKA_EC_PARAMS, params->data, params->len }, { CKA_EC_POINT, point->data, point->len }, { CKA_VALUE, value->data, value->len }, }; mPrivateKey = PrivateKeyFromPrivateKeyTemplate(objID, keyTemplate, PR_ARRAY_SIZE(keyTemplate)); NS_ENSURE_TRUE(mPrivateKey, NS_ERROR_DOM_OPERATION_ERR); return NS_OK; }