static SECStatus OurVerifySignedData(CERTSignedData *sd, CERTCertificate *cert) { SECItem sig; SECKEYPublicKey *pubKey = 0; SECStatus rv; /* check the certificate's validity */ rv = CERT_CertTimesValid(cert); if ( rv ) { return(SECFailure); } /* get cert's public key */ pubKey = CERT_ExtractPublicKey(cert); if ( !pubKey ) { return(SECFailure); } /* check the signature */ sig = sd->signature; DER_ConvertBitString(&sig); rv = OurVerifyData(sd->data.data, sd->data.len, pubKey, &sig, &sd->signatureAlgorithm); SECKEY_DestroyPublicKey(pubKey); if ( rv ) { return(SECFailure); } return(SECSuccess); }
SECStatus VerifySignedData(const CERTSignedData* sd, const CERTCertificate* cert, void* pkcs11PinArg) { if (!sd || !sd->data.data || !sd->signatureAlgorithm.algorithm.data || !sd->signature.data || !cert) { PR_NOT_REACHED("invalid args to VerifySignedData"); PR_SetError(SEC_ERROR_INVALID_ARGS, 0); return SECFailure; } // See bug 921585. if (sd->data.len > static_cast<unsigned int>(std::numeric_limits<int>::max())) { PR_SetError(SEC_ERROR_INVALID_ARGS, 0); return SECFailure; } // convert sig->len from bit counts to byte count. SECItem sig = sd->signature; DER_ConvertBitString(&sig); // Use SECKEY_ExtractPublicKey instead of CERT_ExtractPublicKey because // CERT_ExtractPublicKey would try to do (EC)DSA parameter inheritance, using // the classic (wrong) NSS path building logic. We intentionally do not // support parameter inheritance. ScopedSECKEYPublicKey pubKey(SECKEY_ExtractPublicKey(&cert->subjectPublicKeyInfo)); if (!pubKey) { return SECFailure; } SECOidTag hashAlg; if (VFY_VerifyDataWithAlgorithmID(sd->data.data, static_cast<int>(sd->data.len), pubKey.get(), &sig, &sd->signatureAlgorithm, &hashAlg, pkcs11PinArg) != SECSuccess) { return SECFailure; } // TODO: Ideally, we would do this check before we call // VFY_VerifyDataWithAlgorithmID. But, VFY_VerifyDataWithAlgorithmID gives us // the hash algorithm so it is more convenient to do things in this order. uint32_t policy; if (NSS_GetAlgorithmPolicy(hashAlg, &policy) != SECSuccess) { return SECFailure; } // XXX: I'm not sure why there isn't NSS_USE_ALG_IN_SSL_SIGNATURE, but there // isn't. Since we don't know the context in which we're being called, be as // strict as we can be given the NSS API that is available. static const uint32_t requiredPolicy = NSS_USE_ALG_IN_CERT_SIGNATURE | NSS_USE_ALG_IN_CMS_SIGNATURE; if ((policy & requiredPolicy) != requiredPolicy) { PR_SetError(SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED, 0); return SECFailure; } return SECSuccess; }
int sv_PrintSignedData(FILE *out, SECItem *der, char *m, SECU_PPFunc inner) { PLArenaPool *arena = NULL; CERTSignedData *sd; int rv; /* Strip off the signature */ sd = (CERTSignedData *)PORT_ZAlloc(sizeof(CERTSignedData)); if (!sd) return PORT_GetError(); arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); if (!arena) return SEC_ERROR_NO_MEMORY; rv = SEC_ASN1DecodeItem(arena, sd, SEC_ASN1_GET(CERT_SignedDataTemplate), der); if (rv) { PORT_FreeArena(arena, PR_FALSE); return rv; } /* fprintf(out, "%s:\n", m); */ PORT_Strcat(m, "data."); rv = (*inner)(out, &sd->data, m, 0); if (rv) { PORT_FreeArena(arena, PR_FALSE); return rv; } m[PORT_Strlen(m) - 5] = 0; fprintf(out, "%s", m); sv_PrintAlgorithmID(out, &sd->signatureAlgorithm, "signatureAlgorithm="); DER_ConvertBitString(&sd->signature); fprintf(out, "%s", m); sv_PrintAsHex(out, &sd->signature, "signature="); PORT_FreeArena(arena, PR_FALSE); return 0; }
int sv_PrintSubjectPublicKeyInfo(FILE *out, PLArenaPool *arena, CERTSubjectPublicKeyInfo *i, char *msg) { SECKEYPublicKey *pk; int rv; char mm[200]; sprintf(mm, "%s.publicKeyAlgorithm=", msg); sv_PrintAlgorithmID(out, &i->algorithm, mm); pk = (SECKEYPublicKey *)PORT_ZAlloc(sizeof(SECKEYPublicKey)); if (!pk) return PORT_GetError(); DER_ConvertBitString(&i->subjectPublicKey); switch (SECOID_FindOIDTag(&i->algorithm.algorithm)) { case SEC_OID_PKCS1_RSA_ENCRYPTION: rv = SEC_ASN1DecodeItem(arena, pk, SEC_ASN1_GET(SECKEY_RSAPublicKeyTemplate), &i->subjectPublicKey); if (rv) return rv; sprintf(mm, "%s.rsaPublicKey.", msg); sv_PrintRSAPublicKey(out, pk, mm); break; case SEC_OID_ANSIX9_DSA_SIGNATURE: rv = SEC_ASN1DecodeItem(arena, pk, SEC_ASN1_GET(SECKEY_DSAPublicKeyTemplate), &i->subjectPublicKey); if (rv) return rv; sprintf(mm, "%s.dsaPublicKey.", msg); sv_PrintDSAPublicKey(out, pk, mm); break; default: fprintf(out, "%s=bad SPKI algorithm type\n", msg); return 0; } return 0; }
static void print_basic_response (FILE *out_file, ocspBasicOCSPResponse *basic, int level) { SECItem rawsig; SECU_Indent (out_file, level); fprintf (out_file, "Basic OCSP Response:\n"); level++; print_response_data (out_file, basic->tbsResponseData, level); SECU_PrintAlgorithmID (out_file, &(basic->responseSignature.signatureAlgorithm), "Signature Algorithm", level); rawsig = basic->responseSignature.signature; DER_ConvertBitString (&rawsig); SECU_PrintAsHex (out_file, &rawsig, "Signature", level); print_raw_certificates (out_file, basic->responseSignature.derCerts, level); }
// From http://tools.ietf.org/html/rfc6960#section-4.1.1: // "The hash shall be calculated over the value (excluding tag and length) of // the subject public key field in the issuer's certificate." static der::Result MatchIssuerKey(const SECItem& issuerKeyHash, const CERTCertificate& issuer, /*out*/ bool& match) { if (issuerKeyHash.len != SHA1_LENGTH) { return der::Fail(SEC_ERROR_OCSP_MALFORMED_RESPONSE); } // TODO(bug 966856): support SHA-2 hashes // Copy just the length and data pointer (nothing needs to be freed) of the // subject public key so we can convert the length from bits to bytes, which // is what the digest function expects. SECItem spk = issuer.subjectPublicKeyInfo.subjectPublicKey; DER_ConvertBitString(&spk); static uint8_t hashBuf[SHA1_LENGTH]; if (PK11_HashBuf(SEC_OID_SHA1, hashBuf, spk.data, spk.len) != SECSuccess) { return der::Failure; } match = !memcmp(hashBuf, issuerKeyHash.data, issuerKeyHash.len); return der::Success; }
/* ** secu_PrintPKCS7SignedEnveloped ** Pretty print a PKCS7 singed and enveloped data type (up to version 1). */ int secu_PrintPKCS7SignedAndEnveloped(FILE *out, SEC_PKCS7SignedAndEnvelopedData *src, char *m, int level) { SECAlgorithmID *digAlg; /* pointer for digest algorithms */ SECItem *aCert; /* pointer for certificate */ CERTSignedCrl *aCrl; /* pointer for certificate revocation list */ SEC_PKCS7SignerInfo *sigInfo; /* pointer for signer information */ SEC_PKCS7RecipientInfo *recInfo; /* pointer for recipient information */ int rv, iv; char om[100]; secu_Indent(out, level); fprintf(out, "%s:\n", m); sv_PrintInteger(out, &(src->version), "Version", level + 1); /* Parse and list recipients (this is not optional) */ if (src->recipientInfos != NULL) { secu_Indent(out, level + 1); fprintf(out, "Recipient Information List:\n"); iv = 0; while ((recInfo = src->recipientInfos[iv++]) != NULL) { sprintf(om, "Recipient Information (%x)", iv); secu_PrintRecipientInfo(out, recInfo, om, level + 2); } } /* Parse and list digest algorithms (if any) */ if (src->digestAlgorithms != NULL) { secu_Indent(out, level + 1); fprintf(out, "Digest Algorithm List:\n"); iv = 0; while ((digAlg = src->digestAlgorithms[iv++]) != NULL) { sprintf(om, "Digest Algorithm (%x)", iv); sv_PrintAlgorithmID(out, digAlg, om); } } secu_PrintPKCS7EncContent(out, &src->encContentInfo, "Encrypted Content Information", level + 1); /* Parse and list certificates (if any) */ if (src->rawCerts != NULL) { secu_Indent(out, level + 1); fprintf(out, "Certificate List:\n"); iv = 0; while ((aCert = src->rawCerts[iv++]) != NULL) { sprintf(om, "Certificate (%x)", iv); rv = SECU_PrintSignedData(out, aCert, om, level + 2, SECU_PrintCertificate); if (rv) return rv; } } /* Parse and list CRL's (if any) */ if (src->crls != NULL) { secu_Indent(out, level + 1); fprintf(out, "Signed Revocation Lists:\n"); iv = 0; while ((aCrl = src->crls[iv++]) != NULL) { sprintf(om, "Signed Revocation List (%x)", iv); secu_Indent(out, level + 2); fprintf(out, "%s:\n", om); sv_PrintAlgorithmID(out, &aCrl->signatureWrap.signatureAlgorithm, "Signature Algorithm"); DER_ConvertBitString(&aCrl->signatureWrap.signature); sv_PrintAsHex(out, &aCrl->signatureWrap.signature, "Signature", level+3); SECU_PrintCRLInfo(out, &aCrl->crl, "Certificate Revocation List", level + 3); } } /* Parse and list signatures (if any) */ if (src->signerInfos != NULL) { secu_Indent(out, level + 1); fprintf(out, "Signer Information List:\n"); iv = 0; while ((sigInfo = src->signerInfos[iv++]) != NULL) { sprintf(om, "Signer Information (%x)", iv); secu_PrintSignerInfo(out, sigInfo, om, level + 2); } } return 0; }
/* ** secu_PrintPKCS7Signed ** Pretty print a PKCS7 signed data type (up to version 1). */ int sv_PrintPKCS7Signed(FILE *out, SEC_PKCS7SignedData *src) { SECAlgorithmID *digAlg; /* digest algorithms */ SECItem *aCert; /* certificate */ CERTSignedCrl *aCrl; /* certificate revocation list */ SEC_PKCS7SignerInfo *sigInfo; /* signer information */ int rv, iv; char om[120]; sv_PrintInteger(out, &(src->version), "pkcs7.version="); /* Parse and list digest algorithms (if any) */ if (src->digestAlgorithms != NULL) { iv = 0; while (src->digestAlgorithms[iv] != NULL) iv++; fprintf(out, "pkcs7.digestAlgorithmListLength=%d\n", iv); iv = 0; while ((digAlg = src->digestAlgorithms[iv]) != NULL) { sprintf(om, "pkcs7.digestAlgorithm[%d]=", iv++); sv_PrintAlgorithmID(out, digAlg, om); } } /* Now for the content */ rv = sv_PrintPKCS7ContentInfo(out, &(src->contentInfo), "pkcs7.contentInformation="); if (rv != 0) return rv; /* Parse and list certificates (if any) */ if (src->rawCerts != NULL) { iv = 0; while (src->rawCerts[iv] != NULL) iv++; fprintf(out, "pkcs7.certificateListLength=%d\n", iv); iv = 0; while ((aCert = src->rawCerts[iv]) != NULL) { sprintf(om, "certificate[%d].", iv++); rv = sv_PrintSignedData(out, aCert, om, sv_PrintCertificate); if (rv) return rv; } } /* Parse and list CRL's (if any) */ if (src->crls != NULL) { iv = 0; while (src->crls[iv] != NULL) iv++; fprintf(out, "pkcs7.signedRevocationLists=%d\n", iv); iv = 0; while ((aCrl = src->crls[iv]) != NULL) { sprintf(om, "signedRevocationList[%d].", iv); fprintf(out, "%s", om); sv_PrintAlgorithmID(out, &aCrl->signatureWrap.signatureAlgorithm, "signatureAlgorithm="); DER_ConvertBitString(&aCrl->signatureWrap.signature); fprintf(out, "%s", om); sv_PrintAsHex(out, &aCrl->signatureWrap.signature, "signature="); sprintf(om, "certificateRevocationList[%d].", iv); sv_PrintCRLInfo(out, &aCrl->crl, om); iv++; } } /* Parse and list signatures (if any) */ if (src->signerInfos != NULL) { iv = 0; while (src->signerInfos[iv] != NULL) iv++; fprintf(out, "pkcs7.signerInformationListLength=%d\n", iv); iv = 0; while ((sigInfo = src->signerInfos[iv]) != NULL) { sprintf(om, "signerInformation[%d].", iv++); sv_PrintSignerInfo(out, sigInfo, om); } } return 0; }
SECItem* CreateEncodedOCSPRequest(PLArenaPool* arena, const CERTCertificate* cert, const CERTCertificate* issuerCert) { if (!arena || !cert || !issuerCert) { PR_SetError(SEC_ERROR_INVALID_ARGS, 0); return nullptr; } // We do not add any extensions to the request. // RFC 6960 says "An OCSP client MAY wish to specify the kinds of response // types it understands. To do so, it SHOULD use an extension with the OID // id-pkix-ocsp-response." This use of MAY and SHOULD is unclear. MSIE11 // on Windows 8.1 does not include any extensions, whereas NSS has always // included the id-pkix-ocsp-response extension. Avoiding the sending the // extension is better for OCSP GET because it makes the request smaller, // and thus more likely to fit within the 255 byte limit for OCSP GET that // is specified in RFC 5019 Section 5. // Bug 966856: Add the id-pkix-ocsp-pref-sig-algs extension. // Since we don't know whether the OCSP responder supports anything other // than SHA-1, we have no choice but to use SHA-1 for issuerNameHash and // issuerKeyHash. static const uint8_t hashAlgorithm[11] = { 0x30, 0x09, // SEQUENCE 0x06, 0x05, 0x2B, 0x0E, 0x03, 0x02, 0x1A, // OBJECT IDENTIFIER id-sha1 0x05, 0x00, // NULL }; static const uint8_t hashLen = SHA1_LENGTH; static const unsigned int totalLenWithoutSerialNumberData = 2 // OCSPRequest + 2 // tbsRequest + 2 // requestList + 2 // Request + 2 // reqCert (CertID) + PR_ARRAY_SIZE(hashAlgorithm) // hashAlgorithm + 2 + hashLen // issuerNameHash + 2 + hashLen // issuerKeyHash + 2; // serialNumber (header) // The only way we could have a request this large is if the serialNumber was // ridiculously and unreasonably large. RFC 5280 says "Conforming CAs MUST // NOT use serialNumber values longer than 20 octets." With this restriction, // we allow for some amount of non-conformance with that requirement while // still ensuring we can encode the length values in the ASN.1 TLV structures // in a single byte. if (issuerCert->serialNumber.len > 127u - totalLenWithoutSerialNumberData) { PR_SetError(SEC_ERROR_BAD_DATA, 0); return nullptr; } uint8_t totalLen = static_cast<uint8_t>(totalLenWithoutSerialNumberData + cert->serialNumber.len); SECItem* encodedRequest = SECITEM_AllocItem(arena, nullptr, totalLen); if (!encodedRequest) { return nullptr; } uint8_t* d = encodedRequest->data; *d++ = 0x30; *d++ = totalLen - 2; // OCSPRequest (SEQUENCE) *d++ = 0x30; *d++ = totalLen - 4; // tbsRequest (SEQUENCE) *d++ = 0x30; *d++ = totalLen - 6; // requestList (SEQUENCE OF) *d++ = 0x30; *d++ = totalLen - 8; // Request (SEQUENCE) *d++ = 0x30; *d++ = totalLen - 10; // reqCert (CertID SEQUENCE) // reqCert.hashAlgorithm for (size_t i = 0; i < PR_ARRAY_SIZE(hashAlgorithm); ++i) { *d++ = hashAlgorithm[i]; } // reqCert.issuerNameHash (OCTET STRING) *d++ = 0x04; *d++ = hashLen; if (PK11_HashBuf(SEC_OID_SHA1, d, issuerCert->derSubject.data, issuerCert->derSubject.len) != SECSuccess) { return nullptr; } d += hashLen; // reqCert.issuerKeyHash (OCTET STRING) *d++ = 0x04; *d++ = hashLen; SECItem key = issuerCert->subjectPublicKeyInfo.subjectPublicKey; DER_ConvertBitString(&key); if (PK11_HashBuf(SEC_OID_SHA1, d, key.data, key.len) != SECSuccess) { return nullptr; } d += hashLen; // reqCert.serialNumber (INTEGER) *d++ = 0x02; // INTEGER *d++ = static_cast<uint8_t>(cert->serialNumber.len); for (size_t i = 0; i < cert->serialNumber.len; ++i) { *d++ = cert->serialNumber.data[i]; } PR_ASSERT(d == encodedRequest->data + totalLen); return encodedRequest; }
/* * Decode the DER/BER-encoded item "data" as an OCSP request * and pretty-print the subfields. */ static SECStatus print_request (FILE *out_file, SECItem *data) { CERTOCSPRequest *request; ocspTBSRequest *tbsRequest; int level = 0; PORT_Assert (out_file != NULL); PORT_Assert (data != NULL); if (out_file == NULL || data == NULL) { PORT_SetError (SEC_ERROR_INVALID_ARGS); return SECFailure; } request = CERT_DecodeOCSPRequest (data); if (request == NULL || request->tbsRequest == NULL) return SECFailure; tbsRequest = request->tbsRequest; fprintf (out_file, "TBS Request:\n"); level++; print_ocsp_version (out_file, &(tbsRequest->version), level); /* * XXX Probably should be an interface to get the signer name * without looking inside the tbsRequest at all. */ if (tbsRequest->requestorName != NULL) { SECU_Indent (out_file, level); fprintf (out_file, "XXX print the requestorName\n"); } else { SECU_Indent (out_file, level); fprintf (out_file, "No Requestor Name.\n"); } if (tbsRequest->requestList != NULL) { int i; for (i = 0; tbsRequest->requestList[i] != NULL; i++) { SECU_Indent (out_file, level); fprintf (out_file, "Request %d:\n", i); print_single_request (out_file, tbsRequest->requestList[i], level + 1); } } else { fprintf (out_file, "Request list is empty.\n"); } print_ocsp_extensions (out_file, tbsRequest->requestExtensions, "Request Extensions", level); if (request->optionalSignature != NULL) { ocspSignature *whole_sig; SECItem rawsig; fprintf (out_file, "Signature:\n"); whole_sig = request->optionalSignature; SECU_PrintAlgorithmID (out_file, &(whole_sig->signatureAlgorithm), "Signature Algorithm", level); rawsig = whole_sig->signature; DER_ConvertBitString (&rawsig); SECU_PrintAsHex (out_file, &rawsig, "Signature", level); print_raw_certificates (out_file, whole_sig->derCerts, level); fprintf (out_file, "XXX verify the sig and print result\n"); } else { fprintf (out_file, "No Signature\n"); } CERT_DestroyOCSPRequest (request); return SECSuccess; }
int main(int argc, char **argv) { int verbose=0, force=0; int ascii=0, issuerAscii=0; char *progName=0; PRFileDesc *inFile=0, *issuerCertFile=0; SECItem derCert, derIssuerCert; PLArenaPool *arena=0; CERTSignedData *signedData=0; CERTCertificate *cert=0, *issuerCert=0; SECKEYPublicKey *rsapubkey=0; SECAlgorithmID md5WithRSAEncryption, md2WithRSAEncryption; SECAlgorithmID sha1WithRSAEncryption, rsaEncryption; SECItem spk; int selfSigned=0; int invalid=0; char *inFileName = NULL, *issuerCertFileName = NULL; PLOptState *optstate; PLOptStatus status; SECStatus rv; PORT_Memset(&md5WithRSAEncryption, 0, sizeof(md5WithRSAEncryption)); PORT_Memset(&md2WithRSAEncryption, 0, sizeof(md2WithRSAEncryption)); PORT_Memset(&sha1WithRSAEncryption, 0, sizeof(sha1WithRSAEncryption)); PORT_Memset(&rsaEncryption, 0, sizeof(rsaEncryption)); progName = strrchr(argv[0], '/'); progName = progName ? progName+1 : argv[0]; optstate = PL_CreateOptState(argc, argv, "aAvf"); while ((status = PL_GetNextOpt(optstate)) == PL_OPT_OK) { switch (optstate->option) { case 'v': verbose = 1; break; case 'f': force = 1; break; case 'a': ascii = 1; break; case 'A': issuerAscii = 1; break; case '\0': if (!inFileName) inFileName = PL_strdup(optstate->value); else if (!issuerCertFileName) issuerCertFileName = PL_strdup(optstate->value); else Usage(progName); break; } } if (!inFileName || !issuerCertFileName || status == PL_OPT_BAD) { /* insufficient or excess args */ Usage(progName); } inFile = PR_Open(inFileName, PR_RDONLY, 0); if (!inFile) { fprintf(stderr, "%s: unable to open \"%s\" for reading\n", progName, inFileName); exit(1); } issuerCertFile = PR_Open(issuerCertFileName, PR_RDONLY, 0); if (!issuerCertFile) { fprintf(stderr, "%s: unable to open \"%s\" for reading\n", progName, issuerCertFileName); exit(1); } if (SECU_ReadDERFromFile(&derCert, inFile, ascii, PR_FALSE) != SECSuccess) { printf("Couldn't read input certificate as DER binary or base64\n"); exit(1); } arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); if (arena == 0) { fprintf(stderr,"%s: can't allocate scratch arena!", progName); exit(1); } if (issuerCertFile) { CERTSignedData *issuerCertSD=0; if (SECU_ReadDERFromFile(&derIssuerCert, issuerCertFile, issuerAscii, PR_FALSE) != SECSuccess) { printf("Couldn't read issuer certificate as DER binary or base64.\n"); exit(1); } issuerCertSD = PORT_ArenaZNew(arena, CERTSignedData); if (!issuerCertSD) { fprintf(stderr,"%s: can't allocate issuer signed data!", progName); exit(1); } rv = SEC_ASN1DecodeItem(arena, issuerCertSD, SEC_ASN1_GET(CERT_SignedDataTemplate), &derIssuerCert); if (rv) { fprintf(stderr, "%s: Issuer cert isn't X509 SIGNED Data?\n", progName); exit(1); } issuerCert = createEmptyCertificate(); if (!issuerCert) { printf("%s: can't allocate space for issuer cert.", progName); exit(1); } rv = SEC_ASN1DecodeItem(arena, issuerCert, SEC_ASN1_GET(CERT_CertificateTemplate), &issuerCertSD->data); if (rv) { printf("%s: Does not appear to be an X509 Certificate.\n", progName); exit(1); } } signedData = PORT_ArenaZNew(arena,CERTSignedData); if (!signedData) { fprintf(stderr,"%s: can't allocate signedData!", progName); exit(1); } rv = SEC_ASN1DecodeItem(arena, signedData, SEC_ASN1_GET(CERT_SignedDataTemplate), &derCert); if (rv) { fprintf(stderr, "%s: Does not appear to be X509 SIGNED Data.\n", progName); exit(1); } if (verbose) { printf("Decoded ok as X509 SIGNED data.\n"); } cert = createEmptyCertificate(); if (!cert) { fprintf(stderr, "%s: can't allocate cert", progName); exit(1); } rv = SEC_ASN1DecodeItem(arena, cert, SEC_ASN1_GET(CERT_CertificateTemplate), &signedData->data); if (rv) { fprintf(stderr, "%s: Does not appear to be an X509 Certificate.\n", progName); exit(1); } if (verbose) { printf("Decoded ok as an X509 certificate.\n"); } SECU_RegisterDynamicOids(); rv = SECU_PrintSignedData(stdout, &derCert, "Certificate", 0, (SECU_PPFunc)SECU_PrintCertificate); if (rv) { fprintf(stderr, "%s: Unable to pretty print cert. Error: %d\n", progName, PORT_GetError()); if (!force) { exit(1); } } /* Do various checks on the cert */ printf("\n"); /* Check algorithms */ rv = SECOID_SetAlgorithmID(arena, &md5WithRSAEncryption, SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION, NULL); if (rv) { fprintf(stderr, "%s: failed to set algorithm ID for SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION.\n", progName); exit(1); } rv = SECOID_SetAlgorithmID(arena, &md2WithRSAEncryption, SEC_OID_PKCS1_MD2_WITH_RSA_ENCRYPTION, NULL); if (rv) { fprintf(stderr, "%s: failed to set algorithm ID for SEC_OID_PKCS1_MD2_WITH_RSA_ENCRYPTION.\n", progName); exit(1); } rv = SECOID_SetAlgorithmID(arena, &sha1WithRSAEncryption, SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION, NULL); if (rv) { fprintf(stderr, "%s: failed to set algorithm ID for SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION.\n", progName); exit(1); } rv = SECOID_SetAlgorithmID(arena, &rsaEncryption, SEC_OID_PKCS1_RSA_ENCRYPTION, NULL); if (rv) { fprintf(stderr, "%s: failed to set algorithm ID for SEC_OID_PKCS1_RSA_ENCRYPTION.\n", progName); exit(1); } { int isMD5RSA = (SECOID_CompareAlgorithmID(&cert->signature, &md5WithRSAEncryption) == 0); int isMD2RSA = (SECOID_CompareAlgorithmID(&cert->signature, &md2WithRSAEncryption) == 0); int isSHA1RSA = (SECOID_CompareAlgorithmID(&cert->signature, &sha1WithRSAEncryption) == 0); if (verbose) { printf("\nDoing algorithm checks.\n"); } if (!(isMD5RSA || isMD2RSA || isSHA1RSA)) { printf("PROBLEM: Signature not PKCS1 MD5, MD2, or SHA1 + RSA.\n"); } else if (!isMD5RSA) { printf("WARNING: Signature not PKCS1 MD5 with RSA Encryption\n"); } if (SECOID_CompareAlgorithmID(&cert->signature, &signedData->signatureAlgorithm)) { printf("PROBLEM: Algorithm in sig and certInfo don't match.\n"); } } if (SECOID_CompareAlgorithmID(&cert->subjectPublicKeyInfo.algorithm, &rsaEncryption)) { printf("PROBLEM: Public key algorithm is not PKCS1 RSA Encryption.\n"); } /* Check further public key properties */ spk = cert->subjectPublicKeyInfo.subjectPublicKey; DER_ConvertBitString(&spk); if (verbose) { printf("\nsubjectPublicKey DER\n"); rv = DER_PrettyPrint(stdout, &spk, PR_FALSE); printf("\n"); } rsapubkey = (SECKEYPublicKey *) PORT_ArenaZAlloc(arena,sizeof(SECKEYPublicKey)); if (!rsapubkey) { fprintf(stderr, "%s: rsapubkey allocation failed.\n", progName); exit(1); } rv = SEC_ASN1DecodeItem(arena, rsapubkey, SEC_ASN1_GET(SECKEY_RSAPublicKeyTemplate), &spk); if (rv) { printf("PROBLEM: subjectPublicKey is not a DER PKCS1 RSAPublicKey.\n"); } else { int mlen; int pubexp; if (verbose) { printf("Decoded RSA Public Key ok. Doing key checks.\n"); } PORT_Assert(rsapubkey->keyType == rsaKey); /* XXX RSA */ mlen = checkInteger(&rsapubkey->u.rsa.modulus, "Modulus", verbose); printf("INFO: Public Key modulus length in bits: %d\n", mlen); if (mlen > MAX_MODULUS) { printf("PROBLEM: Modulus length exceeds %d bits.\n", MAX_MODULUS); } if (mlen < 512) { printf("WARNING: Short modulus.\n"); } if (mlen != (1 << (ffs(mlen)-1))) { printf("WARNING: Unusual modulus length (not a power of two).\n"); } checkInteger(&rsapubkey->u.rsa.publicExponent, "Public Exponent", verbose); pubexp = DER_GetInteger(&rsapubkey->u.rsa.publicExponent); if (pubexp != 17 && pubexp != 3 && pubexp != 65537) { printf("WARNING: Public exponent not any of: 3, 17, 65537\n"); } } /* Name checks */ checkName(&cert->issuer, "Issuer Name", verbose); checkName(&cert->subject, "Subject Name", verbose); if (issuerCert) { SECComparison c = CERT_CompareName(&cert->issuer, &issuerCert->subject); if (c) { printf("PROBLEM: Issuer Name and Subject in Issuing Cert differ\n"); } } /* Check if self-signed */ selfSigned = (CERT_CompareName(&cert->issuer, &cert->subject) == 0); if (selfSigned) { printf("INFO: Certificate is self signed.\n"); } else { printf("INFO: Certificate is NOT self-signed.\n"); } /* Validity time check */ if (CERT_CertTimesValid(cert) == SECSuccess) { printf("INFO: Inside validity period of certificate.\n"); } else { printf("PROBLEM: Not in validity period of certificate.\n"); invalid = 1; } /* Signature check if self-signed */ if (selfSigned && !invalid) { if (rsapubkey->u.rsa.modulus.len) { SECStatus ver; if (verbose) { printf("Checking self signature.\n"); } ver = OurVerifySignedData(signedData, cert); if (ver != SECSuccess) { printf("PROBLEM: Verification of self-signature failed!\n"); } else { printf("INFO: Self-signature verifies ok.\n"); } } else { printf("INFO: Not checking signature due to key problems.\n"); } } else if (!selfSigned && !invalid && issuerCert) { SECStatus ver; ver = OurVerifySignedData(signedData, issuerCert); if (ver != SECSuccess) { printf("PROBLEM: Verification of issuer's signature failed!\n"); } else { printf("INFO: Issuer's signature verifies ok.\n"); } } else { printf("INFO: Not checking signature.\n"); } return 0; }
CERTCrlDistributionPoints * CERT_DecodeCRLDistributionPoints (PLArenaPool *arena, SECItem *encodedValue) { CERTCrlDistributionPoints *value = NULL; CRLDistributionPoint **pointList, *point; SECStatus rv = SECSuccess; SECItem newEncodedValue; PORT_Assert (arena); do { value = PORT_ArenaZNew(arena, CERTCrlDistributionPoints); if (value == NULL) { rv = SECFailure; break; } /* 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, &newEncodedValue, encodedValue); if (rv != SECSuccess) break; rv = SEC_QuickDERDecodeItem(arena, &value->distPoints, CERTCRLDistributionPointsTemplate, &newEncodedValue); if (rv != SECSuccess) break; pointList = value->distPoints; while (NULL != (point = *pointList)) { /* get the data if the distributionPointName is not omitted */ if (point->derDistPoint.data != NULL) { rv = SEC_QuickDERDecodeItem(arena, point, DistributionPointNameTemplate, &(point->derDistPoint)); if (rv != SECSuccess) break; switch (point->distPointType) { case generalName: point->distPoint.fullName = cert_DecodeGeneralNames(arena, point->derFullName); rv = point->distPoint.fullName ? SECSuccess : SECFailure; break; case relativeDistinguishedName: break; default: PORT_SetError (SEC_ERROR_EXTENSION_VALUE_INVALID); rv = SECFailure; break; } /* end switch */ if (rv != SECSuccess) break; } /* end if */ /* Get the reason code if it's not omitted in the encoding */ if (point->bitsmap.data != NULL) { SECItem bitsmap = point->bitsmap; DER_ConvertBitString(&bitsmap); rv = SECITEM_CopyItem(arena, &point->reasons, &bitsmap); if (rv != SECSuccess) break; } /* Get the crl issuer name if it's not omitted in the encoding */ if (point->derCrlIssuer != NULL) { point->crlIssuer = cert_DecodeGeneralNames(arena, point->derCrlIssuer); if (!point->crlIssuer) break; } ++pointList; } /* end while points remain */ } while (0); return (rv == SECSuccess ? value : NULL); }
NS_IMETHODIMP nsDataSignatureVerifier::VerifyData(const nsACString & aData, const nsACString & aSignature, const nsACString & aPublicKey, bool *_retval) { // Allocate an arena to handle the majority of the allocations PRArenaPool *arena; arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); if (!arena) return NS_ERROR_OUT_OF_MEMORY; // Base 64 decode the key SECItem keyItem; PORT_Memset(&keyItem, 0, sizeof(SECItem)); if (!NSSBase64_DecodeBuffer(arena, &keyItem, nsPromiseFlatCString(aPublicKey).get(), aPublicKey.Length())) { PORT_FreeArena(arena, false); return NS_ERROR_FAILURE; } // Extract the public key from the data CERTSubjectPublicKeyInfo *pki = SECKEY_DecodeDERSubjectPublicKeyInfo(&keyItem); if (!pki) { PORT_FreeArena(arena, false); return NS_ERROR_FAILURE; } SECKEYPublicKey *publicKey = SECKEY_ExtractPublicKey(pki); SECKEY_DestroySubjectPublicKeyInfo(pki); pki = nullptr; if (!publicKey) { PORT_FreeArena(arena, false); return NS_ERROR_FAILURE; } // Base 64 decode the signature SECItem signatureItem; PORT_Memset(&signatureItem, 0, sizeof(SECItem)); if (!NSSBase64_DecodeBuffer(arena, &signatureItem, nsPromiseFlatCString(aSignature).get(), aSignature.Length())) { SECKEY_DestroyPublicKey(publicKey); PORT_FreeArena(arena, false); return NS_ERROR_FAILURE; } // Decode the signature and algorithm CERTSignedData sigData; PORT_Memset(&sigData, 0, sizeof(CERTSignedData)); SECStatus ss = SEC_QuickDERDecodeItem(arena, &sigData, CERT_SignatureDataTemplate, &signatureItem); if (ss != SECSuccess) { SECKEY_DestroyPublicKey(publicKey); PORT_FreeArena(arena, false); return NS_ERROR_FAILURE; } // Perform the final verification DER_ConvertBitString(&(sigData.signature)); ss = VFY_VerifyDataWithAlgorithmID((const unsigned char*)nsPromiseFlatCString(aData).get(), aData.Length(), publicKey, &(sigData.signature), &(sigData.signatureAlgorithm), NULL, NULL); // Clean up remaining objects SECKEY_DestroyPublicKey(publicKey); PORT_FreeArena(arena, false); *_retval = (ss == SECSuccess); return NS_OK; }