| RSA BSAFE Cert-C |
Certificate Components for C |
| Crypto-C 6.2.1 Developer's Guide | ||
| Search |
/* $Id: scepreq.c,v 1.4 2004/03/02 05:18:42 gsingh Exp $ */ /* scepreq.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 request a certificate from a CA that supports ** SCEP. ** ** 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 "scep.h" #include "pkixpath.h" #include "filelog.h" #include "demoutil.h" #include "pkiutil.h" #include "certutil.h" #include "crlutil.h" #include "keyutil.h" #include "nameutil.h" #include "extnutil.h" #ifdef _MSC_VER # pragma warning (disable: 171) /* invalid type conversion (often of very similar ptrs) */ #endif /* The number of service providers registered when the context is * initialized. */ #define SP_COUNT 3 #define DEFAULT_SCEP_URL "http://century.rsa.com:80/cgi-bin/pkiclient.exe/CertCTest/pkiclient.exe" #define SCEP_PROVIDER_NAME "RSA SCEP PKI Provider" #define EE_DB_NAME "Requestor's Database" /* 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 which the CA uses for SCEP. * You can use the scepdb sample (samples/db/scepdb.c) to obtain the CA cert. * * The caller must also supply the requestor's keypair and name object. * Since this is an end-entity to CA SCEP transaction, the requestor's keypair * will be used to secure the SCEP 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 regInfo for SCEP request. This must contain the challenge * password attribute. */ static int SetRegInfo (ATTRIBUTES_OBJ regInfo); 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_SCEP_INIT_PARAMS scepInitParams = {0}; SERVICE_HANDLER scepHandler = { SPT_PKI, SCEP_PROVIDER_NAME, S_InitializeSCEPPKI }; PKI_MSG_OBJ pkiMsgObj = (PKI_MSG_OBJ)NULL_PTR; PKI_PROTECT_INFO protectInfo = {0}; PKI_CERT_REQ_OBJ certReqObj = (PKI_CERT_REQ_OBJ)NULL_PTR; PKI_CERT_TEMPLATE_OBJ certTemplateObj = (PKI_CERT_TEMPLATE_OBJ)NULL_PTR; NAME_OBJ requestor = (NAME_OBJ)NULL_PTR; EXTENSIONS_OBJ certExtensions = (EXTENSIONS_OBJ)NULL_PTR; B_KEY_OBJ publicKey = (B_KEY_OBJ)NULL_PTR, privateKey = (B_KEY_OBJ)NULL_PTR; ITEM publicKeyBer = {0}; ATTRIBUTES_OBJ regInfo = (ATTRIBUTES_OBJ)NULL_PTR; LIST_OBJ extraCerts = (LIST_OBJ)NULL_PTR; unsigned int reqIndex; PKI_POP_GEN_INFO popGenInfo = {0}; CERT_OBJ certObj; unsigned int count, i; 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 ("SCEP Certificate Request Example\n"); RSA_PrintMessage ("================================\n"); /* Used to hold results and to hold CA cert */ status = C_BindService (ctx, SPT_DATABASE, EE_DB_NAME, &db); if (status != 0) goto CLEANUP; /* Step 1: Register SCEP Provider */ scepInitParams.initChoice = PKI_SCEP_INIT_METHOD_STRUCT; status = RSA_CreateTransportInfoFieldsPrompt (&scepInitParams.method.initStruct.transport, DEFAULT_SCEP_URL); if (status != 0) goto CLEANUP; status = RSA_ScepProfilePrompt (&scepInitParams.method.initStruct.profile); if (status != 0) goto CLEANUP; status = C_RegisterService (ctx, &scepHandler, (POINTER)&scepInitParams, 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_CERT_REQ); if (status != 0) goto CLEANUP; /* The private key will be used later to generate the proof of possession */ status = RSA_GetRequestorInfo (ctx, &requestor, &publicKey, &privateKey); if (status != 0) goto CLEANUP; /* Set Message Protection: * The protectInfo will be used when we call C_RequestPKIMsg. * 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 = InitializePkiProtectInfo (ctx, pkiMsgObj, requestor, publicKey, privateKey, &protectInfo); if (status != 0) goto CLEANUP; /* Because we have set PKI_MSG_PROTECTION_SIGN, note that we must add the SCEP Signer's cert (CA cert) to the senderDb and a valid chain must exist from the SCEP Signer's cert to a cert in the trustedCerts list. */ status = C_SetPKIMsgProtectionType (pkiMsgObj, PKI_MSG_PROTECTION_ENVELOPE_THEN_SIGN); if (status != 0) goto CLEANUP; /* Step 3: Create Certificate Request Object */ status = C_CreatePKICertReqObject (ctx, &certReqObj); if (status != 0) goto CLEANUP; /* We must supply the signature algorithm, subject name, public key, and cert extensions in the cert template */ status = C_CreatePKICertTemplateObject (ctx, &certTemplateObj); if (status != 0) goto CLEANUP; /* Could modify to use the menuutil helper routines to give the user a choice, if desired */ status = C_SetCertTemplateSignatureAlgorithm (certTemplateObj, SA_MD5_WITH_RSA_ENCRYPTION); if (status != 0) goto CLEANUP; status = C_SetCertTemplateSubjectName (certTemplateObj, requestor); if (status != 0) goto CLEANUP; 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_CreateExtensionsObject (&certExtensions, CERT_EXTENSIONS_OBJ, ctx); if (status != 0) goto CLEANUP; RSA_PrintMessage ("Supply desired cert extensions.\n"); status = RSA_GetExtensionsObject (certExtensions, CERT_EXTENSIONS_OBJ); if (status != 0) goto CLEANUP; status = C_SetCertTemplateExtensions (certTemplateObj, certExtensions); if (status != 0) goto CLEANUP; status = C_SetPKICertReqCertTemplate (certReqObj, certTemplateObj); if (status != 0) goto CLEANUP; RSA_PrintMessage ("Supply regInfo for certificate enrollment.\n"); status = C_CreateAttributesObject (®Info); if (status != 0) goto CLEANUP; status = SetRegInfo (regInfo); if (status != 0) goto CLEANUP; status = C_SetPKICertReqRegInfo (certReqObj, regInfo); if (status != 0) goto CLEANUP; /* Specify an action for C_GeneratePKIMsgProofOfPossession */ status = C_SetPKICertReqPOPType (certReqObj, PKI_POP_SIGNATURE); if (status != 0) goto CLEANUP; /* Step 4: Add cert request to PKI message object */ status = C_AddPKIMsg (pkiMsgObj, (POINTER)certReqObj, &reqIndex); if (status != 0) goto CLEANUP; /* Step 5: Generate Proof of Possession of private key */ popGenInfo.method = PKI_POP_METHOD_CHALLENGE; status = C_GeneratePKIMsgProofOfPossession (ctx, SCEP_PROVIDER_NAME, pkiMsgObj, reqIndex, privateKey, &popGenInfo); if (status != 0) goto CLEANUP; /* Retrieve our temporary self-signed cert to put in the database, so that enveloped messages from the server can be decrypted */ status = C_GetPKIMsgExtraCerts (pkiMsgObj, &extraCerts); if (status != 0 && status != E_VALUE_NOT_SET) goto CLEANUP; if (status == E_VALUE_NOT_SET) { status = 0; /* not a fatal error, yet */ RSA_PrintMessage ("No temporary self-signed cert generated.\n"); } else { RSA_PrintMessage ("Adding trusted certs to database...\n"); status = RSA_PrintCertList (extraCerts); if (status != 0) goto CLEANUP; status = C_InsertCertList (protectInfo.info.protectionCtx->database, extraCerts); if (status != 0) goto CLEANUP; /* Besides putting the temporary self-signed cert in the database, explicitly trust them too */ status = C_GetListObjectCount (extraCerts, &count); if (status != 0) goto CLEANUP; for (i = 0; i < count; i++) { status = C_GetListObjectEntry (extraCerts, i, (POINTER *)&certObj); if (status != 0) goto CLEANUP; status = C_AddCertToList (protectInfo.info.protectionCtx->trustedCerts, certObj, NULL); if (status != 0) goto CLEANUP; } } /* Step 6: Send Request Message */ status = C_CreatePKIMsgObject (ctx, &pkiMsgRespObj); if (status != 0) goto CLEANUP; for (;;) { RSA_PrintMessage ("Sending Certificate Request...\n"); status = C_RequestPKIMsg (ctx, SCEP_PROVIDER_NAME, pkiMsgObj, &protectInfo, db, pkiMsgRespObj); if (status != 0) goto CLEANUP; /* Step 7: Process Response Message */ status = RSA_ProcessPkiMsgResp (ctx, pkiMsgRespObj, pkiMsgObj, RSA_DEMO_SCEP, SCEP_PROVIDER_NAME, &protectInfo, db); if (status == 0) break; else if (status != 0 && status != RSA_DEMO_E_ACTION_PENDING) goto CLEANUP; /* You could also print the contents of the db SERVICE handle to see if any additional certs or CRLs were returned. */ RSA_GetCommand (userInput, sizeof (userInput), "Hit enter when cert has been approved"); } CLEANUP: if (status != 0) RSA_PrintError ("scepreq.c", status); else RSA_PrintMessage ("Done!\n"); DestroyPkiProtectInfo (&protectInfo); RSA_DestroyTransportInfoFields (&scepInitParams.method.initStruct.transport); T_free (publicKeyBer.data); B_DestroyKeyObject (&publicKey); B_DestroyKeyObject (&privateKey); C_DestroyAttributesObject (®Info); C_DestroyExtensionsObject (&certExtensions); C_DestroyPKICertTemplateObject (&certTemplateObj); C_DestroyPKICertReqObject (&certReqObj); C_DestroyPKIMsgObject (&pkiMsgObj); C_DestroyPKIMsgObject (&pkiMsgRespObj); C_UnbindService (&db); 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; char userInput[RSA_DEMO_MAX_LINE_LEN]; CERT_OBJ recipientCert = NULL; CERT_FIELDS recipientCertFields; ITEM recipientCertBer = {NULL, 0}, publicKeyBer = {NULL, 0}; SERVICE db = NULL; DB_ITERATOR crlIterator = NULL; LIST_OBJ trustedRoots = NULL, certList = NULL, tmpList = NULL; PKI_SENDER_INFO senderInfo; PKI_RECIPIENT_INFO recipientInfo; unsigned char *tmpSerialNumber = 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 = 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; RSA_PrintMessage ("You can use the scepdb sample to obtain the CA certs.\n"); RSA_PrintMessage ("First supply the SCEP responder cert representing the "); RSA_PrintMessage ("recipient.\n"); status = RSA_GetFileToAllocBuffer (&recipientCertBer.data, &recipientCertBer.len, "Enter name of file containing recipient certificate binary"); 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; RSA_PrintMessage ("SCEP Responder Cert (Recipient)\n"); RSA_PrintCertInfo (recipientCert); status = C_InsertCert (db, recipientCert); if (status != 0) goto CLEANUP; RSA_PrintMessage ("Explicitly trust recipient cert (y/n)? "); status = RSA_GetCommand (userInput, sizeof (userInput), NULL); if (status != 0) goto CLEANUP; if (userInput[0] == 'y') { status = C_AddCertToList (trustedRoots, recipientCert, NULL); if (status != 0) goto CLEANUP; } RSA_PrintMessage ("\nSupply 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; RSA_PrintMessage ("\nOptionally supply other relevant certificates, such "); RSA_PrintMessage ("as those needed to\nconstruct the certificate chain.\n"); RSA_PrintMessage ("Other certs are those such as a CA signer cert that is"); RSA_PrintMessage (" different from the\nrecipient certificate.\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, trustedRoots); 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; /* if our database does not contain CRLs, turn off revocation checking */ status = C_CreateListObject (&tmpList); if (status != 0) goto CLEANUP; C_SelectFirstCRL (db, &crlIterator, tmpList); if (crlIterator == NULL) /* no CRLs present */ pkiProtectInfo->info.protectionCtx->pathOptions = PF_IGNORE_REVOCATION; else pkiProtectInfo->info.protectionCtx->pathOptions = 0; C_DestroyListObject (&tmpList); pkiProtectInfo->info.protectionCtx->pathAlgorithm = PA_X509_V1; 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 SCEP 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; senderInfo.senderId.id.issuerSerialNumber.issuerName = requestor; /* 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; /* For simplicity, we set RSA with MD5 as the signature algorithm. If desired, modify to use menuutil functions to prompt user. */ 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 (recipientCert, &recipientCertFields); if (status != 0) goto CLEANUP; recipientInfo.type = PKI_RECIPIENT_KEY_TRANSPORT; /* hard-coded for simplicity (and let Cert-C generate the IV) */ recipientInfo.contentEncryptionAlgorithmId.algorithmId = EAI_DES; recipientInfo.contentEncryptionAlgorithmId.algorithmParam = NULL; recipientInfo.info.keyTrans.recipientId.type = PKI_ENTITY_ISSUER_SERIAL; recipientInfo.info.keyTrans.recipientId.id.issuerSerialNumber. issuerName = recipientCertFields.issuerName; recipientInfo.info.keyTrans.recipientId.id.issuerSerialNumber. serialNumber.data = recipientCertFields.serialNumber.data; recipientInfo.info.keyTrans.recipientId.id.issuerSerialNumber. serialNumber.len = recipientCertFields.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 (recipientCertBer.data); T_free (publicKeyBer.data); C_FreeIterator (&crlIterator); C_DestroyCertObject (&recipientCert); C_DestroyListObject (&certList); C_DestroyListObject (&tmpList); 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; char userInput[RSA_DEMO_MAX_LINE_LEN]; /* Set challenge password attribute */ status = RSA_GetCommand (userInput, sizeof (userInput), "Enter challenge password"); if (status != 0) goto CLEANUP; status = C_SetChallengePasswordAttribute (regInfo, VT_T61_STRING, (POINTER)userInput, T_strlen (userInput)); CLEANUP: if (status != 0) RSA_PrintError ("SetRegInfo", status); return status; } /* end SetRegInfo */