mirror of
https://github.com/rn10950/RetroZilla.git
synced 2024-11-14 11:40:13 +01:00
98d377b37b
bug920719, bug1026148, bug1028647, bug963150, bug1030486, bug1025729, bug836658, bug1028582, bug1038728, bug1038526, bug1042634, bug1047210, bug1043891, bug1043108, bug1046735, bug1043082, bug1036735, bug1046718, bug1050107, bug1054625, bug1057465, bug1057476, bug1041326, bug1058933, bug1064636, bug1057161, bug1078669, bug1049435, bug1070493, bug1083360, bug1028764, bug1065990, bug1073330, bug1064670, bug1094650
827 lines
22 KiB
C
827 lines
22 KiB
C
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
/*
|
|
* Certificate handling code
|
|
*/
|
|
|
|
#include "seccomon.h"
|
|
#include "secder.h"
|
|
#include "nssilock.h"
|
|
#include "lowkeyi.h"
|
|
#include "secasn1.h"
|
|
#include "secoid.h"
|
|
#include "secerr.h"
|
|
#include "pcert.h"
|
|
|
|
SEC_ASN1_MKSUB(SECOID_AlgorithmIDTemplate)
|
|
|
|
static const SEC_ASN1Template nsslowcert_SubjectPublicKeyInfoTemplate[] = {
|
|
{ SEC_ASN1_SEQUENCE, 0, NULL, sizeof(NSSLOWCERTSubjectPublicKeyInfo) },
|
|
{ SEC_ASN1_INLINE | SEC_ASN1_XTRN,
|
|
offsetof(NSSLOWCERTSubjectPublicKeyInfo,algorithm),
|
|
SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) },
|
|
{ SEC_ASN1_BIT_STRING,
|
|
offsetof(NSSLOWCERTSubjectPublicKeyInfo,subjectPublicKey), },
|
|
{ 0, }
|
|
};
|
|
|
|
static const SEC_ASN1Template nsslowcert_RSAPublicKeyTemplate[] = {
|
|
{ SEC_ASN1_SEQUENCE, 0, NULL, sizeof(NSSLOWKEYPublicKey) },
|
|
{ SEC_ASN1_INTEGER, offsetof(NSSLOWKEYPublicKey,u.rsa.modulus), },
|
|
{ SEC_ASN1_INTEGER, offsetof(NSSLOWKEYPublicKey,u.rsa.publicExponent), },
|
|
{ 0, }
|
|
};
|
|
static const SEC_ASN1Template nsslowcert_DSAPublicKeyTemplate[] = {
|
|
{ SEC_ASN1_INTEGER, offsetof(NSSLOWKEYPublicKey,u.dsa.publicValue), },
|
|
{ 0, }
|
|
};
|
|
static const SEC_ASN1Template nsslowcert_DHPublicKeyTemplate[] = {
|
|
{ SEC_ASN1_INTEGER, offsetof(NSSLOWKEYPublicKey,u.dh.publicValue), },
|
|
{ 0, }
|
|
};
|
|
|
|
/*
|
|
* See bugzilla bug 125359
|
|
* Since NSS (via PKCS#11) wants to handle big integers as unsigned ints,
|
|
* all of the templates above that en/decode into integers must be converted
|
|
* from ASN.1's signed integer type. This is done by marking either the
|
|
* source or destination (encoding or decoding, respectively) type as
|
|
* siUnsignedInteger.
|
|
*/
|
|
|
|
static void
|
|
prepare_low_rsa_pub_key_for_asn1(NSSLOWKEYPublicKey *pubk)
|
|
{
|
|
pubk->u.rsa.modulus.type = siUnsignedInteger;
|
|
pubk->u.rsa.publicExponent.type = siUnsignedInteger;
|
|
}
|
|
|
|
static void
|
|
prepare_low_dsa_pub_key_for_asn1(NSSLOWKEYPublicKey *pubk)
|
|
{
|
|
pubk->u.dsa.publicValue.type = siUnsignedInteger;
|
|
pubk->u.dsa.params.prime.type = siUnsignedInteger;
|
|
pubk->u.dsa.params.subPrime.type = siUnsignedInteger;
|
|
pubk->u.dsa.params.base.type = siUnsignedInteger;
|
|
}
|
|
|
|
static void
|
|
prepare_low_dh_pub_key_for_asn1(NSSLOWKEYPublicKey *pubk)
|
|
{
|
|
pubk->u.dh.prime.type = siUnsignedInteger;
|
|
pubk->u.dh.base.type = siUnsignedInteger;
|
|
pubk->u.dh.publicValue.type = siUnsignedInteger;
|
|
}
|
|
|
|
/*
|
|
* simple cert decoder to avoid the cost of asn1 engine
|
|
*/
|
|
static unsigned char *
|
|
nsslowcert_dataStart(unsigned char *buf, unsigned int length,
|
|
unsigned int *data_length, PRBool includeTag,
|
|
unsigned char* rettag) {
|
|
unsigned char tag;
|
|
unsigned int used_length= 0;
|
|
|
|
/* need at least a tag and a 1 byte length */
|
|
if (length < 2) {
|
|
return NULL;
|
|
}
|
|
|
|
tag = buf[used_length++];
|
|
|
|
if (rettag) {
|
|
*rettag = tag;
|
|
}
|
|
|
|
/* blow out when we come to the end */
|
|
if (tag == 0) {
|
|
return NULL;
|
|
}
|
|
|
|
*data_length = buf[used_length++];
|
|
|
|
if (*data_length&0x80) {
|
|
int len_count = *data_length & 0x7f;
|
|
|
|
if (len_count+used_length > length) {
|
|
return NULL;
|
|
}
|
|
|
|
*data_length = 0;
|
|
|
|
while (len_count-- > 0) {
|
|
*data_length = (*data_length << 8) | buf[used_length++];
|
|
}
|
|
}
|
|
|
|
if (*data_length > (length-used_length) ) {
|
|
*data_length = length-used_length;
|
|
return NULL;
|
|
}
|
|
if (includeTag) *data_length += used_length;
|
|
|
|
return (buf + (includeTag ? 0 : used_length));
|
|
}
|
|
|
|
static void SetTimeType(SECItem* item, unsigned char tagtype)
|
|
{
|
|
switch (tagtype) {
|
|
case SEC_ASN1_UTC_TIME:
|
|
item->type = siUTCTime;
|
|
break;
|
|
|
|
case SEC_ASN1_GENERALIZED_TIME:
|
|
item->type = siGeneralizedTime;
|
|
break;
|
|
|
|
default:
|
|
PORT_Assert(0);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static int
|
|
nsslowcert_GetValidityFields(unsigned char *buf,int buf_length,
|
|
SECItem *notBefore, SECItem *notAfter)
|
|
{
|
|
unsigned char tagtype;
|
|
notBefore->data = nsslowcert_dataStart(buf,buf_length,
|
|
¬Before->len,PR_FALSE, &tagtype);
|
|
if (notBefore->data == NULL) return SECFailure;
|
|
SetTimeType(notBefore, tagtype);
|
|
buf_length -= (notBefore->data-buf) + notBefore->len;
|
|
buf = notBefore->data + notBefore->len;
|
|
notAfter->data = nsslowcert_dataStart(buf,buf_length,
|
|
¬After->len,PR_FALSE, &tagtype);
|
|
if (notAfter->data == NULL) return SECFailure;
|
|
SetTimeType(notAfter, tagtype);
|
|
return SECSuccess;
|
|
}
|
|
|
|
static int
|
|
nsslowcert_GetCertFields(unsigned char *cert,int cert_length,
|
|
SECItem *issuer, SECItem *serial, SECItem *derSN, SECItem *subject,
|
|
SECItem *valid, SECItem *subjkey, SECItem *extensions)
|
|
{
|
|
unsigned char *buf;
|
|
unsigned int buf_length;
|
|
unsigned char *dummy;
|
|
unsigned int dummylen;
|
|
|
|
/* get past the signature wrap */
|
|
buf = nsslowcert_dataStart(cert,cert_length,&buf_length,PR_FALSE, NULL);
|
|
if (buf == NULL) return SECFailure;
|
|
/* get into the raw cert data */
|
|
buf = nsslowcert_dataStart(buf,buf_length,&buf_length,PR_FALSE, NULL);
|
|
if (buf == NULL) return SECFailure;
|
|
/* skip past any optional version number */
|
|
if ((buf[0] & 0xa0) == 0xa0) {
|
|
dummy = nsslowcert_dataStart(buf,buf_length,&dummylen,PR_FALSE, NULL);
|
|
if (dummy == NULL) return SECFailure;
|
|
buf_length -= (dummy-buf) + dummylen;
|
|
buf = dummy + dummylen;
|
|
}
|
|
/* serial number */
|
|
if (derSN) {
|
|
derSN->data=nsslowcert_dataStart(buf,buf_length,&derSN->len,PR_TRUE, NULL);
|
|
/* derSN->data doesn't need to be checked because if it fails so will
|
|
* serial->data below. The only difference between the two calls is
|
|
* whether or not the tags are included in the returned buffer */
|
|
}
|
|
serial->data = nsslowcert_dataStart(buf,buf_length,&serial->len,PR_FALSE, NULL);
|
|
if (serial->data == NULL) return SECFailure;
|
|
buf_length -= (serial->data-buf) + serial->len;
|
|
buf = serial->data + serial->len;
|
|
/* skip the OID */
|
|
dummy = nsslowcert_dataStart(buf,buf_length,&dummylen,PR_FALSE, NULL);
|
|
if (dummy == NULL) return SECFailure;
|
|
buf_length -= (dummy-buf) + dummylen;
|
|
buf = dummy + dummylen;
|
|
/* issuer */
|
|
issuer->data = nsslowcert_dataStart(buf,buf_length,&issuer->len,PR_TRUE, NULL);
|
|
if (issuer->data == NULL) return SECFailure;
|
|
buf_length -= (issuer->data-buf) + issuer->len;
|
|
buf = issuer->data + issuer->len;
|
|
|
|
/* only wanted issuer/SN */
|
|
if (valid == NULL) {
|
|
return SECSuccess;
|
|
}
|
|
/* validity */
|
|
valid->data = nsslowcert_dataStart(buf,buf_length,&valid->len,PR_FALSE, NULL);
|
|
if (valid->data == NULL) return SECFailure;
|
|
buf_length -= (valid->data-buf) + valid->len;
|
|
buf = valid->data + valid->len;
|
|
/*subject */
|
|
subject->data=nsslowcert_dataStart(buf,buf_length,&subject->len,PR_TRUE, NULL);
|
|
if (subject->data == NULL) return SECFailure;
|
|
buf_length -= (subject->data-buf) + subject->len;
|
|
buf = subject->data + subject->len;
|
|
/* subject key info */
|
|
subjkey->data=nsslowcert_dataStart(buf,buf_length,&subjkey->len,PR_TRUE, NULL);
|
|
if (subjkey->data == NULL) return SECFailure;
|
|
buf_length -= (subjkey->data-buf) + subjkey->len;
|
|
buf = subjkey->data + subjkey->len;
|
|
|
|
extensions->data = NULL;
|
|
extensions->len = 0;
|
|
while (buf_length > 0) {
|
|
/* EXTENSIONS */
|
|
if (buf[0] == 0xa3) {
|
|
extensions->data = nsslowcert_dataStart(buf,buf_length,
|
|
&extensions->len, PR_FALSE, NULL);
|
|
/* if the DER is bad, we should fail. Previously we accepted
|
|
* bad DER here and treated the extension as missin */
|
|
if (extensions->data == NULL ||
|
|
(extensions->data - buf) + extensions->len != buf_length)
|
|
return SECFailure;
|
|
buf = extensions->data;
|
|
buf_length = extensions->len;
|
|
/* now parse the SEQUENCE holding the extensions. */
|
|
dummy = nsslowcert_dataStart(buf,buf_length,&dummylen,PR_FALSE,NULL);
|
|
if (dummy == NULL ||
|
|
(dummy - buf) + dummylen != buf_length)
|
|
return SECFailure;
|
|
buf_length -= (dummy - buf);
|
|
buf = dummy;
|
|
/* Now parse the extensions inside this sequence */
|
|
}
|
|
dummy = nsslowcert_dataStart(buf,buf_length,&dummylen,PR_FALSE,NULL);
|
|
if (dummy == NULL) return SECFailure;
|
|
buf_length -= (dummy - buf) + dummylen;
|
|
buf = dummy + dummylen;
|
|
}
|
|
return SECSuccess;
|
|
}
|
|
|
|
static SECStatus
|
|
nsslowcert_GetCertTimes(NSSLOWCERTCertificate *c, PRTime *notBefore, PRTime *notAfter)
|
|
{
|
|
int rv;
|
|
NSSLOWCERTValidity validity;
|
|
|
|
rv = nsslowcert_GetValidityFields(c->validity.data,c->validity.len,
|
|
&validity.notBefore,&validity.notAfter);
|
|
if (rv != SECSuccess) {
|
|
return rv;
|
|
}
|
|
|
|
/* convert DER not-before time */
|
|
rv = DER_DecodeTimeChoice(notBefore, &validity.notBefore);
|
|
if (rv) {
|
|
return(SECFailure);
|
|
}
|
|
|
|
/* convert DER not-after time */
|
|
rv = DER_DecodeTimeChoice(notAfter, &validity.notAfter);
|
|
if (rv) {
|
|
return(SECFailure);
|
|
}
|
|
|
|
return(SECSuccess);
|
|
}
|
|
|
|
/*
|
|
* is certa newer than certb? If one is expired, pick the other one.
|
|
*/
|
|
PRBool
|
|
nsslowcert_IsNewer(NSSLOWCERTCertificate *certa, NSSLOWCERTCertificate *certb)
|
|
{
|
|
PRTime notBeforeA, notAfterA, notBeforeB, notAfterB, now;
|
|
SECStatus rv;
|
|
PRBool newerbefore, newerafter;
|
|
|
|
rv = nsslowcert_GetCertTimes(certa, ¬BeforeA, ¬AfterA);
|
|
if ( rv != SECSuccess ) {
|
|
return(PR_FALSE);
|
|
}
|
|
|
|
rv = nsslowcert_GetCertTimes(certb, ¬BeforeB, ¬AfterB);
|
|
if ( rv != SECSuccess ) {
|
|
return(PR_TRUE);
|
|
}
|
|
|
|
newerbefore = PR_FALSE;
|
|
if ( LL_CMP(notBeforeA, >, notBeforeB) ) {
|
|
newerbefore = PR_TRUE;
|
|
}
|
|
|
|
newerafter = PR_FALSE;
|
|
if ( LL_CMP(notAfterA, >, notAfterB) ) {
|
|
newerafter = PR_TRUE;
|
|
}
|
|
|
|
if ( newerbefore && newerafter ) {
|
|
return(PR_TRUE);
|
|
}
|
|
|
|
if ( ( !newerbefore ) && ( !newerafter ) ) {
|
|
return(PR_FALSE);
|
|
}
|
|
|
|
/* get current time */
|
|
now = PR_Now();
|
|
|
|
if ( newerbefore ) {
|
|
/* cert A was issued after cert B, but expires sooner */
|
|
/* if A is expired, then pick B */
|
|
if ( LL_CMP(notAfterA, <, now ) ) {
|
|
return(PR_FALSE);
|
|
}
|
|
return(PR_TRUE);
|
|
} else {
|
|
/* cert B was issued after cert A, but expires sooner */
|
|
/* if B is expired, then pick A */
|
|
if ( LL_CMP(notAfterB, <, now ) ) {
|
|
return(PR_TRUE);
|
|
}
|
|
return(PR_FALSE);
|
|
}
|
|
}
|
|
|
|
#define SOFT_DEFAULT_CHUNKSIZE 2048
|
|
|
|
static SECStatus
|
|
nsslowcert_KeyFromIssuerAndSN(PLArenaPool *arena,
|
|
SECItem *issuer, SECItem *sn, SECItem *key)
|
|
{
|
|
unsigned int len = sn->len + issuer->len;
|
|
|
|
if (!arena) {
|
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
|
goto loser;
|
|
}
|
|
if (len > NSS_MAX_LEGACY_DB_KEY_SIZE) {
|
|
PORT_SetError(SEC_ERROR_INPUT_LEN);
|
|
goto loser;
|
|
}
|
|
key->data = (unsigned char*)PORT_ArenaAlloc(arena, len);
|
|
if ( !key->data ) {
|
|
goto loser;
|
|
}
|
|
|
|
key->len = len;
|
|
/* copy the serialNumber */
|
|
PORT_Memcpy(key->data, sn->data, sn->len);
|
|
|
|
/* copy the issuer */
|
|
PORT_Memcpy(&key->data[sn->len], issuer->data, issuer->len);
|
|
|
|
return(SECSuccess);
|
|
|
|
loser:
|
|
return(SECFailure);
|
|
}
|
|
|
|
static SECStatus
|
|
nsslowcert_KeyFromIssuerAndSNStatic(unsigned char *space,
|
|
int spaceLen, SECItem *issuer, SECItem *sn, SECItem *key)
|
|
{
|
|
unsigned int len = sn->len + issuer->len;
|
|
|
|
key->data = pkcs11_allocStaticData(len, space, spaceLen);
|
|
if ( !key->data ) {
|
|
goto loser;
|
|
}
|
|
|
|
key->len = len;
|
|
/* copy the serialNumber */
|
|
PORT_Memcpy(key->data, sn->data, sn->len);
|
|
|
|
/* copy the issuer */
|
|
PORT_Memcpy(&key->data[sn->len], issuer->data, issuer->len);
|
|
|
|
return(SECSuccess);
|
|
|
|
loser:
|
|
return(SECFailure);
|
|
}
|
|
|
|
|
|
static char *
|
|
nsslowcert_EmailName(SECItem *derDN, char *space, unsigned int len)
|
|
{
|
|
unsigned char *buf;
|
|
unsigned int buf_length;
|
|
|
|
/* unwrap outer sequence */
|
|
buf=nsslowcert_dataStart(derDN->data,derDN->len,&buf_length,PR_FALSE,NULL);
|
|
if (buf == NULL) return NULL;
|
|
|
|
/* Walk each RDN */
|
|
while (buf_length > 0) {
|
|
unsigned char *rdn;
|
|
unsigned int rdn_length;
|
|
|
|
/* grab next rdn */
|
|
rdn=nsslowcert_dataStart(buf, buf_length, &rdn_length, PR_FALSE, NULL);
|
|
if (rdn == NULL) { return NULL; }
|
|
buf_length -= (rdn - buf) + rdn_length;
|
|
buf = rdn+rdn_length;
|
|
|
|
while (rdn_length > 0) {
|
|
unsigned char *ava;
|
|
unsigned int ava_length;
|
|
unsigned char *oid;
|
|
unsigned int oid_length;
|
|
unsigned char *name;
|
|
unsigned int name_length;
|
|
SECItem oidItem;
|
|
SECOidTag type;
|
|
|
|
/* unwrap the ava */
|
|
ava=nsslowcert_dataStart(rdn, rdn_length, &ava_length, PR_FALSE,
|
|
NULL);
|
|
if (ava == NULL) return NULL;
|
|
rdn_length -= (ava-rdn)+ava_length;
|
|
rdn = ava + ava_length;
|
|
|
|
oid=nsslowcert_dataStart(ava, ava_length, &oid_length, PR_FALSE,
|
|
NULL);
|
|
if (oid == NULL) { return NULL; }
|
|
ava_length -= (oid-ava)+oid_length;
|
|
ava = oid+oid_length;
|
|
|
|
name=nsslowcert_dataStart(ava, ava_length, &name_length, PR_FALSE,
|
|
NULL);
|
|
if (name == NULL) { return NULL; }
|
|
ava_length -= (name-ava)+name_length;
|
|
ava = name+name_length;
|
|
|
|
oidItem.data = oid;
|
|
oidItem.len = oid_length;
|
|
type = SECOID_FindOIDTag(&oidItem);
|
|
if ((type == SEC_OID_PKCS9_EMAIL_ADDRESS) ||
|
|
(type == SEC_OID_RFC1274_MAIL)) {
|
|
/* Email is supposed to be IA5String, so no
|
|
* translation necessary */
|
|
char *emailAddr;
|
|
emailAddr = (char *)pkcs11_copyStaticData(name,name_length+1,
|
|
(unsigned char *)space,len);
|
|
if (emailAddr) {
|
|
emailAddr[name_length] = 0;
|
|
}
|
|
return emailAddr;
|
|
}
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static char *
|
|
nsslowcert_EmailAltName(NSSLOWCERTCertificate *cert, char *space,
|
|
unsigned int len)
|
|
{
|
|
unsigned char *exts;
|
|
unsigned int exts_length;
|
|
|
|
/* unwrap the sequence */
|
|
exts = nsslowcert_dataStart(cert->extensions.data, cert->extensions.len,
|
|
&exts_length, PR_FALSE, NULL);
|
|
/* loop through extension */
|
|
while (exts && exts_length > 0) {
|
|
unsigned char * ext;
|
|
unsigned int ext_length;
|
|
unsigned char *oid;
|
|
unsigned int oid_length;
|
|
unsigned char *nameList;
|
|
unsigned int nameList_length;
|
|
SECItem oidItem;
|
|
SECOidTag type;
|
|
|
|
ext = nsslowcert_dataStart(exts, exts_length, &ext_length,
|
|
PR_FALSE, NULL);
|
|
if (ext == NULL) { break; }
|
|
exts_length -= (ext - exts) + ext_length;
|
|
exts = ext+ext_length;
|
|
|
|
oid=nsslowcert_dataStart(ext, ext_length, &oid_length, PR_FALSE, NULL);
|
|
if (oid == NULL) { break; }
|
|
ext_length -= (oid - ext) + oid_length;
|
|
ext = oid+oid_length;
|
|
oidItem.data = oid;
|
|
oidItem.len = oid_length;
|
|
type = SECOID_FindOIDTag(&oidItem);
|
|
|
|
/* get Alt Extension */
|
|
if (type != SEC_OID_X509_SUBJECT_ALT_NAME) {
|
|
continue;
|
|
}
|
|
|
|
/* skip passed the critical flag */
|
|
if (ext[0] == 0x01) { /* BOOLEAN */
|
|
unsigned char *dummy;
|
|
unsigned int dummy_length;
|
|
dummy = nsslowcert_dataStart(ext, ext_length, &dummy_length,
|
|
PR_FALSE, NULL);
|
|
if (dummy == NULL) { break; }
|
|
ext_length -= (dummy - ext) + dummy_length;
|
|
ext = dummy+dummy_length;
|
|
}
|
|
|
|
|
|
/* unwrap the name list */
|
|
nameList = nsslowcert_dataStart(ext, ext_length, &nameList_length,
|
|
PR_FALSE, NULL);
|
|
if (nameList == NULL) { break; }
|
|
ext_length -= (nameList - ext) + nameList_length;
|
|
ext = nameList+nameList_length;
|
|
nameList = nsslowcert_dataStart(nameList, nameList_length,
|
|
&nameList_length, PR_FALSE, NULL);
|
|
/* loop through the name list */
|
|
while (nameList && nameList_length > 0) {
|
|
unsigned char *thisName;
|
|
unsigned int thisName_length;
|
|
|
|
thisName = nsslowcert_dataStart(nameList, nameList_length,
|
|
&thisName_length, PR_FALSE, NULL);
|
|
if (thisName == NULL) { break; }
|
|
if (nameList[0] == 0xa2) { /* DNS Name */
|
|
SECItem dn;
|
|
char *emailAddr;
|
|
|
|
dn.data = thisName;
|
|
dn.len = thisName_length;
|
|
emailAddr = nsslowcert_EmailName(&dn, space, len);
|
|
if (emailAddr) {
|
|
return emailAddr;
|
|
}
|
|
}
|
|
if (nameList[0] == 0x81) { /* RFC 822name */
|
|
char *emailAddr;
|
|
emailAddr = (char *)pkcs11_copyStaticData(thisName,
|
|
thisName_length+1, (unsigned char *)space,len);
|
|
if (emailAddr) {
|
|
emailAddr[thisName_length] = 0;
|
|
}
|
|
return emailAddr;
|
|
}
|
|
nameList_length -= (thisName-nameList) + thisName_length;
|
|
nameList = thisName + thisName_length;
|
|
}
|
|
break;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static char *
|
|
nsslowcert_GetCertificateEmailAddress(NSSLOWCERTCertificate *cert)
|
|
{
|
|
char *emailAddr = NULL;
|
|
char *str;
|
|
|
|
emailAddr = nsslowcert_EmailName(&cert->derSubject,cert->emailAddrSpace,
|
|
sizeof(cert->emailAddrSpace));
|
|
/* couldn't find the email address in the DN, check the subject Alt name */
|
|
if (!emailAddr && cert->extensions.data) {
|
|
emailAddr = nsslowcert_EmailAltName(cert, cert->emailAddrSpace,
|
|
sizeof(cert->emailAddrSpace));
|
|
}
|
|
|
|
|
|
/* make it lower case */
|
|
str = emailAddr;
|
|
while ( str && *str ) {
|
|
*str = tolower( *str );
|
|
str++;
|
|
}
|
|
return emailAddr;
|
|
|
|
}
|
|
|
|
/*
|
|
* take a DER certificate and decode it into a certificate structure
|
|
*/
|
|
NSSLOWCERTCertificate *
|
|
nsslowcert_DecodeDERCertificate(SECItem *derSignedCert, char *nickname)
|
|
{
|
|
NSSLOWCERTCertificate *cert;
|
|
int rv;
|
|
|
|
/* allocate the certificate structure */
|
|
cert = nsslowcert_CreateCert();
|
|
|
|
if ( !cert ) {
|
|
goto loser;
|
|
}
|
|
|
|
/* point to passed in DER data */
|
|
cert->derCert = *derSignedCert;
|
|
cert->nickname = NULL;
|
|
cert->certKey.data = NULL;
|
|
cert->referenceCount = 1;
|
|
|
|
/* decode the certificate info */
|
|
rv = nsslowcert_GetCertFields(cert->derCert.data, cert->derCert.len,
|
|
&cert->derIssuer, &cert->serialNumber, &cert->derSN, &cert->derSubject,
|
|
&cert->validity, &cert->derSubjKeyInfo, &cert->extensions);
|
|
|
|
if (rv != SECSuccess) {
|
|
goto loser;
|
|
}
|
|
|
|
/* cert->subjectKeyID; x509v3 subject key identifier */
|
|
cert->subjectKeyID.data = NULL;
|
|
cert->subjectKeyID.len = 0;
|
|
cert->dbEntry = NULL;
|
|
cert ->trust = NULL;
|
|
cert ->dbhandle = NULL;
|
|
|
|
/* generate and save the database key for the cert */
|
|
rv = nsslowcert_KeyFromIssuerAndSNStatic(cert->certKeySpace,
|
|
sizeof(cert->certKeySpace), &cert->derIssuer,
|
|
&cert->serialNumber, &cert->certKey);
|
|
if ( rv ) {
|
|
goto loser;
|
|
}
|
|
|
|
/* set the nickname */
|
|
if ( nickname == NULL ) {
|
|
cert->nickname = NULL;
|
|
} else {
|
|
/* copy and install the nickname */
|
|
cert->nickname = pkcs11_copyNickname(nickname,cert->nicknameSpace,
|
|
sizeof(cert->nicknameSpace));
|
|
}
|
|
|
|
#ifdef FIXME
|
|
/* initialize the subjectKeyID */
|
|
rv = cert_GetKeyID(cert);
|
|
if ( rv != SECSuccess ) {
|
|
goto loser;
|
|
}
|
|
#endif
|
|
|
|
/* set the email address */
|
|
cert->emailAddr = nsslowcert_GetCertificateEmailAddress(cert);
|
|
|
|
|
|
cert->referenceCount = 1;
|
|
|
|
return(cert);
|
|
|
|
loser:
|
|
if (cert) {
|
|
nsslowcert_DestroyCertificate(cert);
|
|
}
|
|
|
|
return(0);
|
|
}
|
|
|
|
char *
|
|
nsslowcert_FixupEmailAddr(char *emailAddr)
|
|
{
|
|
char *retaddr;
|
|
char *str;
|
|
|
|
if ( emailAddr == NULL ) {
|
|
return(NULL);
|
|
}
|
|
|
|
/* copy the string */
|
|
str = retaddr = PORT_Strdup(emailAddr);
|
|
if ( str == NULL ) {
|
|
return(NULL);
|
|
}
|
|
|
|
/* make it lower case */
|
|
while ( *str ) {
|
|
*str = tolower( *str );
|
|
str++;
|
|
}
|
|
|
|
return(retaddr);
|
|
}
|
|
|
|
|
|
/*
|
|
* Generate a database key, based on serial number and issuer, from a
|
|
* DER certificate.
|
|
*/
|
|
SECStatus
|
|
nsslowcert_KeyFromDERCert(PLArenaPool *arena, SECItem *derCert, SECItem *key)
|
|
{
|
|
int rv;
|
|
NSSLOWCERTCertKey certkey;
|
|
|
|
PORT_Memset(&certkey, 0, sizeof(NSSLOWCERTCertKey));
|
|
|
|
rv = nsslowcert_GetCertFields(derCert->data, derCert->len,
|
|
&certkey.derIssuer, &certkey.serialNumber, NULL, NULL,
|
|
NULL, NULL, NULL);
|
|
|
|
if ( rv ) {
|
|
goto loser;
|
|
}
|
|
|
|
return(nsslowcert_KeyFromIssuerAndSN(arena, &certkey.derIssuer,
|
|
&certkey.serialNumber, key));
|
|
loser:
|
|
return(SECFailure);
|
|
}
|
|
|
|
NSSLOWKEYPublicKey *
|
|
nsslowcert_ExtractPublicKey(NSSLOWCERTCertificate *cert)
|
|
{
|
|
NSSLOWCERTSubjectPublicKeyInfo spki;
|
|
NSSLOWKEYPublicKey *pubk;
|
|
SECItem os;
|
|
SECStatus rv;
|
|
PLArenaPool *arena;
|
|
SECOidTag tag;
|
|
SECItem newDerSubjKeyInfo;
|
|
|
|
arena = PORT_NewArena (DER_DEFAULT_CHUNKSIZE);
|
|
if (arena == NULL)
|
|
return NULL;
|
|
|
|
pubk = (NSSLOWKEYPublicKey *)
|
|
PORT_ArenaZAlloc(arena, sizeof(NSSLOWKEYPublicKey));
|
|
if (pubk == NULL) {
|
|
PORT_FreeArena (arena, PR_FALSE);
|
|
return NULL;
|
|
}
|
|
|
|
pubk->arena = arena;
|
|
PORT_Memset(&spki,0,sizeof(spki));
|
|
|
|
/* copy the DER into the arena, since Quick DER returns data that points
|
|
into the DER input, which may get freed by the caller */
|
|
rv = SECITEM_CopyItem(arena, &newDerSubjKeyInfo, &cert->derSubjKeyInfo);
|
|
if ( rv != SECSuccess ) {
|
|
PORT_FreeArena (arena, PR_FALSE);
|
|
return NULL;
|
|
}
|
|
|
|
/* we haven't bothered decoding the spki struct yet, do it now */
|
|
rv = SEC_QuickDERDecodeItem(arena, &spki,
|
|
nsslowcert_SubjectPublicKeyInfoTemplate, &newDerSubjKeyInfo);
|
|
if (rv != SECSuccess) {
|
|
PORT_FreeArena (arena, PR_FALSE);
|
|
return NULL;
|
|
}
|
|
|
|
/* Convert bit string length from bits to bytes */
|
|
os = spki.subjectPublicKey;
|
|
DER_ConvertBitString (&os);
|
|
|
|
tag = SECOID_GetAlgorithmTag(&spki.algorithm);
|
|
switch ( tag ) {
|
|
case SEC_OID_X500_RSA_ENCRYPTION:
|
|
case SEC_OID_PKCS1_RSA_ENCRYPTION:
|
|
pubk->keyType = NSSLOWKEYRSAKey;
|
|
prepare_low_rsa_pub_key_for_asn1(pubk);
|
|
rv = SEC_QuickDERDecodeItem(arena, pubk,
|
|
nsslowcert_RSAPublicKeyTemplate, &os);
|
|
if (rv == SECSuccess)
|
|
return pubk;
|
|
break;
|
|
case SEC_OID_ANSIX9_DSA_SIGNATURE:
|
|
pubk->keyType = NSSLOWKEYDSAKey;
|
|
prepare_low_dsa_pub_key_for_asn1(pubk);
|
|
rv = SEC_QuickDERDecodeItem(arena, pubk,
|
|
nsslowcert_DSAPublicKeyTemplate, &os);
|
|
if (rv == SECSuccess) return pubk;
|
|
break;
|
|
case SEC_OID_X942_DIFFIE_HELMAN_KEY:
|
|
pubk->keyType = NSSLOWKEYDHKey;
|
|
prepare_low_dh_pub_key_for_asn1(pubk);
|
|
rv = SEC_QuickDERDecodeItem(arena, pubk,
|
|
nsslowcert_DHPublicKeyTemplate, &os);
|
|
if (rv == SECSuccess) return pubk;
|
|
break;
|
|
#ifndef NSS_DISABLE_ECC
|
|
case SEC_OID_ANSIX962_EC_PUBLIC_KEY:
|
|
pubk->keyType = NSSLOWKEYECKey;
|
|
/* Since PKCS#11 directly takes the DER encoding of EC params
|
|
* and public value, we don't need any decoding here.
|
|
*/
|
|
rv = SECITEM_CopyItem(arena, &pubk->u.ec.ecParams.DEREncoding,
|
|
&spki.algorithm.parameters);
|
|
if ( rv != SECSuccess )
|
|
break;
|
|
|
|
/* Fill out the rest of the ecParams structure
|
|
* based on the encoded params
|
|
*/
|
|
if (LGEC_FillParams(arena, &pubk->u.ec.ecParams.DEREncoding,
|
|
&pubk->u.ec.ecParams) != SECSuccess)
|
|
break;
|
|
|
|
rv = SECITEM_CopyItem(arena, &pubk->u.ec.publicValue, &os);
|
|
if (rv == SECSuccess) return pubk;
|
|
break;
|
|
#endif /* NSS_DISABLE_ECC */
|
|
default:
|
|
rv = SECFailure;
|
|
break;
|
|
}
|
|
|
|
lg_nsslowkey_DestroyPublicKey (pubk);
|
|
return NULL;
|
|
}
|
|
|