| RSA BSAFE Cert-C |
Certificate Components for C |
| Crypto-C 6.2.1 Developer's Guide | ||
| Search |
/* $Id: ocsp.c,v 1.5 2005/02/25 06:09:30 alockwoo Exp $ */ /* ocsp.c ** Copyright (c) 2001-2003, RSA Security Inc. ** ** This file is used to demonstrate how to interface to an RSA Security ** licensed development product. You have a royalty-free right to use, ** modify, reproduce and distribute this demonstration file (including ** any modified version), provided that you agree that RSA Security has ** no warranty, implied or otherwise, or liability for this demonstration ** file or any modified version. ** ** This program prompts the user to provide a collection of trusted certs, ** and other intermediate certs, which can be used for cert path processing ** locally. After the cert path is constructed locally, basically doing ** everything short of revocation checking, then we explicitly check the ** revocation status using OCSP. ** ** For testing unsigned requests, see http://www.valicert.com/ocsp (as one ** example) for more information. ** ** When compiling, define the macro RSA_REQUIRE_FILE_LOG (-D compile ** option, or equivelent) to force the program to return an error code ** if file logging cannot be initialized. For example, if the file ** containing the log message format strings cannot be located (certc.msg ** or equivalent). */ #include "certc.h" #include "ocsp.h" #include "imdb.h" #include "filelog.h" #include "pkixpath.h" #include "crlstat.h" #include "rsacsp.h" #include "demoutil.h" #include "certutil.h" #include "crlutil.h" #ifdef _MSC_VER # pragma warning (disable: 171) /* invalid type conversion (often of very similar ptrs) */ #endif #define SP_COUNT 3 #define IM_DB_NAME "Sample IM Database" /* Register an OCSP service consisting of one or more OCSP responders with * the given Cert-C context. It also populates the given trustedCerts list * with the CA certs using OCSP reponders and the responder certs (the * contents of the OCSP_RESPONDER.responderCAs and * OCSP_RESPONDER.responderCert fields). If signed requests are used, the * private key and certificate of the signer are added to the given database * SERVICE handle. */ static int RegisterOcsp (CERTC_CTX ctx, LIST_OBJ trustedCerts, SERVICE db); /* Using the given Cert-C context and path context, verify that the given * certObj is valid. */ static int VerifyCert (CERTC_CTX ctx, CERT_PATH_CTX *pathCtx, CERT_OBJ certObj); /* Using the given Cert-C context and path context, query any cert status * providers registered with the Cert-C context for the revocation status * of the given certObj. */ static int CheckCertStatus (CERTC_CTX ctx, CERT_PATH_CTX *pathCtx, CERT_OBJ certObj); int main (int argc, char *argv[]) { int status = 0; CERTC_CTX ctx = NULL; SERVICE_HANDLER spTable[SP_COUNT]; POINTER spParams[SP_COUNT]; CERT_PATH_CTX pathCtx; LIST_OBJ trustedCerts = NULL, certList = NULL; SERVICE db = NULL; ITEM certToValidateBer = {NULL, 0}; CERT_OBJ certToValidate = NULL; FILE_LOG_PARAMS logParams = {NULL, NULL}; SERVICE_HANDLER logHandler = { SPT_LOG, "Default File Log", S_InitializeFileLog }; /* Set up RSA_* demoutil options */ status = RSA_SetOptions (&logParams, argc, argv); if (status != 0) goto CLEANUP; RSA_PrintMessage ("OCSP Example\n"); RSA_PrintMessage ("============\n"); spTable[0].type = SPT_DATABASE; spTable[0].name = IM_DB_NAME; spTable[0].Initialize = S_InitializeMemoryDB; spTable[1].type = SPT_CERT_PATH; spTable[1].name = "Cert Path Processing Provider"; spTable[1].Initialize = S_InitializePKIXPath; spTable[2].type = SPT_CRYPTO; spTable[2].name = "BSAFE Crypto-C"; spTable[2].Initialize = S_InitializeDefaultCSP; spParams[0] = NULL; spParams[1] = NULL; spParams[2] = NULL; status = C_InitializeCertC (spTable, spParams, SP_COUNT, &ctx); if (status != 0) goto CLEANUP; /* Attempt to initialize file logging, but unless RSA_REQUIRE_FILE_LOG is * defined, treat it as a non-fatal condition. */ status = C_RegisterService (ctx, &logHandler, (POINTER)&logParams, SERVICE_ORDER_FIRST); #ifdef RSA_REQUIRE_FILE_LOG if (status != 0) goto CLEANUP; #endif status = C_CreateListObject (&trustedCerts); if (status != 0) goto CLEANUP; /* Add roots and intermediate certs to database */ status = C_BindService (ctx, SPT_DATABASE, IM_DB_NAME, &db); if (status != 0) goto CLEANUP; /* Prompt the user for the OCSP Responder information and register it with the Cert-C context. */ status = RegisterOcsp (ctx, trustedCerts, db); if (status != 0) goto CLEANUP; /* Obtain other intermediate certs */ status = C_CreateListObject (&certList); if (status != 0) goto CLEANUP; RSA_PrintMessage ("\nOptionally supply other relevant certificates, such "); RSA_PrintMessage ("as those needed to\nconstruct the certificate chain.\n"); status = RSA_AddCertsToListPrompt (ctx, certList); if (status != 0) goto CLEANUP; RSA_PrintMessage ("\nOther certificates\n"); status = RSA_PrintCertList (certList); if (status != 0) goto CLEANUP; status = C_InsertCertList (db, trustedCerts); if (status != 0) goto CLEANUP; status = C_InsertCertList (db, certList); if (status != 0) goto CLEANUP; /* The use of PF_IGNORE_REVOCATION here simply says that OCSP should not be used when C_BuildCertPath is called. We will explicitly use an OCSP request later to check the cert status. */ pathCtx.pathAlgorithm = PA_PKIX; pathCtx.pathOptions = PF_IGNORE_REVOCATION; pathCtx.trustedCerts = trustedCerts; pathCtx.policies = ANY_POLICY; pathCtx.validationTime = 0; pathCtx.database = db; for (;;) { /* Obtain cert to chain to a trusted root */ status = RSA_GetFileToAllocBuffer (&certToValidateBer.data, &certToValidateBer.len, "Enter name of file containing cert to validate (blank to end)"); if (status == RSA_DEMO_E_CANCEL) break; else if (status != 0) goto CLEANUP; status = C_CreateCertObject (&certToValidate, ctx); if (status != 0) goto CLEANUP; status = C_SetCertBER (certToValidate, certToValidateBer.data, certToValidateBer.len); if (status != 0) goto CLEANUP; /* Check the validity locally first... */ status = VerifyCert (ctx, &pathCtx, certToValidate); if (status != 0) { RSA_PrintMessage ("Cert validation failed when using PA_PKIX.\n"); RSA_PrintMessage ("Trying PA_X509_V1...\n"); pathCtx.pathAlgorithm = PA_X509_V1; status = VerifyCert (ctx, &pathCtx, certToValidate); if (status != 0) goto CLEANUP; } RSA_PrintMessage ("\nCert validated locally.\n"); /* Now do the time-consuming OCSP request... */ RSA_PrintMessage ("Checking revocation status...\n"); status = CheckCertStatus (ctx, &pathCtx, certToValidate); if (status != 0) goto CLEANUP; T_free (certToValidateBer.data); certToValidateBer.data = NULL; C_DestroyCertObject (&certToValidate); } CLEANUP: if (status != 0) RSA_PrintError ("ocsp", status); T_free (certToValidateBer.data); C_DestroyListObject (&trustedCerts); C_DestroyListObject (&certList); C_DestroyCertObject (&certToValidate); C_UnbindService (&db); C_FinalizeCertC (&ctx); return status; } /* end main */ static int RegisterOcsp (CERTC_CTX ctx, LIST_OBJ trustedCerts, SERVICE db) { int status = 0; unsigned int i = 0; char userInput[RSA_DEMO_MAX_LINE_LEN]; LIST_OBJ destList = NULL; ITEM ocspUrl = {NULL, 0}; REVOKE_OCSP_INIT_PARAMS ocspInitParams; OCSP_RESPONDER *responderList = NULL, *currentResponder = NULL; CERT_OBJ responderCert = NULL; ITEM responderCertBer = {NULL, 0}; ALGORITHM_IDENTIFIER sigAlgId = {0, NULL}; CERT_OBJ signerCert = NULL; B_KEY_OBJ signerPvtKey = NULL; LIST_OBJ extraCerts = NULL; POINTER ptr; SERVICE_HANDLER ocspHandler = { SPT_CERT_STATUS, "Cert Revocation Status Provider", S_InitializeOCSP }; /* Start with a clean slate... */ T_memset ((POINTER)&ocspInitParams, 0, sizeof (ocspInitParams)); status = C_CreateListObject (&destList); if (status != 0) goto CLEANUP; /* Supply information about OCSP responder */ status = RSA_GetCommand (userInput, sizeof (userInput), "Enter OCSP responder URL"); if (status != 0) goto CLEANUP; /* if we arrive here, we want to add a responder */ ocspInitParams.initChoice = REVOKE_OCSP_INIT_METHOD_STRUCT; ocspInitParams.method.initStruct.numResponders++; ptr = T_realloc ((POINTER)responderList, ocspInitParams.method.initStruct.numResponders * sizeof (OCSP_RESPONDER)); if (ptr == NULL_PTR) { status = RSA_DEMO_E_ALLOC; goto CLEANUP; } responderList = (OCSP_RESPONDER *) ptr; currentResponder = responderList + (ocspInitParams.method.initStruct.numResponders - 1); T_memset ((POINTER)currentResponder, 0, sizeof (OCSP_RESPONDER)); ocspInitParams.method.initStruct.responders = responderList; ocspUrl.data = (POINTER)userInput; ocspUrl.len = T_strlen (userInput); status = C_AddItemToList (destList, &ocspUrl, NULL); if (status != 0) goto CLEANUP; /* Optionally obtain the responder's certificate. This cert is used to validate signatures on responses from the responder. */ status = RSA_GetFileToAllocBuffer (&responderCertBer.data, &responderCertBer.len, "Optionally supply responder certificate binary"); if (status == 0) { status = C_CreateCertObject (&responderCert, ctx); if (status != 0) goto CLEANUP; status = C_SetCertBER (responderCert, responderCertBer.data, responderCertBer.len); if (status != 0) goto CLEANUP; status = C_AddCertToList (trustedCerts, responderCert, NULL); if (status != 0) goto CLEANUP; } else if (status == RSA_DEMO_E_CANCEL) { /* responderCert remains NULL */ status = 0; } else goto CLEANUP; /* Obtain the CA certificates affiliated with this responder */ RSA_PrintMessage ("\nSupply trusted CA certificates which use %s\n", ocspUrl.data); RSA_PrintMessage ("as the OCSP responder.\n"); status = RSA_AddCertsToListPrompt (ctx, trustedCerts); if (status != 0) goto CLEANUP; RSA_PrintMessage ("\nTrusted Certs\n"); status = RSA_PrintCertList (trustedCerts); if (status != 0) goto CLEANUP; /* Supply information used to sign requests sent to OCSP server. This information is server-specific. */ currentResponder->profile = REVOKE_OCSP_PROFILE_GENERIC; currentResponder->flags = 0; /* use default */ currentResponder->transport.destList = destList; currentResponder->transport.proxyList = NULL; currentResponder->digestAlgorithm = DAI_SHA1; currentResponder->responderCert = responderCert; currentResponder->responderCAs = trustedCerts; currentResponder->timeTolerance = 0; currentResponder->extraRequestExtensions = NULL; currentResponder->dbName = IM_DB_NAME; /* Check if requests to this responder should be signed */ RSA_PrintMessage ("\nSpecify signature algorithm if requests should be"); RSA_PrintMessage (" signed.\nEnter blank for unsigned requests.\n"); status = RSA_ChooseSignatureAlgorithmPrompt (&sigAlgId); if (status != 0 && status != RSA_DEMO_E_CANCEL) goto CLEANUP; else if (status == RSA_DEMO_E_CANCEL) { /* unsigned request */ currentResponder->signer.signatureAlgorithm = SA_UNDEFINED; currentResponder->signer.cert = NULL; currentResponder->signer.extraRequestCerts = NULL; } else { /* signature algorithm specified */ currentResponder->signer.signatureAlgorithm = sigAlgId.algorithmId; status = B_CreateKeyObject (&signerPvtKey); if (status != 0) goto CLEANUP; status = C_CreateCertObject (&signerCert, ctx); if (status != 0) goto CLEANUP; RSA_PrintMessage ("Supply cert and private key to use to sign OCSP "); RSA_PrintMessage ("requests.\n"); status = RSA_GetCertAndPvtKey (ctx, signerCert, signerPvtKey); if (status != 0) goto CLEANUP; status = C_InsertCert (db, signerCert); if (status != 0) goto CLEANUP; status = C_InsertPrivateKey (db, signerCert, signerPvtKey); if (status != 0) goto CLEANUP; currentResponder->signer.cert = signerCert; status = C_CreateListObject (&extraCerts); if (status != 0) goto CLEANUP; RSA_PrintMessage ("Optionally supply additional certs to send to "); RSA_PrintMessage ("the responder.\n"); status = RSA_AddCertsToListPrompt (ctx, extraCerts); if (status != 0) goto CLEANUP; currentResponder->signer.extraRequestCerts = extraCerts; } status = C_RegisterService (ctx, &ocspHandler, (POINTER)&ocspInitParams, SERVICE_ORDER_FIRST); CLEANUP: if (status != 0) RSA_PrintError ("RegisterOcsp", status); B_DestroyKeyObject (&signerPvtKey); C_DestroyListObject (&extraCerts); C_DestroyListObject (&destList); /* each responder will either have a responderCert and signer CERT_OBJ which we created or NULL */ for (i = 0; i < ocspInitParams.method.initStruct.numResponders; i++) { C_DestroyCertObject (&responderList[i].responderCert); C_DestroyCertObject (&responderList[i].signer.cert); } T_free ((POINTER)responderList); return status; } /* end RegisterOcsp */ static int VerifyCert (CERTC_CTX ctx, CERT_PATH_CTX *pathCtx, CERT_OBJ certObj) { int status = 0; LIST_OBJ certPath = NULL, crlList = NULL, crlCerts = NULL; status = C_CreateListObject (&certPath); if (status != 0) goto CLEANUP; status = C_CreateListObject (&crlList); if (status != 0) goto CLEANUP; status = C_CreateListObject (&crlCerts); if (status != 0) goto CLEANUP; status = C_BuildCertPath (ctx, pathCtx, (POINTER)certObj, certPath, crlList, crlCerts, NULL); if (status != 0) goto CLEANUP; /* Print out information about validated cert */ RSA_PrintMessage ("\nChain of certificates to trusted root:\n"); status = RSA_PrintCertList (certPath); if (status != 0) goto CLEANUP; RSA_PrintMessage ("\nCRLs needed to verify chain:\n"); status = RSA_PrintCrlList (crlList); if (status != 0) goto CLEANUP; RSA_PrintMessage ("\nAdditional certs needed to validate CRLs:\n"); status = RSA_PrintCertList (crlCerts); if (status != 0) goto CLEANUP; CLEANUP: if (status != 0) RSA_PrintError ("VerifyCert", status); C_DestroyListObject (&certPath); C_DestroyListObject (&crlList); C_DestroyListObject (&crlCerts); return status; } /* end VerifyCert */ static int CheckCertStatus (CERTC_CTX ctx, CERT_PATH_CTX *pathCtx, CERT_OBJ certObj) { int status = 0; CERT_REVOCATION revStatus; status = C_CheckCertRevocation (ctx, pathCtx, certObj, &revStatus); if (status != 0) goto CLEANUP; switch (revStatus.status) { case CERT_REVOKED: RSA_PrintMessage ("Cert Revoked!\n"); break; case CERT_NOT_REVOKED: RSA_PrintMessage ("Cert Valid!\n"); break; case CERT_REVOCATION_UNKNOWN: RSA_PrintMessage ("Revocation unknown.\n"); break; default: RSA_PrintMessage ("Invalid status value = %d\n", revStatus.status); } CLEANUP: if (status != 0) RSA_PrintError ("CheckCertStatus", status); if (revStatus.evidenceType == CRE_OCSP) C_DestroyOCSPEvidence ((OCSP_EVIDENCE **)&revStatus.evidence); else if (revStatus.evidenceType == CRE_CRL) C_DestroyCRLEvidence ((CRL_EVIDENCE **)&revStatus.evidence); return status; } /* end CheckCertStatus */