Bug 1012549 - 0001. Support import PKCS12 certificate. r=dkeeler r=vchang

This commit is contained in:
Chuck Lee 2015-02-28 21:54:16 +08:00
parent ba755290fc
commit ac8c6a4cd0
4 changed files with 159 additions and 18 deletions

View File

@ -589,6 +589,7 @@ SEC_PKCS12DecoderFinish
SEC_PKCS12DecoderImportBags
SEC_PKCS12DecoderIterateInit
SEC_PKCS12DecoderIterateNext
SEC_PKCS12DecoderRenameCertNicknames
SEC_PKCS12DecoderStart
SEC_PKCS12DecoderUpdate
SEC_PKCS12DecoderValidateBags

View File

@ -10,6 +10,7 @@
#include "WifiCertService.h"
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/Endian.h"
#include "mozilla/ModuleUtils.h"
#include "mozilla/RefPtr.h"
#include "mozilla/dom/ToJSValue.h"
@ -71,9 +72,14 @@ private:
return NS_ERROR_OUT_OF_MEMORY;
}
// Only support DER format now.
return ImportDERBlob(buf, size, mResult.mNickname,
&mResult.mUsageFlag);
// Try import as DER format first.
rv = ImportDERBlob(buf, size);
if (NS_SUCCEEDED(rv)) {
return rv;
}
// Try import as PKCS#12 format.
return ImportPKCS12Blob(buf, size, mPassword);
}
virtual void CallCallback(nsresult rv)
@ -84,20 +90,148 @@ private:
gWifiCertService->DispatchResult(mResult);
}
nsresult ImportDERBlob(char* buf, uint32_t size,
const nsAString& aNickname,
/*out*/ uint16_t* aUsageFlag)
nsresult ImportDERBlob(char* buf, uint32_t size)
{
NS_ENSURE_ARG_POINTER(aUsageFlag);
// Create certificate object.
ScopedCERTCertificate cert(CERT_DecodeCertFromPackage(buf, size));
if (!cert) {
return MapSECStatus(SECFailure);
}
// Import certificate with nickname.
return ImportCert(cert, aNickname, aUsageFlag);
// Import certificate.
return ImportCert(cert);
}
static SECItem*
HandleNicknameCollision(SECItem* aOldNickname, PRBool* aCancel, void* aWincx)
{
const char* dummyName = "Imported User Cert";
const size_t dummyNameLen = strlen(dummyName);
SECItem* newNick = ::SECITEM_AllocItem(nullptr, nullptr, dummyNameLen + 1);
if (!newNick) {
return nullptr;
}
newNick->type = siAsciiString;
// Dummy name, will be renamed later.
memcpy(newNick->data, dummyName, dummyNameLen + 1);
newNick->len = dummyNameLen;
return newNick;
}
static SECStatus
HandleNicknameUpdate(const CERTCertificate *aCert,
const SECItem *default_nickname,
SECItem **new_nickname,
void *arg)
{
WifiCertServiceResultOptions *result = (WifiCertServiceResultOptions *)arg;
nsCString userNickname;
CopyUTF16toUTF8(result->mNickname, userNickname);
nsCString fullNickname;
if (aCert->isRoot && (aCert->nsCertType & NS_CERT_TYPE_SSL_CA)) {
// Accept self-signed SSL CA as server certificate.
fullNickname.AssignLiteral("WIFI_SERVERCERT_");
fullNickname += userNickname;
result->mUsageFlag |= nsIWifiCertService::WIFI_CERT_USAGE_FLAG_SERVER;
} else if (aCert->nsCertType & NS_CERT_TYPE_SSL_CLIENT) {
// User Certificate
fullNickname.AssignLiteral("WIFI_USERCERT_");
fullNickname += userNickname;
result->mUsageFlag |= nsIWifiCertService::WIFI_CERT_USAGE_FLAG_USER;
}
char* nickname;
uint32_t length = fullNickname.GetMutableData(&nickname);
SECItem* newNick = ::SECITEM_AllocItem(nullptr, nullptr, length + 1);
if (!newNick) {
return SECFailure;
}
newNick->type = siAsciiString;
memcpy(newNick->data, nickname, length + 1);
newNick->len = length;
*new_nickname = newNick;
return SECSuccess;
}
nsresult ImportPKCS12Blob(char* buf, uint32_t size, const nsAString& aPassword)
{
nsString password(aPassword);
// password is null-terminated wide-char string.
// passwordItem is required to be big-endian form of password, stored in char
// array, including the null-termination.
uint32_t length = password.Length() + 1;
ScopedSECItem passwordItem(
::SECITEM_AllocItem(nullptr, nullptr, length * sizeof(nsString::char_type)));
if (!passwordItem) {
return NS_ERROR_FAILURE;
}
mozilla::NativeEndian::copyAndSwapToBigEndian(passwordItem->data,
password.BeginReading(),
length);
// Create a decoder.
ScopedSEC_PKCS12DecoderContext p12dcx(SEC_PKCS12DecoderStart(
passwordItem, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr,
nullptr));
if (!p12dcx) {
return NS_ERROR_FAILURE;
}
// Assign data to decorder.
SECStatus srv = SEC_PKCS12DecoderUpdate(p12dcx,
reinterpret_cast<unsigned char*>(buf),
size);
if (srv != SECSuccess) {
return MapSECStatus(srv);
}
// Verify certificates.
srv = SEC_PKCS12DecoderVerify(p12dcx);
if (srv != SECSuccess) {
return MapSECStatus(srv);
}
// Set certificate nickname and usage flag.
srv = SEC_PKCS12DecoderRenameCertNicknames(p12dcx, HandleNicknameUpdate,
&mResult);
// Validate certificates.
srv = SEC_PKCS12DecoderValidateBags(p12dcx, HandleNicknameCollision);
if (srv != SECSuccess) {
return MapSECStatus(srv);
}
// Initialize slot.
ScopedPK11SlotInfo slot(PK11_GetInternalKeySlot());
if (!slot) {
return NS_ERROR_FAILURE;
}
if (PK11_NeedLogin(slot) && PK11_NeedUserInit(slot)) {
srv = PK11_InitPin(slot, "", "");
if (srv != SECSuccess) {
return MapSECStatus(srv);
}
}
// Import cert and key.
srv = SEC_PKCS12DecoderImportBags(p12dcx);
if (srv != SECSuccess) {
return MapSECStatus(srv);
}
// User certificate must be imported from PKCS#12.
return (mResult.mUsageFlag & nsIWifiCertService::WIFI_CERT_USAGE_FLAG_USER)
? NS_OK : NS_ERROR_FAILURE;
}
nsresult ReadBlob(/*out*/ nsCString& aBuf)
@ -128,20 +262,22 @@ private:
return NS_OK;
}
nsresult ImportCert(CERTCertificate* aCert, const nsAString& aNickname,
/*out*/ uint16_t* aUsageFlag)
nsresult ImportCert(CERTCertificate* aCert)
{
NS_ENSURE_ARG_POINTER(aUsageFlag);
nsCString userNickname, fullNickname;
CopyUTF16toUTF8(aNickname, userNickname);
CopyUTF16toUTF8(mResult.mNickname, userNickname);
// Determine certificate nickname by adding prefix according to its type.
if (aCert->isRoot && (aCert->nsCertType & NS_CERT_TYPE_SSL_CA)) {
// Accept self-signed SSL CA as server certificate.
fullNickname.AssignLiteral("WIFI_SERVERCERT_");
fullNickname += userNickname;
*aUsageFlag |= nsIWifiCertService::WIFI_CERT_USAGE_FLAG_SERVER;
mResult.mUsageFlag |= nsIWifiCertService::WIFI_CERT_USAGE_FLAG_SERVER;
} else if (aCert->nsCertType & NS_CERT_TYPE_SSL_CLIENT) {
// User Certificate
fullNickname.AssignLiteral("WIFI_USERCERT_");
fullNickname += userNickname;
mResult.mUsageFlag |= nsIWifiCertService::WIFI_CERT_USAGE_FLAG_USER;
} else {
return NS_ERROR_ABORT;
}
@ -154,7 +290,7 @@ private:
}
// Import certificate, duplicated nickname will cause error.
SECStatus srv = CERT_AddTempCertToPerm(aCert, nickname, NULL);
SECStatus srv = CERT_AddTempCertToPerm(aCert, nickname, nullptr);
if (srv != SECSuccess) {
return MapSECStatus(srv);
}

View File

@ -3469,7 +3469,7 @@ WifiWorker.prototype = {
WifiManager.importCert(msg.data, function(data) {
if (data.status === 0) {
let usageString = ["ServerCert"];
let usageString = ["ServerCert", "UserCert"];
let usageArray = [];
for (let i = 0; i < usageString.length; i++) {
if (data.usageFlag & (0x01 << i)) {

View File

@ -23,6 +23,7 @@
#include "keyhi.h"
#include "cryptohi.h"
#include "pk11pub.h"
#include "pkcs12.h"
#include "sechash.h"
#include "secpkcs7.h"
#include "secport.h"
@ -240,6 +241,9 @@ MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedSEC_PKCS7ContentInfo,
SEC_PKCS7ContentInfo,
SEC_PKCS7DestroyContentInfo)
MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedSEC_PKCS12DecoderContext,
SEC_PKCS12DecoderContext,
SEC_PKCS12DecoderFinish)
namespace internal {
inline void