summaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorgauravsh@chromium.org <gauravsh@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-06-29 00:25:40 +0000
committergauravsh@chromium.org <gauravsh@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-06-29 00:25:40 +0000
commit6a18d073a9794648bb14299f8adcf46b3fff9b56 (patch)
tree813968a4a218a60bc2522fc864da598b6424f6c7 /net
parent390214725736286d9d814e91804306b7e4a83f25 (diff)
downloadchromium_src-6a18d073a9794648bb14299f8adcf46b3fff9b56.zip
chromium_src-6a18d073a9794648bb14299f8adcf46b3fff9b56.tar.gz
chromium_src-6a18d073a9794648bb14299f8adcf46b3fff9b56.tar.bz2
For PKCS12 private keys imported into a hardware backed token, mark them as unextractable
The certificate mangager in Chrome OS has an "import and bind to device" option that allows imported private keys and associated certificates to be imported into a hardware token (typically a TPM). This change makes all private keys that are imported into a hardware token unextractable by setting to CKA_EXTRACTABLE flag to false. Export behavior is also changed to always perform the CKA_EXTRACTABLE flag check irrespective of whether it's an internal (soft) or hardware backed token and refusing export if set to false. Most hardware tokens will enforce this. Internal soft tokens tend to ignore this flag. The flag is made to take precedence irrespective of whether the underlying token will still allow key export. BUG=chromium-os:15838 TEST=unit tests Review URL: http://codereview.chromium.org/7190027 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@90890 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net')
-rw-r--r--net/base/cert_database.h5
-rw-r--r--net/base/cert_database_nss.cc6
-rw-r--r--net/base/cert_database_nss_unittest.cc28
-rw-r--r--net/third_party/mozilla_security_manager/nsPKCS12Blob.cpp106
-rw-r--r--net/third_party/mozilla_security_manager/nsPKCS12Blob.h6
5 files changed, 108 insertions, 43 deletions
diff --git a/net/base/cert_database.h b/net/base/cert_database.h
index 6c9b008..94f49a0 100644
--- a/net/base/cert_database.h
+++ b/net/base/cert_database.h
@@ -113,11 +113,14 @@ class NET_API CertDatabase {
void ListModules(CryptoModuleList* modules, bool need_rw) const;
// Import certificates and private keys from PKCS #12 blob into the module.
+ // If |is_extractable| is false, mark the private key as being unextractable
+ // from the module.
// Returns OK or a network error code such as ERR_PKCS12_IMPORT_BAD_PASSWORD
// or ERR_PKCS12_IMPORT_ERROR.
int ImportFromPKCS12(CryptoModule* module,
const std::string& data,
- const string16& password);
+ const string16& password,
+ bool is_extractable);
// Export the given certificates and private keys into a PKCS #12 blob,
// storing into |output|.
diff --git a/net/base/cert_database_nss.cc b/net/base/cert_database_nss.cc
index 1e753de..179ee82 100644
--- a/net/base/cert_database_nss.cc
+++ b/net/base/cert_database_nss.cc
@@ -156,10 +156,12 @@ void CertDatabase::ListModules(CryptoModuleList* modules, bool need_rw) const {
int CertDatabase::ImportFromPKCS12(
CryptoModule* module,
const std::string& data,
- const string16& password) {
+ const string16& password,
+ bool is_extractable) {
int result = psm::nsPKCS12Blob_Import(module->os_module_handle(),
data.data(), data.size(),
- password);
+ password,
+ is_extractable);
if (result == net::OK)
CertDatabase::NotifyObserversOfUserCertAdded(NULL);
diff --git a/net/base/cert_database_nss_unittest.cc b/net/base/cert_database_nss_unittest.cc
index 6f03d94..70d4224 100644
--- a/net/base/cert_database_nss_unittest.cc
+++ b/net/base/cert_database_nss_unittest.cc
@@ -155,18 +155,20 @@ TEST_F(CertDatabaseNSSTest, ImportFromPKCS12WrongPassword) {
EXPECT_EQ(ERR_PKCS12_IMPORT_BAD_PASSWORD,
cert_db_.ImportFromPKCS12(slot_,
pkcs12_data,
- ASCIIToUTF16("")));
+ ASCIIToUTF16(""),
+ true)); // is_extractable
// Test db should still be empty.
EXPECT_EQ(0U, ListCertsInSlot(slot_->os_module_handle()).size());
}
-TEST_F(CertDatabaseNSSTest, ImportFromPKCS12AndExportAgain) {
+TEST_F(CertDatabaseNSSTest, ImportFromPKCS12AsExtractableAndExportAgain) {
std::string pkcs12_data = ReadTestFile("client.p12");
EXPECT_EQ(OK, cert_db_.ImportFromPKCS12(slot_,
pkcs12_data,
- ASCIIToUTF16("12345")));
+ ASCIIToUTF16("12345"),
+ true)); // is_extractable
CertificateList cert_list = ListCertsInSlot(slot_->os_module_handle());
ASSERT_EQ(1U, cert_list.size());
@@ -183,6 +185,26 @@ TEST_F(CertDatabaseNSSTest, ImportFromPKCS12AndExportAgain) {
// TODO(mattm): further verification of exported data?
}
+TEST_F(CertDatabaseNSSTest, ImportFromPKCS12AsUnextractableAndExportAgain) {
+ std::string pkcs12_data = ReadTestFile("client.p12");
+
+ EXPECT_EQ(OK, cert_db_.ImportFromPKCS12(slot_,
+ pkcs12_data,
+ ASCIIToUTF16("12345"),
+ false)); // is_extractable
+
+ CertificateList cert_list = ListCertsInSlot(slot_->os_module_handle());
+ ASSERT_EQ(1U, cert_list.size());
+ scoped_refptr<X509Certificate> cert(cert_list[0]);
+
+ EXPECT_EQ("testusercert",
+ cert->subject().common_name);
+
+ std::string exported_data;
+ EXPECT_EQ(0, cert_db_.ExportToPKCS12(cert_list, ASCIIToUTF16("exportpw"),
+ &exported_data));
+}
+
TEST_F(CertDatabaseNSSTest, ImportCACert_SSLTrust) {
std::string cert_data = ReadTestFile("root_ca_cert.crt");
diff --git a/net/third_party/mozilla_security_manager/nsPKCS12Blob.cpp b/net/third_party/mozilla_security_manager/nsPKCS12Blob.cpp
index 1175ca0..e314660 100644
--- a/net/third_party/mozilla_security_manager/nsPKCS12Blob.cpp
+++ b/net/third_party/mozilla_security_manager/nsPKCS12Blob.cpp
@@ -149,6 +149,7 @@ int
nsPKCS12Blob_ImportHelper(const char* pkcs12_data,
size_t pkcs12_len,
const string16& password,
+ bool is_extractable,
bool try_zero_length_secitem,
PK11SlotInfo *slot)
{
@@ -165,7 +166,7 @@ nsPKCS12Blob_ImportHelper(const char* pkcs12_data,
unicodeToItem(password.c_str(), &unicodePw);
}
- // initialize the decoder
+ // Initialize the decoder
dcx = SEC_PKCS12DecoderStart(&unicodePw, slot,
// wincx
NULL,
@@ -190,6 +191,37 @@ nsPKCS12Blob_ImportHelper(const char* pkcs12_data,
// import cert and key
srv = SEC_PKCS12DecoderImportBags(dcx);
if (srv) goto finish;
+
+ if (!is_extractable) {
+ SECItem attribute_value;
+ CK_BBOOL attribute_data = CK_FALSE;
+ attribute_value.data = &attribute_data;
+ attribute_value.len = sizeof(attribute_data);
+ CERTCertList* cert_list = SEC_PKCS12DecoderGetCerts(dcx);
+
+ // Iterate through each certificate in the chain and mark corresponding
+ // private key as unextractable.
+ for (CERTCertListNode* node = CERT_LIST_HEAD(cert_list);
+ !CERT_LIST_END(node, cert_list); node = CERT_LIST_NEXT(node)) {
+ SECKEYPrivateKey* privKey = PK11_FindKeyByDERCert(slot,
+ node->cert,
+ NULL); // wincx
+ if (privKey) {
+ // Mark the private key as unextractable.
+ srv = PK11_WriteRawAttribute(PK11_TypePrivKey, privKey, CKA_EXTRACTABLE,
+ &attribute_value);
+ SECKEY_DestroyPrivateKey(privKey);
+ if (srv) {
+ LOG(ERROR) << "Couldn't set CKA_EXTRACTABLE attribute on private "
+ << "key.";
+ break;
+ }
+ }
+ }
+ CERT_DestroyCertList(cert_list);
+ if (srv) goto finish;
+ }
+
import_result = net::OK;
finish:
// If srv != SECSuccess, NSS probably set a specific error code.
@@ -205,29 +237,33 @@ finish:
import_result = net::ERR_PKCS12_IMPORT_FAILED;
}
}
- // finish the decoder
+ // Finish the decoder
if (dcx)
SEC_PKCS12DecoderFinish(dcx);
SECITEM_ZfreeItem(&unicodePw, PR_FALSE);
return import_result;
}
-PRBool
-isExtractable(SECKEYPrivateKey *privKey)
+
+// 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;
- 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);
- }
+ 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 isExtractable;
+ return rv;
}
class PKCS12InitSingleton {
@@ -264,10 +300,11 @@ void EnsurePKCS12Init() {
int nsPKCS12Blob_Import(PK11SlotInfo* slot,
const char* pkcs12_data,
size_t pkcs12_len,
- const string16& password) {
+ const string16& password,
+ bool is_extractable) {
- int rv = nsPKCS12Blob_ImportHelper(pkcs12_data, pkcs12_len, password, false,
- slot);
+ int rv = nsPKCS12Blob_ImportHelper(pkcs12_data, pkcs12_len, password,
+ is_extractable, false, slot);
// When the user entered a zero length password:
// An empty password should be represented as an empty
@@ -275,10 +312,11 @@ int nsPKCS12Blob_Import(PK11SlotInfo* slot,
// NULL UTF16 character), but some applications use a
// zero length SECItem.
// We try both variations, zero length item and empty string,
- // without giving a user prompt when trying the different empty password flavors.
+ // without giving a user prompt when trying the different empty password
+ // flavors.
if (rv == net::ERR_PKCS12_IMPORT_BAD_PASSWORD && password.empty()) {
- rv = nsPKCS12Blob_ImportHelper(pkcs12_data, pkcs12_len, password, true,
- slot);
+ rv = nsPKCS12Blob_ImportHelper(pkcs12_data, pkcs12_len, password,
+ is_extractable, true, slot);
}
return rv;
}
@@ -327,27 +365,25 @@ nsPKCS12Blob_Export(std::string* output,
CERTCertificate* nssCert = certs[i]->os_cert_handle();
DCHECK(nssCert);
- // We can only successfully export certs that are on
- // internal token. Most, if not all, smart card vendors
- // won't let you extract the private key (in any way
- // shape or form) from the card. So let's punt if
- // the cert is not in the internal db.
- if (nssCert->slot && !PK11_IsInternal(nssCert->slot)) {
- // we aren't the internal token, see if the key is extractable.
+ // We only allow certificate and private key extraction if the corresponding
+ // CKA_EXTRACTABLE private key attribute is set to CK_TRUE. Most hardware
+ // tokens including smartcards enforce this behavior. An internal (soft)
+ // token may ignore this attribute (and hence still be able to export) but
+ // we still refuse to attempt an export.
+ // In addition, some tokens may not support this attribute, in which case
+ // we still attempt the export and let the token implementation dictate
+ // the export behavior.
+ if (nssCert->slot) {
SECKEYPrivateKey *privKey=PK11_FindKeyByDERCert(nssCert->slot,
nssCert,
- NULL /* wincx */);
-
- if (privKey) {
- PRBool privKeyIsExtractable = isExtractable(privKey);
-
+ NULL); // wincx
+ if (privKey) {
+ PRBool privKeyIsExtractable;
+ SECStatus rv = isExtractable(privKey, &privKeyIsExtractable);
SECKEY_DestroyPrivateKey(privKey);
- if (!privKeyIsExtractable) {
- LOG(ERROR) << "private key not extractable";
- // TODO(mattm): firefox has a notification dialog about trying to
- // export from a smartcard. I don't think we support smartcards, so
- // we can ignore that for now.
+ if (rv == SECSuccess && !privKeyIsExtractable) {
+ LOG(ERROR) << "Private key is not extractable";
continue;
}
}
diff --git a/net/third_party/mozilla_security_manager/nsPKCS12Blob.h b/net/third_party/mozilla_security_manager/nsPKCS12Blob.h
index 3270469..ace6baa 100644
--- a/net/third_party/mozilla_security_manager/nsPKCS12Blob.h
+++ b/net/third_party/mozilla_security_manager/nsPKCS12Blob.h
@@ -56,12 +56,14 @@ namespace mozilla_security_manager {
// Initialize NSS PKCS#12 libs.
void EnsurePKCS12Init();
-// Import certificate from PKCS#12 blob into the slot.
+// Import the private key and certificate from a PKCS#12 blob into the slot.
+// If |is_extractable| is false, mark the private key as unextractable.
// Returns a net error code.
int nsPKCS12Blob_Import(PK11SlotInfo* slot,
const char* pkcs12_data,
size_t pkcs12_len,
- const string16& password);
+ const string16& password,
+ bool is_extractable);
// Export the given certificates into a PKCS#12 blob, storing into output.
// Returns the number of certificates exported.