//+------------------------------------------------------------------------- // // Microsoft Windows // // Copyright (C) Microsoft Corporation, 1997 - 1999 // // File: makecert.cpp // //-------------------------------------------------------------------------- //+------------------------------------------------------------------------- // // MakeCert - x509 certificate generator // // Generates test certificates for Software Publishers. The default // root key and certificate is stored as a program resource. // // HansHu 2/20/96 created // Philh 5/17/96 changed to use wincert // Xiaohs 5/12/97 localization and change the command line options // //-------------------------------------------------------------------------- #define _CRYPT32_ #include "global.hxx" //+------------------------------------------------------------------------- // contants //-------------------------------------------------------------------------- //allow max 10 extensions per certificate #define MAX_EXT_CNT 10 //+------------------------------------------------------------------------- // Parameters configurable via command line arguments //-------------------------------------------------------------------------- BOOL fUseSubjectPvkFile = FALSE; BOOL fUseSubjectKeyContainer = FALSE; BOOL fUseIssuerPvkFile = FALSE; BOOL fSetSubjectName = FALSE; //use has specify the -n option #if (1) //DSIE: Bug 205195 BOOL fPrivateKeyExportable = FALSE; #endif WCHAR* wszSubjectKey = NULL; WCHAR* wszSubjectCertFile = NULL; WCHAR* wszSubjectStore = NULL; WCHAR* wszSubjectStoreLocation = NULL; DWORD dwSubjectStoreFlag = CERT_SYSTEM_STORE_CURRENT_USER; WCHAR* wszIssuerKey = NULL; WCHAR* wszIssuerCertFile = NULL; WCHAR* wszIssuerStore = NULL; WCHAR* wszIssuerStoreLocation = NULL; DWORD dwIssuerStoreFlag = CERT_SYSTEM_STORE_CURRENT_USER; WCHAR* wszIssuerCertName = NULL; DWORD dwIssuerKeySpec = 0; WCHAR* wszSubjectX500Name = NULL; WCHAR* wszSubjectRequestFile = NULL; WCHAR* wszPolicyLink = NULL; WCHAR* wszOutputFile = NULL; WCHAR* wszAuthority = NULL; WCHAR* wszAlgorithm = NULL; WCHAR* wszCertType = NULL; WCHAR* wszIssuerKeyType = NULL; WCHAR* wszSubjectKeyType = NULL; WCHAR* wszEKUOids = NULL; DWORD dwKeySpec = 0; BOOL fCertIndividual = FALSE; BOOL fCertCommercial = FALSE; BOOL fSelfSigned = FALSE; BOOL fGlueCert = FALSE; BOOL fNetscapeClientAuth = FALSE; BOOL fNoPubKeyPara = FALSE; BOOL fNoVerifyPublic = FALSE; LPWSTR wszIssuerProviderName = NULL; DWORD dwIssuerProviderType = PROV_RSA_FULL; LPWSTR wszSubjectProviderName = NULL; DWORD dwSubjectProviderType = PROV_RSA_FULL; ALG_ID algidHash = CALG_MD5; ULONG ulSerialNumber = 0; // In future, allow serial nubmers of larger size BOOL fSetSerialNumber = FALSE; DWORD dwCertStoreEncodingType = X509_ASN_ENCODING | PKCS_7_ASN_ENCODING; BOOL fIssuerInformation = FALSE; BOOL fSubjectInformation = FALSE; LPWSTR wszDhParaCertFile = NULL; DWORD dwKeyBitLen = 0; WCHAR wszGlue[10]; WCHAR wszKey[10]; WCHAR wszName[40]; WCHAR wszRoot[40]; WCHAR wszMakeCertRoot[40]; //used for indicating to use the root.cer. Root is also a registry name WCHAR wszPlus[10]; WCHAR wszNULL[10]; FILETIME g_ftNotBefore = { 0, 0 }; FILETIME g_ftNotAfter = { 0, 0 }; FILETIME g_ftZero = { 0, 0 }; long nMonths = -1; long pathLenConstraint = -1; BYTE certTypes = 0; // must be of type BYTE CHAR* szSignatureAlgObjId = NULL; static CERT_RDN_ATTR GlueRDNAttr= { SPC_GLUE_RDN_OBJID, CERT_RDN_PRINTABLE_STRING, {0, (BYTE *) wszGlue } }; //Global Data for loading the string #define OPTION_SWITCH_SIZE 5 HMODULE hModule=NULL; //--------------------------------------------------------------------------- // Get the hModule hanlder and init two DLLMain. // //--------------------------------------------------------------------------- BOOL InitModule() { if(!(hModule=GetModuleHandle(NULL))) return FALSE; return TRUE; } //------------------------------------------------------------------------- // // BasicUsage() // // //------------------------------------------------------------------------- static void BasicUsage() { IDSwprintf(hModule,IDS_SYNTAX); IDSwprintf(hModule,IDS_BASIC_OPTIONS); IDSwprintf(hModule,IDS_OPTION_SK_DESC); #if (1) //DSIE: Bug 205195. IDSwprintf(hModule,IDS_OPTION_PE_DESC); #endif IDSwprintf(hModule,IDS_OPTION_SS_DESC); IDSwprintf(hModule,IDS_OPTION_SS_DESC1); IDSwprintf(hModule,IDS_OPTION_SR_DESC); IDS_IDS_IDS_IDSwprintf(hModule,IDS_OPTION_VALUES_DEFAULT, IDS_OPTION_CU, IDS_OPTION_LM,IDS_OPTION_CU ); IDSwprintf(hModule,IDS_OPTION_SERIAL_DESC); IDSwprintf(hModule,IDS_OPTION_AUTH_DESC); IDS_IDS_IDSwprintf(hModule,IDS_OPTION_VALUES_2, IDS_OPTION_AUTH_IND, IDS_OPTION_AUTH_COM); IDSwprintf(hModule,IDS_OPTION_N_DESC); IDSwprintf(hModule,IDS_OPTION_BASIC_DESC); IDSwprintf(hModule,IDS_OPTION_EXTENDED_DESC); } //------------------------------------------------------------------------- // // ExtendedUsage() // // //------------------------------------------------------------------------- static void ExtendedUsage() { IDSwprintf(hModule,IDS_SYNTAX); IDSwprintf(hModule,IDS_EXTENDED_OPTIONS); IDSwprintf(hModule,IDS_OPTION_SC_DESC); IDSwprintf(hModule,IDS_OPTION_SV_DESC); IDSwprintf(hModule,IDS_OPTION_IC_DESC); IDSwprintf(hModule,IDS_OPTION_IK_DESC); IDSwprintf(hModule,IDS_OPTION_IV_DESC); IDSwprintf(hModule,IDS_OPTION_IS_DESC); IDSwprintf(hModule,IDS_OPTION_IR_DESC); IDS_IDS_IDS_IDSwprintf(hModule,IDS_OPTION_VALUES_DEFAULT, IDS_OPTION_CU, IDS_OPTION_LM,IDS_OPTION_CU ); IDSwprintf(hModule,IDS_OPTION_IN_DESC); IDSwprintf(hModule,IDS_OPTION_ALGO_DESC, IDS_OPTION_ALGO); IDS_IDS_IDS_IDSwprintf(hModule,IDS_OPTION_VALUES_DEFAULT,IDS_OPTION_ALGO_MD5, IDS_OPTION_ALGO_SHA, IDS_OPTION_ALGO_MD5); IDSwprintf(hModule,IDS_OPTION_IP_DESC); IDSwprintf(hModule,IDS_OPTION_IY_DESC); IDSwprintf(hModule,IDS_OPTION_SP_DESC); IDSwprintf(hModule,IDS_OPTION_SY_DESC); IDSwprintf(hModule,IDS_OPTION_IKY_DESC); IDS_IDS_IDS_IDSwprintf(hModule,IDS_OPTION_VALUES_KY, IDS_OPTION_KY_SIG, IDS_OPTION_KY_EXC,IDS_OPTION_KY_SIG); IDSwprintf(hModule,IDS_OPTION_SKY_DESC); IDS_IDS_IDS_IDSwprintf(hModule,IDS_OPTION_VALUES_KY, IDS_OPTION_KY_SIG, IDS_OPTION_KY_EXC,IDS_OPTION_KY_SIG); IDSwprintf(hModule,IDS_OPTION_L_DESC); IDSwprintf(hModule,IDS_OPTION_CY_DESC); IDS_IDS_IDSwprintf(hModule,IDS_OPTION_VALUES_2, IDS_OPTION_CY_END, IDS_OPTION_CY_AUTH); IDSwprintf(hModule,IDS_OPTION_B_DESC); IDSwprintf(hModule,IDS_OPTION_M_DESC); IDSwprintf(hModule,IDS_OPTION_E_DESC); IDSwprintf(hModule,IDS_OPTION_H_DESC); // IDSwprintf(hModule,IDS_OPTION_G_DESC); IDSwprintf(hModule,IDS_OPTION_KEY_LEN_DESC); IDSwprintf(hModule,IDS_OPTION_R_DESC); IDSwprintf(hModule,IDS_OPTION_NSCP_DESC); IDSwprintf(hModule,IDS_OPTION_ENHKEY_USAGE_DESC); IDSwprintf(hModule,IDS_OPTION_BASIC_DESC); IDSwprintf(hModule,IDS_OPTION_EXTENDED_DESC); } static void UndocumentedUsage() { IDSwprintf(hModule,IDS_SYNTAX); IDSwprintf(hModule,IDS_OPTION_SQ_DESC); IDSwprintf(hModule,IDS_OPTION_NOPUBKEYPARA_DESC); IDSwprintf(hModule,IDS_OPTION_DH_PARA_DESC); IDSwprintf(hModule,IDS_OPTION_NOPUBVERIFY_DESC); } //+========================================================================= // Local Support Functions //========================================================================== //+------------------------------------------------------------------------- // Error output routines //-------------------------------------------------------------------------- void PrintLastError(int ids) { DWORD dwErr = GetLastError(); IDS_IDS_DW_DWwprintf(hModule,IDS_ERR_LAST, ids, dwErr, dwErr); } //+------------------------------------------------------------------------- // Allocation and free macros //-------------------------------------------------------------------------- #define MakeCertAlloc(p1) ToolUtlAlloc(p1, hModule, IDS_ERR_DESC_ALLOC) #define MakeCertFree(p1) ToolUtlFree(p1) //----------------------------------------------------------------------------- // // Calculate the number of days //----------------------------------------------------------------------------- WORD DaysInMonth(WORD wMonth, WORD wYear) { static int mpMonthDays[] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; // J F M A M J J A S O N D WORD w = (WORD)mpMonthDays[wMonth]; if ((wMonth == 2) && (wYear % 4 == 0) && (wYear%400 == 0 || wYear%100 != 0)) { w += 1; } return w; } //----------------------------------------------------------------------------- // // Convert the string into a FILETIME. Let OLE do a bunch of work for us. //---------------------------------------------------------------------------- BOOL FtFromWStr(LPCWSTR wsz, FILETIME* pft) { memset(pft, 0, sizeof(*pft)); WCHAR wszMonth[3]; DWORD lcid=0; WORD langid=0; //make sure wsz follows the mm/dd/yyyy if(wcslen(wsz)!=wcslen(L"mm/dd/yyyy")) return FALSE; //make sure wsz starts with "mm" wszMonth[0]=wsz[0]; wszMonth[1]=wsz[1]; wszMonth[2]=L'\0'; if(!((_wtol(wszMonth)>0)&&(_wtol(wszMonth)<=12))) return FALSE; if (wsz) { // // The DATE Type // // The DATE type is implemented using an 8-byte floating-point number. // Days are represented by whole number increments starting with 30 // December 1899, midnight as time zero. Hour values are expressed // as the absolute value of the fractional part of the number. // // We are using the English locale since the input format // should always be mm/dd/yyyy // langid=MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US); lcid = MAKELCID (langid, SORT_DEFAULT); DATE date; if (VarDateFromStr((LPWSTR)wsz, lcid, 0, &date) != S_OK) { return FALSE; } if (date < 0) { return FALSE; } // not implemented double days = date; double hours = ((days - (LONG) days) * 24); double minutes= ((hours - (LONG) hours) * 60); double seconds= ((minutes - (LONG) minutes) * 60); double ms = ((seconds - (LONG) seconds) * 1000); SYSTEMTIME st; st.wYear = 1899; st.wMonth = 12; ULONG wDay = 30 + (ULONG)days; st.wHour = (WORD)hours; st.wMinute = (WORD)minutes; st.wSecond = (WORD)seconds; st.wMilliseconds = (WORD)ms; // // Correct for rounding errors in the arithmetic // if (st.wMilliseconds >= 0.5) st.wSecond += 1; st.wMilliseconds = 0; if (st.wSecond >= 60) { st.wMinute += 1; st.wSecond -= 60; } if (st.wMinute >= 60) { st.wHour += 1; st.wMinute -= 60; } if (st.wHour >= 24) { st.wHour -= 24; } while (wDay > DaysInMonth(st.wMonth, st.wYear)) { wDay -= DaysInMonth(st.wMonth, st.wYear); st.wMonth += 1; if (st.wMonth > 12) { st.wMonth = 1; st.wYear += 1; } } st.wDay = (WORD)wDay; st.wDayOfWeek = 0; FILETIME ft; SystemTimeToFileTime(&st, &ft); LocalFileTimeToFileTime(&ft, pft); return TRUE; } else return FALSE; } //------------------------------------------------------------------------- // // Set the parameters. Each parameter can only be set once // //-------------------------------------------------------------------------- BOOL SetParam(WCHAR **pwszParam, WCHAR *wszValue) { if(*pwszParam!=NULL) { IDSwprintf(hModule,IDS_ERR_TOO_MANY_PARAM); return FALSE; } *pwszParam=wszValue; if(NULL==wszValue) return FALSE; return TRUE; } //-------------------------------------------------------------------------- // // Command Line Parsing // //-------------------------------------------------------------------------- static BOOL ParseCommandLine(int argc, WCHAR* wargv[]) { for ( int i = 1; i < argc; ++i ) { WCHAR* p = wargv[i]; if(IDSwcsnicmp(hModule,p, IDS_SWITCH1, 1)!=0 && IDSwcsnicmp(hModule,p,IDS_SWITCH2, 1)!=0) { if(!SetParam(&wszOutputFile,p)) goto BasicErr; else continue; } //move over to the real option ++p; if(IDSwcsicmp(hModule,p, IDS_OPTION_SERIAL)==0) { i++; p=wargv[i]; if(NULL==p) goto BasicErr; ulSerialNumber=_wtol(p); fSetSerialNumber=TRUE; continue; } if(IDSwcsicmp(hModule,p, IDS_OPTION_H)==0) { i++; p=wargv[i]; if(NULL==p) goto ExtendedErr; pathLenConstraint=_wtol(p); if(pathLenConstraint < 0) goto ExtendedErr; continue; } if(IDSwcsicmp(hModule,p, IDS_OPTION_CY)==0) { i++; p=wargv[i]; if(!SetParam(&wszCertType, p)) goto ExtendedErr; continue; } if(IDSwcsicmp(hModule,p, IDS_OPTION_M)==0) { i++; p=wargv[i]; if(NULL==p) goto ExtendedErr; nMonths=_wtol(p); if(nMonths < 0) goto ExtendedErr; continue; } if(IDSwcsicmp(hModule,p, IDS_OPTION_B)==0) { i++; p=wargv[i]; if(NULL==p) goto ExtendedErr; if(!FtFromWStr(p, &g_ftNotBefore)) { IDSwprintf(hModule, IDS_ERR_INVALID_B); goto ExtendedErr; } continue; } if(IDSwcsicmp(hModule,p, IDS_OPTION_E)==0) { i++; p=wargv[i]; if(NULL==p) goto ExtendedErr; if(!FtFromWStr(p, &g_ftNotAfter)) { IDSwprintf(hModule, IDS_ERR_INVALID_E); goto ExtendedErr; } continue; } if(IDSwcsicmp(hModule,p, IDS_OPTION_AUTH)==0) { i++; p=wargv[i]; if(!SetParam(&wszAuthority, p)) goto BasicErr; continue; } if(IDSwcsicmp(hModule,p, IDS_OPTION_R)==0) { fSelfSigned=TRUE; continue; } if(IDSwcsicmp(hModule,p, IDS_OPTION_SK)==0) { i++; p=wargv[i]; if(!SetParam(&wszSubjectKey, p)) { if(TRUE==fUseSubjectPvkFile) IDSwprintf(hModule, IDS_ERR_SK_SV); goto BasicErr; } fUseSubjectKeyContainer=TRUE; fSubjectInformation=TRUE; continue; } if(IDSwcsicmp(hModule,p, IDS_OPTION_SQ)==0) { i++; p=wargv[i]; if(!SetParam(&wszSubjectRequestFile, p)) goto UndocumentedErr; continue; } if(IDSwcsicmp(hModule,p, IDS_OPTION_SV)==0) { i++; p=wargv[i]; if(!SetParam(&wszSubjectKey, p)) { if(TRUE==fUseSubjectKeyContainer) IDSwprintf(hModule, IDS_ERR_SK_SV); goto ExtendedErr; } fSubjectInformation=TRUE; fUseSubjectPvkFile=TRUE; continue; } if(IDSwcsicmp(hModule,p, IDS_OPTION_SC)==0) { i++; p=wargv[i]; if(!SetParam(&wszSubjectCertFile, p)) goto ExtendedErr; fSubjectInformation=TRUE; continue; } if(IDSwcsicmp(hModule,p, IDS_OPTION_SS)==0) { i++; p=wargv[i]; if(!SetParam(&wszSubjectStore, p)) goto BasicErr; continue; } if(IDSwcsicmp(hModule,p, IDS_OPTION_SR)==0) { i++; p=wargv[i]; if(!SetParam(&wszSubjectStoreLocation, p)) goto BasicErr; continue; } if(IDSwcsicmp(hModule,p, IDS_OPTION_N)==0) { i++; p=wargv[i]; if(!SetParam(&wszSubjectX500Name, p)) goto BasicErr; fSetSubjectName = TRUE; continue; } if(IDSwcsicmp(hModule,p, IDS_OPTION_IP)==0) { i++; p=wargv[i]; if(!SetParam(&wszIssuerProviderName, p)) goto ExtendedErr; fIssuerInformation = TRUE; continue; } if(IDSwcsicmp(hModule,p, IDS_OPTION_IY)==0) { i++; p=wargv[i]; if(NULL==p) goto ExtendedErr; dwIssuerProviderType=_wtol(p); fIssuerInformation = TRUE; continue; } if(IDSwcsicmp(hModule,p, IDS_OPTION_SP)==0) { i++; p=wargv[i]; if(!SetParam(&wszSubjectProviderName, p)) goto ExtendedErr; fSubjectInformation=TRUE; continue; } if(IDSwcsicmp(hModule,p, IDS_OPTION_SY)==0) { i++; p=wargv[i]; if(NULL==p) goto ExtendedErr; dwSubjectProviderType=_wtol(p); fSubjectInformation=TRUE; continue; } if(IDSwcsicmp(hModule,p, IDS_OPTION_IK)==0) { i++; p=wargv[i]; if(!SetParam(&wszIssuerKey, p)) goto ExtendedErr; fIssuerInformation = TRUE; continue; } if(IDSwcsicmp(hModule,p, IDS_OPTION_IV)==0) { i++; p=wargv[i]; if(!SetParam(&wszIssuerKey, p)) goto ExtendedErr; fUseIssuerPvkFile=TRUE; fIssuerInformation = TRUE; continue; } if(IDSwcsicmp(hModule,p, IDS_OPTION_IS)==0) { i++; p=wargv[i]; if(!SetParam(&wszIssuerStore, p)) goto ExtendedErr; fIssuerInformation = TRUE; continue; } if(IDSwcsicmp(hModule,p, IDS_OPTION_IR)==0) { i++; p=wargv[i]; if(!SetParam(&wszIssuerStoreLocation,p)) goto ExtendedErr; fIssuerInformation = TRUE; continue; } if(IDSwcsicmp(hModule,p, IDS_OPTION_IN)==0) { i++; p=wargv[i]; if(!SetParam(&wszIssuerCertName,p)) goto ExtendedErr; fIssuerInformation = TRUE; continue; } if(IDSwcsicmp(hModule,p, IDS_OPTION_IC)==0) { i++; p=wargv[i]; if(!SetParam(&wszIssuerCertFile, p)) goto ExtendedErr; fIssuerInformation = TRUE; continue; } if(IDSwcsicmp(hModule,p, IDS_OPTION_L)==0) { i++; p=wargv[i]; if(!SetParam(&wszPolicyLink, p)) goto ExtendedErr; continue; } if(IDSwcsicmp(hModule,p, IDS_OPTION_SKY)==0) { i++; p=wargv[i]; if(!SetParam(&wszSubjectKeyType, p)) goto ExtendedErr; fSubjectInformation=TRUE; continue; } if(IDSwcsicmp(hModule,p, IDS_OPTION_IKY)==0) { i++; p=wargv[i]; if(!SetParam(&wszIssuerKeyType, p)) goto ExtendedErr; fIssuerInformation = TRUE; continue; } if(IDSwcsicmp(hModule,p, IDS_OPTION_ALGO)==0) { i++; p=wargv[i]; if(!SetParam(&wszAlgorithm, p)) goto ExtendedErr; continue; } if (IDSwcsicmp(hModule,p, IDS_OPTION_ENHKEY_USAGE)==0) { i++; p=wargv[i]; if (!SetParam(&wszEKUOids, p)) goto ExtendedErr; continue; } if(IDSwcsicmp(hModule,p, IDS_OPTION_NSCP)==0) { fNetscapeClientAuth = TRUE; continue; } if(IDSwcsicmp(hModule,p, IDS_OPTION_NOPUBVERIFY)==0) { fNoVerifyPublic = TRUE; continue; } if(IDSwcsicmp(hModule,p, IDS_OPTION_EXTENDED)==0) { //display extended options goto ExtendedErr; } if(IDSwcsicmp(hModule,p, IDS_OPTION_TEST)==0) { //display extended options goto UndocumentedErr; } if(IDSwcsicmp(hModule,p, IDS_OPTION_NOPUBKEYPARA)==0) { fNoPubKeyPara = TRUE; continue; } if(IDSwcsicmp(hModule,p, IDS_OPTION_DH_PARA)==0) { i++; p=wargv[i]; if(!SetParam(&wszDhParaCertFile, p)) goto UndocumentedErr; continue; } if(IDSwcsicmp(hModule,p, IDS_OPTION_KEY_LEN)==0) { i++; p=wargv[i]; if(NULL==p) goto UndocumentedErr; dwKeyBitLen=_wtol(p); continue; } if(IDSwcsicmp(hModule,p, IDS_OPTION_PE)==0) { fPrivateKeyExportable=TRUE; continue; } //display basic options goto BasicErr; } //Only change container name if request private key //to be exportable. This way we maintain backward //compatibility, and allow user to request private //key to be marked as exportable. //Note: If the private key is not marked as exportable, // the hardcoded container name, "JoeSoft", is // always used, which means more than one cert // can share the same key pair. //Note2: If the user specifies the container name, use that one instead if (fPrivateKeyExportable && (wszSubjectKey == NULL)) { UUID uuidContainerName; RPC_STATUS rpcs; rpcs = UuidCreate(&uuidContainerName); if ((rpcs != RPC_S_OK) && (rpcs != RPC_S_UUID_LOCAL_ONLY) && (rpcs != RPC_S_UUID_NO_ADDRESS)) { IDSwprintf(hModule, IDS_ERR_PE_CONTAINER); goto BasicErr; } if (RPC_S_OK != UuidToStringU(&uuidContainerName, &wszSubjectKey)) { IDSwprintf(hModule, IDS_ERR_PE_CONTAINER); goto BasicErr; } } //make sure the either output file or the subject' cert store is set if((wszOutputFile==NULL) && (wszSubjectStore==NULL)) { IDSwprintf(hModule, IDS_ERR_NO_OUTPUT); goto BasicErr; } //set the authority if(wszAuthority) { if(IDSwcsicmp(hModule,wszAuthority, IDS_OPTION_AUTH_IND) == 0) fCertIndividual = TRUE; else { if(IDSwcsicmp(hModule,wszAuthority, IDS_OPTION_AUTH_COM) == 0) fCertCommercial = TRUE; else { IDSwprintf(hModule,IDS_ERR_NO_AUTH); goto BasicErr; } } } //set the algorithm if(wszAlgorithm) { if(IDSwcsicmp(hModule,wszAlgorithm, IDS_OPTION_ALGO_SHA) == 0) algidHash = CALG_SHA; else { if(IDSwcsicmp(hModule,wszAlgorithm, IDS_OPTION_ALGO_MD5) == 0) algidHash = CALG_MD5; else { IDSwprintf(hModule,IDS_ERR_NO_ALGO); goto ExtendedErr; } } } //set the cert type if(wszCertType) { if(IDSwcsicmp(hModule,wszCertType, IDS_OPTION_CY_END) == 0) certTypes |= CERT_END_ENTITY_SUBJECT_FLAG; else { if(IDSwcsicmp(hModule,wszCertType, IDS_OPTION_CY_AUTH) == 0) certTypes |= CERT_CA_SUBJECT_FLAG; else { IDSwprintf(hModule,IDS_ERR_NO_CY); goto ExtendedErr; } } } //set the issuer key type if(wszIssuerKeyType) { if(IDSwcsicmp(hModule,wszIssuerKeyType, IDS_OPTION_KY_SIG) == 0) dwIssuerKeySpec = AT_SIGNATURE; else { if(IDSwcsicmp(hModule,wszIssuerKeyType, IDS_OPTION_KY_EXC) == 0) dwIssuerKeySpec = AT_KEYEXCHANGE; else dwIssuerKeySpec=_wtol(wszIssuerKeyType); } } //set the subject key type if(wszSubjectKeyType) { if(IDSwcsicmp(hModule,wszSubjectKeyType, IDS_OPTION_KY_SIG) == 0) dwKeySpec = AT_SIGNATURE; else { if(IDSwcsicmp(hModule,wszSubjectKeyType, IDS_OPTION_KY_EXC) == 0) dwKeySpec = AT_KEYEXCHANGE; else dwKeySpec=_wtol(wszSubjectKeyType); } } //determing the issuer store location if(wszIssuerStoreLocation) { if(IDSwcsicmp(hModule,wszIssuerStoreLocation, IDS_OPTION_CU) == 0) dwIssuerStoreFlag = CERT_SYSTEM_STORE_CURRENT_USER; else { if(IDSwcsicmp(hModule,wszIssuerStoreLocation, IDS_OPTION_LM) == 0) dwIssuerStoreFlag = CERT_SYSTEM_STORE_LOCAL_MACHINE; else { IDSwprintf(hModule,IDS_ERR_NO_IR); goto ExtendedErr; } } } //determind the subject store location if(wszSubjectStoreLocation) { if(IDSwcsicmp(hModule,wszSubjectStoreLocation, IDS_OPTION_CU) == 0) dwSubjectStoreFlag = CERT_SYSTEM_STORE_CURRENT_USER; else { if(IDSwcsicmp(hModule,wszSubjectStoreLocation, IDS_OPTION_LM) == 0) dwSubjectStoreFlag = CERT_SYSTEM_STORE_LOCAL_MACHINE; else { IDSwprintf(hModule,IDS_ERR_NO_IR); goto BasicErr; } } } //wszIssuerStore and wszIssuerKey can not be set at the same time if(wszIssuerKey || wszIssuerProviderName || wszIssuerKeyType) { if(wszIssuerStore || wszIssuerCertName) { //remind user that -ik, -iv, -ip can not be //set with -is, -in options IDSwprintf(hModule,IDS_ERR_TOO_MANY_STORE_KEY); goto ExtendedErr; } } //wszCertFile and wszCertName can not be set at the same time if(wszIssuerCertFile && wszIssuerCertName) { IDSwprintf(hModule, IDS_ERR_CERT_FILE_NAME); goto ExtendedErr; } //is wszIsserCertFile is NULL if(wszIssuerCertFile==NULL) { //we init wszIssuerKey to "MakeCertRoot" if there is no store //information if(wszIssuerStore==NULL) { if(wszIssuerKey) { //if wszIssuerKey is set, we have to set the IssuerCertFile IDSwprintf(hModule, IDS_ERR_NO_ISSUER_CER_FILE); goto ExtendedErr; } else { wszIssuerKey=wszMakeCertRoot; } } } else { //either wszIssuerStore or wszIssuerKey should be set if((!wszIssuerStore) && (!wszIssuerKey)) { IDSwprintf(hModule, IDS_ERR_EITHER_STORE_OR_KEY); goto ExtendedErr; } } //for self signed certificate, user should not supply //issuer information if(fIssuerInformation && fSelfSigned) { IDSwprintf(hModule, IDS_NO_ISSUER_FOR_SELF_SIGNED); goto ExtendedErr; } //user can not request a self signed certificate with //a PKCS10 file. We neither generate or have access //to the private key if(fSelfSigned && wszSubjectRequestFile) { IDSwprintf(hModule, IDS_NO_PKCS10_AND_SELF_SIGNED); goto ExtendedErr; } if(fSubjectInformation && wszSubjectRequestFile) { IDSwprintf(hModule, IDS_NO_PKCS10_AND_SUBJECT_PVK); goto ExtendedErr; } //for self signed certificate, copy the provider type //to the issuer so that the signatureAlgObjID will //be corrrect if(fSelfSigned) dwIssuerProviderType = dwSubjectProviderType; // Set the signature and public key algorithm parameters if (PROV_DSS == dwIssuerProviderType || PROV_DSS_DH == dwIssuerProviderType) szSignatureAlgObjId = szOID_X957_SHA1DSA; else if (algidHash == CALG_SHA) szSignatureAlgObjId = szOID_OIWSEC_sha1RSASign; else szSignatureAlgObjId = szOID_RSA_MD5RSA; return TRUE; BasicErr: BasicUsage(); return FALSE; ExtendedErr: ExtendedUsage(); return FALSE; UndocumentedErr: UndocumentedUsage(); return FALSE; } static BOOL MakeCert(); //+------------------------------------------------------------------------- // Check if creating a self signed certificate //-------------------------------------------------------------------------- static BOOL IsSelfSignedCert() { return fSelfSigned; } //-------------------------------------------------------------------------- // // wmain // //-------------------------------------------------------------------------- extern "C" int __cdecl wmain(int argc, WCHAR ** wargv) { int status = 0; //get the module handle if(!InitModule()) return -1; //load the string for Glue cert attribute if(!LoadStringU(hModule, IDS_GLUE,wszGlue, sizeof(wszGlue)/sizeof(wszGlue[0]))) return -1; //load the string for wszSubjectKey and wszSubjectX500Name if(!LoadStringU(hModule, IDS_JOE_SOFT, wszKey, sizeof(wszKey)/sizeof(wszKey[0])) || !LoadStringU(hModule, IDS_JOE_NAME, wszName, sizeof(wszName)/sizeof(wszName[0])) || !LoadStringU(hModule, IDS_MAKECERT_ROOT, wszMakeCertRoot, sizeof(wszMakeCertRoot)/sizeof(wszMakeCertRoot[0])) || !LoadStringU(hModule, IDS_PLUS, wszPlus, sizeof(wszPlus)/sizeof(wszPlus[0])) || !LoadStringU(hModule, IDS_ROOT, wszRoot, sizeof(wszRoot)/sizeof(wszRoot[0])) ) return -1; LoadStringU(hModule, IDS_NULL, wszNULL, sizeof(wszNULL)/sizeof(wszNULL[0])); // Parse the command line if (!ParseCommandLine(argc, wargv)) { return -1; } //init wszSubjectKey and wszSubjectX500Name if(wszSubjectKey==NULL) wszSubjectKey=wszKey; if(wszSubjectX500Name==NULL) wszSubjectX500Name=wszName; if (FAILED(CoInitialize(NULL))) { IDSwprintf(hModule,IDS_ERR_COINIT); return -1; } // Get to work and make the certificate if (!MakeCert()) { CoUninitialize(); goto ErrorReturn; } //print out the success information IDSwprintf(hModule,IDS_SUCCEEDED); CommonReturn: CoUninitialize(); return status; ErrorReturn: status = -1; IDSwprintf(hModule,IDS_ERR_FAILED); goto CommonReturn; } //+========================================================================= // MakeCert support functions //========================================================================== static BOOL IsRootKey(); static PCCERT_CONTEXT GetRootCertContext(); static HCRYPTPROV GetRootProv(OUT LPWSTR *ppwszTmpContainer); static PCCERT_CONTEXT GetIssuerCertContext(); static BOOL VerifyIssuerKey( IN HCRYPTPROV hProv, IN PCERT_PUBLIC_KEY_INFO pIssuerKeyInfo ); static HCRYPTPROV GetIssuerProv(OUT LPWSTR *ppwszTmpContainer); static HCRYPTPROV GetSubjectProv(OUT LPWSTR *ppwszTmpContainer); static HCRYPTPROV GetProvFromStore(IN LPWSTR pwszStoreName); static BOOL GetPublicKey( HCRYPTPROV hProv, PCERT_PUBLIC_KEY_INFO *ppPubKeyInfo ); static BOOL GetSubject( OUT PCCERT_CONTEXT *ppCertContext, OUT BYTE **ppbEncodedName, OUT DWORD *pcbEncodedName ); static BOOL GetRequestInfo( OUT PCERT_REQUEST_INFO *ppStuff); static BOOL EncodeSubject( OUT BYTE **ppbEncoded, IN OUT DWORD *pcbEncoded ); static BOOL SignAndEncodeCert( HCRYPTPROV hProv, PCRYPT_ALGORITHM_IDENTIFIER pSignatureAlgorithm, BYTE *pbToBeSigned, DWORD cbToBeSigned, BYTE **ppbEncoded, DWORD *pcbEncoded ); static BOOL CreateAuthorityKeyId( IN HCRYPTPROV hProv, PCERT_INFO pIssuerCert, OUT BYTE **ppbEncoded, IN OUT DWORD *pcbEncoded ); static BOOL CreateSpcSpAgency( OUT BYTE **ppbEncoded, IN OUT DWORD *pcbEncoded ); static BOOL CreateEnhancedKeyUsage( OUT BYTE **ppbEncoded, IN OUT DWORD *pcbEncoded ); static BOOL CreateKeyUsage( OUT BYTE **ppbEncoded, IN OUT DWORD *pcbEncoded ); static BOOL CreateBasicConstraints( OUT BYTE **ppbEncoded, IN OUT DWORD *pcbEncoded ); static void BytesToWStr(ULONG cb, void* pv, LPWSTR wsz); BOOL SaveCertToStore(HCRYPTPROV hProv, DWORD dwEncodingType, LPWSTR wszStore, DWORD dwFlag, BYTE *pbEncodedCert, DWORD cbEncodedCert, LPWSTR wszPvk, BOOL fPvkFile, DWORD dwKeySpecification, LPWSTR wszCapiProv, DWORD dwCapiProvType); PCCERT_CONTEXT GetIssuerCertAndStore(HCERTSTORE *phCertStore, BOOL *pfMore); HRESULT GetCertHashFromFile(LPWSTR pwszCertFile, BYTE **ppHash, DWORD *pcbHash, BOOL *pfMore); BOOL EmptySubject(CERT_NAME_BLOB *pSubject); BOOL GetExtensionsFromRequest(PCERT_REQUEST_INFO pReqInfo, DWORD *pdwCount, PCERT_EXTENSIONS **pprgExtensions); //+========================================================================= // Support functions to generate DH keys having the 'Q'parameter //========================================================================== static BOOL GenDhKey( IN HCRYPTPROV hProv, IN DWORD dwFlags ); static BOOL UpdateDhPublicKey( IN OUT PCERT_PUBLIC_KEY_INFO *ppPubKeyInfo ); static BOOL IsDh3Csp(); //+------------------------------------------------------------------------- // Make the subject certificate. If the subject doesn't have a private // key, then, create. //-------------------------------------------------------------------------- static BOOL MakeCert() { BOOL fResult; HCRYPTPROV hIssuerProv = 0; LPWSTR pwszTmpIssuerContainer = NULL; BOOL fDidIssuerAcquire=FALSE; LPWSTR pwszTmpIssuerProvName=NULL; DWORD dwTmpIssuerProvType; PCCERT_CONTEXT pIssuerCertContext = NULL; HCERTSTORE hIssuerCertStore=NULL; PCERT_INFO pIssuerCert =NULL; // not allocated PCERT_REQUEST_INFO pReqInfo =NULL; HCRYPTPROV hSubjectProv = 0; LPWSTR pwszTmpSubjectContainer = NULL; PCCERT_CONTEXT pSubjectCertContext = NULL; DWORD dwRequestExtensions=0; PCERT_EXTENSIONS *rgpRequestExtensions=NULL; DWORD dwExtIndex=0; DWORD dwPerExt=0; DWORD dwExistExt=0; PCERT_PUBLIC_KEY_INFO pSubjectPubKeyInfo = NULL; // not allocated PCERT_PUBLIC_KEY_INFO pAllocSubjectPubKeyInfo = NULL; BYTE *pbSubjectEncoded = NULL; DWORD cbSubjectEncoded =0; BYTE *pbKeyIdEncoded = NULL; DWORD cbKeyIdEncoded =0; BYTE *pbSerialNumber = NULL; DWORD cbSerialNumber = 0; BYTE *pbSpcSpAgencyEncoded = NULL; DWORD cbSpcSpAgencyEncoded =0; BYTE *pbSpcCommonNameEncoded = NULL; DWORD cbSpcCommonNameEncoded =0; BYTE *pbKeyUsageEncoded = NULL; DWORD cbKeyUsageEncoded =0; BYTE *pbFinancialCriteria = NULL; DWORD cbFinancialCriteria =0; BYTE *pbBasicConstraintsEncoded = NULL; DWORD cbBasicConstraintsEncoded =0; BYTE *pbCertEncoded = NULL; DWORD cbCertEncoded =0; BYTE *pbEKUEncoded = NULL; DWORD cbEKUEncoded = 0; CERT_INFO Cert; GUID SerialNumber; CERT_EXTENSION *rgExt=NULL; DWORD dwExtAlloc=0; DWORD cExt = 0; CRYPT_ALGORITHM_IDENTIFIER SignatureAlgorithm = { szSignatureAlgObjId, 0, 0 }; BYTE *pbIssuerEncoded; // not allocated DWORD cbIssuerEncoded; if (wszSubjectRequestFile) { if (!GetRequestInfo(&pReqInfo)) { IDSwprintf(hModule,IDS_INVALID_REQUEST_FILE, wszSubjectRequestFile); goto ErrorReturn; } pSubjectPubKeyInfo = &(pReqInfo->SubjectPublicKeyInfo); if(!GetExtensionsFromRequest(pReqInfo, &dwRequestExtensions, &rgpRequestExtensions)) { IDSwprintf(hModule,IDS_INVALID_ATTR_REQUEST_FILE, wszSubjectRequestFile); goto ErrorReturn; } //if the subject informatin is empt or user has supplied the subject //name through the command line, we use the command line options if(fSetSubjectName || wszSubjectCertFile || EmptySubject(&(pReqInfo->Subject))) { if (wszSubjectCertFile) { // Get encoded subject name and public key from the subject cert if (!GetSubject(&pSubjectCertContext, &pbSubjectEncoded, &cbSubjectEncoded)) goto ErrorReturn; } else { // Encode the subject name if (!EncodeSubject(&pbSubjectEncoded, &cbSubjectEncoded)) goto ErrorReturn; } } else { cbSubjectEncoded = pReqInfo->Subject.cbData; pbSubjectEncoded = (BYTE *) MakeCertAlloc(cbSubjectEncoded); if(NULL == pbSubjectEncoded) goto ErrorReturn; memcpy(pbSubjectEncoded, pReqInfo->Subject.pbData, cbSubjectEncoded); } } else { if (wszSubjectCertFile) { // Get encoded subject name and public key from the subject cert if (!GetSubject(&pSubjectCertContext, &pbSubjectEncoded, &cbSubjectEncoded)) goto ErrorReturn; pSubjectPubKeyInfo = &pSubjectCertContext->pCertInfo->SubjectPublicKeyInfo; pbSerialNumber = pSubjectCertContext->pCertInfo->SerialNumber.pbData; cbSerialNumber = pSubjectCertContext->pCertInfo->SerialNumber.cbData; } else { // // Get access to the subject's (public) key, creating it if necessary // if (0 == (hSubjectProv = GetSubjectProv(&pwszTmpSubjectContainer))) goto ErrorReturn; if (!GetPublicKey(hSubjectProv, &pAllocSubjectPubKeyInfo)) goto ErrorReturn; pSubjectPubKeyInfo = pAllocSubjectPubKeyInfo; // // Encode the subject name // if (!EncodeSubject(&pbSubjectEncoded, &cbSubjectEncoded)) goto ErrorReturn; } } // // Get access to the issuer's (private) key // if( IsSelfSignedCert()) { hIssuerProv=hSubjectProv; dwIssuerKeySpec=dwKeySpec; pbIssuerEncoded = pbSubjectEncoded; cbIssuerEncoded = cbSubjectEncoded; pIssuerCert = &Cert; if (!VerifyIssuerKey(hIssuerProv, pSubjectPubKeyInfo)) goto ErrorReturn; } else { //get the hProv from the certificate store if(wszIssuerStore) { BOOL fMore=FALSE; pwszTmpIssuerContainer=NULL; //get the non-root private key set based on the store name //first, get the certificate context from the cert store if(NULL==(pIssuerCertContext=GetIssuerCertAndStore( &hIssuerCertStore, &fMore))) { if(fMore==FALSE) IDSwprintf(hModule, IDS_ERR_NO_ISSUER_CERT, wszIssuerStore); else IDSwprintf(hModule, IDS_ERR_MORE_ISSUER_CERT, wszIssuerStore); goto ErrorReturn; } //second, get the hProv from the certifcate context if(!GetCryptProvFromCert( NULL, pIssuerCertContext, &hIssuerProv, &dwIssuerKeySpec, &fDidIssuerAcquire, &pwszTmpIssuerContainer, &pwszTmpIssuerProvName, &dwTmpIssuerProvType)) { IDSwprintf(hModule, IDS_ERR_NO_PROV_FROM_CERT); goto ErrorReturn; } } else { if (0 == (hIssuerProv = GetIssuerProv(&pwszTmpIssuerContainer))) goto ErrorReturn; // Get the Issuer's Certificate if (NULL == (pIssuerCertContext = GetIssuerCertContext())) goto ErrorReturn; } // Verify the issuer's key. Its public key must match the one // in the issuer's provider // pIssuerCert = pIssuerCertContext->pCertInfo; if ((!fNoVerifyPublic) && (!VerifyIssuerKey(hIssuerProv, &pIssuerCert->SubjectPublicKeyInfo))) goto ErrorReturn; pbIssuerEncoded = pIssuerCert->Subject.pbData; cbIssuerEncoded = pIssuerCert->Subject.cbData; } // // Update the CERT_INFO // memset(&Cert, 0, sizeof(Cert)); Cert.dwVersion = CERT_V3; if (0 != cbSerialNumber) { Cert.SerialNumber.pbData = pbSerialNumber; Cert.SerialNumber.cbData = cbSerialNumber; } else if (fSetSerialNumber) { Cert.SerialNumber.pbData = (BYTE *) &ulSerialNumber; Cert.SerialNumber.cbData = sizeof(ulSerialNumber); } else if (0 == Cert.SerialNumber.cbData) { CoCreateGuid(&SerialNumber); Cert.SerialNumber.pbData = (BYTE *) &SerialNumber; Cert.SerialNumber.cbData = sizeof(SerialNumber); } Cert.SignatureAlgorithm = SignatureAlgorithm; Cert.Issuer.pbData = pbIssuerEncoded; Cert.Issuer.cbData = cbIssuerEncoded; { SYSTEMTIME st; //decide NotBefore // Let the user override the default validity endpoints // if (CompareFileTime(&g_ftNotBefore, &g_ftZero) != 0) { Cert.NotBefore = g_ftNotBefore; } else { // Default validity: now through end of 2039 GetSystemTimeAsFileTime(&Cert.NotBefore); } //decide NotAfter if (CompareFileTime(&g_ftNotAfter, &g_ftZero) != 0) { Cert.NotAfter = g_ftNotAfter; } else { memset(&st, 0, sizeof(st)); st.wYear = 2039; st.wMonth = 12; st.wDay = 31; st.wHour = 23; st.wMinute= 59; st.wSecond= 59; SystemTimeToFileTime(&st, &Cert.NotAfter); } //add the number of months if (nMonths >= 0) { //if the user has specified NotAfter with -E switch, error if(CompareFileTime(&g_ftNotAfter, &g_ftZero) != 0) goto ErrorReturn; if (nMonths > 0) { FILETIME tempFT; DWORD dwMonth; SYSTEMTIME tempST; BOOL fFirstDayOfMonth; // // Cert.NotBefore is stored as UTC, but the user has entered // nMonths based on local time, so convert to local time, then: // NotAfter = (NotBefore - 1 second) + nMonths // if (!FileTimeToLocalFileTime(&Cert.NotBefore, &tempFT)) goto ErrorReturn; // // if the day is the first day of the month, then subtract // one second after the months are added to the NotBefore // time instead of before the months are added, otherwise // we could end up with the wrong ending date. // if (FileTimeToSystemTime(&tempFT, &tempST)) { fFirstDayOfMonth = (tempST.wDay == 1); } else { goto ErrorReturn; } // Subtract one second from the starting date, and then // add the number of months to that time // // FILETIME is in units of 100 nanoseconds (10**-7) if (!fFirstDayOfMonth) { unsigned __int64* pli = (unsigned __int64*) &tempFT; *pli -= 10000000; // ten million } if (!FileTimeToSystemTime(&tempFT, &st)) goto ErrorReturn; dwMonth = (DWORD) nMonths + st.wMonth; while (dwMonth > 12) { dwMonth -= 12; st.wYear += 1; } st.wMonth = (WORD) dwMonth; // // This loop is because the ending month may not have as // many days as the starting month... so the initial // ending day may not even exist, thus, loop until we // find one that does or we go below 28 (no month ever has // less than 28 days) // while(!SystemTimeToFileTime(&st, &tempFT)) { if(st.wDay >= 29 ) st.wDay--; else goto ErrorReturn; } // // if first day of month then subtract our one second // after month calculations // if (fFirstDayOfMonth) { unsigned __int64* pli = (unsigned __int64*) &tempFT; *pli -= 10000000; // ten million } if (!LocalFileTimeToFileTime(&tempFT, &Cert.NotAfter)) goto ErrorReturn; } else { if (!FileTimeToSystemTime(&Cert.NotBefore, &st)) goto ErrorReturn; if (!SystemTimeToFileTime(&st, &Cert.NotAfter)) goto ErrorReturn; } } } Cert.Subject.pbData = pbSubjectEncoded; Cert.Subject.cbData = cbSubjectEncoded; Cert.SubjectPublicKeyInfo = *pSubjectPubKeyInfo; //allocate memory to hold all the extensions dwExtAlloc = MAX_EXT_CNT; for(dwExtIndex=0; dwExtIndex < dwRequestExtensions; dwExtIndex++) dwExtAlloc += (rgpRequestExtensions[dwExtIndex])->cExtension; rgExt = (CERT_EXTENSION *)MakeCertAlloc(dwExtAlloc * sizeof(CERT_EXTENSION)); if(NULL == rgExt) goto ErrorReturn; memset(rgExt, 0, dwExtAlloc * sizeof(CERT_EXTENSION)); cExt=0; // Cert Extensions if (fNetscapeClientAuth) { // Set Netscape specific extensions static BYTE rgXxxxData[] = { 0x30, 0x03, 0x02, 0x01, 0x00 }; rgExt[cExt].pszObjId = "2.5.29.19"; rgExt[cExt].fCritical = FALSE; rgExt[cExt].Value.pbData = rgXxxxData; rgExt[cExt].Value.cbData = sizeof(rgXxxxData); cExt++; static BYTE rgNscpData[] = { 0x03, 0x02, 0x07, 0x80 }; rgExt[cExt].pszObjId = "2.16.840.1.113730.1.1"; rgExt[cExt].fCritical = FALSE; rgExt[cExt].Value.pbData = rgNscpData; rgExt[cExt].Value.cbData = sizeof(rgNscpData); cExt++; } if (pathLenConstraint >= 0 || certTypes) { if (!CreateBasicConstraints( &pbBasicConstraintsEncoded, &cbBasicConstraintsEncoded)) goto ErrorReturn; rgExt[cExt].pszObjId = szOID_BASIC_CONSTRAINTS2; rgExt[cExt].fCritical = TRUE; rgExt[cExt].Value.pbData = pbBasicConstraintsEncoded; rgExt[cExt].Value.cbData = cbBasicConstraintsEncoded; cExt++; } if (fCertCommercial || fCertIndividual) { if (!CreateKeyUsage( &pbKeyUsageEncoded, &cbKeyUsageEncoded)) goto ErrorReturn; rgExt[cExt].pszObjId = szOID_KEY_USAGE_RESTRICTION; rgExt[cExt].fCritical = TRUE; rgExt[cExt].Value.pbData = pbKeyUsageEncoded; rgExt[cExt].Value.cbData = cbKeyUsageEncoded; cExt++; } if (wszPolicyLink) { if (!CreateSpcSpAgency( &pbSpcSpAgencyEncoded, &cbSpcSpAgencyEncoded)) goto ErrorReturn; rgExt[cExt].pszObjId = SPC_SP_AGENCY_INFO_OBJID; rgExt[cExt].fCritical = FALSE; rgExt[cExt].Value.pbData = pbSpcSpAgencyEncoded; rgExt[cExt].Value.cbData = cbSpcSpAgencyEncoded; cExt++; } //if user has specified fCertCommercial or fCertIndividual, //we add the code signing oid to the EKU extensions if (wszEKUOids || fCertCommercial || fCertIndividual) { if (!CreateEnhancedKeyUsage( &pbEKUEncoded, &cbEKUEncoded)) goto ErrorReturn; rgExt[cExt].pszObjId = szOID_ENHANCED_KEY_USAGE; rgExt[cExt].fCritical = FALSE; rgExt[cExt].Value.pbData = pbEKUEncoded; rgExt[cExt].Value.cbData = cbEKUEncoded; cExt++; } if (!CreateAuthorityKeyId( hIssuerProv, pIssuerCert, &pbKeyIdEncoded, &cbKeyIdEncoded)) goto ErrorReturn; rgExt[cExt].pszObjId = szOID_AUTHORITY_KEY_IDENTIFIER; rgExt[cExt].fCritical = FALSE; rgExt[cExt].Value.pbData = pbKeyIdEncoded; rgExt[cExt].Value.cbData = cbKeyIdEncoded; cExt++; //we now combine the extension from the certificate request file. //In case of duplication of extensions, the command line options //have higher priority for(dwExtIndex=0; dwExtIndex < dwRequestExtensions; dwExtIndex++) { for(dwPerExt=0; dwPerExt < rgpRequestExtensions[dwExtIndex]->cExtension; dwPerExt++) { for(dwExistExt=0; dwExistExtrgExtension[dwPerExt]).pszObjId)) break; } //we merge if this is a new extension if(dwExistExt == cExt) { memcpy(&(rgExt[cExt]), &(rgpRequestExtensions[dwExtIndex]->rgExtension[dwPerExt]), sizeof(CERT_EXTENSION)); cExt++; } } } Cert.rgExtension = rgExt; Cert.cExtension = cExt; // // Sign and encode the certificate // cbCertEncoded = 0; CryptSignAndEncodeCertificate( hIssuerProv, dwIssuerKeySpec, X509_ASN_ENCODING, X509_CERT_TO_BE_SIGNED, &Cert, &Cert.SignatureAlgorithm, NULL, // pvHashAuxInfo NULL, // pbEncoded &cbCertEncoded ); if (cbCertEncoded == 0) { PrintLastError(IDS_ERR_SIGN_ENCODE_CB); goto ErrorReturn; } pbCertEncoded = (BYTE *) MakeCertAlloc(cbCertEncoded); if (pbCertEncoded == NULL) goto ErrorReturn; if (!CryptSignAndEncodeCertificate( hIssuerProv, dwIssuerKeySpec, X509_ASN_ENCODING, X509_CERT_TO_BE_SIGNED, &Cert, &Cert.SignatureAlgorithm, NULL, // pvHashAuxInfo pbCertEncoded, &cbCertEncoded )) { PrintLastError(IDS_ERR_SIGN_ENCODE); goto ErrorReturn; } //output the encoded certificate to an output file if(wszOutputFile) { if (S_OK!=OpenAndWriteToFile(wszOutputFile, pbCertEncoded, cbCertEncoded)) { PrintLastError(IDS_ERR_DESC_WRITE); goto ErrorReturn; } } //output the encoded certificate to an cerificate store if(wszSubjectStore) { if((!SaveCertToStore(hSubjectProv, dwCertStoreEncodingType, wszSubjectStore, dwSubjectStoreFlag, pbCertEncoded, cbCertEncoded, wszSubjectKey, fUseSubjectPvkFile, dwKeySpec, wszSubjectProviderName, dwSubjectProviderType))) { PrintLastError(IDS_ERR_DESC_SAVE_STORE); goto ErrorReturn; } } fResult = TRUE; goto CommonReturn; ErrorReturn: fResult = FALSE; CommonReturn: if((!IsSelfSignedCert()) && hIssuerProv) { if(wszIssuerStore) { FreeCryptProvFromCert(fDidIssuerAcquire, hIssuerProv, pwszTmpIssuerProvName, dwTmpIssuerProvType, pwszTmpIssuerContainer); } else { PvkFreeCryptProv(hIssuerProv, wszIssuerProviderName, dwIssuerProviderType, pwszTmpIssuerContainer); } } PvkFreeCryptProv(hSubjectProv, wszSubjectProviderName, dwSubjectProviderType,pwszTmpSubjectContainer); if (pIssuerCertContext) CertFreeCertificateContext(pIssuerCertContext); if(hIssuerCertStore) CertCloseStore(hIssuerCertStore, 0); if (pSubjectCertContext) CertFreeCertificateContext(pSubjectCertContext); //pReqInfo is allocated via CryptQueryObject if (pReqInfo) LocalFree((HLOCAL)pReqInfo); if (pAllocSubjectPubKeyInfo) MakeCertFree(pAllocSubjectPubKeyInfo); if (pbSubjectEncoded) MakeCertFree(pbSubjectEncoded); if (pbKeyIdEncoded) MakeCertFree(pbKeyIdEncoded); if (pbSpcSpAgencyEncoded) MakeCertFree(pbSpcSpAgencyEncoded); if (pbEKUEncoded) MakeCertFree(pbEKUEncoded); if (pbSpcCommonNameEncoded) MakeCertFree(pbSpcCommonNameEncoded); if (pbKeyUsageEncoded) MakeCertFree(pbKeyUsageEncoded); if (pbFinancialCriteria) MakeCertFree(pbFinancialCriteria); if (pbBasicConstraintsEncoded) MakeCertFree(pbBasicConstraintsEncoded); if (pbCertEncoded) MakeCertFree(pbCertEncoded); if (rgpRequestExtensions) { for(dwExtIndex=0; dwExtIndexPublicKey.pbData, pIssuerKeyInfo->PublicKey.cbData, 0, // dwFlags rgProvKey, &cbProvKey) || !CryptDecodeObject(X509_ASN_ENCODING, RSA_CSP_PUBLICKEYBLOB, pPubKeyInfo->PublicKey.pbData, pPubKeyInfo->PublicKey.cbData, 0, // dwFlags rgCertKey, &cbCertKey) || cbProvKey == 0 || cbProvKey != cbCertKey || memcmp(rgProvKey, rgCertKey, cbProvKey) != 0) { IDSwprintf(hModule,IDS_ERR_MISMATCH); goto ErrorReturn; } } fResult = TRUE; goto CommonReturn; ErrorReturn: fResult = FALSE; CommonReturn: if (pPubKeyInfo) MakeCertFree(pPubKeyInfo); return fResult; } //+------------------------------------------------------------------------- // Get the issuer's private signature key provider //-------------------------------------------------------------------------- static HCRYPTPROV GetIssuerProv(OUT LPWSTR *ppwszTmpContainer) { HCRYPTPROV hProv=0; WCHAR wszIssuerSig[40]; //load the string "Issuer Signature" if(!LoadStringU(hModule, IDS_ISSUER_SIGNATURE, wszIssuerSig, sizeof(wszIssuerSig)/sizeof(wszIssuerSig[0]))) { IDSwprintf(hModule,IDS_ERR_ROOT_KEY); return NULL; } if (IsRootKey()) { wszIssuerKey=wszRoot; // Get root key from the program's resoures and create a temporary // key container hProv = GetRootProv(ppwszTmpContainer); wszIssuerKey=wszMakeCertRoot; } else { // get the non-root private key set from either pvk file // of the key container if(fUseIssuerPvkFile) { if(S_OK!=PvkGetCryptProv( NULL, wszIssuerSig, wszIssuerProviderName, dwIssuerProviderType, wszIssuerKey, NULL, &dwIssuerKeySpec, ppwszTmpContainer, &hProv)) hProv=0; } else { if(S_OK!=PvkGetCryptProv( NULL, wszIssuerSig, wszIssuerProviderName, dwIssuerProviderType, NULL, wszIssuerKey, &dwIssuerKeySpec, ppwszTmpContainer, &hProv)) hProv=0; } if (hProv == 0) IDSwprintf(hModule,IDS_ERR_ISSUER_KEY, wszIssuerKey); } return hProv; } //+------------------------------------------------------------------------- // Get the subject's private key provider //-------------------------------------------------------------------------- static HCRYPTPROV GetSubjectProv(OUT LPWSTR *ppwszTmpContainer) { HCRYPTPROV hProv=0; WCHAR wszKeyName[40]; int ids; WCHAR *wszRegKeyName=NULL; BOOL fResult; HCRYPTKEY hKey=NULL; DWORD dwFlags = 0; DWORD dwRequiredKeySpec; HCRYPTKEY hPubKey; UUID TmpContainerUuid; if(dwKeySpec==AT_SIGNATURE) ids=IDS_SUB_SIG; else ids=IDS_SUB_EXCHANGE; //load the string if(!LoadStringU(hModule, ids, wszKeyName, sizeof(wszKeyName)/sizeof(wszKeyName[0]))) goto CreateKeyError; //try to get the hProv from either a private key file or //key container if(fUseSubjectPvkFile) { // Try to open the PVK file if it already exists: if(S_OK != PvkGetCryptProv(NULL, wszKeyName, wszSubjectProviderName, dwSubjectProviderType, wszSubjectKey, NULL, &dwKeySpec, ppwszTmpContainer, &hProv)) hProv=0; } else // try to open the key container to see if it exists: { if (dwSubjectStoreFlag == CERT_SYSTEM_STORE_LOCAL_MACHINE) { // For machine keysets, we have to do all this ourselves: if(!CryptAcquireContextU(&hProv, wszSubjectKey, wszSubjectProviderName, dwSubjectProviderType, CRYPT_MACHINE_KEYSET)) hProv=0; else { //try to figure out the key specification if(dwKeySpec==0) dwRequiredKeySpec=AT_SIGNATURE; else dwRequiredKeySpec=dwKeySpec; //make sure dwKeySpec is the correct key spec if (CryptGetUserKey(hProv, dwRequiredKeySpec, &hPubKey)) { CryptDestroyKey(hPubKey); dwKeySpec = dwRequiredKeySpec; } else { //we fail if user required another key spec if(dwKeySpec != 0) { // Doesn't have the specified public key CryptReleaseContext(hProv, 0); hProv = 0; } else { //now we try AT_EXCHANGE key dwRequiredKeySpec=AT_KEYEXCHANGE; if (CryptGetUserKey(hProv, dwRequiredKeySpec, &hPubKey)) { CryptDestroyKey(hPubKey); dwKeySpec = dwRequiredKeySpec; } else { // Doesn't have the specified public key CryptReleaseContext(hProv, 0); hProv = 0; } } } } } else { // For user keysets we can use this function to try to acquire the key: if(S_OK != PvkGetCryptProv(NULL, wszKeyName, wszSubjectProviderName, dwSubjectProviderType, NULL, wszSubjectKey, &dwKeySpec, ppwszTmpContainer, &hProv)) hProv=0; } } // If the keyset didn't already exist, generate a new private keyset: if (0 == hProv) { //now that we have to generate private keys, generate //AT_SIGNATURE key by default: if(dwKeySpec==0) dwKeySpec=AT_SIGNATURE; //when the subject PVK file is used if(fUseSubjectPvkFile) { // Create a temporary keyset to load the private key into if (CoCreateGuid((GUID *)&TmpContainerUuid) != S_OK) { goto CreateKeyError; } if (NULL == (wszRegKeyName = (LPWSTR) MakeCertAlloc (((sizeof(UUID) * 2 + 1) * sizeof(WCHAR))))) goto CreateKeyError; BytesToWStr(sizeof(UUID), &TmpContainerUuid, wszRegKeyName); // Open a new key container if (!CryptAcquireContextU( &hProv, wszRegKeyName, wszSubjectProviderName, dwSubjectProviderType, CRYPT_NEWKEYSET // dwFlags )) goto CreateKeyError; // generate new keys in the key container if (AT_KEYEXCHANGE == dwKeySpec && PROV_DSS_DH == dwSubjectProviderType) { if (!GenDhKey( hProv, CRYPT_EXPORTABLE // dwFlags )) goto ErrorReturn; } else if (!CryptGenKey( hProv, dwKeySpec, (dwKeyBitLen << 16) | CRYPT_EXPORTABLE, &hKey )) goto CreateKeyError; else CryptDestroyKey(hKey); // Save the key into the file and delete from the provider // HANDLE hFile = CreateFileU( wszSubjectKey, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, // lpsa CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL // hTemplateFile ); if (hFile == INVALID_HANDLE_VALUE) { if (GetLastError() == ERROR_FILE_EXISTS) IDSwprintf(hModule,IDS_ERR_SUB_FILE_EXIST, wszSubjectKey); else IDSwprintf(hModule,IDS_ERR_SUB_FILE_CREATE, wszSubjectKey); fResult = FALSE; } else { dwFlags = 0; if (AT_KEYEXCHANGE == dwKeySpec && PROV_DSS_DH == dwSubjectProviderType && IsDh3Csp()) dwFlags |= CRYPT_BLOB_VER3; fResult = PvkPrivateKeySave( hProv, hFile, dwKeySpec, NULL, // hwndOwner wszKeyName, dwFlags ); } //release hProv CryptReleaseContext(hProv, 0); fResult &= CryptAcquireContextU( &hProv, wszRegKeyName, wszSubjectProviderName, dwSubjectProviderType, CRYPT_DELETEKEYSET); hProv = 0; if (hFile != INVALID_HANDLE_VALUE) { CloseHandle(hFile); if (!fResult) DeleteFileU(wszSubjectKey); } if (!fResult) goto CreateKeyError; //get hProv if(S_OK != PvkGetCryptProv(NULL, wszKeyName, wszSubjectProviderName, dwSubjectProviderType, wszSubjectKey, NULL, &dwKeySpec, ppwszTmpContainer, &hProv)) hProv=0; } else { // If we're not using a PVK file: // generate a machine keyset if the cert is going to the machine store dwFlags = CRYPT_NEWKEYSET; if (dwSubjectStoreFlag == CERT_SYSTEM_STORE_LOCAL_MACHINE) dwFlags |= CRYPT_MACHINE_KEYSET; // Open a new key container if (!CryptAcquireContextU( &hProv, wszSubjectKey, wszSubjectProviderName, dwSubjectProviderType, dwFlags // dwFlags )) goto CreateKeyError; // Before, dwFlags contained flags for CryptAcquireContext. // Now these are flags to CSPGenKey dwFlags = 0; if (fPrivateKeyExportable) dwFlags |= CRYPT_EXPORTABLE; if (AT_KEYEXCHANGE == dwKeySpec && PROV_DSS_DH == dwSubjectProviderType) { if (!GenDhKey( hProv, dwFlags // dwFlags )) goto ErrorReturn; } else if (!CryptGenKey( hProv, dwKeySpec, (dwKeyBitLen << 16) | dwFlags, &hKey )) goto CreateKeyError; else CryptDestroyKey(hKey); //try to get the user key if (CryptGetUserKey( hProv, dwKeySpec, &hKey )) { CryptDestroyKey(hKey); } else { // Doesn't have the specified public key CryptReleaseContext(hProv, 0); hProv=0; } } if (0 == hProv ) { IDSwprintf(hModule,IDS_ERR_SUB_KEY, wszSubjectKey); goto ErrorReturn; } }//hProv==0 goto CommonReturn; CreateKeyError: IDSwprintf(hModule,IDS_ERR_SUB_KEY_CREATE, wszSubjectKey); ErrorReturn: if (hProv) { CryptReleaseContext(hProv, 0); hProv = 0; } CommonReturn: if(wszRegKeyName) MakeCertFree(wszRegKeyName); return hProv; } //+------------------------------------------------------------------------- // Allocate and get the public key info for the provider //-------------------------------------------------------------------------- static BOOL GetPublicKey( HCRYPTPROV hProv, PCERT_PUBLIC_KEY_INFO *ppPubKeyInfo ) { BOOL fResult; PCERT_PUBLIC_KEY_INFO pPubKeyInfo = NULL; DWORD cbPubKeyInfo; cbPubKeyInfo = 0; CryptExportPublicKeyInfo( hProv, dwKeySpec, X509_ASN_ENCODING, NULL, // pPubKeyInfo &cbPubKeyInfo ); if (cbPubKeyInfo == 0) { PrintLastError(IDS_ERR_EXPORT_PUB); goto ErrorReturn; } if (NULL == (pPubKeyInfo = (PCERT_PUBLIC_KEY_INFO) MakeCertAlloc(cbPubKeyInfo))) goto ErrorReturn; if (!CryptExportPublicKeyInfo( hProv, dwKeySpec, X509_ASN_ENCODING, pPubKeyInfo, &cbPubKeyInfo )) { PrintLastError(IDS_ERR_EXPORT_PUB); goto ErrorReturn; } if (fNoPubKeyPara) { pPubKeyInfo->Algorithm.Parameters.cbData = 0; pPubKeyInfo->Algorithm.Parameters.pbData = NULL; } if (AT_KEYEXCHANGE == dwKeySpec && PROV_DSS_DH == dwSubjectProviderType) { if (!UpdateDhPublicKey(&pPubKeyInfo)) goto ErrorReturn; } fResult = TRUE; goto CommonReturn; ErrorReturn: fResult = FALSE; if (pPubKeyInfo) { MakeCertFree(pPubKeyInfo); pPubKeyInfo = NULL; } CommonReturn: *ppPubKeyInfo = pPubKeyInfo; return fResult; } //+------------------------------------------------------------------------- // Encode the Glue Name from the input name by prepending the following // CERT_RDN_ATTR: // pszObjID = SPC_GLUE_RDN_OBJID // dwValueType = CERT_RDN_PRINTABLE_STRING // Value = "Glue" //-------------------------------------------------------------------------- static BOOL EncodeGlueName( IN PCERT_NAME_BLOB pName, OUT BYTE **ppbEncodedGlueName, OUT DWORD *pcbEncodedGlueName ) { BOOL fResult; PCERT_NAME_INFO pNameInfo = NULL; DWORD cbNameInfo; CERT_NAME_INFO GlueNameInfo; DWORD cGlueRDN; PCERT_RDN pGlueRDN = NULL; BYTE *pbEncodedGlueName = NULL; DWORD cbEncodedGlueName; DWORD i; cbNameInfo = 0; CryptDecodeObject(X509_ASN_ENCODING, X509_UNICODE_NAME, pName->pbData, pName->cbData, 0, // dwFlags NULL, // pNameInfo &cbNameInfo ); if (cbNameInfo == 0) goto ErrorReturn; if (NULL == (pNameInfo = (PCERT_NAME_INFO) MakeCertAlloc(cbNameInfo))) goto ErrorReturn; if (!CryptDecodeObject(X509_ASN_ENCODING, X509_UNICODE_NAME, pName->pbData, pName->cbData, 0, // dwFlags pNameInfo, &cbNameInfo)) goto ErrorReturn; cGlueRDN = pNameInfo->cRDN + 1; if (NULL == (pGlueRDN = (PCERT_RDN) MakeCertAlloc(cGlueRDN * sizeof(CERT_RDN)))) goto ErrorReturn; pGlueRDN[0].cRDNAttr = 1; pGlueRDN[0].rgRDNAttr = &GlueRDNAttr; for (i = 1; i < cGlueRDN; i++) pGlueRDN[i] = pNameInfo->rgRDN[i - 1]; GlueNameInfo.cRDN = cGlueRDN; GlueNameInfo.rgRDN = pGlueRDN; cbEncodedGlueName = 0; CryptEncodeObject(X509_ASN_ENCODING, X509_UNICODE_NAME, &GlueNameInfo, NULL, // pbEncodedGlueName &cbEncodedGlueName ); if (cbEncodedGlueName == 0) goto ErrorReturn; if (NULL == (pbEncodedGlueName = (BYTE *) MakeCertAlloc(cbEncodedGlueName))) goto ErrorReturn; if (!CryptEncodeObject(X509_ASN_ENCODING, X509_UNICODE_NAME, &GlueNameInfo, pbEncodedGlueName, &cbEncodedGlueName)) goto ErrorReturn; fResult = TRUE; goto CommonReturn; ErrorReturn: if (pbEncodedGlueName) { MakeCertFree(pbEncodedGlueName); pbEncodedGlueName = NULL; } cbEncodedGlueName = 0; fResult = FALSE; CommonReturn: if (pNameInfo) MakeCertFree(pNameInfo); if (pGlueRDN) MakeCertFree(pGlueRDN); *ppbEncodedGlueName = pbEncodedGlueName; *pcbEncodedGlueName = cbEncodedGlueName; return fResult; } //+------------------------------------------------------------------------- // Get the subject's cert context and encoded name //-------------------------------------------------------------------------- static BOOL GetRequestInfo(OUT CERT_REQUEST_INFO **ppCertInfo) { BOOL fResult = FALSE; fResult = CryptQueryObject( CERT_QUERY_OBJECT_FILE, wszSubjectRequestFile, CERT_QUERY_CONTENT_FLAG_PKCS10, CERT_QUERY_FORMAT_FLAG_ALL, CRYPT_DECODE_ALLOC_FLAG, NULL, NULL, NULL, NULL, NULL, (const void **)ppCertInfo); return fResult; } //+------------------------------------------------------------------------- // GetExtensionsFromRequest // // We get all the requested extensions from the PKCS10 request //-------------------------------------------------------------------------- BOOL GetExtensionsFromRequest(PCERT_REQUEST_INFO pReqInfo, DWORD *pdwCount, PCERT_EXTENSIONS **pprgExtensions) { DWORD dwIndex = 0; BOOL fResult = FALSE; PCRYPT_ATTRIBUTE pAttr=NULL; DWORD cbData=0; *pdwCount =0; *pprgExtensions=NULL; if(!pReqInfo) goto CLEANUP; // Look for the RSA extension OID first: for(dwIndex=0; dwIndex < pReqInfo->cAttribute; dwIndex++) { if(0 == strcmp((pReqInfo->rgAttribute[dwIndex]).pszObjId, szOID_RSA_certExtensions)) break; } if( dwIndex == pReqInfo->cAttribute) { // We could not find the RSA OID. Try the old Microsoft OID next: for(dwIndex=0; dwIndex < pReqInfo->cAttribute; dwIndex++) { if(0 == strcmp((pReqInfo->rgAttribute[dwIndex]).pszObjId, SPC_CERT_EXTENSIONS_OBJID)) break; } if( dwIndex == pReqInfo->cAttribute) { // We could not find the requested extensions. fResult = TRUE; goto CLEANUP; } } pAttr=&(pReqInfo->rgAttribute[dwIndex]); if(0 == pAttr->cValue) { fResult=TRUE; goto CLEANUP; } *pprgExtensions = (PCERT_EXTENSIONS *)MakeCertAlloc((pAttr->cValue) * sizeof(PCERT_EXTENSIONS)); if(NULL == (*pprgExtensions)) goto CLEANUP; memset(*pprgExtensions, 0, (pAttr->cValue) * sizeof(PCERT_EXTENSIONS)); for(dwIndex=0; dwIndexcValue; dwIndex++) { cbData = 0; if(!CryptDecodeObject(PKCS_7_ASN_ENCODING | X509_ASN_ENCODING, X509_EXTENSIONS, pAttr->rgValue[dwIndex].pbData, pAttr->rgValue[dwIndex].cbData, 0, NULL, &cbData)) goto CLEANUP; (*pprgExtensions)[dwIndex]=(PCERT_EXTENSIONS)MakeCertAlloc(cbData); if(NULL == (*pprgExtensions)[dwIndex]) goto CLEANUP; if(!CryptDecodeObject(PKCS_7_ASN_ENCODING | X509_ASN_ENCODING, X509_EXTENSIONS, pAttr->rgValue[dwIndex].pbData, pAttr->rgValue[dwIndex].cbData, 0, (*pprgExtensions)[dwIndex], &cbData)) goto CLEANUP; } *pdwCount=pAttr->cValue; fResult = TRUE; CLEANUP: if(FALSE == fResult) { //we need to free the memory if(*pprgExtensions) { for(dwIndex=0; dwIndexcValue; dwIndex++) { if((*pprgExtensions)[dwIndex]) MakeCertFree((*pprgExtensions)[dwIndex]); } MakeCertFree(*pprgExtensions); *pprgExtensions = NULL; } *pdwCount=0; } return fResult; } //+------------------------------------------------------------------------- // Get the subject's cert context and encoded name //-------------------------------------------------------------------------- static BOOL GetSubject( OUT PCCERT_CONTEXT *ppCertContext, OUT BYTE **ppbEncodedName, OUT DWORD *pcbEncodedName ) { BOOL fResult; PCCERT_CONTEXT pCert = NULL; BYTE *pb; DWORD cb; BYTE *pbEncodedName = NULL; DWORD cbEncodedName; if (S_OK==RetrieveBLOBFromFile(wszSubjectCertFile,&cb, &pb)) { pCert = CertCreateCertificateContext(X509_ASN_ENCODING, pb, cb); UnmapViewOfFile(pb); } if (pCert == NULL) goto BadFile; if (0 == (cbEncodedName = pCert->pCertInfo->Subject.cbData)) goto BadFile; if (fGlueCert ) { if (!EncodeGlueName( &pCert->pCertInfo->Subject, &pbEncodedName, &cbEncodedName)) goto ErrorReturn; } else { if (NULL == (pbEncodedName = (BYTE *) MakeCertAlloc(cbEncodedName))) goto ErrorReturn; memcpy(pbEncodedName, pCert->pCertInfo->Subject.pbData, cbEncodedName); } fResult = TRUE; goto CommonReturn; BadFile: IDSwprintf(hModule, IDS_ERR_CANNOT_LOAD_SUB_CERT, wszSubjectCertFile); ErrorReturn: if (pbEncodedName) { MakeCertFree(pbEncodedName); pbEncodedName = NULL; } cbEncodedName = 0; if (pCert) { CertFreeCertificateContext(pCert); pCert = NULL; } fResult = FALSE; CommonReturn: *ppCertContext = pCert; *ppbEncodedName = pbEncodedName; *pcbEncodedName = cbEncodedName; return fResult; } //+------------------------------------------------------------------------- // Convert and encode the subject's X500 formatted name //-------------------------------------------------------------------------- static BOOL EncodeSubject( OUT BYTE **ppbEncoded, IN OUT DWORD *pcbEncoded ) { BOOL fResult; DWORD cbEncodedSubject=0; BYTE *pbEncodedSubject=NULL; BYTE *pbEncoded = NULL; DWORD cbEncoded; CERT_NAME_BLOB SubjectInfo; //encode the wszSubjectX500Name into an encoded X509_NAME if(!CertStrToNameW( X509_ASN_ENCODING, wszSubjectX500Name, CERT_NAME_STR_REVERSE_FLAG, NULL, NULL, &cbEncodedSubject, NULL)) { PrintLastError(IDS_CERT_STR_TO_NAME); goto ErrorReturn; } pbEncodedSubject = (BYTE *) MakeCertAlloc(cbEncodedSubject); if (pbEncodedSubject == NULL) goto ErrorReturn; if(!CertStrToNameW( X509_ASN_ENCODING, wszSubjectX500Name, CERT_NAME_STR_REVERSE_FLAG, NULL, pbEncodedSubject, &cbEncodedSubject, NULL)) { PrintLastError(IDS_CERT_STR_TO_NAME); goto ErrorReturn; } SubjectInfo.cbData=cbEncodedSubject; SubjectInfo.pbData=pbEncodedSubject; //add the GLUE CDRT_RDN_ATTR if (fGlueCert) { if(!EncodeGlueName(&SubjectInfo, &pbEncoded, &cbEncoded)) goto ErrorReturn; } else { cbEncoded=cbEncodedSubject; pbEncoded=pbEncodedSubject; } fResult = TRUE; goto CommonReturn; ErrorReturn: if (pbEncoded) { MakeCertFree(pbEncoded); pbEncoded = NULL; } cbEncoded = 0; fResult = FALSE; CommonReturn: //we need to free the memory for pbEncodedSubject for GlueCert if(fGlueCert) { if(pbEncodedSubject) { MakeCertFree(pbEncodedSubject); pbEncodedSubject=NULL; } } *ppbEncoded = pbEncoded; *pcbEncoded = cbEncoded; return fResult; } // The test root's public key isn't encoded properly in the certificate. // It's missing a leading zero to make it a unsigned integer. static BYTE rgbTestRoot[] = { #include "root.h" }; static CERT_PUBLIC_KEY_INFO TestRootPublicKeyInfo = { szOID_RSA_RSA, 0, NULL, sizeof(rgbTestRoot), rgbTestRoot, 0 }; static BYTE rgbTestRootInfoAsn[] = { #include "rootasn.h" }; //+------------------------------------------------------------------------- // X509 Extensions: Allocate and Encode functions //-------------------------------------------------------------------------- static BOOL CreateAuthorityKeyId( IN HCRYPTPROV hProv, IN PCERT_INFO pIssuerCert, OUT BYTE **ppbEncoded, IN OUT DWORD *pcbEncoded ) { BOOL fResult; BYTE *pbEncoded = NULL; DWORD cbEncoded; CERT_AUTHORITY_KEY_ID_INFO KeyIdInfo; #define MAX_HASH_LEN 20 BYTE rgbHash[MAX_HASH_LEN]; DWORD cbHash = MAX_HASH_LEN; // Issuer's KeyId: MD5 hash of the encoded issuer's public key info // First check if the issuer is the test root with an incorrectly // encoded public key. if (CertComparePublicKeyInfo( X509_ASN_ENCODING, &pIssuerCert->SubjectPublicKeyInfo, &TestRootPublicKeyInfo )) { if (!CryptHashCertificate( hProv, CALG_MD5, 0, // dwFlags rgbTestRootInfoAsn, sizeof(rgbTestRootInfoAsn), rgbHash, &cbHash)) { PrintLastError(IDS_CRYPT_HASH_CERT); goto ErrorReturn; } } else { if (!CryptHashPublicKeyInfo( hProv, CALG_MD5, 0, // dwFlags X509_ASN_ENCODING, &pIssuerCert->SubjectPublicKeyInfo, rgbHash, &cbHash)) { PrintLastError(IDS_CRYPT_HASP_PUB); goto ErrorReturn; } } KeyIdInfo.KeyId.pbData = rgbHash; KeyIdInfo.KeyId.cbData = cbHash; // Issuer's Issuer KeyIdInfo.CertIssuer = pIssuerCert->Issuer; // Issuer's SerialNumber KeyIdInfo.CertSerialNumber = pIssuerCert->SerialNumber; cbEncoded = 0; CryptEncodeObject(X509_ASN_ENCODING, X509_AUTHORITY_KEY_ID, &KeyIdInfo, NULL, // pbEncoded &cbEncoded ); if (cbEncoded == 0) { PrintLastError(IDS_ENCODE_AUTH_KEY); goto ErrorReturn; } pbEncoded = (BYTE *) MakeCertAlloc(cbEncoded); if (pbEncoded == NULL) goto ErrorReturn; if (!CryptEncodeObject(X509_ASN_ENCODING, X509_AUTHORITY_KEY_ID, &KeyIdInfo, pbEncoded, &cbEncoded )) { PrintLastError(IDS_ENCODE_AUTH_KEY); goto ErrorReturn; } fResult = TRUE; goto CommonReturn; ErrorReturn: if (pbEncoded) { MakeCertFree(pbEncoded); pbEncoded = NULL; } cbEncoded = 0; fResult = FALSE; CommonReturn: *ppbEncoded = pbEncoded; *pcbEncoded = cbEncoded; return fResult; } static BOOL CreateSpcSpAgency( OUT BYTE **ppbEncoded, IN OUT DWORD *pcbEncoded ) { BOOL fResult; BYTE *pbEncoded = NULL; DWORD cbEncoded; SPC_LINK PolicyLink; SPC_SP_AGENCY_INFO AgencyInfo; memset(&AgencyInfo, 0, sizeof(AgencyInfo)); if (wszPolicyLink) { PolicyLink.dwLinkChoice = SPC_URL_LINK_CHOICE; PolicyLink.pwszUrl = wszPolicyLink; AgencyInfo.pPolicyInformation = &PolicyLink; } cbEncoded = 0; CryptEncodeObject(X509_ASN_ENCODING, SPC_SP_AGENCY_INFO_OBJID, &AgencyInfo, NULL, // pbEncoded &cbEncoded); if (cbEncoded == 0) { PrintLastError(IDS_ENCODE_SPC_AGENCY); goto ErrorReturn; } pbEncoded = (BYTE *) MakeCertAlloc(cbEncoded); if (pbEncoded == NULL) goto ErrorReturn; if (!CryptEncodeObject(X509_ASN_ENCODING, SPC_SP_AGENCY_INFO_STRUCT, &AgencyInfo, pbEncoded, &cbEncoded )) { PrintLastError(IDS_ENCODE_SPC_AGENCY); goto ErrorReturn; } fResult = TRUE; goto CommonReturn; ErrorReturn: if (pbEncoded) { MakeCertFree(pbEncoded); pbEncoded = NULL; } cbEncoded = 0; fResult = FALSE; CommonReturn: *ppbEncoded = pbEncoded; *pcbEncoded = cbEncoded; return fResult; } static BOOL CreateEnhancedKeyUsage( OUT BYTE **ppbEncoded, IN OUT DWORD *pcbEncoded ) { BOOL fResult = FALSE; LPBYTE pbEncoded =NULL; DWORD cbEncoded =0; DWORD cCount =0; LPSTR psz=NULL; LPSTR pszTok=NULL; DWORD cTok = 0; PCERT_ENHKEY_USAGE pUsage =NULL; LPSTR pszCodeSigning = szOID_PKIX_KP_CODE_SIGNING; BOOL fFound=FALSE; if(wszEKUOids) { if ( WSZtoSZ(wszEKUOids, &psz) != S_OK ) goto CLEANUP; // // Count the number of OIDs as well as converting from comma delimited // to NULL character delimited // pszTok = strtok(psz, ","); while ( pszTok != NULL ) { cTok++; pszTok = strtok(NULL, ","); } // // Allocate a cert enhanced key usage structure and fill it in with // the string tokens // // we allocate one more string for the code signing OIDs // pUsage = (PCERT_ENHKEY_USAGE)new BYTE [sizeof(CERT_ENHKEY_USAGE) + ( (cTok + 1) * sizeof(LPSTR) )]; if(NULL == pUsage) goto CLEANUP; pszTok = psz; pUsage->cUsageIdentifier = cTok; pUsage->rgpszUsageIdentifier = (LPSTR *)((LPBYTE)pUsage+sizeof(CERT_ENHKEY_USAGE)); for ( cCount = 0; cCount < cTok; cCount++ ) { pUsage->rgpszUsageIdentifier[cCount] = pszTok; pszTok = pszTok+strlen(pszTok)+1; } //we add the code signing OID if use has specified commerical or individual signing if(fCertCommercial || fCertIndividual) { //check to see if the code signing OID is alreayd present for(cCount = 0; cCount < pUsage->cUsageIdentifier; cCount++) { if(0 == strcmp(pszCodeSigning,pUsage->rgpszUsageIdentifier[cCount])) { fFound=TRUE; break; } } if(FALSE == fFound) { (pUsage->rgpszUsageIdentifier)[pUsage->cUsageIdentifier] = pszCodeSigning; (pUsage->cUsageIdentifier)++ ; } } } else { if(fCertCommercial || fCertIndividual) { pUsage = (PCERT_ENHKEY_USAGE)new BYTE [sizeof(CERT_ENHKEY_USAGE)]; if(NULL == pUsage) goto CLEANUP; pUsage->cUsageIdentifier = 1; pUsage->rgpszUsageIdentifier = &pszCodeSigning; } else { goto CLEANUP; } } // // Encode the usage // if(!CryptEncodeObject( X509_ASN_ENCODING, szOID_ENHANCED_KEY_USAGE, pUsage, NULL, &cbEncoded )) goto CLEANUP; pbEncoded = new BYTE [cbEncoded]; if(NULL == pbEncoded) goto CLEANUP; fResult = CryptEncodeObject( X509_ASN_ENCODING, szOID_ENHANCED_KEY_USAGE, pUsage, pbEncoded, &cbEncoded ); // // Cleanup // CLEANUP: if(pUsage) delete[] pUsage; if(psz) MakeCertFree(psz); if ( TRUE == fResult) { *ppbEncoded = pbEncoded; *pcbEncoded = cbEncoded; } else { if(pbEncoded) delete[] pbEncoded; } return fResult; } static BOOL CreateKeyUsage( OUT BYTE **ppbEncoded, IN OUT DWORD *pcbEncoded ) { BOOL fResult; BYTE *pbEncoded = NULL; DWORD cbEncoded; CERT_KEY_USAGE_RESTRICTION_INFO KeyUsageInfo; BYTE bRestrictedKeyUsage; DWORD cCertPolicyId; LPSTR rgpszIndividualCertPolicyElementId[1] = { SPC_INDIVIDUAL_SP_KEY_PURPOSE_OBJID }; LPSTR rgpszCommercialCertPolicyElementId[1] = { SPC_COMMERCIAL_SP_KEY_PURPOSE_OBJID }; CERT_POLICY_ID rgCertPolicyId[2]; memset(&KeyUsageInfo, 0, sizeof(KeyUsageInfo)); bRestrictedKeyUsage = CERT_DIGITAL_SIGNATURE_KEY_USAGE; KeyUsageInfo.RestrictedKeyUsage.pbData = &bRestrictedKeyUsage; KeyUsageInfo.RestrictedKeyUsage.cbData = 1; KeyUsageInfo.RestrictedKeyUsage.cUnusedBits = 7; cCertPolicyId = 0; if (fCertIndividual) { rgCertPolicyId[cCertPolicyId].cCertPolicyElementId = 1; rgCertPolicyId[cCertPolicyId].rgpszCertPolicyElementId = rgpszIndividualCertPolicyElementId; cCertPolicyId++; } if (fCertCommercial) { rgCertPolicyId[cCertPolicyId].cCertPolicyElementId = 1; rgCertPolicyId[cCertPolicyId].rgpszCertPolicyElementId = rgpszCommercialCertPolicyElementId; cCertPolicyId++; } if (cCertPolicyId > 0) { KeyUsageInfo.cCertPolicyId = cCertPolicyId; KeyUsageInfo.rgCertPolicyId = rgCertPolicyId; } cbEncoded = 0; CryptEncodeObject(X509_ASN_ENCODING, X509_KEY_USAGE_RESTRICTION, &KeyUsageInfo, NULL, // pbEncoded &cbEncoded ); if (cbEncoded == 0) { PrintLastError(IDS_ENCODE_KEY_USAGE); goto ErrorReturn; } pbEncoded = (BYTE *) MakeCertAlloc(cbEncoded); if (pbEncoded == NULL) goto ErrorReturn; if (!CryptEncodeObject(X509_ASN_ENCODING, X509_KEY_USAGE_RESTRICTION, &KeyUsageInfo, pbEncoded, &cbEncoded )) { PrintLastError(IDS_ENCODE_KEY_USAGE); goto ErrorReturn; } fResult = TRUE; goto CommonReturn; ErrorReturn: if (pbEncoded) { MakeCertFree(pbEncoded); pbEncoded = NULL; } cbEncoded = 0; fResult = FALSE; CommonReturn: *ppbEncoded = pbEncoded; *pcbEncoded = cbEncoded; return fResult; } static BOOL CreateBasicConstraints( OUT BYTE **ppbEncoded, IN OUT DWORD *pcbEncoded ) { BOOL fResult; BYTE *pbEncoded = NULL; DWORD cbEncoded; CERT_BASIC_CONSTRAINTS2_INFO Info2; memset(&Info2, 0, sizeof(Info2)); if (certTypes == 0) certTypes = CERT_END_ENTITY_SUBJECT_FLAG; if (CERT_CA_SUBJECT_FLAG & certTypes) { Info2.fCA = TRUE; } if (pathLenConstraint < 0) { Info2.fPathLenConstraint = FALSE; } else { Info2.fPathLenConstraint = TRUE; Info2.dwPathLenConstraint = pathLenConstraint; } cbEncoded = 0; CryptEncodeObject(X509_ASN_ENCODING, X509_BASIC_CONSTRAINTS2, &Info2, NULL, // pbEncoded &cbEncoded ); if (cbEncoded == 0) { PrintLastError(IDS_ENCODE_BASIC_CONSTRAINTS2); goto ErrorReturn; } pbEncoded = (BYTE *) MakeCertAlloc(cbEncoded); if (pbEncoded == NULL) goto ErrorReturn; if (!CryptEncodeObject(X509_ASN_ENCODING, X509_BASIC_CONSTRAINTS2, &Info2, pbEncoded, &cbEncoded )) { PrintLastError(IDS_ENCODE_BASIC_CONSTRAINTS2); goto ErrorReturn; } fResult = TRUE; goto CommonReturn; ErrorReturn: if (pbEncoded) { MakeCertFree(pbEncoded); pbEncoded = NULL; } cbEncoded = 0; fResult = FALSE; CommonReturn: *ppbEncoded = pbEncoded; *pcbEncoded = cbEncoded; return fResult; } //+------------------------------------------------------------------------- // Converts the bytes into WCHAR hex // // Needs (cb * 2 + 1) * sizeof(WCHAR) bytes of space in wsz //-------------------------------------------------------------------------- static void BytesToWStr(ULONG cb, void* pv, LPWSTR wsz) { BYTE* pb = (BYTE*) pv; for (ULONG i = 0; i> 4; *wsz++ = (b <= 9) ? b + L'0' : (b - 10) + L'A'; b = *pb & 0x0F; *wsz++ = (b <= 9) ? b + L'0' : (b - 10) + L'A'; pb++; } *wsz++ = 0; } //----------------------------------------------------------------------- // // Get the hash from a cert file // //-------------------------------------------------------------------------- HRESULT GetCertHashFromFile(LPWSTR pwszCertFile, BYTE **ppHash, DWORD *pcbHash, BOOL *pfMore) { HRESULT hr; HCERTSTORE hCertStore=NULL; PCCERT_CONTEXT pSigningCert=NULL; PCCERT_CONTEXT pPreCert=NULL; PCCERT_CONTEXT pDupCert=NULL; DWORD dwCount=0; if(!ppHash || !pcbHash || !pfMore) return E_INVALIDARG; //init *pcbHash=0; *ppHash=NULL; *pfMore=FALSE; //open a cert store hCertStore=CertOpenStore(CERT_STORE_PROV_FILENAME_W, dwCertStoreEncodingType, NULL, 0, pwszCertFile); if(hCertStore==NULL) { hr=SignError(); goto CLEANUP; } while(pDupCert=CertEnumCertificatesInStore(hCertStore, pPreCert)) { dwCount++; // Fail if there is more than one cert in the store if(dwCount > 1) { CertFreeCertificateContext(pDupCert); pDupCert=NULL; CertFreeCertificateContext(pSigningCert); pSigningCert=NULL; *pfMore=TRUE; hr=E_FAIL; goto CLEANUP; } pPreCert=pDupCert; pSigningCert=CertDuplicateCertificateContext(pDupCert); } if(pSigningCert==NULL) { hr=CRYPT_E_NO_DECRYPT_CERT; goto CLEANUP; } //get the hash if(!CertGetCertificateContextProperty(pSigningCert, CERT_SHA1_HASH_PROP_ID, NULL, pcbHash)) { hr=SignError(); goto CLEANUP; } *ppHash=(BYTE *)ToolUtlAlloc(*pcbHash); if(!(*ppHash)) { hr=E_OUTOFMEMORY; goto CLEANUP; } if(!CertGetCertificateContextProperty(pSigningCert, CERT_SHA1_HASH_PROP_ID, *ppHash, pcbHash)) { hr=SignError(); goto CLEANUP; } hr=S_OK; CLEANUP: if(pSigningCert) CertFreeCertificateContext(pSigningCert); if(hCertStore) CertCloseStore(hCertStore, 0); if(hr!=S_OK) { if(*ppHash) { ToolUtlFree(*ppHash); *ppHash=NULL; } } return hr; } //----------------------------------------------------------------------- // // Get the signing certificate // //-------------------------------------------------------------------------- PCCERT_CONTEXT GetIssuerCertAndStore(HCERTSTORE *phCertStore, BOOL *pfMore) { PCCERT_CONTEXT pSigningCert=NULL; PCCERT_CONTEXT pPreCert=NULL; PCCERT_CONTEXT pDupCert=NULL; BYTE *pHash=NULL; DWORD cbHash; HCERTSTORE hCertStore=NULL; CRYPT_HASH_BLOB HashBlob; DWORD dwCount=0; //init the output if(!phCertStore || !pfMore) return NULL; *phCertStore=NULL; *pfMore=FALSE; //open a cert store hCertStore=CertOpenStore(CERT_STORE_PROV_SYSTEM_W, dwCertStoreEncodingType, NULL, dwIssuerStoreFlag|CERT_STORE_READONLY_FLAG, wszIssuerStore); if(!hCertStore) return NULL; //get the hash of the certificate. Find the cert based on //pwszCertFile if(wszIssuerCertFile) { if(S_OK != GetCertHashFromFile(wszIssuerCertFile, &pHash, &cbHash, pfMore)) goto CLEANUP; HashBlob.cbData=cbHash; HashBlob.pbData=pHash; pSigningCert=CertFindCertificateInStore(hCertStore, X509_ASN_ENCODING, 0, CERT_FIND_SHA1_HASH, &HashBlob, NULL); } else { //find the certificate with the common name if(wszIssuerCertName) { while(pDupCert=CertFindCertificateInStore(hCertStore, X509_ASN_ENCODING, 0, CERT_FIND_SUBJECT_STR_W, wszIssuerCertName, pPreCert)) { dwCount++; if(dwCount > 1) { CertFreeCertificateContext(pDupCert); pDupCert=NULL; CertFreeCertificateContext(pSigningCert); pSigningCert=NULL; *pfMore=TRUE; goto CLEANUP; } pPreCert=pDupCert; pSigningCert=CertDuplicateCertificateContext(pDupCert); } } else { //no searching criteria, find the only cert in the store while(pDupCert=CertEnumCertificatesInStore(hCertStore, pPreCert)) { dwCount++; if(dwCount > 1) { CertFreeCertificateContext(pDupCert); pDupCert=NULL; CertFreeCertificateContext(pSigningCert); pSigningCert=NULL; *pfMore=TRUE; goto CLEANUP; } pPreCert=pDupCert; pSigningCert=CertDuplicateCertificateContext(pDupCert); } } } CLEANUP: if(pHash) ToolUtlFree(pHash); if(pSigningCert) { *phCertStore=hCertStore; } else { //free the hCertStore CertCloseStore(hCertStore, 0); } return pSigningCert; } //----------------------------------------------------------------------- // // EmptySubject // //-------------------------------------------------------------------------- BOOL EmptySubject(CERT_NAME_BLOB *pSubject) { BOOL fEmpty = TRUE; CERT_NAME_INFO *pCertNameInfo=NULL; DWORD cbData =0; if(!pSubject) goto CLEANUP; if(!CryptDecodeObject(PKCS_7_ASN_ENCODING | X509_ASN_ENCODING, X509_UNICODE_NAME, pSubject->pbData, pSubject->cbData, 0, NULL, &cbData)) goto CLEANUP; pCertNameInfo = (CERT_NAME_INFO *)MakeCertAlloc(cbData); if(NULL == pCertNameInfo) goto CLEANUP; if(!CryptDecodeObject(PKCS_7_ASN_ENCODING | X509_ASN_ENCODING, X509_UNICODE_NAME, pSubject->pbData, pSubject->cbData, 0, pCertNameInfo, &cbData)) goto CLEANUP; if((pCertNameInfo->cRDN) > 0) fEmpty = FALSE; CLEANUP: if(pCertNameInfo) MakeCertFree(pCertNameInfo); return fEmpty; } //+========================================================================= // Support functions to generate DH keys having the 'Q'parameter //========================================================================== static BOOL EncodeAndAllocObject( IN LPCSTR lpszStructType, IN const void *pvStructInfo, OUT BYTE **ppbEncoded, IN OUT DWORD *pcbEncoded ) { BOOL fResult; BYTE *pbEncoded = NULL; DWORD cbEncoded = 0; if (!CryptEncodeObject( X509_ASN_ENCODING, lpszStructType, pvStructInfo, NULL, &cbEncoded )) goto ErrorReturn; if (NULL == (pbEncoded = (BYTE *) MakeCertAlloc(cbEncoded))) goto ErrorReturn; if (!CryptEncodeObject( X509_ASN_ENCODING, lpszStructType, pvStructInfo, pbEncoded, &cbEncoded )) goto ErrorReturn; fResult = TRUE; CommonReturn: *ppbEncoded = pbEncoded; *pcbEncoded = cbEncoded; return fResult; ErrorReturn: fResult = FALSE; MakeCertFree(pbEncoded); pbEncoded = NULL; goto CommonReturn; } static BOOL DecodeAndAllocObject( IN LPCSTR lpszStructType, IN const BYTE *pbEncoded, IN DWORD cbEncoded, OUT void **ppvStructInfo, IN OUT DWORD *pcbStructInfo ) { BOOL fResult; void *pvStructInfo = NULL; DWORD cbStructInfo = 0; if (!CryptDecodeObject( X509_ASN_ENCODING, lpszStructType, pbEncoded, cbEncoded, 0, // dwFlags NULL, &cbStructInfo )) goto ErrorReturn; if (NULL == (pvStructInfo = MakeCertAlloc(cbStructInfo))) goto ErrorReturn; if (!CryptDecodeObject( X509_ASN_ENCODING, lpszStructType, pbEncoded, cbEncoded, 0, // dwFlags pvStructInfo, &cbStructInfo )) goto ErrorReturn; fResult = TRUE; CommonReturn: *ppvStructInfo = pvStructInfo; *pcbStructInfo = cbStructInfo; return fResult; ErrorReturn: fResult = FALSE; MakeCertFree(pvStructInfo); pvStructInfo = NULL; goto CommonReturn; } static BYTE rgbDhQ[21]; static CRYPT_UINT_BLOB DhQ = {0, NULL}; static BOOL GetDhParaFromCertFile( OUT PCERT_X942_DH_PARAMETERS *ppX942DhPara ) { BOOL fResult; PCCERT_CONTEXT pDhCert = NULL; PCERT_X942_DH_PARAMETERS pX942DhPara = NULL; BYTE *pb; DWORD cb; if (S_OK == RetrieveBLOBFromFile(wszDhParaCertFile, &cb, &pb)) { pDhCert = CertCreateCertificateContext(X509_ASN_ENCODING, pb, cb); UnmapViewOfFile(pb); } if (pDhCert == NULL) goto DhParaCertFileError; if (!DecodeAndAllocObject( X942_DH_PARAMETERS, pDhCert->pCertInfo->SubjectPublicKeyInfo.Algorithm.Parameters.pbData, pDhCert->pCertInfo->SubjectPublicKeyInfo.Algorithm.Parameters.cbData, (void **) &pX942DhPara, &cb )) goto DhParaCertFileError; fResult = TRUE; CommonReturn: CertFreeCertificateContext(pDhCert); *ppX942DhPara = pX942DhPara; return fResult; DhParaCertFileError: IDSwprintf(hModule, IDS_ERR_DH_PARA_FILE, wszDhParaCertFile); MakeCertFree(pX942DhPara); pX942DhPara = NULL; fResult = FALSE; goto CommonReturn; } static BOOL GetDhParaFromDssKey( OUT PCERT_DSS_PARAMETERS *ppDssPara ) { BOOL fResult; HCRYPTPROV hProv = 0; HCRYPTKEY hKey = 0; WCHAR *wszRegKeyName = NULL; UUID TmpContainerUuid; PCERT_DSS_PARAMETERS pDssPara = NULL; PCERT_PUBLIC_KEY_INFO pPubKeyInfo = NULL; DWORD cbPubKeyInfo; DWORD cbDssPara; // Create a temporary keyset to load the private key into if (CoCreateGuid((GUID *)&TmpContainerUuid) != S_OK) { goto CreateKeyError; } if (NULL == (wszRegKeyName = (LPWSTR) MakeCertAlloc (((sizeof(UUID) * 2 + 1) * sizeof(WCHAR))))) goto CreateKeyError; BytesToWStr(sizeof(UUID), &TmpContainerUuid, wszRegKeyName); // Open a new key container if (!CryptAcquireContextU( &hProv, wszRegKeyName, wszSubjectProviderName, dwSubjectProviderType, CRYPT_NEWKEYSET // dwFlags )) { hProv = 0; goto CreateKeyError; } if (0 == dwKeyBitLen) dwKeyBitLen = 512; // generate new DSS key in the key container if (!CryptGenKey( hProv, AT_SIGNATURE, (dwKeyBitLen << 16) | CRYPT_EXPORTABLE, &hKey )) goto CreateKeyError; else CryptDestroyKey(hKey); cbPubKeyInfo = 0; CryptExportPublicKeyInfo( hProv, AT_SIGNATURE, X509_ASN_ENCODING, NULL, // pPubKeyInfo &cbPubKeyInfo ); if (cbPubKeyInfo == 0) { PrintLastError(IDS_ERR_EXPORT_PUB); goto ErrorReturn; } if (NULL == (pPubKeyInfo = (PCERT_PUBLIC_KEY_INFO) MakeCertAlloc(cbPubKeyInfo))) goto ErrorReturn; if (!CryptExportPublicKeyInfo( hProv, AT_SIGNATURE, X509_ASN_ENCODING, pPubKeyInfo, &cbPubKeyInfo )) { PrintLastError(IDS_ERR_EXPORT_PUB); goto ErrorReturn; } if (!DecodeAndAllocObject( X509_DSS_PARAMETERS, pPubKeyInfo->Algorithm.Parameters.pbData, pPubKeyInfo->Algorithm.Parameters.cbData, (void **) &pDssPara, &cbDssPara )) goto CreateKeyError; // Save away the DSS 'Q' parameter. It will be used in GetPublicKey() // to update the DH parameters in the PublicKeyInfo if (pDssPara->q.cbData <= sizeof(rgbDhQ)) { memcpy(rgbDhQ, pDssPara->q.pbData, pDssPara->q.cbData); DhQ.cbData = pDssPara->q.cbData; DhQ.pbData = rgbDhQ; } fResult = TRUE; CommonReturn: if (hProv) { // Delete the just created DSS key CryptReleaseContext(hProv, 0); CryptAcquireContextU( &hProv, wszRegKeyName, wszSubjectProviderName, dwSubjectProviderType, CRYPT_DELETEKEYSET ); } MakeCertFree(wszRegKeyName); MakeCertFree(pPubKeyInfo); *ppDssPara = pDssPara; return fResult; CreateKeyError: IDSwprintf(hModule,IDS_ERR_SUB_KEY_CREATE, wszSubjectKey); ErrorReturn: MakeCertFree(pDssPara); pDssPara = NULL; fResult = FALSE; goto CommonReturn; } #ifndef DH3 #define DH3 (((DWORD)'D'<<8)+((DWORD)'H'<<16)+((DWORD)'3'<<24)) #endif static BOOL CreateDh3PubKeyStruc( IN PCERT_X942_DH_PARAMETERS pX942DhPara, OUT PUBLICKEYSTRUC **ppPubKeyStruc, OUT DWORD *pcbPubKeyStruc ) { BOOL fResult; PUBLICKEYSTRUC *pPubKeyStruc = NULL; DWORD cbPubKeyStruc; BYTE *pbKeyBlob; DHPUBKEY_VER3 *pCspPubKey; BYTE *pbKey; DWORD cbP; DWORD cbQ; DWORD cbJ; DWORD cb; cbP = pX942DhPara->p.cbData; cbQ = pX942DhPara->q.cbData; cbJ = pX942DhPara->j.cbData; if (0 == cbQ) { *ppPubKeyStruc = NULL; *pcbPubKeyStruc = 0; return TRUE; } // The CAPI public key representation consists of the following sequence: // - PUBLICKEYSTRUC // - DHPUBKEY_VER3 // - rgbP[cbP] // - rgbQ[cbQ] // - rgbG[cbP] // - rgbJ[cbJ] -- optional // - rgbY[cbP] -- will be omitted here cbPubKeyStruc = sizeof(PUBLICKEYSTRUC) + sizeof(DHPUBKEY_VER3) + cbP + cbQ + cbP + cbJ; if (NULL == (pPubKeyStruc = (PUBLICKEYSTRUC *) MakeCertAlloc( cbPubKeyStruc))) goto ErrorReturn; memset(pPubKeyStruc, 0, cbPubKeyStruc); pbKeyBlob = (BYTE *) pPubKeyStruc; pCspPubKey = (DHPUBKEY_VER3 *) (pbKeyBlob + sizeof(PUBLICKEYSTRUC)); pbKey = pbKeyBlob + sizeof(PUBLICKEYSTRUC) + sizeof(DHPUBKEY_VER3); pPubKeyStruc->bType = PUBLICKEYBLOB; pPubKeyStruc->bVersion = 3; pPubKeyStruc->aiKeyAlg = CALG_DH_SF; pCspPubKey->magic = DH3; pCspPubKey->bitlenP = cbP * 8; pCspPubKey->bitlenQ = cbQ * 8; pCspPubKey->bitlenJ = cbJ * 8; pCspPubKey->DSSSeed.counter = 0xFFFFFFFF; if (pX942DhPara->pValidationParams) { PCERT_X942_DH_VALIDATION_PARAMS pValidationParams; pValidationParams = pX942DhPara->pValidationParams; if (0 != pValidationParams->pgenCounter && sizeof(pCspPubKey->DSSSeed.seed) == pValidationParams->seed.cbData) { pCspPubKey->DSSSeed.counter = pValidationParams->pgenCounter; memcpy(pCspPubKey->DSSSeed.seed, pValidationParams->seed.pbData, sizeof(pCspPubKey->DSSSeed.seed)); } } // rgbP[cbP] memcpy(pbKey, pX942DhPara->p.pbData, cbP); pbKey += cbP; // rgbQ[cbQ] memcpy(pbKey, pX942DhPara->q.pbData, cbQ); pbKey += cbQ; // rgbG[cbP] cb = pX942DhPara->g.cbData; if (0 == cb || cb > cbP) goto ErrorReturn; memcpy(pbKey, pX942DhPara->g.pbData, cb); if (cbP > cb) memset(pbKey + cb, 0, cbP - cb); pbKey += cbP; // rgbJ[cbJ] if (cbJ) { memcpy(pbKey, pX942DhPara->j.pbData, cbJ); pbKey += cbJ; } fResult = TRUE; CommonReturn: *ppPubKeyStruc = pPubKeyStruc; *pcbPubKeyStruc = cbPubKeyStruc; return fResult; ErrorReturn: MakeCertFree(pPubKeyStruc); pPubKeyStruc = NULL; cbPubKeyStruc = 0; fResult = FALSE; goto CommonReturn; } static BOOL IsDh3Csp() { BOOL fResult; HCRYPTPROV hProv = 0; HCRYPTKEY hKey = 0; WCHAR *wszRegKeyName = NULL; UUID TmpContainerUuid; PCERT_X942_DH_PARAMETERS pX942DhPara = NULL; PCERT_PUBLIC_KEY_INFO pPubKeyInfo = NULL; DWORD cbPubKeyInfo; DWORD cbX942DhPara; // Create a temporary keyset to load the private key into if (CoCreateGuid((GUID *)&TmpContainerUuid) != S_OK) { goto CreateKeyError; } if (NULL == (wszRegKeyName = (LPWSTR) MakeCertAlloc (((sizeof(UUID) * 2 + 1) * sizeof(WCHAR))))) goto CreateKeyError; BytesToWStr(sizeof(UUID), &TmpContainerUuid, wszRegKeyName); // Open a new key container if (!CryptAcquireContextU( &hProv, wszRegKeyName, wszSubjectProviderName, dwSubjectProviderType, CRYPT_NEWKEYSET // dwFlags )) { hProv = 0; goto CreateKeyError; } // generate new DH key in the key container if (!CryptGenKey( hProv, AT_KEYEXCHANGE, (512 << 16) | CRYPT_EXPORTABLE, &hKey )) goto CreateKeyError; else CryptDestroyKey(hKey); cbPubKeyInfo = 0; CryptExportPublicKeyInfo( hProv, AT_KEYEXCHANGE, X509_ASN_ENCODING, NULL, // pPubKeyInfo &cbPubKeyInfo ); if (cbPubKeyInfo == 0) { PrintLastError(IDS_ERR_EXPORT_PUB); goto ErrorReturn; } if (NULL == (pPubKeyInfo = (PCERT_PUBLIC_KEY_INFO) MakeCertAlloc(cbPubKeyInfo))) goto ErrorReturn; if (!CryptExportPublicKeyInfo( hProv, AT_KEYEXCHANGE, X509_ASN_ENCODING, pPubKeyInfo, &cbPubKeyInfo )) { PrintLastError(IDS_ERR_EXPORT_PUB); goto ErrorReturn; } if (!DecodeAndAllocObject( X942_DH_PARAMETERS, pPubKeyInfo->Algorithm.Parameters.pbData, pPubKeyInfo->Algorithm.Parameters.cbData, (void **) &pX942DhPara, &cbX942DhPara )) goto CreateKeyError; if (pX942DhPara->q.cbData) // Q para is only supported in Dh3 version of CSP fResult = TRUE; else fResult = FALSE; CommonReturn: if (hProv) { // Delete the just created DH key CryptReleaseContext(hProv, 0); CryptAcquireContextU( &hProv, wszRegKeyName, wszSubjectProviderName, dwSubjectProviderType, CRYPT_DELETEKEYSET ); } MakeCertFree(wszRegKeyName); MakeCertFree(pX942DhPara); MakeCertFree(pPubKeyInfo); return fResult; CreateKeyError: IDSwprintf(hModule,IDS_ERR_SUB_KEY_CREATE, wszSubjectKey); ErrorReturn: fResult = FALSE; goto CommonReturn; } static BOOL GenDhKey( IN HCRYPTPROV hProv, IN DWORD dwFlags ) { BOOL fResult; HCRYPTKEY hKey = 0; PCERT_X942_DH_PARAMETERS pX942DhPara = NULL; PCERT_DSS_PARAMETERS pDssPara = NULL; PCRYPT_UINT_BLOB pP; PCRYPT_UINT_BLOB pG; DWORD cbP; PUBLICKEYSTRUC *pDh3PubKeyStruc = NULL; DWORD cbDh3PubKeyStruc; BOOL fSetKeyPara; if (wszDhParaCertFile) { if (!GetDhParaFromCertFile(&pX942DhPara)) goto ErrorReturn; CreateDh3PubKeyStruc(pX942DhPara, &pDh3PubKeyStruc, &cbDh3PubKeyStruc); pP = &pX942DhPara->p; pG = &pX942DhPara->g; } else if (dwKeyBitLen > 1024 || IsDh3Csp()) { // generate new keys in the key container if (!CryptGenKey( hProv, AT_KEYEXCHANGE, (dwKeyBitLen << 16) | dwFlags, &hKey )) { hKey = 0; goto CreateKeyError; } else goto SuccessReturn; } else { if (!GetDhParaFromDssKey(&pDssPara)) goto ErrorReturn; pP = &pDssPara->p; pG = &pDssPara->g; } cbP = pP->cbData; if (!CryptGenKey( hProv, CALG_DH_SF, ((cbP * 8) << 16) | CRYPT_PREGEN | dwFlags, &hKey)) { hKey = 0; goto CreateKeyError; } fSetKeyPara = FALSE; if (pDh3PubKeyStruc) { CRYPT_DATA_BLOB Dh3Blob; Dh3Blob.pbData = (PBYTE) pDh3PubKeyStruc; Dh3Blob.cbData = cbDh3PubKeyStruc; if (CryptSetKeyParam( hKey, KP_PUB_PARAMS, (PBYTE) &Dh3Blob, 0)) // dwFlags fSetKeyPara = TRUE; } if (!fSetKeyPara) { if (!CryptSetKeyParam( hKey, KP_P, (PBYTE) pP, 0)) // dwFlags goto CreateKeyError; // Note, the length of G can be less than length P. Pad with leading // zeroes in little endian form. if (pG->cbData < cbP) { DWORD cbG = pG->cbData; // We are done using P parameter. Overwrite with the G parameter and // pad with leading zeroes in little endian form. memcpy(pP->pbData, pG->pbData, cbG); memset(pP->pbData + cbG, 0, cbP - cbG); pG = pP; } if (!CryptSetKeyParam( hKey, KP_G, (PBYTE) pG, 0)) // dwFlags goto CreateKeyError; } if (!CryptSetKeyParam( hKey, KP_X, NULL, // pbData 0)) // dwFlags goto CreateKeyError; SuccessReturn: fResult = TRUE; CommonReturn: if (hKey) CryptDestroyKey(hKey); MakeCertFree(pDh3PubKeyStruc); MakeCertFree(pX942DhPara); MakeCertFree(pDssPara); return fResult; CreateKeyError: IDSwprintf(hModule,IDS_ERR_SUB_KEY_CREATE, wszSubjectKey); ErrorReturn: fResult = FALSE; goto CommonReturn; } static BOOL UpdateDhPublicKey( IN OUT PCERT_PUBLIC_KEY_INFO *ppPubKeyInfo ) { BOOL fResult; PCERT_PUBLIC_KEY_INFO pPubKeyInfo = *ppPubKeyInfo; PCERT_X942_DH_PARAMETERS pX942DhPara = NULL; DWORD cbDhPara; PCERT_X942_DH_PARAMETERS pX942DhParaCertFile = NULL; BYTE *pbReencodedPara = NULL; DWORD cbReencodedPara; BYTE *pbReencodedPubKeyInfo = NULL; DWORD cbReencodedPubKeyInfo; PCERT_PUBLIC_KEY_INFO pUpdatedPubKeyInfo = NULL; DWORD cbUpdatedPubKeyInfo; if (NULL == wszDhParaCertFile && 0 == DhQ.cbData) return TRUE; if (!DecodeAndAllocObject( X942_DH_PARAMETERS, pPubKeyInfo->Algorithm.Parameters.pbData, pPubKeyInfo->Algorithm.Parameters.cbData, (void **) &pX942DhPara, &cbDhPara )) { PrintLastError(IDS_ERR_EXPORT_PUB); goto ErrorReturn; } if (wszDhParaCertFile) { if (!GetDhParaFromCertFile(&pX942DhParaCertFile)) goto ErrorReturn; if (!CertCompareIntegerBlob(&pX942DhPara->p, &pX942DhParaCertFile->p)) goto DhParaCertFileError; if (!CertCompareIntegerBlob(&pX942DhPara->g, &pX942DhParaCertFile->g)) goto DhParaCertFileError; // Use Dh parameters from the CertFile MakeCertFree(pX942DhPara); pX942DhPara = pX942DhParaCertFile; pX942DhParaCertFile = NULL; } else if (pX942DhPara->q.cbData) { MakeCertFree(pX942DhPara); return TRUE; } else // Use Q parameter saved away when the DH key was generated pX942DhPara->q = DhQ; // Re-encode the DH parameters if (!EncodeAndAllocObject( X942_DH_PARAMETERS, pX942DhPara, &pbReencodedPara, &cbReencodedPara )) { PrintLastError(IDS_ERR_EXPORT_PUB); goto ErrorReturn; } if (0 == strcmp(szOID_RSA_DH, pPubKeyInfo->Algorithm.pszObjId)) pPubKeyInfo->Algorithm.pszObjId = szOID_ANSI_X942_DH; // Re-encode the PublicKeyInfo using the above re-encoded DH parameters pPubKeyInfo->Algorithm.Parameters.pbData = pbReencodedPara; pPubKeyInfo->Algorithm.Parameters.cbData = cbReencodedPara; if (!EncodeAndAllocObject( X509_PUBLIC_KEY_INFO, pPubKeyInfo, &pbReencodedPubKeyInfo, &cbReencodedPubKeyInfo )) { PrintLastError(IDS_ERR_EXPORT_PUB); goto ErrorReturn; } // Decode to get the updated public key info if (!DecodeAndAllocObject( X509_PUBLIC_KEY_INFO, pbReencodedPubKeyInfo, cbReencodedPubKeyInfo, (void **) &pUpdatedPubKeyInfo, &cbUpdatedPubKeyInfo )) { PrintLastError(IDS_ERR_EXPORT_PUB); goto ErrorReturn; } fResult = TRUE; CommonReturn: MakeCertFree(pbReencodedPubKeyInfo); MakeCertFree(pbReencodedPara); MakeCertFree(pX942DhPara); MakeCertFree(pX942DhParaCertFile); MakeCertFree(pPubKeyInfo); *ppPubKeyInfo = pUpdatedPubKeyInfo; return fResult; DhParaCertFileError: IDSwprintf(hModule, IDS_ERR_DH_PARA_FILE, wszDhParaCertFile); ErrorReturn: MakeCertFree(pUpdatedPubKeyInfo); pUpdatedPubKeyInfo = NULL; fResult = FALSE; goto CommonReturn; }