| RSA BSAFE Cert-C |
Certificate Components for C |
| Crypto-C 6.2.1 Developer's Guide | ||
| Search |
/* $Id: fulfill.c,v 1.4 2004/03/02 05:18:34 gsingh Exp $ */ /* fulfill.c ** Copyright (c) 1995-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. */ #include <time.h> #include <stdio.h> #include "certc.h" #include "aglobal.h" #include "bsafe.h" #include "demo.h" #include "bcert.h" #include "dutil.h" #include "stdlibrf.h" #include "userextn.h" #include "dexten.h" #include "myprint.h" #include "fulfill.h" #include "inoutcl.h" #ifdef TEST_MEMORY_LEAK #include "memlog.h" #endif static UINT4 CURRENT_SERIAL = (UINT4)0; #if 0 static unsigned char CERT_POLICY_ID_1[] = "Certicate Policy ID 1"; static unsigned char CERT_POLICY_ID_2[] = "Certicate Policy ID 2"; #endif /* demo unique serial number. Real application should make a unique serial number for each certificate issued by a certificate authority. */ static unsigned char SERIAL_NUMBER[] = { 0x02, 0x01, 0x01, 0x01 }; static int AddCertExtensions PROTO_LIST ((EXTENSIONS_OBJ)); #if 0 static void ConverseToString PROTO_LIST ((unsigned char *, UINT4)); static UINT4 ConverseToUINT4 PROTO_LIST ((unsigned char *)); #endif #ifdef _BCERT_API_ static int DoSignCert PROTO_LIST ((CERT_OBJ, B_ALGORITHM_OBJ, A_SURRENDER_CTX *)); #else static int DoSignCert PROTO_LIST ((CERT_OBJ)); static int GetCertObjFromPKCS10BER PROTO_LIST ((SESSION_CTX *, CERT_OBJ, ITEM *)); #endif /* _BCERT_API_ */ static int GetCertRequestInfo PROTO_LIST ((CERT_OBJ, SESSION_CTX *)); static int GetNextSerialNumberAlloc PROTO_LIST ((unsigned char **, unsigned int *)); /* Read the certificate request file and use the authority private key to fulfill the request. The result is outputted into a file. */ int FulfillCertRequest (sessionContext) SESSION_CTX *sessionContext; { CERT_OBJ certObject = NULL_PTR; ITEM ber; int status; do { #ifdef _BCERT_API_ if ((status = C_CreateCertObject (&certObject, sessionContext->applContext)) != 0) break; #else if ((status = C_CreateCertObject (&certObject, sessionContext->certcContext)) != 0) break; #endif /* _BCERT_API_ */ if ((status = GetCertRequestInfo (certObject, sessionContext)) != 0) break; #ifdef _BCERT_API_ if ((status = DoSignCert (certObject, sessionContext->randomObject, &sessionContext->surrender)) != 0) break; #else if ((status = DoSignCert (certObject)) != 0) break; #endif /* _BCERT_API_ */ PrintCertObject (certObject, "\n\nFulfilled Certificate information:", &sessionContext->ioContext); /* Get the BER encoding of the cert request */ if ((status = C_GetCertDER (certObject, &ber.data, &ber.len)) != 0) break; /* Write to a file */ if ((status = WriteToFile (&ber, "approved certificate", &sessionContext->ioContext)) != 0) break; } while (0); C_DestroyCertObject (&certObject); return (status); } #ifdef _BCERT_API_ int DoVerifyCert (certObject, surrender) CERT_OBJ certObject; A_SURRENDER_CTX *surrender; #else int DoVerifyCert (certObject) CERT_OBJ certObject; #endif /* _BCERT_API_ */ { B_KEY_OBJ keyObject = NULL_PTR; ITEM publicKeyInfo; int status; do { if ((status = B_CreateKeyObject (&keyObject)) != 0) break; publicKeyInfo.data = ISSUER_PUBLIC_KEY; publicKeyInfo.len = sizeof (ISSUER_PUBLIC_KEY); if ((status = B_SetKeyInfo (keyObject, KI_RSAPublicBER, (POINTER)&publicKeyInfo)) != 0) break; #ifdef _BCERT_API_ status = C_VerifyCertSignature (certObject, keyObject, surrender); #else status = C_VerifyCertSignature (certObject, keyObject); #endif /* _BCERT_API_ */ } while (0); B_DestroyKeyObject (&keyObject); return (status); } /* This function decomposes a certificate request into a certObject. Updates the certObject with issuer name, new certificate serial number, and validity period. It also adds a signing time attribute to the certificate extension object. Note: The signing time attribute may be inserted in the subjectDirectoryAttributes extension. It is used here as a value for the user-defined extension "USER_DEFINED_EXTENSION" */ static int GetCertRequestInfo (certObject, sessionContext) CERT_OBJ certObject; SESSION_CTX *sessionContext; { CERT_FIELDS certFields; ITEM certRequestBER; int status; #ifdef _BCERT_API_ unsigned char digest[32]; unsigned int digestLen; #endif /* _BCERT_API_ */ certRequestBER.data = NULL_PTR; certRequestBER.len = 0; T_memset ((POINTER)&certFields, 0, sizeof (certFields)); do { /* Read in the cert request information from input file */ if ((status = ReadFromFile (&certRequestBER, "certificate request", &sessionContext->ioContext)) != 0) break; PrintMessage ("\nValidating signature on the certificate request...", 0, IO_CTX_PROMPT, &sessionContext->ioContext); /* Transfer certificate request information into an unsigned cert object. This process first validates the signature on the certificate request to make sure that the public key is really binded to the private key which was used to sign the request. Then it will initialize the validity to one year period. */ #ifdef _BCERT_API_ if ((status = C_DecomposePKCSCertRequestBER (certObject, (ATTRIBUTES_OBJ)NULL_PTR, certRequestBER.data, certRequestBER.len, digest, &digestLen, &sessionContext->surrender)) != 0) break; #else if ((status = GetCertObjFromPKCS10BER (sessionContext, certObject, &certRequestBER)) != 0) break; #endif /* _BCERT_API_ */ /* Get out cert request fields */ if ((status = C_GetCertFields (certObject, &certFields)) != 0) break; /* This serial number should be unique for each certificate that the issuer is going to generate. */ if ((status = GetNextSerialNumberAlloc (&certFields.serialNumber.data, &certFields.serialNumber.len)) != 0) break; /* Since C_DecomposePKCSCertRequestBER() sets the issuer name to be the same as the subject name. We must reset the issuer name to the correct name. Note that the subject name is subordinate to the issuer name even though we don't check for subordination here. */ C_ResetNameObject (certFields.issuerName); if ((status = MakeNameObject (certFields.issuerName, (unsigned char *)NULL_PTR, 0)) != 0) break; if ((status = AddCertExtensions (certFields.certExtensions)) != 0) break; /* Update the certObject with new information in the certFields. It is important that we call C_SetCertFields(). Otherwise, the certObject will still be in the state after the C_DecomposePKCSCertRequestBER() call. */ /* The validity is already set for 1 year period from now. Make it validate 2 days later. */ certFields.validity.start += (UINT4)48 * 3600; certFields.validity.end += (UINT4)48 * 3600; /* BCERT 1.0 only supports certificate request version 1, therefore the certFields.version is set to CERT_VERSION_1. Since we add some v3 certificate extensions to certFields.certExtensions, we need to change the version to CERT_VERSION_3. */ certFields.version = CERT_VERSION_3; status = C_SetCertFields (certObject, &certFields); } while (0); T_free ((POINTER)certRequestBER.data); T_free ((POINTER)certFields.serialNumber.data); return (status); } /* This routine will add 3 certificate extensions to the extensionsObject. A signing time attribute is added as a user-defined extension type. keyUsage and privateKeyUsagePeriod extensions will also be added. */ static int AddCertExtensions (extObject) EXTENSIONS_OBJ extObject; { ATTRIBUTES_OBJ attributes = (ATTRIBUTES_OBJ)NULL_PTR; UINT4 currentTime, keyUsage; PRIVATE_KEY_USAGE_PERIOD privateKeyValidity; int status; do { if ((status = C_CreateAttributesObject (&attributes)) != 0) break; /* Create signing time attribute as an extension value */ T_time (¤tTime); if ((status = C_SetSigningTimeAttribute (attributes, currentTime)) != 0) break; /* Add a user-defined extension */ if ((status = AddExtensions (extObject, USER_DEFINED_EXTENSION, USER_DEFINED_EXTENSION_LEN, (POINTER)attributes)) != 0) break; keyUsage = (UINT4)(CF_DIGITAL_SIGNATURE | CF_CRL_SIGN); /* Add a key usage extension */ if ((status = AddExtensions (extObject, ET_KEY_USAGE, ET_KEY_USAGE_LEN, (POINTER)&keyUsage)) != 0) break; privateKeyValidity.end.year = (unsigned short)((privateKeyValidity.start.year = 1996) + 1); privateKeyValidity.end.month = (privateKeyValidity.start.month = 11); privateKeyValidity.end.day = (privateKeyValidity.start.day = 06); privateKeyValidity.end.hour = (privateKeyValidity.start.hour = 21); privateKeyValidity.end.minute = (privateKeyValidity.start.minute = 6); privateKeyValidity.end.second = (privateKeyValidity.start.second = 27); privateKeyValidity.end.microSecond = ((privateKeyValidity.start.microSecond = 1111) + 2222); privateKeyValidity.end.timeZone = (short int)((privateKeyValidity.start.timeZone = 0) - 10); /* Add a private key usage period extension */ if ((status = AddExtensions (extObject, ET_PRIVATE_KEY_USAGE_PERIOD, ET_PRIVATE_KEY_USAGE_PERIOD_LEN, (POINTER)&privateKeyValidity)) != 0) break; } while (0); C_DestroyAttributesObject (&attributes); return (status); } /* Sign a certificate with the issuer's private key. */ #ifdef _BCERT_API_ static int DoSignCert (certObject, randomObject, surrender) CERT_OBJ certObject; B_ALGORITHM_OBJ randomObject; A_SURRENDER_CTX *surrender; #else static int DoSignCert (certObject) CERT_OBJ certObject; #endif /* _BCERT_API_ */ { B_KEY_OBJ keyObject = NULL_PTR; ITEM privateKeyInfo; int status; do { if ((status = B_CreateKeyObject (&keyObject)) != 0) break; privateKeyInfo.data = ISSUER_PRIVATE_KEY; privateKeyInfo.len = sizeof (ISSUER_PRIVATE_KEY); if ((status = B_SetKeyInfo (keyObject, KI_PKCS_RSAPrivateBER, (POINTER)&privateKeyInfo)) != 0) break; #ifdef _BCERT_API_ status = C_SignCert (certObject, keyObject, randomObject, surrender); #else status = C_SignCert (certObject, keyObject); #endif /* _BCERT_API_ */ } while (0); B_DestroyKeyObject (&keyObject); return (status); } #ifndef _BCERT_API_ /* Since C_DecomposePKCSCertRequestBER was deprecated, we must construct a procedure which creates a CERT_OBJ template using the information contained in a PKCS #10 message. */ static int GetCertObjFromPKCS10BER (SESSION_CTX *sessionContext, CERT_OBJ certObj, ITEM *pkcs10BER) { int status = 0; PKCS10_OBJ pkcs10Obj = NULL; PKCS10_FIELDS pkcs10Fields; CERT_FIELDS certFields; ITEM subjectName = {NULL, 0}, publicKeyBER = {NULL, 0}; status = C_CreatePKCS10Object (sessionContext->certcContext, &pkcs10Obj); if (status != 0) goto CLEANUP; status = C_SetPKCS10BER (pkcs10Obj, pkcs10BER->data, pkcs10BER->len); if (status != 0) goto CLEANUP; /* Verify that the PKCS #10 request was signed with the private key corresponding to the public key contained in the PKCS #10 request. */ status = C_VerifyPKCS10Signature (pkcs10Obj); if (status != 0) goto CLEANUP; status = C_GetPKCS10Fields (pkcs10Obj, &pkcs10Fields); if (status != 0) goto CLEANUP; status = C_GetCertFields (certObj, &certFields); if (status != 0) goto CLEANUP; certFields.version = CERT_VERSION_1; certFields.signatureAlgorithm = SA_MD5_WITH_RSA_ENCRYPTION; certFields.serialNumber.data = NULL; certFields.serialNumber.len = 0; status = C_GetNameDER (pkcs10Fields.subjectName, &subjectName.data, &subjectName.len); if (status != 0) goto CLEANUP; /* as a default, set issuer name to be the subject name */ status = C_SetNameBER (certFields.issuerName, subjectName.data, subjectName.len); if (status != 0) goto CLEANUP; status = C_SetNameBER (certFields.subjectName, subjectName.data, subjectName.len); if (status != 0) goto CLEANUP; /* by default, set validity to 1 year */ T_time (&certFields.validity.start); certFields.validity.end = certFields.validity.start + ((3600 * 24 * 365) - 1); publicKeyBER.len = pkcs10Fields.publicKey.len; publicKeyBER.data = T_malloc (publicKeyBER.len); if (publicKeyBER.data == NULL) { status = E_ALLOC; goto CLEANUP; } T_memcpy (publicKeyBER.data, pkcs10Fields.publicKey.data, publicKeyBER.len); certFields.publicKey.data = publicKeyBER.data; certFields.publicKey.len = publicKeyBER.len; status = C_SetCertFields (certObj, &certFields); CLEANUP: C_DestroyPKCS10Object (&pkcs10Obj); T_free (publicKeyBER.data); return status; } /* end GetCertObjFromPKCS10BER */ #endif /* _BCERT_API_ */ static UINT4 ConvertToUINT4 (text) unsigned char *text; { UINT4 number = 0L; int i, j; for (i=0, j=3; i < 4; i++, j--) number |= ((UINT4) (*(text + i))) << (j * 8); return (number); } static void ConvertToString (text, number) unsigned char *text; UINT4 number; { int i, j; /* start i at 1 because we will add on the 0x20 at the beginning when we return from this function */ for (i= 1, j = 3; i < 5; i++, j--) *(text + i) = (unsigned char)((number >> (j*8)) & 0xff); } static int GetNextSerialNumberAlloc (text, textLen) unsigned char **text; unsigned int *textLen; { int status; if ((*text = (unsigned char *)T_malloc (6)) == 0) return (status = E_ALLOC); if (!CURRENT_SERIAL) CURRENT_SERIAL = ConvertToUINT4 (SERIAL_NUMBER); ++CURRENT_SERIAL; ConvertToString (*text, CURRENT_SERIAL); **text = 0x20; *textLen = 5; return (0); }