/* * NSS_CMSSignedData_VerifyCertsOnly - verify the certs in a certs-only message */ SECStatus NSS_CMSSignedData_VerifyCertsOnly(NSSCMSSignedData *sigd, CERTCertDBHandle *certdb, SECCertUsage usage) { CERTCertificate *cert; SECStatus rv = SECSuccess; int i; int count; PRTime now; if (!sigd || !certdb || !sigd->rawCerts) { PORT_SetError(SEC_ERROR_INVALID_ARGS); return SECFailure; } count = NSS_CMSArray_Count((void**)sigd->rawCerts); now = PR_Now(); for (i=0; i < count; i++) { if (sigd->certs && sigd->certs[i]) { cert = CERT_DupCertificate(sigd->certs[i]); } else { cert = CERT_FindCertByDERCert(certdb, sigd->rawCerts[i]); if (!cert) { rv = SECFailure; break; } } rv |= CERT_VerifyCert(certdb, cert, PR_TRUE, usage, now, NULL, NULL); CERT_DestroyCertificate(cert); } return rv; }
/* * Verify the specified certificate (whose nickname is "cert_name"). * OCSP is already turned on, so we just need to call the standard * certificate verification API and let it do all the work. */ static SECStatus verify_cert (FILE *out_file, CERTCertDBHandle *handle, CERTCertificate *cert, const char *cert_name, SECCertUsage cert_usage, PRTime verify_time) { SECStatus rv = SECFailure; if (handle == NULL || cert == NULL) return rv; rv = CERT_VerifyCert (handle, cert, PR_TRUE, cert_usage, verify_time, NULL, NULL); fprintf (out_file, "Verification of certificate \"%s\" ", cert_name); if (rv == SECSuccess) { fprintf (out_file, "succeeded.\n"); } else { const char *error_string = SECU_Strerror(PORT_GetError()); fprintf (out_file, "failed. Reason:\n"); if (error_string != NULL && PORT_Strlen(error_string) > 0) fprintf (out_file, "%s\n", error_string); else fprintf (out_file, "Unknown\n"); } rv = SECSuccess; return rv; }
/* * SecCmsSignedDataVerifyCertsOnly - verify the certs in a certs-only message */ OSStatus SecCmsSignedDataVerifyCertsOnly(SecCmsSignedDataRef sigd, SecKeychainRef keychainOrArray, CFTypeRef policies) { SecCertificateRef cert; OSStatus rv = SECSuccess; int i; int count; if (!sigd || !keychainOrArray || !sigd->rawCerts) { PORT_SetError(SEC_ERROR_INVALID_ARGS); return SECFailure; } count = SecCmsArrayCount((void**)sigd->rawCerts); for (i=0; i < count; i++) { if (sigd->certs && CFArrayGetCount(sigd->certs) > i) { cert = (SecCertificateRef)CFArrayGetValueAtIndex(sigd->certs, i); CFRetain(cert); } else { cert = CERT_FindCertByDERCert(keychainOrArray, sigd->rawCerts[i]); if (!cert) { rv = SECFailure; break; } } rv |= CERT_VerifyCert(keychainOrArray, cert, sigd->rawCerts, policies, CFAbsoluteTimeGetCurrent(), NULL); CFRelease(cert); } return rv; }
SECStatus NSS_CMSSignerInfo_VerifyCertificate(NSSCMSSignerInfo *signerinfo, CERTCertDBHandle *certdb, SECCertUsage certusage) { CERTCertificate *cert; PRTime stime; if ((cert = NSS_CMSSignerInfo_GetSigningCertificate(signerinfo, certdb)) == NULL) { signerinfo->verificationStatus = NSSCMSVS_SigningCertNotFound; return SECFailure; } /* * Get and convert the signing time; if available, it will be used * both on the cert verification and for importing the sender * email profile. */ if (NSS_CMSSignerInfo_GetSigningTime (signerinfo, &stime) != SECSuccess) stime = PR_Now(); /* not found or conversion failed, so check against now */ /* * XXX This uses the signing time, if available. Additionally, we * might want to, if there is no signing time, get the message time * from the mail header itself, and use that. That would require * a change to our interface though, and for S/MIME callers to pass * in a time (and for non-S/MIME callers to pass in nothing, or * maybe make them pass in the current time, always?). */ if (CERT_VerifyCert(certdb, cert, PR_TRUE, certusage, stime, signerinfo->cmsg->pwfn_arg, NULL) != SECSuccess) { signerinfo->verificationStatus = NSSCMSVS_SigningCertNotTrusted; return SECFailure; } return SECSuccess; }
/* This is the "default" authCert callback function. It is called when a * certificate message is received from the peer and the local application * has not registered an authCert callback function. */ SECStatus SSL_AuthCertificate(void *arg, PRFileDesc *fd, PRBool checkSig, PRBool isServer) { SECStatus rv; CERTCertDBHandle * handle; sslSocket * ss; SECCertUsage certUsage; const char * hostname = NULL; PRTime now = PR_Now(); SECItemArray * certStatusArray; ss = ssl_FindSocket(fd); PORT_Assert(ss != NULL); if (!ss) { return SECFailure; } handle = (CERTCertDBHandle *)arg; certStatusArray = &ss->sec.ci.sid->peerCertStatus; if (certStatusArray->len) { PORT_SetError(0); if (CERT_CacheOCSPResponseFromSideChannel(handle, ss->sec.peerCert, now, &certStatusArray->items[0], ss->pkcs11PinArg) != SECSuccess) { PRErrorCode error = PR_GetError(); PORT_Assert(error != 0); } } /* this may seem backwards, but isn't. */ certUsage = isServer ? certUsageSSLClient : certUsageSSLServer; rv = CERT_VerifyCert(handle, ss->sec.peerCert, checkSig, certUsage, now, ss->pkcs11PinArg, NULL); if ( rv != SECSuccess || isServer ) return rv; /* cert is OK. This is the client side of an SSL connection. * Now check the name field in the cert against the desired hostname. * NB: This is our only defense against Man-In-The-Middle (MITM) attacks! */ hostname = ss->url; if (hostname && hostname[0]) rv = CERT_VerifyCertName(ss->sec.peerCert, hostname); else rv = SECFailure; if (rv != SECSuccess) PORT_SetError(SSL_ERROR_BAD_CERT_DOMAIN); return rv; }
/* * Callback from SSL for checking a (possibly) expired * certificate the peer presents. */ SECStatus JSSL_ConfirmExpiredPeerCert(void *arg, PRFileDesc *fd, PRBool checkSig, PRBool isServer) { SECStatus rv=SECFailure; SECCertUsage certUsage; CERTCertificate* peerCert=NULL; int64 notAfter, notBefore; certUsage = isServer ? certUsageSSLClient : certUsageSSLServer; peerCert = SSL_PeerCertificate(fd); if (peerCert) { rv = CERT_GetCertTimes(peerCert, ¬Before, ¬After); if (rv != SECSuccess) goto finish; /* * Verify the certificate based on it's expiry date. This should * always succeed, if the cert is trusted. It doesn't care if * the cert has expired. */ rv = CERT_VerifyCert(CERT_GetDefaultCertDB(), peerCert, checkSig, certUsage, notAfter, NULL /*pinarg*/, NULL /* log */); } if ( rv != SECSuccess ) goto finish; if( ! isServer ) { /* This is the client side of an SSL connection. * Now check the name field in the cert against the desired hostname. * NB: This is our only defense against Man-In-The-Middle (MITM) attacks! */ if( peerCert == NULL ) { rv = SECFailure; } else { char* hostname = NULL; hostname = SSL_RevealURL(fd); /* really is a hostname, not a URL */ if (hostname && hostname[0]) { rv = CERT_VerifyCertName(peerCert, hostname); PORT_Free(hostname); } else { rv = SECFailure; } } } finish: if (peerCert!=NULL) CERT_DestroyCertificate(peerCert); return rv; }
static PKIX_Error* pkix_pl_OcspResponse_VerifyResponse( PKIX_PL_OcspResponse *response, PKIX_ProcessingParams *procParams, SECCertUsage certUsage, void **state, PKIX_BuildResult **buildResult, void **pNBIOContext, void *plContext) { SECStatus rv = SECFailure; PKIX_ENTER(OCSPRESPONSE, "pkix_pl_OcspResponse_VerifyResponse"); if (response->verifyFcn != NULL) { void *lplContext = NULL; PKIX_CHECK( PKIX_PL_NssContext_Create(((SECCertificateUsage)1) << certUsage, PKIX_FALSE, NULL, &lplContext), PKIX_NSSCONTEXTCREATEFAILED); PKIX_CHECK( (response->verifyFcn)((PKIX_PL_Object*)response->pkixSignerCert, NULL, response->producedAtDate, procParams, pNBIOContext, state, buildResult, NULL, lplContext), PKIX_CERTVERIFYKEYUSAGEFAILED); rv = SECSuccess; } else { rv = CERT_VerifyCert(response->handle, response->signerCert, PKIX_TRUE, certUsage, response->producedAt, NULL, NULL); if (rv != SECSuccess) { PKIX_ERROR(PKIX_CERTVERIFYKEYUSAGEFAILED); } } cleanup: if (rv != SECSuccess) { PORT_SetError(SEC_ERROR_OCSP_INVALID_SIGNING_CERT); } PKIX_RETURN(OCSPRESPONSE); }
/* * SecCmsSignerInfoAddMSSMIMEEncKeyPrefs - add a SMIMEEncryptionKeyPreferences attribute to the * authenticated (i.e. signed) attributes of "signerinfo", using the OID prefered by Microsoft. * * This is expected to be included in outgoing signed messages for email (S/MIME), * if compatibility with Microsoft mail clients is wanted. */ OSStatus SecCmsSignerInfoAddMSSMIMEEncKeyPrefs(SecCmsSignerInfoRef signerinfo, SecCertificateRef cert, SecKeychainRef keychainOrArray) { SecCmsAttribute *attr; CSSM_DATA_PTR smimeekp = NULL; void *mark; PLArenaPool *poolp; #if 0 CFTypeRef policy; /* verify this cert for encryption */ policy = CERT_PolicyForCertUsage(certUsageEmailRecipient); if (CERT_VerifyCert(keychainOrArray, cert, policy, CFAbsoluteTimeGetCurrent(), NULL) != SECSuccess) { CFRelease(policy); return SECFailure; } CFRelease(policy); #endif poolp = signerinfo->cmsg->poolp; mark = PORT_ArenaMark(poolp); smimeekp = SECITEM_AllocItem(poolp, NULL, 0); if (smimeekp == NULL) goto loser; /* create new signing time attribute */ if (SecSMIMECreateMSSMIMEEncKeyPrefs((SecArenaPoolRef)poolp, smimeekp, cert) != SECSuccess) goto loser; if ((attr = SecCmsAttributeCreate(poolp, SEC_OID_MS_SMIME_ENCRYPTION_KEY_PREFERENCE, smimeekp, PR_TRUE)) == NULL) goto loser; if (SecCmsSignerInfoAddAuthAttr(signerinfo, attr) != SECSuccess) goto loser; PORT_ArenaUnmark (poolp, mark); return SECSuccess; loser: PORT_ArenaRelease (poolp, mark); return SECFailure; }
/* * NSS_CMSSignerInfo_AddMSSMIMEEncKeyPrefs - add a SMIMEEncryptionKeyPreferences attribute to the * authenticated (i.e. signed) attributes of "signerinfo", using the OID preferred by Microsoft. * * This is expected to be included in outgoing signed messages for email (S/MIME), * if compatibility with Microsoft mail clients is wanted. */ SECStatus NSS_CMSSignerInfo_AddMSSMIMEEncKeyPrefs(NSSCMSSignerInfo *signerinfo, CERTCertificate *cert, CERTCertDBHandle *certdb) { NSSCMSAttribute *attr; SECItem *smimeekp = NULL; void *mark; PLArenaPool *poolp; /* verify this cert for encryption */ if (CERT_VerifyCert(certdb, cert, PR_TRUE, certUsageEmailRecipient, PR_Now(), signerinfo->cmsg->pwfn_arg, NULL) != SECSuccess) { return SECFailure; } poolp = signerinfo->cmsg->poolp; mark = PORT_ArenaMark(poolp); smimeekp = SECITEM_AllocItem(poolp, NULL, 0); if (smimeekp == NULL) goto loser; /* create new signing time attribute */ if (NSS_SMIMEUtil_CreateMSSMIMEEncKeyPrefs(poolp, smimeekp, cert) != SECSuccess) goto loser; if ((attr = NSS_CMSAttribute_Create(poolp, SEC_OID_MS_SMIME_ENCRYPTION_KEY_PREFERENCE, smimeekp, PR_TRUE)) == NULL) goto loser; if (NSS_CMSSignerInfo_AddAuthAttr(signerinfo, attr) != SECSuccess) goto loser; PORT_ArenaUnmark (poolp, mark); return SECSuccess; loser: PORT_ArenaRelease (poolp, mark); return SECFailure; }
int main(int argc, char *argv[]) { int opt; long fin = 0; int use_pkix = 0; SECStatus rv; char pbuf[1024]; PRBool crlcheck = PR_FALSE; PRBool ocspcheck = PR_FALSE; PRBool strict = PR_FALSE; CERTCertDBHandle *handle = NULL; CERTCertificate **certout = NULL; CERTVerifyLog vfy_log; CERTVerifyLog vfy_log2; CERTVerifyLog *cur_log; CERTValOutParam *pkixout = NULL; SECItem c1; SECItem c2; SECItem *certs[2]; certs[0] = &c1; certs[1] = &c2; int numcerts = 0; while ((opt = getopt(argc, argv, "u:d:e:pn:s:coSr")) != -1) { switch(opt) { /* usage type */ case 'u': set_usage(optarg); break; case 'd': db_dir = optarg; break; case 's': sub_file = optarg; break; case 'c': crlcheck = PR_TRUE; break; case 'o': ocspcheck = PR_TRUE; break; case 'S': strict = PR_TRUE; break; case 'e': end_file = optarg; break; case 'p': use_pkix = 1; break; case 'n': rightca_nick = optarg; break; case 'r': retry_verify = PR_TRUE; break; default: print_usage(); break; } } if (db_dir == NULL) db_dir = "testfiles/"; if (end_file == NULL) end_file = "testfiles/end.pem"; get_file(certs[numcerts++], end_file); if (sub_file != NULL) { get_file(certs[numcerts++], sub_file); } snprintf(pbuf, sizeof(pbuf), "sql:%s", db_dir); if (NSS_Initialize(pbuf, "", "", "secmod.db", 0x1) != SECSuccess) { printf("NSS_Initialize failed %d\n", PORT_GetError()); exit(-1); } if ((handle = CERT_GetDefaultCertDB()) == NULL) { printf("NULL handle\n"); exit(-1); } if (ocspcheck) { CERT_EnableOCSPChecking(handle); CERT_DisableOCSPDefaultResponder(handle); if (strict) CERT_SetOCSPFailureMode(ocspMode_FailureIsNotAVerificationFailure); } rv = CERT_ImportCerts(handle, 0, numcerts, certs, &certout, PR_FALSE, PR_FALSE, NULL); if (rv != SECSuccess) { printf("CERT_ImportCerts failed %d\n", PORT_GetError()); exit(-1); } vfy_log.count = 0; vfy_log.head = NULL; vfy_log.tail = NULL; vfy_log.arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); vfy_log2.count = 0; vfy_log2.head = NULL; vfy_log2.tail = NULL; vfy_log2.arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); if (use_pkix) { int in_idx = 0; CERTValInParam cvin[7]; CERTValOutParam cvout[3]; CERTCertList *trustcl = NULL; CERTRevocationFlags rev; PRUint64 revFlagsLeaf[2] = { 0, 0 }; PRUint64 revFlagsChain[2] = { 0, 0 }; zero(&cvin); /* ??? is this reasonable? */ zero(&cvout); /* ??? is this reasonable? */ zero(&rev); /* ??? is this reasonable? */ if (rightca_nick == NULL) rightca_nick = "root"; if ((trustcl = get_trust_certlist(handle, rightca_nick)) == NULL) { printf("Couldn't find trust anchor\n"); exit(-1); } cvin[in_idx].type = cert_pi_useAIACertFetch; cvin[in_idx++].value.scalar.b = PR_TRUE; cvin[in_idx].type = cert_pi_revocationFlags; cvin[in_idx++].value.pointer.revocation = &rev; cvin[in_idx].type = cert_pi_trustAnchors; cvin[in_idx++].value.pointer.chain = trustcl; cvin[in_idx].type = cert_pi_useOnlyTrustAnchors; cvin[in_idx++].value.scalar.b = PR_TRUE; set_rev_per_meth(&rev, revFlagsLeaf, revFlagsChain); set_rev_params(&rev, crlcheck, ocspcheck, strict); cvin[in_idx].type = cert_pi_end; cvout[0].type = cert_po_errorLog; cvout[0].value.pointer.log = &vfy_log; cur_log = &vfy_log; cvout[1].type = cert_po_certList; cvout[1].value.pointer.chain = NULL; cvout[2].type = cert_po_end; pkixout = &cvout[0]; pkixredo: rv = CERT_PKIXVerifyCert(*certout, pkixusage, cvin, cvout, NULL); //CERT_DestroyCertList(trustcl); } else { cur_log = &vfy_log; vfyredo: rv = CERT_VerifyCert(handle, *certout, PR_TRUE, usage, PR_Now(), NULL, cur_log); } if (rv != SECSuccess || cur_log->count > 0) { if (cur_log->count > 0 && cur_log->head != NULL) { fin = err_stat(cur_log->head); } else { fin = PORT_GetError(); } if (fin == SEC_ERROR_INADEQUATE_KEY_USAGE) { printf("SEC_ERROR_INADEQUATE_KEY_USAGE : Certificate key usage inadequate for attempted operation.\n" ); } else if (fin == SEC_ERROR_INADEQUATE_CERT_TYPE) { printf("SEC_ERROR_INADEQUATE_CERT_TYPE : Certificate type not approved for application.\n" ); } else { printf("OTHER : %ld", fin); } } if ((fin == SEC_ERROR_INADEQUATE_CERT_TYPE || fin == SEC_ERROR_INADEQUATE_KEY_USAGE) && retry_verify && !retried) { printf("Retrying verification\n"); fin = 0; retried = PR_TRUE; if (use_pkix) { pkixout[0].value.pointer.log = &vfy_log2; cur_log = &vfy_log2; pkixout[1].value.pointer.chain = NULL; if (pkixusage == certificateUsageSSLClient) { pkixusage = certificateUsageSSLServer; } else { pkixusage = certificateUsageSSLClient; } goto pkixredo; } else { if (usage == certUsageSSLClient) { usage = certUsageSSLServer; } else { usage = certUsageSSLClient; } goto vfyredo; } } PORT_FreeArena(vfy_log.arena, PR_FALSE); PORT_FreeArena(vfy_log2.arena, PR_FALSE); NSS_Shutdown(); exit(fin == 0 ? 0 : 1); }
SECStatus NSS_CMSSignedData_ImportCerts(NSSCMSSignedData *sigd, CERTCertDBHandle *certdb, SECCertUsage certusage, PRBool keepcerts) { int certcount; CERTCertificate **certArray = NULL; CERTCertList *certList = NULL; CERTCertListNode *node; SECStatus rv; SECItem **rawArray; int i; PRTime now; if (!sigd) { PORT_SetError(SEC_ERROR_INVALID_ARGS); return SECFailure; } certcount = NSS_CMSArray_Count((void **)sigd->rawCerts); /* get the certs in the temp DB */ rv = CERT_ImportCerts(certdb, certusage, certcount, sigd->rawCerts, &certArray, PR_FALSE, PR_FALSE, NULL); if (rv != SECSuccess) { goto loser; } /* save the certs so they don't get destroyed */ for (i = 0; i < certcount; i++) { CERTCertificate *cert = certArray[i]; if (cert) NSS_CMSSignedData_AddTempCertificate(sigd, cert); } if (!keepcerts) { goto done; } /* build a CertList for filtering */ certList = CERT_NewCertList(); if (certList == NULL) { rv = SECFailure; goto loser; } for (i = 0; i < certcount; i++) { CERTCertificate *cert = certArray[i]; if (cert) cert = CERT_DupCertificate(cert); if (cert) CERT_AddCertToListTail(certList, cert); } /* filter out the certs we don't want */ rv = CERT_FilterCertListByUsage(certList, certusage, PR_FALSE); if (rv != SECSuccess) { goto loser; } /* go down the remaining list of certs and verify that they have * valid chains, then import them. */ now = PR_Now(); for (node = CERT_LIST_HEAD(certList); !CERT_LIST_END(node, certList); node = CERT_LIST_NEXT(node)) { CERTCertificateList *certChain; if (CERT_VerifyCert(certdb, node->cert, PR_TRUE, certusage, now, NULL, NULL) != SECSuccess) { continue; } certChain = CERT_CertChainFromCert(node->cert, certusage, PR_FALSE); if (!certChain) { continue; } /* * CertChain returns an array of SECItems, import expects an array of * SECItem pointers. Create the SECItem Pointers from the array of * SECItems. */ rawArray = (SECItem **)PORT_Alloc(certChain->len * sizeof(SECItem *)); if (!rawArray) { CERT_DestroyCertificateList(certChain); continue; } for (i = 0; i < certChain->len; i++) { rawArray[i] = &certChain->certs[i]; } (void)CERT_ImportCerts(certdb, certusage, certChain->len, rawArray, NULL, keepcerts, PR_FALSE, NULL); PORT_Free(rawArray); CERT_DestroyCertificateList(certChain); } rv = SECSuccess; /* XXX CRL handling */ done: if (sigd->signerInfos != NULL) { /* fill in all signerinfo's certs */ for (i = 0; sigd->signerInfos[i] != NULL; i++) (void)NSS_CMSSignerInfo_GetSigningCertificate( sigd->signerInfos[i], certdb); } loser: /* now free everything */ if (certArray) { CERT_DestroyCertArray(certArray, certcount); } if (certList) { CERT_DestroyCertList(certList); } return rv; }
/* * SecCmsSignerInfoSign - sign something * */ OSStatus SecCmsSignerInfoSign(SecCmsSignerInfoRef signerinfo, CSSM_DATA_PTR digest, CSSM_DATA_PTR contentType) { SecCertificateRef cert; SecPrivateKeyRef privkey = NULL; SECOidTag digestalgtag; SECOidTag pubkAlgTag; CSSM_DATA signature = { 0 }; OSStatus rv; PLArenaPool *poolp, *tmppoolp = NULL; const SECAlgorithmID *algID; SECAlgorithmID freeAlgID; //CERTSubjectPublicKeyInfo *spki; PORT_Assert (digest != NULL); poolp = signerinfo->cmsg->poolp; switch (signerinfo->signerIdentifier.identifierType) { case SecCmsSignerIDIssuerSN: privkey = signerinfo->signingKey; signerinfo->signingKey = NULL; cert = signerinfo->cert; if (SecCertificateGetAlgorithmID(cert,&algID)) { PORT_SetError(SEC_ERROR_INVALID_ALGORITHM); goto loser; } break; case SecCmsSignerIDSubjectKeyID: privkey = signerinfo->signingKey; signerinfo->signingKey = NULL; #if 0 spki = SECKEY_CreateSubjectPublicKeyInfo(signerinfo->pubKey); SECKEY_DestroyPublicKey(signerinfo->pubKey); signerinfo->pubKey = NULL; SECOID_CopyAlgorithmID(NULL, &freeAlgID, &spki->algorithm); SECKEY_DestroySubjectPublicKeyInfo(spki); algID = &freeAlgID; #else #if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR)) if (SecKeyGetAlgorithmID(signerinfo->pubKey,&algID)) { #else /* TBD: Unify this code. Currently, iOS has an incompatible * SecKeyGetAlgorithmID implementation. */ if (true) { #endif PORT_SetError(SEC_ERROR_INVALID_ALGORITHM); goto loser; } CFRelease(signerinfo->pubKey); signerinfo->pubKey = NULL; #endif break; default: PORT_SetError(SEC_ERROR_UNSUPPORTED_MESSAGE_TYPE); goto loser; } digestalgtag = SecCmsSignerInfoGetDigestAlgTag(signerinfo); /* * XXX I think there should be a cert-level interface for this, * so that I do not have to know about subjectPublicKeyInfo... */ pubkAlgTag = SECOID_GetAlgorithmTag(algID); if (signerinfo->signerIdentifier.identifierType == SecCmsSignerIDSubjectKeyID) { SECOID_DestroyAlgorithmID(&freeAlgID, PR_FALSE); } #if 0 // @@@ Not yet /* Fortezza MISSI have weird signature formats. * Map them to standard DSA formats */ pubkAlgTag = PK11_FortezzaMapSig(pubkAlgTag); #endif if (signerinfo->authAttr != NULL) { CSSM_DATA encoded_attrs; /* find and fill in the message digest attribute. */ rv = SecCmsAttributeArraySetAttr(poolp, &(signerinfo->authAttr), SEC_OID_PKCS9_MESSAGE_DIGEST, digest, PR_FALSE); if (rv != SECSuccess) goto loser; if (contentType != NULL) { /* if the caller wants us to, find and fill in the content type attribute. */ rv = SecCmsAttributeArraySetAttr(poolp, &(signerinfo->authAttr), SEC_OID_PKCS9_CONTENT_TYPE, contentType, PR_FALSE); if (rv != SECSuccess) goto loser; } if ((tmppoolp = PORT_NewArena (1024)) == NULL) { PORT_SetError(SEC_ERROR_NO_MEMORY); goto loser; } /* * Before encoding, reorder the attributes so that when they * are encoded, they will be conforming DER, which is required * to have a specific order and that is what must be used for * the hash/signature. We do this here, rather than building * it into EncodeAttributes, because we do not want to do * such reordering on incoming messages (which also uses * EncodeAttributes) or our old signatures (and other "broken" * implementations) will not verify. So, we want to guarantee * that we send out good DER encodings of attributes, but not * to expect to receive them. */ if (SecCmsAttributeArrayReorder(signerinfo->authAttr) != SECSuccess) goto loser; encoded_attrs.Data = NULL; encoded_attrs.Length = 0; if (SecCmsAttributeArrayEncode(tmppoolp, &(signerinfo->authAttr), &encoded_attrs) == NULL) goto loser; rv = SEC_SignData(&signature, encoded_attrs.Data, (int)encoded_attrs.Length, privkey, digestalgtag, pubkAlgTag); PORT_FreeArena(tmppoolp, PR_FALSE); /* awkward memory management :-( */ tmppoolp = 0; } else { rv = SGN_Digest(privkey, digestalgtag, pubkAlgTag, &signature, digest); } SECKEY_DestroyPrivateKey(privkey); privkey = NULL; if (rv != SECSuccess) goto loser; if (SECITEM_CopyItem(poolp, &(signerinfo->encDigest), &signature) != SECSuccess) goto loser; SECITEM_FreeItem(&signature, PR_FALSE); if(pubkAlgTag == SEC_OID_EC_PUBLIC_KEY) { /* * RFC 3278 section section 2.1.1 states that the signatureAlgorithm * field contains the full ecdsa-with-SHA1 OID, not plain old ecPublicKey * as would appear in other forms of signed datas. However Microsoft doesn't * do this, it puts ecPublicKey there, and if we put ecdsa-with-SHA1 there, * MS can't verify - presumably because it takes the digest of the digest * before feeding it to ECDSA. * We handle this with a preference; default if it's not there is * "Microsoft compatibility mode". */ if(!SecCmsMsEcdsaCompatMode()) { pubkAlgTag = SEC_OID_ECDSA_WithSHA1; } /* else violating the spec for compatibility */ } if (SECOID_SetAlgorithmID(poolp, &(signerinfo->digestEncAlg), pubkAlgTag, NULL) != SECSuccess) goto loser; return SECSuccess; loser: if (signature.Length != 0) SECITEM_FreeItem (&signature, PR_FALSE); if (privkey) SECKEY_DestroyPrivateKey(privkey); if (tmppoolp) PORT_FreeArena(tmppoolp, PR_FALSE); return SECFailure; } OSStatus SecCmsSignerInfoVerifyCertificate(SecCmsSignerInfoRef signerinfo, SecKeychainRef keychainOrArray, CFTypeRef policies, SecTrustRef *trustRef) { SecCertificateRef cert; CFAbsoluteTime stime; OSStatus rv; CSSM_DATA_PTR *otherCerts; if ((cert = SecCmsSignerInfoGetSigningCertificate(signerinfo, keychainOrArray)) == NULL) { dprintf("SecCmsSignerInfoVerifyCertificate: no signing cert\n"); signerinfo->verificationStatus = SecCmsVSSigningCertNotFound; return SECFailure; } /* * Get and convert the signing time; if available, it will be used * both on the cert verification and for importing the sender * email profile. */ CFTypeRef timeStampPolicies=SecPolicyCreateAppleTimeStampingAndRevocationPolicies(policies); if (SecCmsSignerInfoGetTimestampTimeWithPolicy(signerinfo, timeStampPolicies, &stime) != SECSuccess) if (SecCmsSignerInfoGetSigningTime(signerinfo, &stime) != SECSuccess) stime = CFAbsoluteTimeGetCurrent(); CFReleaseSafe(timeStampPolicies); rv = SecCmsSignedDataRawCerts(signerinfo->sigd, &otherCerts); if(rv) { return rv; } rv = CERT_VerifyCert(keychainOrArray, cert, otherCerts, policies, stime, trustRef); dprintfRC("SecCmsSignerInfoVerifyCertificate after vfy: certp %p cert.rc %d\n", cert, (int)CFGetRetainCount(cert)); if (rv || !trustRef) { if (PORT_GetError() == SEC_ERROR_UNTRUSTED_CERT) { /* Signature or digest level verificationStatus errors should supercede certificate level errors, so only change the verificationStatus if the status was GoodSignature. */ if (signerinfo->verificationStatus == SecCmsVSGoodSignature) signerinfo->verificationStatus = SecCmsVSSigningCertNotTrusted; } } /* FIXME isn't this leaking the cert? */ dprintf("SecCmsSignerInfoVerifyCertificate: CertVerify rtn %d\n", (int)rv); return rv; }
// Based on nsNSSCertificateDB::handleCACertDownload, minus the UI bits. bool ImportCACerts(const net::CertificateList& certificates, net::X509Certificate* root, net::NSSCertDatabase::TrustBits trustBits, net::NSSCertDatabase::ImportCertFailureList* not_imported) { if (certificates.empty() || !root) return false; crypto::ScopedPK11Slot slot(crypto::GetPublicNSSKeySlot()); if (!slot.get()) { LOG(ERROR) << "Couldn't get internal key slot!"; return false; } // Mozilla had some code here to check if a perm version of the cert exists // already and use that, but CERT_NewTempCertificate actually does that // itself, so we skip it here. if (!CERT_IsCACert(root->os_cert_handle(), NULL)) { not_imported->push_back(net::NSSCertDatabase::ImportCertFailure( root, net::ERR_IMPORT_CA_CERT_NOT_CA)); } else if (root->os_cert_handle()->isperm) { // Mozilla just returns here, but we continue in case there are other certs // in the list which aren't already imported. // TODO(mattm): should we set/add trust if it differs from the present // settings? not_imported->push_back(net::NSSCertDatabase::ImportCertFailure( root, net::ERR_IMPORT_CERT_ALREADY_EXISTS)); } else { // Mozilla uses CERT_AddTempCertToPerm, however it is privately exported, // and it doesn't take the slot as an argument either. Instead, we use // PK11_ImportCert and CERT_ChangeCertTrust. SECStatus srv = PK11_ImportCert( slot.get(), root->os_cert_handle(), CK_INVALID_HANDLE, root->GetDefaultNickname(net::CA_CERT).c_str(), PR_FALSE /* includeTrust (unused) */); if (srv != SECSuccess) { LOG(ERROR) << "PK11_ImportCert failed with error " << PORT_GetError(); return false; } if (!SetCertTrust(root, net::CA_CERT, trustBits)) return false; } PRTime now = PR_Now(); // Import additional delivered certificates that can be verified. // This is sort of merged in from Mozilla's ImportValidCACertsInList. Mozilla // uses CERT_FilterCertListByUsage to filter out non-ca certs, but we want to // keep using X509Certificates, so that we can use them to build the // |not_imported| result. So, we keep using our net::CertificateList and // filter it ourself. for (size_t i = 0; i < certificates.size(); i++) { const scoped_refptr<net::X509Certificate>& cert = certificates[i]; if (cert == root) { // we already processed that one continue; } // Mozilla uses CERT_FilterCertListByUsage(certList, certUsageAnyCA, // PR_TRUE). Afaict, checking !CERT_IsCACert on each cert is equivalent. if (!CERT_IsCACert(cert->os_cert_handle(), NULL)) { not_imported->push_back(net::NSSCertDatabase::ImportCertFailure( cert, net::ERR_IMPORT_CA_CERT_NOT_CA)); VLOG(1) << "skipping cert (non-ca)"; continue; } if (cert->os_cert_handle()->isperm) { not_imported->push_back(net::NSSCertDatabase::ImportCertFailure( cert, net::ERR_IMPORT_CERT_ALREADY_EXISTS)); VLOG(1) << "skipping cert (perm)"; continue; } if (CERT_VerifyCert(CERT_GetDefaultCertDB(), cert->os_cert_handle(), PR_TRUE, certUsageVerifyCA, now, NULL, NULL) != SECSuccess) { // TODO(mattm): use better error code (map PORT_GetError to an appropriate // error value). (maybe make MapSecurityError or MapCertErrorToCertStatus // public.) not_imported->push_back(net::NSSCertDatabase::ImportCertFailure( cert, net::ERR_FAILED)); VLOG(1) << "skipping cert (verify) " << PORT_GetError(); continue; } // Mozilla uses CERT_ImportCerts, which doesn't take a slot arg. We use // PK11_ImportCert instead. SECStatus srv = PK11_ImportCert( slot.get(), cert->os_cert_handle(), CK_INVALID_HANDLE, cert->GetDefaultNickname(net::CA_CERT).c_str(), PR_FALSE /* includeTrust (unused) */); if (srv != SECSuccess) { LOG(ERROR) << "PK11_ImportCert failed with error " << PORT_GetError(); // TODO(mattm): Should we bail or continue on error here? Mozilla doesn't // check error code at all. not_imported->push_back(net::NSSCertDatabase::ImportCertFailure( cert, net::ERR_IMPORT_CA_CERT_FAILED)); } } // Any errors importing individual certs will be in listed in |not_imported|. return true; }
/* * [noscript] void importEmailCertificates(in charPtr data, in unsigned long length, * in nsIInterfaceRequestor ctx); */ NS_IMETHODIMP nsNSSCertificateDB::ImportEmailCertificate(PRUint8 * data, PRUint32 length, nsIInterfaceRequestor *ctx) { nsNSSShutDownPreventionLock locker; SECStatus srv = SECFailure; nsresult nsrv = NS_OK; CERTCertDBHandle *certdb; CERTCertificate **certArray = NULL; CERTCertList *certList = NULL; CERTCertListNode *node; PRTime now; SECCertUsage certusage; SECItem **rawArray; int numcerts; int i; PRArenaPool *arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); if (!arena) return NS_ERROR_OUT_OF_MEMORY; CERTDERCerts *certCollection = getCertsFromPackage(arena, data, length); if (!certCollection) { PORT_FreeArena(arena, PR_FALSE); return NS_ERROR_FAILURE; } certdb = CERT_GetDefaultCertDB(); certusage = certUsageEmailRecipient; numcerts = certCollection->numcerts; rawArray = (SECItem **) PORT_Alloc(sizeof(SECItem *) * numcerts); if ( !rawArray ) { nsrv = NS_ERROR_FAILURE; goto loser; } for (i=0; i < numcerts; i++) { rawArray[i] = &certCollection->rawCerts[i]; } srv = CERT_ImportCerts(certdb, certusage, numcerts, rawArray, &certArray, PR_FALSE, PR_FALSE, NULL); PORT_Free(rawArray); rawArray = NULL; if (srv != SECSuccess) { nsrv = NS_ERROR_FAILURE; goto loser; } // build a CertList for filtering certList = CERT_NewCertList(); if (certList == NULL) { nsrv = NS_ERROR_FAILURE; goto loser; } for (i=0; i < numcerts; i++) { CERTCertificate *cert = certArray[i]; if (cert) cert = CERT_DupCertificate(cert); if (cert) CERT_AddCertToListTail(certList, cert); } /* go down the remaining list of certs and verify that they have * valid chains, then import them. */ now = PR_Now(); for (node = CERT_LIST_HEAD(certList); !CERT_LIST_END(node,certList); node = CERT_LIST_NEXT(node)) { bool alert_and_skip = false; if (!node->cert) { continue; } if (CERT_VerifyCert(certdb, node->cert, PR_TRUE, certusage, now, ctx, NULL) != SECSuccess) { alert_and_skip = true; } CERTCertificateList *certChain = nsnull; CERTCertificateListCleaner chainCleaner(certChain); if (!alert_and_skip) { certChain = CERT_CertChainFromCert(node->cert, certusage, PR_FALSE); if (!certChain) { alert_and_skip = true; } } if (alert_and_skip) { nsCOMPtr<nsIX509Cert> certToShow = new nsNSSCertificate(node->cert); DisplayCertificateAlert(ctx, "NotImportingUnverifiedCert", certToShow); continue; } /* * CertChain returns an array of SECItems, import expects an array of * SECItem pointers. Create the SECItem Pointers from the array of * SECItems. */ rawArray = (SECItem **) PORT_Alloc(certChain->len * sizeof(SECItem *)); if (!rawArray) { continue; } for (i=0; i < certChain->len; i++) { rawArray[i] = &certChain->certs[i]; } CERT_ImportCerts(certdb, certusage, certChain->len, rawArray, NULL, PR_TRUE, PR_FALSE, NULL); CERT_SaveSMimeProfile(node->cert, NULL, NULL); PORT_Free(rawArray); } loser: if (certArray) { CERT_DestroyCertArray(certArray, numcerts); } if (certList) { CERT_DestroyCertList(certList); } if (arena) PORT_FreeArena(arena, PR_TRUE); return nsrv; }
nsresult nsNSSCertificateDB::ImportValidCACertsInList(CERTCertList *certList, nsIInterfaceRequestor *ctx) { SECItem **rawArray; /* filter out the certs we don't want */ SECStatus srv = CERT_FilterCertListByUsage(certList, certUsageAnyCA, PR_TRUE); if (srv != SECSuccess) { return NS_ERROR_FAILURE; } /* go down the remaining list of certs and verify that they have * valid chains, if yes, then import. */ PRTime now = PR_Now(); CERTCertListNode *node; for (node = CERT_LIST_HEAD(certList); !CERT_LIST_END(node,certList); node = CERT_LIST_NEXT(node)) { bool alert_and_skip = false; if (CERT_VerifyCert(CERT_GetDefaultCertDB(), node->cert, PR_TRUE, certUsageVerifyCA, now, ctx, NULL) != SECSuccess) { alert_and_skip = true; } CERTCertificateList *certChain = nsnull; CERTCertificateListCleaner chainCleaner(certChain); if (!alert_and_skip) { certChain = CERT_CertChainFromCert(node->cert, certUsageAnyCA, PR_FALSE); if (!certChain) { alert_and_skip = true; } } if (alert_and_skip) { nsCOMPtr<nsIX509Cert> certToShow = new nsNSSCertificate(node->cert); DisplayCertificateAlert(ctx, "NotImportingUnverifiedCert", certToShow); continue; } /* * CertChain returns an array of SECItems, import expects an array of * SECItem pointers. Create the SECItem Pointers from the array of * SECItems. */ rawArray = (SECItem **) PORT_Alloc(certChain->len * sizeof(SECItem *)); if (!rawArray) { continue; } for (int i=0; i < certChain->len; i++) { rawArray[i] = &certChain->certs[i]; } CERT_ImportCerts(CERT_GetDefaultCertDB(), certUsageAnyCA, certChain->len, rawArray, NULL, PR_TRUE, PR_TRUE, NULL); PORT_Free(rawArray); } return NS_OK; }
/* * XXXX the following needs to be done in the S/MIME layer code * after signature of a signerinfo is verified */ SECStatus NSS_SMIMESignerInfo_SaveSMIMEProfile(NSSCMSSignerInfo *signerinfo) { CERTCertificate *cert = NULL; SECItem *profile = NULL; NSSCMSAttribute *attr; SECItem *stime = NULL; SECItem *ekp; CERTCertDBHandle *certdb; int save_error; SECStatus rv; PRBool must_free_cert = PR_FALSE; certdb = CERT_GetDefaultCertDB(); /* sanity check - see if verification status is ok (unverified does not count...) */ if (signerinfo->verificationStatus != NSSCMSVS_GoodSignature) return SECFailure; /* find preferred encryption cert */ if (!NSS_CMSArray_IsEmpty((void **)signerinfo->authAttr) && (attr = NSS_CMSAttributeArray_FindAttrByOidTag(signerinfo->authAttr, SEC_OID_SMIME_ENCRYPTION_KEY_PREFERENCE, PR_TRUE)) != NULL) { /* we have a SMIME_ENCRYPTION_KEY_PREFERENCE attribute! */ ekp = NSS_CMSAttribute_GetValue(attr); if (ekp == NULL) return SECFailure; /* we assume that all certs coming with the message have been imported to the */ /* temporary database */ cert = NSS_SMIMEUtil_GetCertFromEncryptionKeyPreference(certdb, ekp); if (cert == NULL) return SECFailure; must_free_cert = PR_TRUE; } if (cert == NULL) { /* no preferred cert found? * find the cert the signerinfo is signed with instead */ cert = NSS_CMSSignerInfo_GetSigningCertificate(signerinfo, certdb); if (cert == NULL || cert->emailAddr == NULL || !cert->emailAddr[0]) return SECFailure; } /* verify this cert for encryption (has been verified for signing so far) */ /* don't verify this cert for encryption. It may just be a signing cert. * that's OK, we can still save the S/MIME profile. The encryption cert * should have already been saved */ #ifdef notdef if (CERT_VerifyCert(certdb, cert, PR_TRUE, certUsageEmailRecipient, PR_Now(), signerinfo->cmsg->pwfn_arg, NULL) != SECSuccess) { if (must_free_cert) CERT_DestroyCertificate(cert); return SECFailure; } #endif /* XXX store encryption cert permanently? */ /* * Remember the current error set because we do not care about * anything set by the functions we are about to call. */ save_error = PORT_GetError(); if (!NSS_CMSArray_IsEmpty((void **)signerinfo->authAttr)) { attr = NSS_CMSAttributeArray_FindAttrByOidTag(signerinfo->authAttr, SEC_OID_PKCS9_SMIME_CAPABILITIES, PR_TRUE); profile = NSS_CMSAttribute_GetValue(attr); attr = NSS_CMSAttributeArray_FindAttrByOidTag(signerinfo->authAttr, SEC_OID_PKCS9_SIGNING_TIME, PR_TRUE); stime = NSS_CMSAttribute_GetValue(attr); } rv = CERT_SaveSMimeProfile (cert, profile, stime); if (must_free_cert) CERT_DestroyCertificate(cert); /* * Restore the saved error in case the calls above set a new * one that we do not actually care about. */ PORT_SetError (save_error); return rv; }
/* * XXXX the following needs to be done in the S/MIME layer code * after signature of a signerinfo is verified */ OSStatus SecCmsSignerInfoSaveSMIMEProfile(SecCmsSignerInfoRef signerinfo) { SecCertificateRef cert = NULL; CSSM_DATA_PTR profile = NULL; SecCmsAttribute *attr; CSSM_DATA_PTR utc_stime = NULL; CSSM_DATA_PTR ekp; int save_error; OSStatus rv; Boolean must_free_cert = PR_FALSE; OSStatus status; SecKeychainRef keychainOrArray; status = SecKeychainCopyDefault(&keychainOrArray); /* sanity check - see if verification status is ok (unverified does not count...) */ if (signerinfo->verificationStatus != SecCmsVSGoodSignature) return SECFailure; /* find preferred encryption cert */ if (!SecCmsArrayIsEmpty((void **)signerinfo->authAttr) && (attr = SecCmsAttributeArrayFindAttrByOidTag(signerinfo->authAttr, SEC_OID_SMIME_ENCRYPTION_KEY_PREFERENCE, PR_TRUE)) != NULL) { /* we have a SMIME_ENCRYPTION_KEY_PREFERENCE attribute! */ ekp = SecCmsAttributeGetValue(attr); if (ekp == NULL) return SECFailure; /* we assume that all certs coming with the message have been imported to the */ /* temporary database */ cert = SecSMIMEGetCertFromEncryptionKeyPreference(keychainOrArray, ekp); if (cert == NULL) return SECFailure; must_free_cert = PR_TRUE; } if (cert == NULL) { /* no preferred cert found? * find the cert the signerinfo is signed with instead */ CFStringRef emailAddress=NULL; cert = SecCmsSignerInfoGetSigningCertificate(signerinfo, keychainOrArray); if (cert == NULL) return SECFailure; if (SecCertificateGetEmailAddress(cert,&emailAddress)) return SECFailure; } /* verify this cert for encryption (has been verified for signing so far) */ /* don't verify this cert for encryption. It may just be a signing cert. * that's OK, we can still save the S/MIME profile. The encryption cert * should have already been saved */ #ifdef notdef if (CERT_VerifyCert(keychainOrArray, cert, certUsageEmailRecipient, CFAbsoluteTimeGetCurrent(), NULL) != SECSuccess) { if (must_free_cert) CERT_DestroyCertificate(cert); return SECFailure; } #endif /* XXX store encryption cert permanently? */ /* * Remember the current error set because we do not care about * anything set by the functions we are about to call. */ save_error = PORT_GetError(); if (!SecCmsArrayIsEmpty((void **)signerinfo->authAttr)) { attr = SecCmsAttributeArrayFindAttrByOidTag(signerinfo->authAttr, SEC_OID_PKCS9_SMIME_CAPABILITIES, PR_TRUE); profile = SecCmsAttributeGetValue(attr); attr = SecCmsAttributeArrayFindAttrByOidTag(signerinfo->authAttr, SEC_OID_PKCS9_SIGNING_TIME, PR_TRUE); utc_stime = SecCmsAttributeGetValue(attr); } rv = CERT_SaveSMimeProfile (cert, profile, utc_stime); if (must_free_cert) CERT_DestroyCertificate(cert); /* * Restore the saved error in case the calls above set a new * one that we do not actually care about. */ PORT_SetError (save_error); return rv; }
static SECStatus ClassicVerifyCert(CERTCertificate * cert, const SECCertificateUsage usage, const PRTime time, nsIInterfaceRequestor * pinArg, /*optional out*/ CERTCertList **validationChain, /*optional out*/ CERTVerifyLog *verifyLog) { SECStatus rv; SECCertUsage enumUsage; if (validationChain) { switch(usage){ case certificateUsageSSLClient: enumUsage = certUsageSSLClient; break; case certificateUsageSSLServer: enumUsage = certUsageSSLServer; break; case certificateUsageSSLServerWithStepUp: enumUsage = certUsageSSLServerWithStepUp; break; case certificateUsageSSLCA: enumUsage = certUsageSSLCA; break; case certificateUsageEmailSigner: enumUsage = certUsageEmailSigner; break; case certificateUsageEmailRecipient: enumUsage = certUsageEmailRecipient; break; case certificateUsageObjectSigner: enumUsage = certUsageObjectSigner; break; case certificateUsageUserCertImport: enumUsage = certUsageUserCertImport; break; case certificateUsageVerifyCA: enumUsage = certUsageVerifyCA; break; case certificateUsageProtectedObjectSigner: enumUsage = certUsageProtectedObjectSigner; break; case certificateUsageStatusResponder: enumUsage = certUsageStatusResponder; break; case certificateUsageAnyCA: enumUsage = certUsageAnyCA; break; default: return SECFailure; } } if (usage == certificateUsageSSLServer) { /* SSL server cert verification has always used CERT_VerifyCert, so we * continue to use it for SSL cert verification to minimize the risk of * there being any differnce in results between CERT_VerifyCert and * CERT_VerifyCertificate. */ rv = CERT_VerifyCert(CERT_GetDefaultCertDB(), cert, true, certUsageSSLServer, time, pinArg, verifyLog); } else { rv = CERT_VerifyCertificate(CERT_GetDefaultCertDB(), cert, true, usage, time, pinArg, verifyLog, nullptr); } if (rv == SECSuccess && validationChain) { PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("VerifyCert: getting chain in 'classic' \n")); *validationChain = CERT_GetCertChainFromCert(cert, time, enumUsage); if (!*validationChain) { rv = SECFailure; } } return rv; }
/********************************************************************* * * L i s t C e r t s */ int ListCerts(char *key, int list_certs) { int failed = 0; SECStatus rv; char *ugly_list; CERTCertDBHandle *db; CERTCertificate *cert; CERTVerifyLog errlog; errlog.arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); if (errlog.arena == NULL) { out_of_memory(); } errlog.head = NULL; errlog.tail = NULL; errlog.count = 0; ugly_list = PORT_ZAlloc(16); if (ugly_list == NULL) { out_of_memory(); } *ugly_list = 0; db = CERT_GetDefaultCertDB(); if (list_certs == 2) { PR_fprintf(outputFD, "\nS Certificates\n"); PR_fprintf(outputFD, "- ------------\n"); } else { PR_fprintf(outputFD, "\nObject signing certificates\n"); PR_fprintf(outputFD, "---------------------------------------\n"); } num_trav_certs = 0; /* Traverse ALL tokens in all slots, authenticating to them all */ rv = PK11_TraverseSlotCerts(cert_trav_callback, (void *)&list_certs, &pwdata); if (rv) { PR_fprintf(outputFD, "**Traverse of ALL slots & tokens failed**\n"); return -1; } if (num_trav_certs == 0) { PR_fprintf(outputFD, "You don't appear to have any object signing certificates.\n"); } if (list_certs == 2) { PR_fprintf(outputFD, "- ------------\n"); } else { PR_fprintf(outputFD, "---------------------------------------\n"); } if (list_certs == 1) { PR_fprintf(outputFD, "For a list including CA's, use \"%s -L\"\n", PROGRAM_NAME); } if (list_certs == 2) { PR_fprintf(outputFD, "Certificates that can be used to sign objects have *'s to " "their left.\n"); } if (key) { /* Do an analysis of the given cert */ cert = PK11_FindCertFromNickname(key, &pwdata); if (cert) { PR_fprintf(outputFD, "\nThe certificate with nickname \"%s\" was found:\n", cert->nickname); PR_fprintf(outputFD, "\tsubject name: %s\n", cert->subjectName); PR_fprintf(outputFD, "\tissuer name: %s\n", cert->issuerName); PR_fprintf(outputFD, "\n"); rv = CERT_CertTimesValid(cert); if (rv != SECSuccess) { PR_fprintf(outputFD, "**This certificate is expired**\n"); } else { PR_fprintf(outputFD, "This certificate is not expired.\n"); } rv = CERT_VerifyCert(db, cert, PR_TRUE, certUsageObjectSigner, PR_Now(), &pwdata, &errlog); if (rv != SECSuccess) { failed = 1; if (errlog.count > 0) { PR_fprintf(outputFD, "**Certificate validation failed for the " "following reason(s):**\n"); } else { PR_fprintf(outputFD, "**Certificate validation failed**"); } } else { PR_fprintf(outputFD, "This certificate is valid.\n"); } displayVerifyLog(&errlog); } else { failed = 1; PR_fprintf(outputFD, "The certificate with nickname \"%s\" was NOT FOUND\n", key); } } if (errlog.arena != NULL) { PORT_FreeArena(errlog.arena, PR_FALSE); } if (failed) { return -1; } return 0; }
/* * Callback from SSL for checking a (possibly) expired * certificate the peer presents. * * obj - a jobject -> instance of a class implementing * the SSLCertificateApprovalCallback interface */ SECStatus JSSL_JavaCertAuthCallback(void *arg, PRFileDesc *fd, PRBool checkSig, PRBool isServer) { CERTCertificate *peerCert=NULL; CERTVerifyLog log; JNIEnv *env; jobject validityStatus; jmethodID addReasonMethod; int certUsage; int checkcn_rv; jmethodID approveMethod; jboolean result; char *hostname=NULL; SECStatus retval = SECFailure; SECStatus verificationResult; PR_ASSERT(arg != NULL); PR_ASSERT(fd != NULL); /* initialize logging structures */ log.arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); if( log.arena == NULL ) return SECFailure; log.head = NULL; log.tail = NULL; log.count = 0; /* get the JNI environment */ if((*JSS_javaVM)->AttachCurrentThread(JSS_javaVM, (void**)&env, NULL) != 0){ PR_ASSERT(PR_FALSE); goto finish; } /* First, get a handle on the cert that the peer presented */ peerCert = SSL_PeerCertificate(fd); /* if peer didn't present a cert, why am I called? */ if (peerCert == NULL) goto finish; certUsage = isServer ? certUsageSSLClient : certUsageSSLServer; /* * verify it against current time - (can't use * CERT_VerifyCertNow() since it doesn't allow passing of * logging parameter) */ verificationResult = CERT_VerifyCert( CERT_GetDefaultCertDB(), peerCert, checkSig, certUsage, PR_Now(), NULL /*pinarg*/, &log); if (verificationResult == SECSuccess && log.count > 0) { verificationResult = SECFailure; } /* * Verify the domain name of the cert. */ hostname = SSL_RevealURL(fd); /* really is a hostname, not a URL */ if (hostname && hostname[0]) { checkcn_rv = CERT_VerifyCertName(peerCert, hostname); PORT_Free(hostname); } else { checkcn_rv = SECFailure; } if (checkcn_rv != SECSuccess) { addToVerifyLog(env, &log,peerCert,SSL_ERROR_BAD_CERT_DOMAIN,0); if((*env)->ExceptionOccurred(env) != NULL) goto finish; verificationResult = SECFailure; } /* * create a new ValidityStatus object */ { jclass clazz; jmethodID cons; clazz = (*env)->FindClass(env, SSLCERT_APP_CB_VALIDITY_STATUS_CLASS); if( clazz == NULL ) goto finish; cons = (*env)->GetMethodID(env, clazz, PLAIN_CONSTRUCTOR, PLAIN_CONSTRUCTOR_SIG); if( cons == NULL ) goto finish; validityStatus = (*env)->NewObject(env, clazz, cons); if( validityStatus == NULL ) { goto finish; } /* get the addReason methodID while we're at it */ addReasonMethod = (*env)->GetMethodID(env, clazz, SSLCERT_APP_CB_VALIDITY_STATUS_ADD_REASON_NAME, SSLCERT_APP_CB_VALIDITY_STATUS_ADD_REASON_SIG); if( addReasonMethod == NULL ) { goto finish; } } /* * Load up the ValidityStatus object with all the reasons for failure */ if (verificationResult == SECFailure) { CERTVerifyLogNode *node; int error; CERTCertificate *errorcert=NULL; int depth; jobject ninjacert; node = log.head; while (node) { error = node->error; errorcert = node->cert; node->cert = NULL; depth = node->depth; ninjacert = JSS_PK11_wrapCert(env,&errorcert); (*env)->CallVoidMethod(env, validityStatus, addReasonMethod, error, ninjacert, depth ); node = node->next; } } /* * Call the approval callback */ { jobject approvalCallbackObj; jclass approvalCallbackClass; jobject peerninjacert; approvalCallbackObj = (jobject) arg; approvalCallbackClass = (*env)->GetObjectClass(env,approvalCallbackObj); approveMethod = (*env)->GetMethodID( env, approvalCallbackClass, SSLCERT_APP_CB_APPROVE_NAME, SSLCERT_APP_CB_APPROVE_SIG); if( approveMethod == NULL ) { PR_ASSERT(PR_FALSE); goto finish; } peerninjacert = JSS_PK11_wrapCert(env,&peerCert); if( peerninjacert == NULL) { PR_ASSERT(PR_FALSE); goto finish; } result = (*env)->CallBooleanMethod(env, approvalCallbackObj, approveMethod, peerninjacert, validityStatus); if( result == JNI_TRUE ) { retval = SECSuccess; } } finish: if( peerCert != NULL ) { CERT_DestroyCertificate(peerCert); } PORT_FreeArena(log.arena, PR_FALSE); return retval; }