| RSA BSAFE Cert-C |
Certificate Components for C |
| Crypto-C 6.2.1 Developer's Guide | ||
| Search |
/* $Id: kcscrs.c,v 1.4 2004/03/02 05:18:39 gsingh Exp $ */ /* kcscrs.c ** Copyright (c) 2000-2002, 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. ** ** IMPORTANT NOTES (Please read before attempting to build/run) ** =============== ** Before running this program, users must put the appropriate information in ** kcscrs.cfg specifying the URL of the server. The CRS_PROV_CFG_URL macro ** below should be set to the location of the kcscrs.cfg file. ** ** In addition, the proper jurisdiction information must be filled in the ** regInfoFields array below for "corp_company" and "org_unit". ** ** You must also be prepared to supply the CRS signer binary BER-encoded X.509 ** certificate. This may be obtained from the KCS 5.5 Subscriber Services web ** page. Go to the appropriate jurisdiction and obtain the base-64 encoded ** CRS trusted root. For the purposes of this demo, use the b64 sample to ** convert the base-64 X.509 to a binary X.509. ** ** 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" #include "pkiutil.h" /* Used to locate configuration information */ #define CRS_PROV_CFG_URL "file: /* Name of PKI service provider */ #define PKI_SP_NAME "KCS CRS Transaction Provider" /* Name of database associated with cert path context. */ #define EE_DB_NAME "Requestor's Database" #define SP_COUNT 5 /* For this example, we have the EE sending messages to the CA in order to * get an immediate response. Therefore, we use this procedure to associate * a requestor's database with the given ctx, containing the credentials * necessary to communicate the CA. * * In this example, we use enveloped and signed messages. Therefore, this * routine must be given the root certificate of the CRS signer * * The caller must also supply the requestor's keypair and name object. * Since this is an end-entity to CA CRS transaction, the requestor's keypair * will be used to secure the CRS messages. In addition, the name object * describing the sender must be identical to the subject name in the cert * template, telling Cert-C to generate a self-signed certificate for the * end-entity to use in the transaction to request an actual certificate * issued by the CA. * * This function calls C_SetPKIMsgSender and C_SetPKIMsgRecipient to modify * the information in the given certReq. * * DestroyPkiProtectInfo must be called when pkiProtectInfo is no longer * needed. */ static int InitializePkiProtectInfo (CERTC_CTX ctx, PKI_MSG_OBJ certReq, NAME_OBJ requestor, B_KEY_OBJ publicKey, B_KEY_OBJ privateKey, PKI_PROTECT_INFO *pkiProtectInfo); 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; char temp[RSA_DEMO_MAX_LINE_LEN]; PKI_MSG_OBJ certReq = NULL, certResp = NULL; PKI_MSG_FIELDS msgFields; PKI_CERTREQ_FIELDS creqFields; PKI_CERTRESP_FIELDS certRespFields; NAME_OBJ requestorName = NULL; B_KEY_OBJ publicKey = NULL, privateKey = NULL; ITEM publicBer = {NULL, 0}, certDer = {NULL, 0}; PKI_PROTECT_INFO pkiProtectInfo = {{NULL}}; EXTENSIONS_OBJ certExtensions = 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, EE_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)); status = RSA_SetOptions (&logParams, argc, argv); if (status != 0) goto CLEANUP; RSA_PrintMessage ("KCS 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_ENVELOPE_THEN_SIGN); status = RSA_GetRequestorInfo (ctx, &requestorName, &publicKey, &privateKey); if (status != 0) goto CLEANUP; status = C_SetPKIMsgFields (certReq, &msgFields); if (status != 0) goto CLEANUP; /* Set certReq sender and recipient. Also, obtain pkiProtectInfo. */ status = InitializePkiProtectInfo (ctx, certReq, requestorName, publicKey, privateKey, &pkiProtectInfo); if (status != 0) goto CLEANUP; status = C_GetPKICertRequestFields (certReq, &creqFields); if (status != 0) goto CLEANUP; IgnoreAll (creqFields.flags); /* following LRM p.212, for an EE to CA transaction */ creqFields.certTemplate.serialNumber.data = T_malloc (1); creqFields.certTemplate.serialNumber.len = 1; T_memset (creqFields.certTemplate.serialNumber.data, 0, 1); ClearIgnoreFlag (creqFields.flags, PKI_CERTREQFLAGS_IGNORE_TEMPLATE_SERIAL); creqFields.certTemplate.subjectName = requestorName; 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); status = C_CreateExtensionsObject (&certExtensions, CERT_EXTENSIONS_OBJ, ctx); if (status != 0) goto CLEANUP; RSA_PrintMessage ("\nOptionally supply desired cert extensions. "); RSA_PrintMessage ("Enter blank to omit.\n"); status = RSA_GetFileToExtensionsObject (ctx, &certExtensions); if (status == 0) { creqFields.certTemplate.certExtensions = certExtensions; ClearIgnoreFlag (creqFields.flags, PKI_CERTREQFLAGS_IGNORE_TEMPLATE_CERTEXTS); } else if (status != 0 && status != RSA_DEMO_E_CANCEL) goto CLEANUP; RSA_PrintMessage ("Supply regInfo for certificate enrollment.\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; /* Save self-signed cert and private key to open enveloped messages from CRS responder. */ status = C_GetPKIMsgFields (certReq, &msgFields); if (status != 0) goto CLEANUP; if (!(msgFields.flags & PKI_MSGFLAGS_IGNORE_EXTRACERTS)) { RSA_PrintMessage ("Adding certs to database...\n"); status = RSA_PrintCertList (msgFields.extraCerts); if (status != 0) goto CLEANUP; /* Put the temporary self-signed cert in the database */ status = C_InsertCertList (pkiProtectInfo.info.protectionCtx->database, msgFields.extraCerts); if (status != 0) goto CLEANUP; status = C_InsertPrivateKeyBySPKI (pkiProtectInfo.info.protectionCtx->database, &publicBer, privateKey); if (status != 0) goto CLEANUP; } else RSA_PrintMessage ("No self-signed cert available!\n"); status = C_CreatePKIMsgObject (ctx, &certResp); if (status != 0) goto CLEANUP; for (;;) { /* Write and send the request and read in the response. */ RSA_PrintMessage ("Constructing and sending request. Please wait.\n"); 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.statusInfo.status != PKI_STATUS_WAITING) break; /* We don't care about what this RSA_GetCommand call does, we just use it * as a blocking mechanism so that we wait to send our duplicate request once * we know it has a chance of succeeding. */ RSA_GetCommand (temp, sizeof (temp), "Hit enter when cert has been approved"); } /* In the case of C_WritePKICertRequestMsg, C_SendPKIMsg, and * C_ReadPKICertResponseMsg being used in place of C_RequestPKICert, the PKI * providers currently place all of the returned certs in the * certRespFields.extraCerts list and it is the application's responsibility to * filter these certs to find the one containing the public key for which * certification was requested. */ 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 ("kcscrs.c", status); T_free (publicBer.data); T_free (creqFields.certTemplate.serialNumber.data); DestroyPkiProtectInfo (&pkiProtectInfo); B_DestroyKeyObject (&publicKey); B_DestroyKeyObject (&privateKey); C_DestroyAttributesObject (&creqFields.regInfo); C_DestroyExtensionsObject (&certExtensions); C_DestroyNameObject (&requestorName); C_DestroyPKIMsgObject (&certReq); C_DestroyPKIMsgObject (&certResp); C_FinalizeCertC (&ctx); return status; } /* end main */ static int InitializePkiProtectInfo (CERTC_CTX ctx, PKI_MSG_OBJ certReq, NAME_OBJ requestor, B_KEY_OBJ publicKey, B_KEY_OBJ privateKey, PKI_PROTECT_INFO *pkiProtectInfo) { int status = 0; CERT_OBJ rootCert = NULL; CERT_FIELDS rootCertFields; ITEM rootCertBer = {NULL, 0}, publicKeyBer = {NULL, 0}; SERVICE db = NULL; LIST_OBJ trustedRoots = NULL, certList = NULL; PKI_SENDER_INFO senderInfo; PKI_RECIPIENT_INFO recipientInfo; NAME_OBJ requestorCopy = NULL, recipientCopy = NULL; ITEM nameBer = {NULL, 0}; unsigned char *tmpSerialNumber = NULL, *serialNumber = NULL; T_memset ((POINTER)&senderInfo, 0, sizeof (senderInfo)); T_memset ((POINTER)&recipientInfo, 0, sizeof (recipientInfo)); 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 = RSA_GetFileToAllocBuffer (&rootCertBer.data, &rootCertBer.len, "Enter name of file containing CRS root certificate binary"); if (status != 0) goto CLEANUP; status = C_CreateCertObject (&rootCert, ctx); if (status != 0) goto CLEANUP; status = C_SetCertBER (rootCert, rootCertBer.data, rootCertBer.len); if (status != 0) goto CLEANUP; RSA_PrintMessage ("Root Certificate\n"); RSA_PrintCertInfo (rootCert); status = C_BindService (ctx, SPT_DATABASE, EE_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; status = C_AddCertToList (trustedRoots, rootCert, NULL); if (status != 0) goto CLEANUP; RSA_PrintMessage ("\nOptionally supply other trusted root certificates.\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; status = C_InsertCertList (db, 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; status = C_InsertCertList (db, 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; 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 = PF_VALIDATION_TIME_NOW; pkiProtectInfo->info.protectionCtx->database = db; /* Since we are an end-entity requesting a certificate, we use the keypair * we want certified (along with a self-signed cert, generated internally by * Cert-C) in the CRS message exchanges. Add the requestor's private key to * the database (requestor is the sender). */ status = RSA_GetKeyBer (RSA_DEMO_PUBLIC_KEY, publicKey, &publicKeyBer); if (status != 0) goto CLEANUP; status = C_InsertPrivateKeyBySPKI (db, &publicKeyBer, privateKey); if (status != 0) goto CLEANUP; /* Set sender information */ senderInfo.senderId.type = PKI_ENTITY_ISSUER_SERIAL; /* make a copy of the root issuer name */ status = C_CreateNameObject (&requestorCopy); if (status != 0) goto CLEANUP; status = C_GetNameDER (requestor, &nameBer.data, &nameBer.len); if (status != 0) goto CLEANUP; status = C_SetNameBER (requestorCopy, nameBer.data, nameBer.len); if (status != 0) goto CLEANUP; senderInfo.senderId.id.issuerSerialNumber.issuerName = requestorCopy; /* The sender (requestor) does not have a certificate. Set these values to tell the provider to generate a temporary self-signed cert */ tmpSerialNumber = T_malloc (1); T_memset (tmpSerialNumber, 0, 1); senderInfo.senderId.id.issuerSerialNumber.serialNumber.data = tmpSerialNumber; senderInfo.senderId.id.issuerSerialNumber.serialNumber.len = 1; /* hard-coded for simplicity */ senderInfo.digestAlgorithmId.algorithmId = DAI_MD5; senderInfo.digestAlgorithmId.algorithmParam = NULL; senderInfo.signatureAlgorithmId.algorithmId = SA_RSA_ENCRYPTION; senderInfo.signatureAlgorithmId.algorithmParam = NULL; status = C_SetPKIMsgSender (certReq, &senderInfo); if (status != 0) goto CLEANUP; /* Since this example uses enveloped and signed messages, we need to set the recipient appropriately. */ status = C_GetCertFields (rootCert, &rootCertFields); if (status != 0) goto CLEANUP; /* make a copy of the root issuer name */ status = C_CreateNameObject (&recipientCopy); if (status != 0) goto CLEANUP; status = C_GetNameDER (rootCertFields.issuerName, &nameBer.data, &nameBer.len); if (status != 0) goto CLEANUP; status = C_SetNameBER (recipientCopy, nameBer.data, nameBer.len); if (status != 0) goto CLEANUP; /* make a copy of the CRS root serial number */ serialNumber = T_malloc (rootCertFields.serialNumber.len); if (serialNumber == NULL) { status = RSA_DEMO_E_ALLOC; goto CLEANUP; } T_memcpy (serialNumber, rootCertFields.serialNumber.data, rootCertFields.serialNumber.len); recipientInfo.type = PKI_RECIPIENT_KEY_TRANSPORT; /* hard-coded for simplicity (and let Cert-C generate the IV) */ recipientInfo.contentEncryptionAlgorithmId.algorithmId = EAI_DES3; recipientInfo.contentEncryptionAlgorithmId.algorithmParam = NULL; recipientInfo.info.keyTrans.recipientId.type = PKI_ENTITY_ISSUER_SERIAL; recipientInfo.info.keyTrans.recipientId.id.issuerSerialNumber. issuerName = recipientCopy; recipientInfo.info.keyTrans.recipientId.id.issuerSerialNumber. serialNumber.data = serialNumber; recipientInfo.info.keyTrans.recipientId.id.issuerSerialNumber. serialNumber.len = rootCertFields.serialNumber.len; /* Here's where we make the decision about which algorithm to use to encrypt the symmetric key. For now, the only supported algorithm is KA_RSA_ENCRYPTION, or RSA PKCS #1 v1.5 block 02 padding. In the future, other possiblilties could include PKCS #2 OAEP, or SET OAEP. To be robust, a real application could examine the rootCertFields.publicKey to see what the possible algorithms are. For example, a DSA certificate would not be suitable for enveloping at all whereas a cert with an OID indicating RSAEncryption would be valid for either PKCS #1 v1.5 block 02 padding or OAEP. In addition, an application may want to examine the key usage extension in the rootCertFields.certExtensions. */ recipientInfo.info.keyTrans.keyEncryptionAlgorithmId.algorithmId = KA_RSA_ENCRYPTION; recipientInfo.info.keyTrans.keyEncryptionAlgorithmId.algorithmParam = NULL; status = C_SetPKIMsgRecipient (certReq, &recipientInfo); CLEANUP: if (status != 0) { DestroyPkiProtectInfo (pkiProtectInfo); RSA_PrintError ("InitializePkiProtectInfo", status); } T_free (tmpSerialNumber); T_free (serialNumber); T_free (rootCertBer.data); T_free (publicKeyBer.data); C_DestroyNameObject (&requestorCopy); C_DestroyNameObject (&recipientCopy); C_DestroyCertObject (&rootCert); C_DestroyListObject (&certList); 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", "legacy-certc", VT_UTF8_STRING}, {"org_unit", "test", 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 */