| RSA BSAFE Cert-C |
Certificate Components for C |
| Crypto-C 6.2.1 Developer's Guide | ||
| Search |
/* $Id: cmpku.c,v 1.4 2004/03/02 05:18:35 gsingh Exp $ */ /* cmpku.c ** Copyright (c) 2002-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 can be used to send a key update request to a CMP server. ** The key update request should be for a valid, non-revoked certificate. ** If the cert given for key update is not valid or revoked, the server ** should return an error. This sample also contains an option to allow ** for key archival, provided that it is supported by the server. ** ** 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 "rsacsp.h" #include "imdb.h" #include "cmp.h" #include "pkixpath.h" #include "filelog.h" #include "demoutil.h" #include "pkiutil.h" #include "certutil.h" #include "keyutil.h" #include "nameutil.h" /* The number of service providers registered when the context is * initialized. */ #define SP_COUNT 3 #define DEFAULT_CMP_URL "cmptcp://tr4.rsa.com:829" #define CMP_PROVIDER_NAME "RSA CMP Provider" #define EE_DB_NAME "Requestor's Database" int main (int argc, char *argv[]) { int status = 0; char userInput[RSA_DEMO_MAX_LINE_LEN]; CERTC_CTX ctx = NULL; SERVICE_HANDLER spTable[SP_COUNT] = { {SPT_CRYPTO, "Default CSP", S_InitializeDefaultCSP}, {SPT_DATABASE, EE_DB_NAME, S_InitializeMemoryDB}, {SPT_CERT_PATH, "Cert Path Processing Provider", S_InitializePKIXPath} }; POINTER spParams[SP_COUNT] = {0}; SERVICE db = (SERVICE)NULL_PTR; FILE_LOG_PARAMS logParams = {0}; SERVICE_HANDLER logHandler = { SPT_LOG, "Default File Log", S_InitializeFileLog }; PKI_CMP_SP_INIT_PARAMS cmpInitParams = {0}; SERVICE_HANDLER cmpHandler = { SPT_PKI, CMP_PROVIDER_NAME, S_InitializeCMP }; PKI_MSG_OBJ pkiMsgObj = (PKI_MSG_OBJ)NULL_PTR; PKI_PROTECT_INFO protectInfo = {0}; CERT_PATH_CTX senderCertCtx = {0}; LIST_OBJ trustedCerts = (LIST_OBJ)NULL_PTR; CERT_OBJ senderCert = (CERT_OBJ)NULL_PTR; CERT_FIELDS senderCertFields; B_KEY_OBJ senderPvtKey = (B_KEY_OBJ)NULL_PTR; PKI_SENDER_INFO senderInfo = {0}; GENERAL_NAME senderGeneralName = {0}; CERT_OBJ recipientCert = (CERT_OBJ)NULL_PTR; CERT_FIELDS recipientCertFields; ITEM recipientCertBer = {0}; PKI_RECIPIENT_INFO recipientInfo = {0}; GENERAL_NAME recipientGeneralName = {0}; PKI_KEY_UPDATE_REQ_OBJ keyUpdateReqObj = (PKI_KEY_UPDATE_REQ_OBJ)NULL_PTR; PKI_CERT_TEMPLATE_OBJ certTemplateObj = (PKI_CERT_TEMPLATE_OBJ)NULL_PTR; B_KEY_OBJ publicKey = (B_KEY_OBJ)NULL_PTR, privateKey = (B_KEY_OBJ)NULL_PTR; ITEM publicKeyBer = {0}; ATTRIBUTES_OBJ controls = (ATTRIBUTES_OBJ)NULL_PTR; unsigned int reqIndex; PKI_POP_GEN_INFO popGenInfo = {0}; PKI_MSG_OBJ pkiMsgRespObj = (PKI_MSG_OBJ)NULL_PTR; status = RSA_SetOptions (&logParams, argc, argv); if (status != 0) goto CLEANUP; 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 failure to register 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 RSA_PrintMessage ("CMP Key Update Request Example\n"); RSA_PrintMessage ("==============================\n"); /* Step 1: Register CMP Provider */ cmpInitParams.initChoice = PKI_CMP_INIT_METHOD_STRUCT; status = RSA_CreateTransportInfoFieldsPrompt (&cmpInitParams.method.initStruct.transport, DEFAULT_CMP_URL); if (status != 0) goto CLEANUP; status = RSA_CmpProfilePrompt (&cmpInitParams.method.initStruct.profile); if (status != 0) goto CLEANUP; status = C_RegisterService (ctx, &cmpHandler, (POINTER)&cmpInitParams, SERVICE_ORDER_LAST); if (status != 0) goto CLEANUP; /* Step 2: Create PKI message object */ status = C_CreatePKIMsgObject (ctx, &pkiMsgObj); if (status != 0) goto CLEANUP; status = C_SetPKIMsgType (pkiMsgObj, PKI_MSGTYPE_KEY_UPDATE_REQ); if (status != 0) goto CLEANUP; /* Set Message Protection: * This code could be modified to use an alternate protection type. * The protectInfo will be used when we call C_RequestPKIMsg. * The sender's CERT_PATH_CTX contains the cert to be updated, with the * existing private key, and here we set it to be explicitly trusted. * Since we are using a CERT_PATH_CTX, we need to register a provider * of type SPT_CERT_PATH with the current CERTC_CTX. That was done * in this sample during the call to C_InitializeCertC. */ status = C_BindService (ctx, SPT_DATABASE, EE_DB_NAME, &db); if (status != 0) goto CLEANUP; RSA_PrintMessage ("Supply existing cert and private key.\n"); RSA_PrintMessage ("Do not generate a new cert unless you are doing "); RSA_PrintMessage ("negative testing.\n"); status = C_CreateCertObject (&senderCert, ctx); if (status != 0) goto CLEANUP; status = B_CreateKeyObject (&senderPvtKey); if (status != 0) goto CLEANUP; status = RSA_GetCertAndPvtKey (ctx, senderCert, senderPvtKey); if (status != 0) goto CLEANUP; status = C_CreateListObject (&trustedCerts); if (status != 0) goto CLEANUP; status = C_AddCertToList (trustedCerts, senderCert, (unsigned int *)NULL_PTR); if (status != 0) goto CLEANUP; status = C_InsertCert (db, senderCert); if (status != 0) goto CLEANUP; status = C_InsertPrivateKey (db, senderCert, senderPvtKey); if (status != 0) goto CLEANUP; /* Because we have set PKI_MSG_PROTECTION_SIGN, note that we must add the CMP Signer's cert (CA cert) to the senderDb and a valid chain must exist from the CMP Signer's cert to a cert in the trustedCerts list. */ status = C_SetPKIMsgProtectionType (pkiMsgObj, PKI_MSG_PROTECTION_SIGN); if (status != 0) goto CLEANUP; /* Set Sender: * If your server uses an alternate method of identifiying the requestor, * modify this code to popluate the PKI_SENDER_INFO with the appropriate * information. Here we use the sender certificate (and matching private * key) that was just supplied above. */ status = C_GetCertFields (senderCert, &senderCertFields); if (status != 0) goto CLEANUP; /* PKI_ENTITY_ISSUER_SERIAL is currently not supported by the CMP provider. as a workaround, use PKI_ENTITY_GENERALNAME_KEYID */ #if 0 senderInfo.senderId.type = PKI_ENTITY_ISSUER_SERIAL; senderInfo.senderId.id.issuerSerialNumber.issuerName = senderCertFields.issuerName; senderInfo.senderId.id.issuerSerialNumber.serialNumber.data = senderCertFields.serialNumber.data; senderInfo.senderId.id.issuerSerialNumber.serialNumber.len = senderCertFields.serialNumber.len; #else senderGeneralName.altNameType = CN_DIRECTORY_NAME; senderGeneralName.altName.directoryName = senderCertFields.subjectName; senderInfo.senderId.type = PKI_ENTITY_GENERALNAME_KEYID; senderInfo.senderId.id.generalNameKeyId.name = &senderGeneralName; senderInfo.senderId.id.generalNameKeyId.keyId.data = NULL_PTR; senderInfo.senderId.id.generalNameKeyId.keyId.len = 0; #endif /* For simplicity, we set RSA with SHA1 as the signature algorithm. If desired, modify to use menuutil functions to prompt user. */ senderInfo.digestAlgorithmId.algorithmId = DAI_SHA1; senderInfo.digestAlgorithmId.algorithmParam = NULL_PTR; senderInfo.signatureAlgorithmId.algorithmId = SA_RSA_ENCRYPTION; senderInfo.signatureAlgorithmId.algorithmParam = NULL_PTR; status = C_SetPKIMsgSender (pkiMsgObj, &senderInfo); if (status != 0) goto CLEANUP; /* Set the Recipient DN, identifying the CA. If a cert is available, * extract the subject name from the CA cert. Otherwise, prompt the user * to supply the DN in a name object. */ RSA_PrintMessage ("Supply the CA certificate to identify the CA as the "); RSA_PrintMessage ("recipient. This cert\nwill be used to envelope a "); RSA_PrintMessage ("message for the CA if you are using key archival\nand "); RSA_PrintMessage ("will be used to verify signed messages from the CA.\n"); status = RSA_GetFileToAllocBuffer (&recipientCertBer.data, &recipientCertBer.len, "Enter name of file containing recipient cert"); if (status != 0) goto CLEANUP; status = C_CreateCertObject (&recipientCert, ctx); if (status != 0) goto CLEANUP; status = C_SetCertBER (recipientCert, recipientCertBer.data, recipientCertBer.len); if (status != 0) goto CLEANUP; status = C_GetCertFields (recipientCert, &recipientCertFields); if (status != 0) goto CLEANUP; recipientGeneralName.altNameType = CN_DIRECTORY_NAME; recipientGeneralName.altName.directoryName = recipientCertFields.subjectName; status = C_InsertCert (db, recipientCert); if (status != 0) goto CLEANUP; /* In addition, we'll just implicitly trust this cert */ status = C_AddCertToList (trustedCerts, recipientCert, (unsigned int *)NULL_PTR); if (status != 0) goto CLEANUP; recipientInfo.type = PKI_RECIPIENT_GENERALNAME_KEYID; recipientInfo.info.generalNameKeyId.name = &recipientGeneralName; status = C_SetPKIMsgRecipient (pkiMsgObj, &recipientInfo); if (status != 0) goto CLEANUP; /* Step 3: Create Key Update Request Object */ status = C_CreatePKIKeyUpdateReqObject (ctx, &keyUpdateReqObj); if (status != 0) goto CLEANUP; /* The private key will be used later to generate the proof of possession */ RSA_PrintMessage ("Supply replacement keypair for certification.\n"); status = RSA_GetKeypair (ctx, &publicKey, &privateKey); if (status != 0) goto CLEANUP; /* In this example, we fill in the cert template with the information from the existing certificate. Modify this code if the requirements of your server are different. */ status = C_CreatePKICertTemplateObject (ctx, &certTemplateObj); if (status != 0) goto CLEANUP; status = C_GetPKICertTemplateFromCertObject (senderCert, certTemplateObj); if (status != 0) goto CLEANUP; /* replace the public key in the cert template with the new one */ status = RSA_GetKeyBer (RSA_DEMO_PUBLIC_KEY, publicKey, &publicKeyBer); if (status != 0) goto CLEANUP; status = C_SetCertTemplatePublicKey (certTemplateObj, &publicKeyBer); if (status != 0) goto CLEANUP; status = C_SetPKICertReqCertTemplate (keyUpdateReqObj, certTemplateObj); if (status != 0) goto CLEANUP; /* Specify an action for C_GeneratePKIMsgProofOfPossession */ status = C_SetPKICertReqPOPType (keyUpdateReqObj, PKI_POP_SIGNATURE); if (status != 0) goto CLEANUP; /* Since we are replacing an existing certificate, we need to supply the oldCertId control, as specified in section 6.5 of RFC 2511 */ status = C_CreateAttributesObject (&controls); if (status != 0) goto CLEANUP; status = RSA_SetOldCertIdControl (ctx, senderCert, controls); if (status != 0) goto CLEANUP; /* Here, we give the user the option to perform key archival */ status = RSA_GetCommand (userInput, sizeof (userInput), "Perform key archival (y/n)?"); if (status != 0) goto CLEANUP; if (userInput[0] == 'y') { PKI_ARCHIVE_OPTION archiveOption = {0}; archiveOption.choice = ARCHIVE_ENCRYPTED_KEY; archiveOption.option.encryptedKey.keyWrapChoice = ENCRYPTED_VALUE; archiveOption.option.encryptedKey.pRecipient = &recipientInfo; archiveOption.option.encryptedKey.privateKey = privateKey; /* The contentEncryptionAlgorithmId field of the recipientInfo was not set previously. This demo could be modified to use RSA_ChooseSymEncAlgPrompt in the menuutil.h file, but for now, we do 3DES for simplicity. */ recipientInfo.contentEncryptionAlgorithmId.algorithmId = EAI_DES3; recipientInfo.contentEncryptionAlgorithmId.algorithmParam = NULL_PTR; status = S_AddPKIArchiveOptions (ctx, &archiveOption, controls); if (status != 0) goto CLEANUP; } else RSA_PrintMessage ("No key archival.\n"); status = C_SetPKICertReqControls (keyUpdateReqObj, controls); if (status != 0) goto CLEANUP; /* Step 4: Add cert request to PKI message object */ status = C_AddPKIMsg (pkiMsgObj, (POINTER)keyUpdateReqObj, &reqIndex); if (status != 0) goto CLEANUP; /* Step 5: Generate Proof of Possession of private key */ /* See the CMP specification for information on "Proof of Possession (POP) * of Private Key". Here, we indicate that our proof of possession will be * the requestor's ability to decrypt the issued cert in the confirmation * message. This is done to avoid having to send the private key to the CA. */ popGenInfo.method = PKI_POP_METHOD_ENCRYPT_CERT; status = C_GeneratePKIMsgProofOfPossession (ctx, CMP_PROVIDER_NAME, pkiMsgObj, reqIndex, privateKey, &popGenInfo); if (status != 0) goto CLEANUP; /* Step 6: Send Request Message */ status = C_CreatePKIMsgObject (ctx, &pkiMsgRespObj); if (status != 0) goto CLEANUP; senderCertCtx.pathAlgorithm = PA_X509_V1; senderCertCtx.pathOptions = PF_IGNORE_REVOCATION; senderCertCtx.trustedCerts = trustedCerts; senderCertCtx.policies = ANY_POLICY; senderCertCtx.validationTime = PF_VALIDATION_TIME_NOW; senderCertCtx.database = db; protectInfo.info.protectionCtx = &senderCertCtx; RSA_PrintMessage ("Sending Key Update Request...\n"); status = C_RequestPKIMsg (ctx, CMP_PROVIDER_NAME, pkiMsgObj, &protectInfo, db, pkiMsgRespObj); if (status != 0) goto CLEANUP; /* Add our new private key into the database */ status = C_InsertPrivateKeyBySPKI (db, &publicKeyBer, privateKey); if (status != 0) goto CLEANUP; /* Step 7: Process Response Message */ status = RSA_ProcessPkiMsgResp (ctx, pkiMsgRespObj, pkiMsgObj, RSA_DEMO_CMP, CMP_PROVIDER_NAME, &protectInfo, db); if (status != 0) goto CLEANUP; CLEANUP: if (status != 0) RSA_PrintError ("cmpku.c", status); else RSA_PrintMessage ("Done!\n"); RSA_DestroyTransportInfoFields (&cmpInitParams.method.initStruct.transport); T_free (publicKeyBer.data); B_DestroyKeyObject (&publicKey); B_DestroyKeyObject (&privateKey); B_DestroyKeyObject (&senderPvtKey); C_DestroyAttributesObject (&controls); C_DestroyListObject (&trustedCerts); C_DestroyCertObject (&senderCert); C_DestroyPKICertTemplateObject (&certTemplateObj); C_DestroyPKIKeyUpdateReqObject (&keyUpdateReqObj); C_DestroyPKIMsgObject (&pkiMsgObj); C_DestroyPKIMsgObject (&pkiMsgRespObj); C_UnbindService (&db); C_FinalizeCertC (&ctx); return status; } /* end main */