| /* 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: |
| case SEC_OID_PKCS1_RSA_PSS_SIGNATURE: |
| 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; |
| 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; |
| default: |
| rv = SECFailure; |
| break; |
| } |
| |
| lg_nsslowkey_DestroyPublicKey(pubk); |
| return NULL; |
| } |