| RSA BSAFE Cert-C |
Certificate Components for C |
| Crypto-C 6.2.1 Developer's Guide | ||
| Search |
/* $Id: verisign.c,v 1.5 2004/03/02 05:18:39 gsingh Exp $ */ /* verisign.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. ** ** This example has an RA communicating with the CA (OnSite) on behalf of an ** end-entity in order to get an immediate response. Customers using the ** APIs/SPI in this example must already have access to an OnSite CA, or made ** arrangements with Verisign to access a CRS responder. More information is ** available at http://onsite.verisign.com or in the CRS Profile ** Specification document available from Verisign. ** ** 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 "filelog.h" #include "rsacsp.h" #include "imdb.h" #include "crlstat.h" #include "pkixpath.h" #include "crs.h" #include "demoutil.h" #include "pkiutil.h" #include "certutil.h" #include "crlutil.h" #include "nameutil.h" #include "keyutil.h" #include "p12util.h" #ifdef _MSC_VER # pragma warning (disable: 171) /* invalid type conversion (often of very similar ptrs) */ #endif /* Used to locate configuration information */ #define CRS_PROV_CFG_URL "file: /* Name of PKI service provider */ #define PKI_SP_NAME "Verisign OnSite Transaction Provider" /* Name of database associated with RA. This is where the RA PKCS #12 info * will be placed in InitializePkiProtectInfo. */ #define RA_DB_NAME "RA Database" #define SP_COUNT 5 /* Obtain requestor subject name and keypair. The calling application must * destroy the requestor, publicKey, and privateKey objects. */ static int GetRequestorInfo (CERTC_CTX ctx, NAME_OBJ *requestor, B_KEY_OBJ *publicKey, B_KEY_OBJ *privateKey); /* For this example, we have the RA sending messages to the CA in order to * get an immediate response. Therefore, we use this procedure to associate * an "RA Database" with the given ctx, containing the credentials necessary * to send a message to the CA, identifying the sender as an RA to which an * immediate approval is possible. If an end-entity communicates directly * with the CA, the end-entity will have to wait until the request has been * approved. (Currently, the human RA would go to a web interface to approve * the end-entity request) * * This example prompts the user for trusted root certificates and the PKCS * #12 message containing the private key (alternatively, this can be * modified to instead ask for the B_KEY_OBJ and CERT_OBJ if desired). Since * this PKCS #12 information will be used to send the message to the CA, a * SIGNER_INFO structure with the RA's information is also created for use in * the PKI_MSG_FIELDS.sender field. * * Calling function must free sender->signerCertId.id.issuerSerialNumber. * serialNumber.data. * * DestroyPkiProtectInfo must be called when pkiProtectInfo is no longer * needed. */ static int InitializePkiProtectInfo (CERTC_CTX ctx, PKI_PROTECT_INFO *pkiProtectInfo, SIGNER_INFO *sender); static void DestroyPkiProtectInfo (PKI_PROTECT_INFO *pkiProtectInfo); /* Collect name-value pairs specified by the RA for certificate enrollment. * The Verisign documentation (CRS Profile Specification) specifies the info * needed. */ static int SetRegInfo (ATTRIBUTES_OBJ regInfo); int main (int argc, char *argv[]) { int status = 0; CERTC_CTX ctx = NULL; PKI_MSG_OBJ certReq = NULL, certResp = NULL; PKI_MSG_FIELDS msgFields; PKI_CERTREQ_FIELDS creqFields; PKI_CERTRESP_FIELDS certRespFields; B_KEY_OBJ publicKey = NULL, privateKey = NULL; ITEM publicBer = {NULL, 0}, reqBin = {NULL, 0}, respBin = {NULL, 0}; ITEM certDer = {NULL, 0}; PKI_PROTECT_INFO pkiProtectInfo = {{NULL}}; SERVICE_HANDLER spTable[SP_COUNT] = { {SPT_CRYPTO, "Default CSP", S_InitializeDefaultCSP}, {SPT_CERT_STATUS, "Cert Status Provider", S_InitializeCRLStatus}, {SPT_CERT_PATH, "Path Provider", S_InitializePKIXPath}, {SPT_PKI, PKI_SP_NAME, S_InitializeCRS}, {SPT_DATABASE, RA_DB_NAME, S_InitializeMemoryDB} }; POINTER spParams[SP_COUNT]; PKI_CRS_INIT_PARAMS crsInitParams = {(unsigned char *)CRS_PROV_CFG_URL}; FILE_LOG_PARAMS logParams = {NULL, NULL}; SERVICE_HANDLER logHandler = { SPT_LOG, "Default File Log", S_InitializeFileLog }; spParams[0] = NULL; spParams[1] = NULL; spParams[2] = NULL; spParams[3] = (POINTER)&crsInitParams; spParams[4] = NULL; /* To guarantee unused fields are set to 0, and to facilitate cleanup */ T_memset ((POINTER)&creqFields, 0, sizeof (PKI_CERTREQ_FIELDS)); /* Dynamically-allocated memory will be placed here later. For defensive * programming purposes, we set it to NULL so that if an error occurs before * it is set, we will not free random memory. */ msgFields.sender.signerCertId.id.issuerSerialNumber.serialNumber.data = NULL; status = RSA_SetOptions (&logParams, argc, argv); if (status != 0) goto CLEANUP; RSA_PrintMessage ("CRS Provider Demonstration\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 status = C_CreatePKIMsgObject (ctx, &certReq); if (status != 0) goto CLEANUP; status = C_GetPKIMsgFields (certReq, &msgFields); if (status != 0) goto CLEANUP; /* just to be absolutely sure... */ IgnoreAll (msgFields.flags); msgFields.msgType = PKI_MSGTYPE_CERTREQ; ClearIgnoreFlag (msgFields.flags, PKI_MSGFLAGS_IGNORE_MSGTYPE); SetMsgWrapType (msgFields.flags, PKI_MSGFLAGS_WRAP_SIGN); status = InitializePkiProtectInfo (ctx, &pkiProtectInfo, &msgFields.sender); if (status != 0) goto CLEANUP; ClearIgnoreFlag (msgFields.flags, PKI_MSGFLAGS_IGNORE_SENDER); status = C_SetPKIMsgFields (certReq, &msgFields); if (status != 0) goto CLEANUP; status = C_GetPKICertRequestFields (certReq, &creqFields); if (status != 0) goto CLEANUP; IgnoreAll (creqFields.flags); /* For our RA-signed request, we only need provide a subject name and * public key of the end-entity. */ status = GetRequestorInfo (ctx, &creqFields.certTemplate.subjectName, &publicKey, &privateKey); if (status != 0) goto CLEANUP; ClearIgnoreFlag (creqFields.flags, PKI_CERTREQFLAGS_IGNORE_TEMPLATE_SUBJECTNAME); status = RSA_GetKeyBer (RSA_DEMO_PUBLIC_KEY, publicKey, &publicBer); if (status != 0) goto CLEANUP; creqFields.certTemplate.publicKey.data = publicBer.data; creqFields.certTemplate.publicKey.len = publicBer.len; ClearIgnoreFlag (creqFields.flags, PKI_CERTREQFLAGS_IGNORE_TEMPLATE_PUBLICKEY); RSA_PrintMessage ("Supply regInfo for certificate enrollment. You "); RSA_PrintMessage ("will have to refer\nto the Verisign CRS Profile "); RSA_PrintMessage ("Specification - for example, section 3.1.1\nwhere "); RSA_PrintMessage ("name-value pairs are given so an example name "); RSA_PrintMessage ("would be corp_company\nand value might be Acme Inc."); RSA_PrintMessage (" This information is filled in by the RA.\n"); status = C_CreateAttributesObject (&creqFields.regInfo); if (status != 0) goto CLEANUP; status = SetRegInfo (creqFields.regInfo); if (status != 0) goto CLEANUP; ClearIgnoreFlag (creqFields.flags, PKI_CERTREQFLAGS_IGNORE_REGINFO); status = C_SetPKICertRequestFields (certReq, &creqFields); if (status != 0) goto CLEANUP; status = C_GeneratePKIProofOfPossession (ctx, PKI_SP_NAME, certReq, privateKey, NULL); if (status != 0) goto CLEANUP; status = C_CreatePKIMsgObject (ctx, &certResp); if (status != 0) goto CLEANUP; status = C_RequestPKICert (ctx, PKI_SP_NAME, certReq, &pkiProtectInfo, NULL, certResp); if (status != 0) goto CLEANUP; status = C_GetPKICertResponseFields (certResp, &certRespFields); if (status != 0) goto CLEANUP; RSA_PrintMessage ("Certificate Request Status\n"); status = RSA_PrintPkiStatusInfo (&certRespFields.statusInfo); if (status != 0) goto CLEANUP; if (certRespFields.flags & PKI_CERTRESPFLAGS_IGNORE_CERT) { RSA_PrintMessage ("Certificate not returned.\n"); status = RSA_DEMO_E_INVALID_PARAMETER; goto CLEANUP; } RSA_PrintMessage ("Resulting Certificate\n"); status = RSA_PrintCertObject (certRespFields.cert); status = C_GetCertDER (certRespFields.cert, &certDer.data, &certDer.len); if (status != 0) goto CLEANUP; status = RSA_WriteDataToFile (certDer.data, certDer.len, "Enter name of file to store cert binary"); CLEANUP: if (status != 0) RSA_PrintError ("verisign.c", status); T_free (publicBer.data); T_free (reqBin.data); T_free (respBin.data); T_free (msgFields.sender.signerCertId.id.issuerSerialNumber.serialNumber. data); DestroyPkiProtectInfo (&pkiProtectInfo); B_DestroyKeyObject (&publicKey); B_DestroyKeyObject (&privateKey); C_DestroyAttributesObject (&creqFields.regInfo); C_DestroyPKIMsgObject (&certReq); C_DestroyPKIMsgObject (&certResp); C_FinalizeCertC (&ctx); return status; } /* end main */ static int GetRequestorInfo (CERTC_CTX ctx, NAME_OBJ *requestor, B_KEY_OBJ *publicKey, B_KEY_OBJ *privateKey) { int status = 0; ITEM publicBer = {NULL, 0}, privateBer = {NULL, 0}; *publicKey = NULL; *privateKey = NULL; status = C_CreateNameObject (requestor); if (status != 0) goto CLEANUP; status = RSA_GetNameObject (*requestor, "requestor subject"); if (status != 0) goto CLEANUP; RSA_PrintMessage ("Supply the requestor's keypair\n"); /* Prompt the user for the BER-encoded public key. If the user enters a * blank, a new keypair will be generated on the fly and publicBer will be * {NULL, 0}. Otherwise, if the user supplies a public key, a private key * must also be supplied. */ status = RSA_GetFileToAllocBuffer (&publicBer.data, &publicBer.len, "Enter name of file containing public key BER (blank to create)"); if (status == RSA_DEMO_E_CANCEL) status = RSA_GenerateKeypair (ctx, publicKey, privateKey); else if (status == 0) /* user supplied public key, must supply private key also */ status = RSA_GetFileToAllocBuffer (&privateBer.data, &privateBer.len, "Enter name of file containing private key BER (blank to cancel)"); if (status != 0) /* if one of the previous calls failed */ goto CLEANUP; if (publicBer.data != NULL) { /* RSA_GenerateKeypair was not called in this case */ status = B_CreateKeyObject (publicKey); if (status != 0) goto CLEANUP; status = B_CreateKeyObject (privateKey); if (status != 0) goto CLEANUP; status = RSA_SetKeyBer (RSA_DEMO_PUBLIC_KEY, *publicKey, publicBer); if (status != 0) goto CLEANUP; status = RSA_SetKeyBer (RSA_DEMO_PRIVATE_KEY, *privateKey, privateBer); if (status != 0) goto CLEANUP; } CLEANUP: if (status != 0) { C_DestroyNameObject (requestor); B_DestroyKeyObject (publicKey); B_DestroyKeyObject (privateKey); RSA_PrintError ("GetRequestorInfo", status); } if (privateBer.data != NULL) { T_memset (privateBer.data, 0, privateBer.len); T_free (privateBer.data); } T_free (publicBer.data); return status; } /* end GetRequestorInfo */ static int InitializePkiProtectInfo (CERTC_CTX ctx, PKI_PROTECT_INFO *pkiProtectInfo, SIGNER_INFO *sender) { int status = 0; char userInput[RSA_DEMO_MAX_LINE_LEN], password[RSA_DEMO_MAX_LINE_LEN]; SERVICE db = NULL; ITEM passwordItem = {NULL, 0}; LIST_OBJ trustedRoots = NULL, certList = NULL; ITEM raCertBer = {NULL, 0}; CERT_OBJ raCert = NULL; CERT_FIELDS raCertFields; NAME_OBJ issuerCopy = NULL; ITEM nameBer = {NULL, 0}; unsigned char *serialNumber = NULL; pkiProtectInfo->info.protectionCtx = (CERT_PATH_CTX *)T_malloc (sizeof (CERT_PATH_CTX)); if (pkiProtectInfo->info.protectionCtx == NULL) { status = RSA_DEMO_E_ALLOC; goto CLEANUP; } T_memset ((POINTER)pkiProtectInfo->info.protectionCtx, 0, sizeof (CERT_PATH_CTX)); status = C_BindService (ctx, SPT_DATABASE, RA_DB_NAME, &db); if (status != 0) goto CLEANUP; status = C_CreateListObject (&trustedRoots); if (status != 0) goto CLEANUP; status = C_CreateListObject (&certList); if (status != 0) goto CLEANUP; RSA_PrintMessage ("\nSupply trusted root certificates. Normally, for the "); RSA_PrintMessage ("purposes of this example,\nit should only be one "); RSA_PrintMessage ("certificate.\n"); status = RSA_AddCertsToListPrompt (ctx, trustedRoots); if (status != 0) goto CLEANUP; RSA_PrintMessage ("\nTrusted Roots\n"); status = RSA_PrintCertList (trustedRoots); 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; RSA_PrintMessage ("\nOptionally supply additional CRLs for chaining.\n"); status = RSA_AddCrlsToDbPrompt (ctx, db); if (status != 0) goto CLEANUP; /* For simplicity, take PKCS #12. Can use other methods if need be. */ status = RSA_GetCommand (userInput, sizeof (userInput), "\nEnter name of PKCS #12 binary file containing RA private key"); if (status != 0) goto CLEANUP; status = C_InsertCertList (db, trustedRoots); if (status != 0) goto CLEANUP; status = C_InsertCertList (db, certList); if (status != 0) goto CLEANUP; status = RSA_GetCommand (password, sizeof (password), "Enter password to decrypt private key"); if (status != 0) goto CLEANUP; status = RSA_FormatPkcs12Password (password, &passwordItem); if (status != 0) goto CLEANUP; status = C_ImportPKCS12 (ctx, userInput, &passwordItem, db, 0); if (status != 0) goto CLEANUP; pkiProtectInfo->info.protectionCtx->pathAlgorithm = PA_X509_V1; pkiProtectInfo->info.protectionCtx->pathOptions = PF_IGNORE_REVOCATION; pkiProtectInfo->info.protectionCtx->trustedCerts = trustedRoots; pkiProtectInfo->info.protectionCtx->policies = ANY_POLICY; pkiProtectInfo->info.protectionCtx->validationTime = 0; pkiProtectInfo->info.protectionCtx->database = db; status = RSA_GetFileToAllocBuffer (&raCertBer.data, &raCertBer.len, "Enter name of file containing RA certificate"); if (status != 0) goto CLEANUP; status = C_CreateCertObject (&raCert, ctx); if (status != 0) goto CLEANUP; status = C_SetCertBER (raCert, raCertBer.data, raCertBer.len); if (status != 0) goto CLEANUP; RSA_PrintMessage ("RA Certificate\n"); RSA_PrintCertInfo (raCert); status = C_GetCertFields (raCert, &raCertFields); if (status != 0) goto CLEANUP; /* make sure the RA cert is in the database */ status = C_SelectCertByIssuerSerial (db, raCertFields.issuerName, &raCertFields.serialNumber, certList); if (status != 0) goto CLEANUP; /* provider documentation says not to set signedAttributes and * unsignedAttributes field of SIGNER_INFO. */ sender->signerCertId.type = ISSUER_SERIAL; /* make a copy of the RA issuer name */ status = C_CreateNameObject (&issuerCopy); if (status != 0) goto CLEANUP; status = C_GetNameDER (raCertFields.issuerName, &nameBer.data, &nameBer.len); if (status != 0) goto CLEANUP; status = C_SetNameBER (issuerCopy, nameBer.data, nameBer.len); if (status != 0) goto CLEANUP; sender->signerCertId.id.issuerSerialNumber.issuerName = issuerCopy; /* make a copy of the RA serial number */ serialNumber = T_malloc (raCertFields.serialNumber.len); if (serialNumber == NULL) { status = RSA_DEMO_E_ALLOC; goto CLEANUP; } T_memcpy (serialNumber, raCertFields.serialNumber.data, raCertFields.serialNumber.len); sender->signerCertId.id.issuerSerialNumber.serialNumber.data = serialNumber; sender->signerCertId.id.issuerSerialNumber.serialNumber.len = raCertFields.serialNumber.len; /* hard-coded for simplicity */ sender->digestAlgorithmId.algorithmId = DAI_MD5; sender->digestAlgorithmId.algorithmParam = NULL; sender->signatureAlgorithmId.algorithmId = SA_RSA_ENCRYPTION; sender->signatureAlgorithmId.algorithmParam = NULL; CLEANUP: if (status != 0) { T_free (serialNumber); C_DestroyNameObject (&issuerCopy); DestroyPkiProtectInfo (pkiProtectInfo); RSA_PrintError ("InitializePkiProtectInfo", status); } C_DestroyListObject (&certList); T_memset (passwordItem.data, 0, passwordItem.len); T_free (passwordItem.data); T_memset ((POINTER)password, 0, sizeof (password)); T_free (raCertBer.data); C_DestroyCertObject (&raCert); return status; } /* end InitializePkiProtectInfo */ static void DestroyPkiProtectInfo (PKI_PROTECT_INFO *pkiProtectInfo) { if (pkiProtectInfo->info.protectionCtx == NULL) return; if (pkiProtectInfo->info.protectionCtx->trustedCerts != NULL) C_DestroyListObject (&pkiProtectInfo->info.protectionCtx->trustedCerts); if (pkiProtectInfo->info.protectionCtx->policies != ANY_POLICY) C_DestroyListObject (&pkiProtectInfo->info.protectionCtx->policies); if (pkiProtectInfo->info.protectionCtx->database != NULL) C_UnbindService (&pkiProtectInfo->info.protectionCtx->database); if (pkiProtectInfo->info.protectionCtx != NULL) T_free ((POINTER)pkiProtectInfo->info.protectionCtx); pkiProtectInfo->info.protectionCtx = NULL; } /* end DestroyPkiProtectInfo */ static int SetRegInfo (ATTRIBUTES_OBJ regInfo) { int status = 0; RSA_DEMO_REG_INFO_ENTRY regInfoFields[] = { {"common_name", NULL, VT_UTF8_STRING}, {"mail_email", NULL, VT_UTF8_STRING}, {"mail_firstName", NULL, VT_UTF8_STRING}, {"mail_lastName", NULL, VT_UTF8_STRING}, {"corp_company", "RSA Security Inc.", VT_UTF8_STRING}, {"org_unit", "Public", VT_UTF8_STRING}, {"cert_type", "end-user", VT_UTF8_STRING}, {"authenticate", "YES", VT_UTF8_STRING}, {"embed_email", "yes", VT_UTF8_STRING}, {"challenge", "password", VT_UTF8_STRING} }; status = RSA_RegInfoPrompt (regInfo, regInfoFields, sizeof (regInfoFields) / sizeof (RSA_DEMO_REG_INFO_ENTRY)); if (status != 0) RSA_PrintError ("CreateRegInfo", status); return status; } /* end CreateRegInfo */