зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1012549 - 0001. Support import PKCS12 certificate. r=dkeeler r=vchang
This commit is contained in:
Родитель
c4ff04039a
Коммит
ce50eac5c5
|
@ -589,6 +589,7 @@ SEC_PKCS12DecoderFinish
|
||||||
SEC_PKCS12DecoderImportBags
|
SEC_PKCS12DecoderImportBags
|
||||||
SEC_PKCS12DecoderIterateInit
|
SEC_PKCS12DecoderIterateInit
|
||||||
SEC_PKCS12DecoderIterateNext
|
SEC_PKCS12DecoderIterateNext
|
||||||
|
SEC_PKCS12DecoderRenameCertNicknames
|
||||||
SEC_PKCS12DecoderStart
|
SEC_PKCS12DecoderStart
|
||||||
SEC_PKCS12DecoderUpdate
|
SEC_PKCS12DecoderUpdate
|
||||||
SEC_PKCS12DecoderValidateBags
|
SEC_PKCS12DecoderValidateBags
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
#include "WifiCertService.h"
|
#include "WifiCertService.h"
|
||||||
|
|
||||||
#include "mozilla/ClearOnShutdown.h"
|
#include "mozilla/ClearOnShutdown.h"
|
||||||
|
#include "mozilla/Endian.h"
|
||||||
#include "mozilla/ModuleUtils.h"
|
#include "mozilla/ModuleUtils.h"
|
||||||
#include "mozilla/RefPtr.h"
|
#include "mozilla/RefPtr.h"
|
||||||
#include "mozilla/dom/ToJSValue.h"
|
#include "mozilla/dom/ToJSValue.h"
|
||||||
|
@ -71,9 +72,14 @@ private:
|
||||||
return NS_ERROR_OUT_OF_MEMORY;
|
return NS_ERROR_OUT_OF_MEMORY;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only support DER format now.
|
// Try import as DER format first.
|
||||||
return ImportDERBlob(buf, size, mResult.mNickname,
|
rv = ImportDERBlob(buf, size);
|
||||||
&mResult.mUsageFlag);
|
if (NS_SUCCEEDED(rv)) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try import as PKCS#12 format.
|
||||||
|
return ImportPKCS12Blob(buf, size, mPassword);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void CallCallback(nsresult rv)
|
virtual void CallCallback(nsresult rv)
|
||||||
|
@ -84,20 +90,148 @@ private:
|
||||||
gWifiCertService->DispatchResult(mResult);
|
gWifiCertService->DispatchResult(mResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult ImportDERBlob(char* buf, uint32_t size,
|
nsresult ImportDERBlob(char* buf, uint32_t size)
|
||||||
const nsAString& aNickname,
|
|
||||||
/*out*/ uint16_t* aUsageFlag)
|
|
||||||
{
|
{
|
||||||
NS_ENSURE_ARG_POINTER(aUsageFlag);
|
|
||||||
|
|
||||||
// Create certificate object.
|
// Create certificate object.
|
||||||
ScopedCERTCertificate cert(CERT_DecodeCertFromPackage(buf, size));
|
ScopedCERTCertificate cert(CERT_DecodeCertFromPackage(buf, size));
|
||||||
if (!cert) {
|
if (!cert) {
|
||||||
return MapSECStatus(SECFailure);
|
return MapSECStatus(SECFailure);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Import certificate with nickname.
|
// Import certificate.
|
||||||
return ImportCert(cert, aNickname, aUsageFlag);
|
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)
|
nsresult ReadBlob(/*out*/ nsCString& aBuf)
|
||||||
|
@ -128,20 +262,22 @@ private:
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult ImportCert(CERTCertificate* aCert, const nsAString& aNickname,
|
nsresult ImportCert(CERTCertificate* aCert)
|
||||||
/*out*/ uint16_t* aUsageFlag)
|
|
||||||
{
|
{
|
||||||
NS_ENSURE_ARG_POINTER(aUsageFlag);
|
|
||||||
|
|
||||||
nsCString userNickname, fullNickname;
|
nsCString userNickname, fullNickname;
|
||||||
|
|
||||||
CopyUTF16toUTF8(aNickname, userNickname);
|
CopyUTF16toUTF8(mResult.mNickname, userNickname);
|
||||||
// Determine certificate nickname by adding prefix according to its type.
|
// Determine certificate nickname by adding prefix according to its type.
|
||||||
if (aCert->isRoot && (aCert->nsCertType & NS_CERT_TYPE_SSL_CA)) {
|
if (aCert->isRoot && (aCert->nsCertType & NS_CERT_TYPE_SSL_CA)) {
|
||||||
// Accept self-signed SSL CA as server certificate.
|
// Accept self-signed SSL CA as server certificate.
|
||||||
fullNickname.AssignLiteral("WIFI_SERVERCERT_");
|
fullNickname.AssignLiteral("WIFI_SERVERCERT_");
|
||||||
fullNickname += userNickname;
|
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 {
|
} else {
|
||||||
return NS_ERROR_ABORT;
|
return NS_ERROR_ABORT;
|
||||||
}
|
}
|
||||||
|
@ -154,7 +290,7 @@ private:
|
||||||
}
|
}
|
||||||
|
|
||||||
// Import certificate, duplicated nickname will cause error.
|
// Import certificate, duplicated nickname will cause error.
|
||||||
SECStatus srv = CERT_AddTempCertToPerm(aCert, nickname, NULL);
|
SECStatus srv = CERT_AddTempCertToPerm(aCert, nickname, nullptr);
|
||||||
if (srv != SECSuccess) {
|
if (srv != SECSuccess) {
|
||||||
return MapSECStatus(srv);
|
return MapSECStatus(srv);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3469,7 +3469,7 @@ WifiWorker.prototype = {
|
||||||
|
|
||||||
WifiManager.importCert(msg.data, function(data) {
|
WifiManager.importCert(msg.data, function(data) {
|
||||||
if (data.status === 0) {
|
if (data.status === 0) {
|
||||||
let usageString = ["ServerCert"];
|
let usageString = ["ServerCert", "UserCert"];
|
||||||
let usageArray = [];
|
let usageArray = [];
|
||||||
for (let i = 0; i < usageString.length; i++) {
|
for (let i = 0; i < usageString.length; i++) {
|
||||||
if (data.usageFlag & (0x01 << i)) {
|
if (data.usageFlag & (0x01 << i)) {
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
#include "keyhi.h"
|
#include "keyhi.h"
|
||||||
#include "cryptohi.h"
|
#include "cryptohi.h"
|
||||||
#include "pk11pub.h"
|
#include "pk11pub.h"
|
||||||
|
#include "pkcs12.h"
|
||||||
#include "sechash.h"
|
#include "sechash.h"
|
||||||
#include "secpkcs7.h"
|
#include "secpkcs7.h"
|
||||||
#include "secport.h"
|
#include "secport.h"
|
||||||
|
@ -240,6 +241,9 @@ MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedSEC_PKCS7ContentInfo,
|
||||||
SEC_PKCS7ContentInfo,
|
SEC_PKCS7ContentInfo,
|
||||||
SEC_PKCS7DestroyContentInfo)
|
SEC_PKCS7DestroyContentInfo)
|
||||||
|
|
||||||
|
MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedSEC_PKCS12DecoderContext,
|
||||||
|
SEC_PKCS12DecoderContext,
|
||||||
|
SEC_PKCS12DecoderFinish)
|
||||||
namespace internal {
|
namespace internal {
|
||||||
|
|
||||||
inline void
|
inline void
|
||||||
|
|
Загрузка…
Ссылка в новой задаче