This source file includes following definitions.
- unicodeToItem
- write_export_data
- nickname_collision
- pip_ucs2_ascii_conversion_fn
- nsPKCS12Blob_ImportHelper
- isExtractable
- EnsurePKCS12Init
- nsPKCS12Blob_Import
- nsPKCS12Blob_Export
#include "net/third_party/mozilla_security_manager/nsPKCS12Blob.h"
#include <pk11pub.h>
#include <pkcs12.h>
#include <p12plcy.h>
#include <secerr.h>
#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/strings/string_util.h"
#include "crypto/nss_util_internal.h"
#include "net/base/net_errors.h"
#include "net/cert/x509_certificate.h"
namespace mozilla_security_manager {
namespace {
void unicodeToItem(const PRUnichar *uni, SECItem *item)
{
int len = 0;
while (uni[len++] != 0);
SECITEM_AllocItem(NULL, item, sizeof(PRUnichar) * len);
#ifdef IS_LITTLE_ENDIAN
int i = 0;
for (i=0; i<len; i++) {
item->data[2*i ] = (unsigned char )(uni[i] << 8);
item->data[2*i+1] = (unsigned char )(uni[i]);
}
#else
memcpy(item->data, uni, item->len);
#endif
}
void write_export_data(void* arg, const char* buf, unsigned long len) {
std::string* dest = reinterpret_cast<std::string*>(arg);
dest->append(buf, len);
}
SECItem* PR_CALLBACK
nickname_collision(SECItem *old_nick, PRBool *cancel, void *wincx)
{
char *nick = NULL;
SECItem *ret_nick = NULL;
CERTCertificate* cert = (CERTCertificate*)wincx;
if (!cancel || !cert) {
return NULL;
}
if (!old_nick)
VLOG(1) << "no nickname for cert in PKCS12 file.";
nick = CERT_MakeCANickname(cert);
if (!nick) {
return NULL;
}
if(old_nick && old_nick->data && old_nick->len &&
PORT_Strlen(nick) == old_nick->len &&
!PORT_Strncmp((char *)old_nick->data, nick, old_nick->len)) {
PORT_Free(nick);
PORT_SetError(SEC_ERROR_IO);
return NULL;
}
VLOG(1) << "using nickname " << nick;
ret_nick = PORT_ZNew(SECItem);
if(ret_nick == NULL) {
PORT_Free(nick);
return NULL;
}
ret_nick->data = (unsigned char *)nick;
ret_nick->len = PORT_Strlen(nick);
return ret_nick;
}
PRBool
pip_ucs2_ascii_conversion_fn(PRBool toUnicode,
unsigned char *inBuf,
unsigned int inBufLen,
unsigned char *outBuf,
unsigned int maxOutBufLen,
unsigned int *outBufLen,
PRBool swapBytes)
{
CHECK_GE(maxOutBufLen, inBufLen);
*outBufLen = inBufLen;
memcpy(outBuf, inBuf, inBufLen);
return PR_TRUE;
}
int
nsPKCS12Blob_ImportHelper(const char* pkcs12_data,
size_t pkcs12_len,
const base::string16& password,
bool is_extractable,
bool try_zero_length_secitem,
PK11SlotInfo *slot,
net::CertificateList* imported_certs)
{
DCHECK(pkcs12_data);
DCHECK(slot);
int import_result = net::ERR_PKCS12_IMPORT_FAILED;
SECStatus srv = SECSuccess;
SEC_PKCS12DecoderContext *dcx = NULL;
SECItem unicodePw;
SECItem attribute_value;
CK_BBOOL attribute_data = CK_FALSE;
const SEC_PKCS12DecoderItem* decoder_item = NULL;
unicodePw.type = siBuffer;
unicodePw.len = 0;
unicodePw.data = NULL;
if (!try_zero_length_secitem) {
unicodeToItem(password.c_str(), &unicodePw);
}
dcx = SEC_PKCS12DecoderStart(&unicodePw, slot,
NULL,
NULL, NULL, NULL, NULL, NULL);
if (!dcx) {
srv = SECFailure;
goto finish;
}
srv = SEC_PKCS12DecoderUpdate(dcx,
(unsigned char*)pkcs12_data,
pkcs12_len);
if (srv) goto finish;
srv = SEC_PKCS12DecoderVerify(dcx);
if (srv) goto finish;
srv = SEC_PKCS12DecoderValidateBags(dcx, nickname_collision);
if (srv) goto finish;
srv = SEC_PKCS12DecoderImportBags(dcx);
if (srv) goto finish;
attribute_value.data = &attribute_data;
attribute_value.len = sizeof(attribute_data);
srv = SEC_PKCS12DecoderIterateInit(dcx);
if (srv) goto finish;
if (imported_certs)
imported_certs->clear();
while (SEC_PKCS12DecoderIterateNext(dcx, &decoder_item) == SECSuccess) {
if (decoder_item->type != SEC_OID_PKCS12_V1_CERT_BAG_ID)
continue;
CERTCertificate* cert = PK11_FindCertFromDERCertItem(
slot, decoder_item->der,
NULL);
if (!cert) {
LOG(ERROR) << "Could not grab a handle to the certificate in the slot "
<< "from the corresponding PKCS#12 DER certificate.";
continue;
}
if (imported_certs) {
net::X509Certificate::OSCertHandles intermediates;
imported_certs->push_back(
net::X509Certificate::CreateFromHandle(cert, intermediates));
}
if (!decoder_item->hasKey) {
CERT_DestroyCertificate(cert);
continue;
}
if (!is_extractable) {
SECKEYPrivateKey* privKey = PK11_FindPrivateKeyFromCert(slot, cert,
NULL);
if (privKey) {
srv = PK11_WriteRawAttribute(PK11_TypePrivKey, privKey, CKA_EXTRACTABLE,
&attribute_value);
SECKEY_DestroyPrivateKey(privKey);
if (srv) {
LOG(ERROR) << "Could not set CKA_EXTRACTABLE attribute on private "
<< "key.";
CERT_DestroyCertificate(cert);
break;
}
}
}
CERT_DestroyCertificate(cert);
if (srv) goto finish;
}
import_result = net::OK;
finish:
if (srv != SECSuccess) {
int error = PORT_GetError();
LOG(ERROR) << "PKCS#12 import failed with error " << error;
switch (error) {
case SEC_ERROR_BAD_PASSWORD:
case SEC_ERROR_PKCS12_PRIVACY_PASSWORD_INCORRECT:
import_result = net::ERR_PKCS12_IMPORT_BAD_PASSWORD;
break;
case SEC_ERROR_PKCS12_INVALID_MAC:
import_result = net::ERR_PKCS12_IMPORT_INVALID_MAC;
break;
case SEC_ERROR_BAD_DER:
case SEC_ERROR_PKCS12_DECODING_PFX:
case SEC_ERROR_PKCS12_CORRUPT_PFX_STRUCTURE:
import_result = net::ERR_PKCS12_IMPORT_INVALID_FILE;
break;
case SEC_ERROR_PKCS12_UNSUPPORTED_MAC_ALGORITHM:
case SEC_ERROR_PKCS12_UNSUPPORTED_TRANSPORT_MODE:
case SEC_ERROR_PKCS12_UNSUPPORTED_PBE_ALGORITHM:
case SEC_ERROR_PKCS12_UNSUPPORTED_VERSION:
import_result = net::ERR_PKCS12_IMPORT_UNSUPPORTED;
break;
default:
import_result = net::ERR_PKCS12_IMPORT_FAILED;
break;
}
}
if (dcx)
SEC_PKCS12DecoderFinish(dcx);
SECITEM_ZfreeItem(&unicodePw, PR_FALSE);
return import_result;
}
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;
}
class PKCS12InitSingleton {
public:
PKCS12InitSingleton() {
SEC_PKCS12EnableCipher(PKCS12_RC4_40, 1);
SEC_PKCS12EnableCipher(PKCS12_RC4_128, 1);
SEC_PKCS12EnableCipher(PKCS12_RC2_CBC_40, 1);
SEC_PKCS12EnableCipher(PKCS12_RC2_CBC_128, 1);
SEC_PKCS12EnableCipher(PKCS12_DES_56, 1);
SEC_PKCS12EnableCipher(PKCS12_DES_EDE3_168, 1);
SEC_PKCS12SetPreferredCipher(PKCS12_DES_EDE3_168, 1);
PORT_SetUCS2_ASCIIConversionFunction(pip_ucs2_ascii_conversion_fn);
}
};
static base::LazyInstance<PKCS12InitSingleton>::Leaky g_pkcs12_init_singleton =
LAZY_INSTANCE_INITIALIZER;
}
void EnsurePKCS12Init() {
g_pkcs12_init_singleton.Get();
}
int nsPKCS12Blob_Import(PK11SlotInfo* slot,
const char* pkcs12_data,
size_t pkcs12_len,
const base::string16& password,
bool is_extractable,
net::CertificateList* imported_certs) {
int rv = nsPKCS12Blob_ImportHelper(pkcs12_data, pkcs12_len, password,
is_extractable, false, slot,
imported_certs);
if (rv == net::ERR_PKCS12_IMPORT_BAD_PASSWORD && password.empty()) {
rv = nsPKCS12Blob_ImportHelper(pkcs12_data, pkcs12_len, password,
is_extractable, true, slot, imported_certs);
}
return rv;
}
int
nsPKCS12Blob_Export(std::string* output,
const net::CertificateList& certs,
const base::string16& password)
{
int return_count = 0;
SECStatus srv = SECSuccess;
SEC_PKCS12ExportContext *ecx = NULL;
SEC_PKCS12SafeInfo *certSafe = NULL, *keySafe = NULL;
SECItem unicodePw;
unicodePw.type = siBuffer;
unicodePw.len = 0;
unicodePw.data = NULL;
int numCertsExported = 0;
unicodeToItem(password.c_str(), &unicodePw);
ecx = SEC_PKCS12CreateExportContext(NULL, NULL, NULL , NULL);
if (!ecx) {
srv = SECFailure;
goto finish;
}
srv = SEC_PKCS12AddPasswordIntegrity(ecx, &unicodePw, SEC_OID_SHA1);
if (srv) goto finish;
for (size_t i=0; i<certs.size(); i++) {
DCHECK(certs[i].get());
CERTCertificate* nssCert = certs[i]->os_cert_handle();
DCHECK(nssCert);
if (nssCert->slot) {
SECKEYPrivateKey *privKey=PK11_FindKeyByDERCert(nssCert->slot,
nssCert,
NULL);
if (privKey) {
PRBool privKeyIsExtractable = PR_FALSE;
SECStatus rv = isExtractable(privKey, &privKeyIsExtractable);
SECKEY_DestroyPrivateKey(privKey);
if (rv == SECSuccess && !privKeyIsExtractable) {
LOG(ERROR) << "Private key is not extractable";
continue;
}
}
}
keySafe = SEC_PKCS12CreateUnencryptedSafe(ecx);
if (!SEC_PKCS12IsEncryptionAllowed() || PK11_IsFIPS()) {
certSafe = keySafe;
} else {
certSafe = SEC_PKCS12CreatePasswordPrivSafe(ecx, &unicodePw,
SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_40_BIT_RC2_CBC);
}
if (!certSafe || !keySafe) {
LOG(ERROR) << "!certSafe || !keySafe " << certSafe << " " << keySafe;
srv = SECFailure;
goto finish;
}
srv = SEC_PKCS12AddCertAndKey(ecx, certSafe, NULL, nssCert,
CERT_GetDefaultCertDB(),
keySafe, NULL, PR_TRUE, &unicodePw,
SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_3KEY_TRIPLE_DES_CBC);
if (srv) goto finish;
++numCertsExported;
}
if (!numCertsExported) goto finish;
srv = SEC_PKCS12Encode(ecx, write_export_data, output);
if (srv) goto finish;
return_count = numCertsExported;
finish:
if (srv)
LOG(ERROR) << "PKCS#12 export failed with error " << PORT_GetError();
if (ecx)
SEC_PKCS12DestroyExportContext(ecx);
SECITEM_ZfreeItem(&unicodePw, PR_FALSE);
return return_count;
}
}