| RSA BSAFE Cert-C |
Certificate Components for C |
| Crypto-C 6.2.1 Developer's Guide | ||
| Search |
/* $Id: chain.c,v 1.5 2004/03/02 05:18:42 gsingh Exp $ */ /* chain.c ** Copyright (c) 1999-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. ** ** A simple example of the certification path processing API. We have certs ** and CRLs hard-coded in this example. First, we create a list of the trusted ** root certificates. Then add the remaining certs and CRLs to an instance of ** the in-memory database for use in the CERT_PATH_CTX. ** ** 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 "imdb.h" #include "filelog.h" #include "pkixpath.h" #include "crlstat.h" #include "demoutil.h" #include "certutil.h" #include "crlutil.h" #include "keyutil.h" #include "timeutil.h" #include "chaindata.h" #define SP_COUNT 3 /* Create a list object and fill it with trusted root certs. * * Note that the calling procedure must call C_DestroyListObject on rootCerts * when it is no longer needed. */ int CreateRootList (CERTC_CTX ctx, LIST_OBJ *rootCerts) { int status = 0, i = 0, numRoots = 0; CERT_PATH_CTX pathCtx; CERT_OBJ certObj = (CERT_OBJ)0; B_KEY_OBJ publicKey = (B_KEY_OBJ)0; ITEM roots[2] = { {expiredRoot, sizeof (expiredRoot)}, {rootCert, sizeof (rootCert)} }; status = C_CreateListObject (rootCerts); if (status != 0) goto CLEANUP; /* In this procedure, we have self-signed root certificates so we can verify * the certificate signature with the public key in the cert. However, in * practice, we can have certs which are not self-signed as part of the * trusted collection of certs. For example, for a cert whose issuer is * unrecognized, a client program may prompt the user with the certificate * information and ask whether or not the user wants to explicitly trust the * certificate. */ numRoots = sizeof (roots) / sizeof (roots[0]); for (i = 0; i < numRoots; i++) { status = C_CreateCertObject (&certObj, ctx); if (status != 0) goto CLEANUP; status = C_SetCertBER (certObj, roots[i].data, roots[i].len); if (status != 0) goto CLEANUP; status = RSA_GetPubKeyFromCert (certObj, &publicKey); if (status != 0) goto CLEANUP; /* C_VerifyCertSignature only verifies the certificate signature and does * not check for example whether or not the cert is expired. For more * strict checking, use C_ValidateCert. * * For this example, we disable the validity period check to simulate a * situation where we import a trusted root at a time when it is valid, * but expires while it is in the trusted store. If we change pathOptions * to 0, we will see that Cert-C complains about the expiredRoot (examine * the chainlog.txt file). */ pathCtx.pathAlgorithm = PA_PKIX; pathCtx.pathOptions = PF_IGNORE_VALIDATION_TIME; pathCtx.trustedCerts = (LIST_OBJ)0; pathCtx.policies = ANY_POLICY; pathCtx.validationTime = 0; pathCtx.database = (SERVICE)0; status = C_ValidateCert (ctx, &pathCtx, certObj, publicKey); if (status == E_NOT_VALIDATED) { RSA_PrintMessage ("The following certificate was not added to the list"); RSA_PrintMessage (" of trusted roots:\n"); status = RSA_PrintCertInfo (certObj); if (status != 0) goto CLEANUP; } else if (status != 0) goto CLEANUP; else { RSA_PrintMessage ("\nAdding Trusted Certificate...\n"); status = RSA_PrintCertInfo (certObj); if (status != 0) goto CLEANUP; status = C_AddCertToList (*rootCerts, certObj, (unsigned int *)0); if (status != 0) goto CLEANUP; } C_DestroyCertObject (&certObj); B_DestroyKeyObject (&publicKey); } CLEANUP: if (status != 0) { RSA_PrintError ("CreateRootList", status); C_DestroyListObject (rootCerts); } C_DestroyCertObject (&certObj); B_DestroyKeyObject (&publicKey); return status; } /* end CreateRootList */ /* Takes a Cert-C Context and the name of the database in which to insert * the sample intermediate certs and CRLs from chaindata.h. */ int InsertSampleData (CERTC_CTX ctx, char *dbName) { int status = 0, i = 0, numCerts = 0, numCrls = 0; /* use this setup to allow the use of the for loop below */ ITEM certs[5] = { {expiredRoot, sizeof (expiredRoot)}, {rootCert, sizeof (rootCert)}, {issuer1, sizeof (issuer1)}, {issuer2, sizeof (issuer2)}, {issuer3, sizeof (issuer3)} }; ITEM crls[2] = { {rootCrl, sizeof (rootCrl)}, {issuer1Crl, sizeof (issuer1Crl)} }; SERVICE db = (SERVICE)0; CERT_OBJ certObj = (CERT_OBJ)0; CRL_OBJ crlObj = (CRL_OBJ)0; status = C_BindService (ctx, SPT_DATABASE, dbName, &db); if (status != 0) goto CLEANUP; numCerts = sizeof (certs) / sizeof (certs[0]); for (i = 0; i < numCerts; i++) { status = C_CreateCertObject (&certObj, ctx); if (status != 0) goto CLEANUP; status = C_SetCertBER (certObj, certs[i].data, certs[i].len); if (status != 0) goto CLEANUP; RSA_PrintMessage ("\nInserting cert into database...\n"); status = RSA_PrintCertInfo (certObj); if (status != 0) goto CLEANUP; status = C_InsertCert (db, certObj); if (status != 0) goto CLEANUP; C_DestroyCertObject (&certObj); } numCrls = sizeof (crls) / sizeof (crls[0]); for (i = 0; i < numCrls; i++) { status = C_CreateCRLObject (&crlObj, ctx); if (status != 0) goto CLEANUP; status = C_SetCRLBER (crlObj, crls[i].data, crls[i].len); if (status != 0) goto CLEANUP; RSA_PrintMessage ("\nInserting CRL into database...\n"); status = RSA_PrintCrlInfo (crlObj); if (status != 0) goto CLEANUP; status = C_InsertCRL (db, crlObj); if (status != 0) goto CLEANUP; C_DestroyCRLObject (&crlObj); } CLEANUP: if (status != 0) RSA_PrintError ("InsertSampleData", status); C_DestroyCertObject (&certObj); C_DestroyCRLObject (&crlObj); C_UnbindService (&db); return status; } /* end InsertSampleData */ /* Examine the end entity certificates and if valid, construct the path to * trusted root. This function requires a list containing the trusted roots * and the name of the registered database provider where other certs and CRLs * were stored. */ int VerifyEndEntities (CERTC_CTX ctx, LIST_OBJ rootCerts, char *dbName) { int status, i; SERVICE db = (SERVICE)0; CERT_PATH_CTX pathCtx; CERT_OBJ endEntity = (CERT_OBJ)0; LIST_OBJ certPath = (LIST_OBJ)0, crls = (LIST_OBJ)0, crlCerts = (LIST_OBJ)0; ITEM certs[6] = { {badCert, sizeof (badCert)}, /* no, since root is expired */ {subject1, sizeof (subject1)}, /* no, it was revoked */ {subject2, sizeof (subject2)}, /* no, its issuer was revoked */ {subject3, sizeof (subject3)}, /* no, its issuer expired */ {subject0, sizeof (subject0)}, /* valid */ {subject11, sizeof (subject11)} /* valid */ }; RSA_PrintMessage ("\n***Expected Results***\n"); RSA_PrintMessage ("badCert - not valid, its root is expired\n"); RSA_PrintMessage ("Subject 1 - not valid, it was revoked by Issuer 1\n"); RSA_PrintMessage ("Subject 2 - not valid, its issuer was revoked by Root\n"); RSA_PrintMessage ("Subject 3 - not valid, its issuer is expired\n"); RSA_PrintMessage ("Subject 0 - valid\n"); RSA_PrintMessage ("Subject 11 - valid\n"); /* obtain the handle to the database where InsertSampleData put all the info */ status = C_BindService (ctx, SPT_DATABASE, dbName, &db); if (status != 0) goto CLEANUP; /* use PA_X509_V1 instead of PA_PKIX to omit examination of extensions */ pathCtx.pathAlgorithm = PA_X509_V1; /* The PF_* flags may be used to customize path processing behavior. We use the normal behavior here. */ pathCtx.pathOptions = 0; pathCtx.trustedCerts = rootCerts; pathCtx.policies = ANY_POLICY; pathCtx.validationTime = 0; /* current time */ pathCtx.database = db; for (i = 0; i < sizeof (certs) / sizeof (certs[0]); i++) { status = C_CreateListObject (&certPath); if (status != 0) goto CLEANUP; status = C_CreateListObject (&crls); if (status != 0) goto CLEANUP; status = C_CreateListObject (&crlCerts); if (status != 0) goto CLEANUP; status = C_CreateCertObject (&endEntity, ctx); if (status != 0) goto CLEANUP; status = C_SetCertBER (endEntity, certs[i].data, certs[i].len); if (status != 0) goto CLEANUP; RSA_PrintMessage ("\n***Attempting to construct certificate chain for\n"); status = RSA_PrintCertInfo (endEntity); if (status != 0) goto CLEANUP; status = C_BuildCertPath (ctx, &pathCtx, (POINTER)endEntity, certPath, crls, crlCerts, (LIST_OBJ)0); if (status == E_PATH_NOT_FOUND) RSA_PrintMessage ("\nNo path can be constructed.\n"); else if (status != 0) goto CLEANUP; /* if a PKCS #7 certs-only message (certsonly.c) were to be constructed for this end entity, the certs in this list are normally included */ 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 (crls); if (status != 0) goto CLEANUP; RSA_PrintMessage ("\nAdditional certs needed to validate CRLs:\n"); status = RSA_PrintCertList (crlCerts); if (status != 0) goto CLEANUP; C_DestroyListObject (&certPath); C_DestroyListObject (&crls); C_DestroyListObject (&crlCerts); C_DestroyCertObject (&endEntity); } CLEANUP: if (status != 0) RSA_PrintError ("VerifyEndEntities", status); C_DestroyListObject (&certPath); C_DestroyListObject (&crls); C_DestroyListObject (&crlCerts); C_DestroyCertObject (&endEntity); C_UnbindService (&db); return status; } /* end VerifyEndEntities */ int main (int argc, char *argv[]) { int status; CERTC_CTX ctx = (CERTC_CTX)0; SERVICE_HANDLER spTable[SP_COUNT]; POINTER spParams[SP_COUNT]; LIST_OBJ rootCerts = (LIST_OBJ)0; FILE_LOG_PARAMS logParams = {(char *)0, (char *)0}; SERVICE_HANDLER logHandler = { SPT_LOG, "Default File Log", S_InitializeFileLog }; status = RSA_SetOptions (&logParams, argc, argv); if (status != 0) goto CLEANUP; spTable[0].type = SPT_DATABASE; spTable[0].name = "Sample IM Database"; spTable[0].Initialize = S_InitializeMemoryDB; spTable[1].type = SPT_CERT_PATH; spTable[1].name = "Cert Path Processing Provider"; spTable[1].Initialize = S_InitializePKIXPath; /* unless the CERT_PATH_CTX for C_BuildCertPath uses PF_IGNORE_REVOCATION * as one of the pathOptions, we need a cert status provider to check CRLs */ spTable[2].type = SPT_CERT_STATUS; spTable[2].name = "Cert Revocation Status Provider"; spTable[2].Initialize = S_InitializeCRLStatus; spParams[0] = NULL_PTR; spParams[1] = NULL_PTR; spParams[2] = NULL_PTR; /* Note that the procedures which require access to the database where the * CRLs and intermediate certs are stored are given the name of the * registered database provider. This is to illustrate that each function * is then able to access the database by binding to a SERVICE given the * name. An alternate approach would be to bind a SERVICE here in main * and pass that instead of the string name of the database instance. */ RSA_PrintMessage ("Cert Path Validation Sample\n"); RSA_PrintMessage ("===========================\n"); 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 /* put the root certificates into a list after validating them */ status = CreateRootList (ctx, &rootCerts); if (status != 0) goto CLEANUP; /* add a collection of intermediate certs into the database */ status = InsertSampleData (ctx, spTable[0].name); if (status != 0) goto CLEANUP; /* attempt to chain end entity certs to trusted roots */ status = VerifyEndEntities (ctx, rootCerts, spTable[0].name); if (status != 0) goto CLEANUP; CLEANUP: if (status != 0) RSA_PrintError ("chain.c", status); C_DestroyListObject (&rootCerts); C_FinalizeCertC (&ctx); return status; } /* end main */