//+------------------------------------------------------------------------- // // Microsoft Windows // // Copyright (C) Microsoft Corporation, 1995 - 1999 // // File: certgen.cpp // //-------------------------------------------------------------------------- #include #pragma hdrstop #include #include "encode.h" #include "rsa.h" #include "md5.h" #include #include #include #include #include "csprop.h" #define MSTOSEC(ms) (((ms) + 1000 - 1)/1000) DWORD g_crdnMax; HCRYPTPROV g_hMe = NULL; WCHAR g_wszTestKey[] = L"CertGen_TestKey"; static unsigned char MD5_PRELUDE[] = { 0x30, 0x20, 0x30, 0x0c, 0x06, 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x05, 0x05, 0x00, 0x04, 0x10 }; BYTE g_CAPIPrivateKey[1000]; DWORD g_cbPrivateKey; //LPBSAFE_PRV_KEY g_pRSAPrivateKey; DWORD g_cbRSAPrivateKey; LPBSAFE_PUB_KEY g_pRSAPublicKey; DWORD g_cbRSAPublicKey; WCHAR *g_pwszConfig = NULL; typedef struct { DWORD magic; // Should always be RSA2 DWORD bitlen; // bit size of key DWORD pubexp; // public exponent } EXPORT_PRV_KEY; BOOL g_fRPC = FALSE; BOOL g_fRenewal = FALSE; BOOL g_fSave = FALSE; BOOL g_fPrintProperties = FALSE; BOOL g_fDebug = FALSE; BOOL g_fIgnoreAccessDenied = FALSE; BOOL g_fTime = FALSE; BOOL g_fIgnoreError = FALSE; BOOL g_fAllowDups = FALSE; BOOL g_fShowTime = FALSE; LONG g_IntervalCount; DWORD g_MaximumCount = MAXDWORD; DWORD g_DispatchFlags = DISPSETUP_COMFIRST; BOOL IsCharPrintableString(TCHAR chChar); WCHAR wszUsage[] = TEXT("Usage: CertGen [options]\n") TEXT("Options are:\n") TEXT(" -a - ignore denied requests\n") TEXT(" -c # - generate # certs\n") TEXT(" -config server\\CAName - specify CA config string\n") TEXT(" -renewal - generate renewal requests\n") TEXT(" -rpc - use RPC to connect to server\n") TEXT(" -r - put request/cert/chain info into test.req/test.crt/testchain.crt\n") TEXT(" -t # - print time statistics every # certs\n") TEXT(" -p - print properties from cert created\n") TEXT(" -i - don't stop on request errors\n") TEXT(" -z - allow duplicate subject name components\n") TEXT(" -m - print start/end time\n") ; HRESULT SeedRNG(void) { HRESULT hr; unsigned int seed; if (!CryptGenRandom(g_hMe, sizeof(seed), (BYTE *) &seed)) { hr = myHLastError(); _JumpError(hr, error, "CryptGenRandom"); } srand(seed); hr = S_OK; error: return(hr); } HRESULT GenerateString( DWORD cnt, BYTE *pbStr) { HRESULT hr; DWORD i; BYTE *pb; hr = SeedRNG(); _JumpIfError(hr, error, "SeedRNG"); pb = pbStr; for (i = 0; i < cnt; i++) { do { *pb = rand() % 0x7f; } while (!IsCharPrintableString(*pb)); pb++; } *pb = '\0'; // Turn leading and trailing Blanks into '.' characters? if (g_fAllowDups && 0 < cnt) { if (' ' == *pbStr) { *pbStr = '.'; } pb--; if (' ' == *pb) { *pb = '.'; } } error: return(hr); } void FreeLocalMemory( NAMETABLE *pNameTable) { NAMEENTRY *pNameEntry = NULL; DWORD i; pNameEntry = pNameTable->pNameEntry; for (i = 0; i < pNameTable->cnt; i++) { if (NULL != pNameEntry->pbData) { LocalFree(pNameEntry->pbData); } pNameEntry++; } LocalFree(pNameTable->pNameEntry); } HRESULT GenerateNameTable( NAMETABLE *pNameTable) { HRESULT hr; NAMEENTRY *pNameEntryAlloc = NULL; NAMEENTRY *pNameEntry; DWORD cbString; BYTE *pbString; DWORD i; DWORD j; DWORD cRetry; hr = SeedRNG(); _JumpIfError(hr, error, "SeedRNG"); pNameTable->cnt = rand() % g_crdnMax; // 0 is Ok if (1 < g_fPrintProperties) { wprintf(L"NumEntries = %u\n", pNameTable->cnt); } for (i = 0; i < g_crdnSubject; i++) { g_ardnSubject[i].cbRemain = g_ardnSubject[i].cbMaxConcatenated; } pNameEntryAlloc = (NAMEENTRY *) LocalAlloc( LMEM_FIXED | LMEM_ZEROINIT, pNameTable->cnt * sizeof(NAMEENTRY)); if (NULL == pNameEntryAlloc) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "LocalAlloc"); } pNameTable->pNameEntry = pNameEntryAlloc; for (i = 0; i < pNameTable->cnt; i++) { RDNENTRY *prdne; pNameEntry = &pNameTable->pNameEntry[i]; for (cRetry = 0; !g_fAllowDups || cRetry < 2 * pNameTable->cnt; cRetry++) { pNameEntry->iRDN = rand() % g_crdnSubject; prdne = &g_ardnSubject[pNameEntry->iRDN]; if (g_fAllowDups) { if (2 > prdne->cbRemain) { continue; // Skip if less than 2 characters left } } else { for (j = 0; j < i; j++) { if (pNameEntry->iRDN == pNameTable->pNameEntry[j].iRDN) { break; } } if (j < i) { continue; // Skip if a disallowed duplicate } } break; } if (g_fAllowDups && cRetry >= 2 * pNameTable->cnt) { if (1 < g_fPrintProperties) { wprintf(L"Reducing NumEntries = %u --> %i\n", pNameTable->cnt, i); } pNameTable->cnt = i; // too many retries -- reduce count & quit break; } pNameEntry->pszObjId = prdne->pszObjId; pNameEntry->BerTag = prdne->BerTag; assert(2 <= prdne->cbRemain); do { cbString = rand() % min(prdne->cbMaxString, prdne->cbRemain); } while (0 == cbString); // Reduce remaining count by length of string plus separator: "\n" if (1 < g_fPrintProperties) { wprintf( L" RDN(%u): %hs=%u/%u/%u/", i, prdne->pszShortName, cbString, prdne->cbMaxString, prdne->cbRemain); } prdne->cbRemain -= cbString; if (0 < prdne->cbRemain) { prdne->cbRemain--; } // Limit each string to (prdne->cbMaxString + 1) chars, including // trailing '\0': assert(cbString <= prdne->cbMaxString); // leave room for '\0' in DB pbString = (BYTE *) LocalAlloc(LMEM_FIXED, cbString + 1); if (NULL == pbString) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "LocalAlloc"); } hr = GenerateString(cbString, pbString); _JumpIfError(hr, error, "GenerateString"); if (1 < g_fPrintProperties) { wprintf(L"%u: \"%hs\"\n", prdne->cbRemain, pbString); } pNameEntry->cbData = cbString; pNameEntry->pbData = pbString; } pNameEntryAlloc = NULL; error: if (NULL != pNameEntryAlloc) { FreeLocalMemory(pNameTable); } return(hr); } HRESULT GenerateTestNameTable( NAMETABLE *pNameTable) { HRESULT hr; NAMEENTRY *pNameEntryAlloc = NULL; NAMEENTRY *pNameEntry; DWORD cbString; BYTE *pbString; DWORD i; DWORD j; char szTest[2]; pNameEntryAlloc = (NAMEENTRY *) LocalAlloc( LMEM_FIXED | LMEM_ZEROINIT, sizeof(NAMEENTRY) * g_crdnSubject); if (NULL == pNameEntryAlloc) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "LocalAlloc"); } pNameTable->cnt = g_crdnSubject; pNameTable->pNameEntry = pNameEntryAlloc; szTest[0] = 'a'; szTest[1] = '\0'; for (i = 0; i < g_crdnSubject; i++) { pNameEntry = &pNameTable->pNameEntry[i]; pNameEntry->pszObjId = g_ardnSubject[i].pszObjId; pNameEntry->BerTag = g_ardnSubject[i].BerTag; pbString = (BYTE *) LocalAlloc(LMEM_FIXED, sizeof(szTest)); if (NULL == pbString) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "LocalAlloc"); } CopyMemory(pbString, szTest, sizeof(szTest)); pNameEntry->cbData = sizeof(szTest) - 1; pNameEntry->pbData = pbString; if ('z' == szTest[0]) { szTest[0] = 'a'; } else { szTest[0]++; } } pNameEntryAlloc = NULL; hr = S_OK; error: if (NULL != pNameEntryAlloc) { FreeLocalMemory(pNameTable); } return(hr); } BOOL PreparePrivateKeyForImport( IN BYTE *pbBlob, IN DWORD cbBlob, OUT BSAFE_PRV_KEY *pPriKey, IN OUT DWORD *pcbPriKey, OUT BSAFE_PUB_KEY *pPubKey, IN OUT DWORD *pcbPubKey) { EXPORT_PRV_KEY *pExportKey = (EXPORT_PRV_KEY *) pbBlob; DWORD cbHalfModLen; DWORD cbPub; DWORD cbPri; BYTE *pbIn; BYTE *pbOut; if (RSA2 != pExportKey->magic) { return(FALSE); } cbHalfModLen = pExportKey->bitlen / 16; cbPub = sizeof(BSAFE_PUB_KEY) + (cbHalfModLen + sizeof(DWORD)) * 2; cbPri = sizeof(BSAFE_PRV_KEY) + (cbHalfModLen + sizeof(DWORD)) * 10; if (NULL == pPriKey || NULL == pPubKey) { *pcbPubKey = cbPub; *pcbPriKey = cbPri; return(TRUE); } if (*pcbPubKey < cbPub || *pcbPriKey < cbPri) { *pcbPubKey = cbPub; *pcbPriKey = cbPri; return(FALSE); } else { // form the public key ZeroMemory(pPubKey, *pcbPubKey); pPubKey->magic = RSA1; pPubKey->keylen = (cbHalfModLen + sizeof(DWORD)) * 2; pPubKey->bitlen = pExportKey->bitlen; pPubKey->datalen = cbHalfModLen * 2 - 1; pPubKey->pubexp = pExportKey->pubexp; pbIn = pbBlob + sizeof(EXPORT_PRV_KEY); pbOut = (BYTE *) pPubKey + sizeof(BSAFE_PUB_KEY); CopyMemory(pbOut, pbIn, cbHalfModLen * 2); // form the private key ZeroMemory(pPriKey, *pcbPriKey); pPriKey->magic = pExportKey->magic; pPriKey->keylen = (cbHalfModLen + sizeof(DWORD)) * 2; pPriKey->bitlen = pExportKey->bitlen; pPriKey->datalen = cbHalfModLen * 2 - 1; pPriKey->pubexp = pExportKey->pubexp; pbOut = (BYTE *) pPriKey + sizeof(BSAFE_PRV_KEY); CopyMemory(pbOut, pbIn, cbHalfModLen * 2); pbOut += (cbHalfModLen + sizeof(DWORD)) * 2; pbIn += cbHalfModLen * 2; CopyMemory(pbOut, pbIn, cbHalfModLen); pbOut += cbHalfModLen + sizeof(DWORD); pbIn += cbHalfModLen; CopyMemory(pbOut, pbIn, cbHalfModLen); pbOut += cbHalfModLen + sizeof(DWORD); pbIn += cbHalfModLen; CopyMemory(pbOut, pbIn, cbHalfModLen); pbOut += cbHalfModLen + sizeof(DWORD); pbIn += cbHalfModLen; CopyMemory(pbOut, pbIn, cbHalfModLen); pbOut += cbHalfModLen + sizeof(DWORD); pbIn += cbHalfModLen; CopyMemory(pbOut, pbIn, cbHalfModLen); pbOut += cbHalfModLen + sizeof(DWORD); pbIn += cbHalfModLen; CopyMemory(pbOut, pbIn, cbHalfModLen * 2); } *pcbPubKey = cbPub; *pcbPriKey = cbPri; return(TRUE); } HRESULT GetPrivateKeyStuff( PctPrivateKey **ppKey) { HRESULT hr; BYTE *pbData; PctPrivateKey *pKey = NULL; HCRYPTKEY hKey = NULL; if (!CryptAcquireContext( &g_hMe, g_wszTestKey, MS_DEF_PROV, PROV_RSA_FULL, CRYPT_DELETEKEYSET)) { hr = myHLastError(); _PrintError(hr, "CryptAcquireContext"); } if (!CryptAcquireContext( &g_hMe, g_wszTestKey, MS_DEF_PROV, PROV_RSA_FULL, CRYPT_NEWKEYSET)) { hr = myHLastError(); _JumpError(hr, error, "CryptAcquireContext"); } if (!CryptGetUserKey(g_hMe, AT_SIGNATURE, &hKey)) { hr = myHLastError(); _PrintError2(hr, "CryptGetUserKey", hr); if (!CryptGenKey(g_hMe, AT_SIGNATURE, CRYPT_EXPORTABLE, &hKey)) { hr = myHLastError(); _JumpError(hr, error, "CryptGenKey"); } } g_cbPrivateKey = sizeof(g_CAPIPrivateKey); if (!CryptExportKey( hKey, 0, PRIVATEKEYBLOB, 0L, &g_CAPIPrivateKey[0], &g_cbPrivateKey)) { hr = myHLastError(); _JumpError(hr, error, "CryptExportKey"); } pbData = &g_CAPIPrivateKey[sizeof(BLOBHEADER)]; if (!PreparePrivateKeyForImport( pbData, g_cbPrivateKey - sizeof(BLOBHEADER), NULL, &g_cbRSAPrivateKey, NULL, &g_cbRSAPublicKey)) { hr = NTE_BAD_KEY; _JumpError(hr, error, "PreparePrivateKeyForImport"); } pKey = (PctPrivateKey *) LocalAlloc( LMEM_FIXED, g_cbRSAPrivateKey + sizeof(PctPrivateKey)); g_pRSAPublicKey = (BSAFE_PUB_KEY *) LocalAlloc( LMEM_FIXED, g_cbRSAPublicKey); if (pKey == NULL || g_pRSAPublicKey == NULL) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "LocalAlloc"); } pKey->cbKey = g_cbRSAPrivateKey; if (!PreparePrivateKeyForImport( pbData, g_cbPrivateKey - sizeof(BLOBHEADER), (BSAFE_PRV_KEY *) pKey->pKey, &g_cbRSAPrivateKey, g_pRSAPublicKey, &g_cbRSAPublicKey)) { hr = NTE_BAD_KEY; _JumpError(hr, error, "PreparePrivateKeyForImport"); } hr = S_OK; error: if (NULL != hKey) { CryptDestroyKey(hKey); } *ppKey = pKey; return(hr); } VOID ReverseMemCopy( OUT BYTE *pbDest, IN BYTE const *pbSource, IN DWORD cb) { BYTE *pb; pb = pbDest + cb - 1; do { *pb-- = *pbSource++; } while (pb >= pbDest); } BOOL WINAPI SigRSAMD5Sign( IN BYTE *pbData, IN DWORD cbData, OUT BYTE *pbSigned, OUT DWORD *pcbSigned, IN PctPrivateKey const *pKey) { MD5_CTX DigCtx; BSAFE_PRV_KEY *pk = (BSAFE_PRV_KEY *) pKey->pKey; BYTE LocalBuffer[300]; BYTE LocalOutput[300]; DWORD cb; //DumpHex(pbData, cbData); if (pk->datalen > sizeof(LocalBuffer)) { return(FALSE); } // Generate the checksum MD5Init(&DigCtx); MD5Update(&DigCtx, pbData, cbData); MD5Final(&DigCtx); FillMemory(LocalBuffer, pk->keylen, 0); ReverseMemCopy(LocalBuffer, DigCtx.digest, 16); ReverseMemCopy(LocalBuffer + 16, MD5_PRELUDE, sizeof(MD5_PRELUDE)); cb = sizeof(MD5_PRELUDE) + 16; LocalBuffer[cb++] = 0; while (cb < pk->datalen - 1) { LocalBuffer[cb++] = 0xff; } // Make into pkcs block type 1 LocalBuffer[pk->datalen - 1] = 1; *pcbSigned = pk->datalen + 1; if (!BSafeDecPrivate(pk, LocalBuffer, LocalOutput)) { return(FALSE); } ReverseMemCopy(pbSigned, LocalOutput, *pcbSigned); //DumpHex(pbSigned, *pcbSigned); return(TRUE); } long EncodeSubjectPubKeyInfo( IN PctPrivateKey const *pKey, OUT BYTE *pbBuffer) { BYTE *pbEncoded; LONG cbResult; LONG cbResultHeader; LONG PkResult; LONG PkResultHeader; BYTE *pbSave; BYTE *pbBitString; BYTE *pbBitStringBase; BYTE *pbTop; DWORD EstimatedLength; BSAFE_PRV_KEY *pk = (BSAFE_PRV_KEY *) pKey->pKey; // Encode public key now... EstimatedLength = pk->datalen + 32; pbEncoded = pbBuffer; cbResultHeader = EncodeHeader(pbEncoded, EstimatedLength); pbEncoded += cbResultHeader; pbTop = pbEncoded; cbResult = EncodeAlgorithm(pbEncoded, ALGTYPE_KEYEXCH_RSA_MD5); if (0 > cbResult) { return(-1); } pbEncoded += cbResult; // now, serialize the rsa key data: pbBitString = (BYTE *) LocalAlloc(LMEM_FIXED, EstimatedLength); if (NULL == pbBitString) { return(-1); } pbBitStringBase = pbBitString; // Encode the Sequence header, public key base and exponent as integers PkResultHeader = EncodeHeader(pbBitString, EstimatedLength); pbBitString += PkResultHeader; pbSave = pbBitString; PkResult = EncodeInteger(pbBitString, (BYTE *) (pk + 1), pk->keylen); pbBitString += PkResult; PkResult = EncodeInteger(pbBitString, (BYTE *) &pk->pubexp, sizeof(DWORD)); pbBitString += PkResult; // Rewrite the bitstring header with an accurate length. PkResult = EncodeHeader( pbBitStringBase, SAFE_SUBTRACT_POINTERS(pbBitString, pbSave)); // Encode the public key sequence as a raw bitstring, and free the memory. cbResult = EncodeBitString( pbEncoded, pbBitStringBase, SAFE_SUBTRACT_POINTERS(pbBitString, pbBitStringBase)); pbEncoded += cbResult; LocalFree(pbBitStringBase); // Rewrite the header with an accurate length. cbResult = EncodeHeader(pbBuffer, SAFE_SUBTRACT_POINTERS(pbEncoded, pbTop)); return(cbResult + SAFE_SUBTRACT_POINTERS(pbEncoded, pbTop)); } #if 0 a0 BER_OPTIONAL | 0 -- Request Attributes 30 BER_SEQUENCE 06 BER_OBJECT_ID -- szOID_CERT_EXTENSIONS 31 BER_SET 30 BER_SEQUENCE 30 BER_SEQUENCE (extension[0]) 06 BER_OBJECT_ID 01 BER_BOOL (Optional) 04 BER_OCTET_STRING 30 BER_SEQUENCE (extension[1]) 06 BER_OBJECT_ID 04 BER_OCTET_STRING 30 BER_SEQUENCE (extension[2]) 06 BER_OBJECT_ID 04 BER_OCTET_STRING #endif long AllocEncodeUnicodeString( IN WCHAR const *pwszCertType, OUT BYTE **ppbOut) { BYTE *pb = NULL; LONG cb; cb = EncodeUnicodeString(NULL, pwszCertType); pb = (BYTE *) LocalAlloc(LMEM_FIXED, cb); if (NULL == pb) { cb = -1; goto error; } *ppbOut = pb; EncodeUnicodeString(pb, pwszCertType); error: return(cb); } long AllocEncodeExtensionArray( IN DWORD cExt, IN CERT_EXTENSION const *aExt, OUT BYTE **ppbExtensions) { BYTE *pb; DWORD i; LONG cb; LONG cbExtTotal; LONG acbLen[3]; LONG *acbExt = NULL; *ppbExtensions = NULL; acbExt = (LONG *) LocalAlloc(LMEM_FIXED, cExt * sizeof(acbExt[0])); if (NULL == acbExt) { cbExtTotal = -1; _JumpError(-1, error, "LocalAlloc"); } // Construct size from the bottom up. cbExtTotal = 0; for (i = 0; i < cExt; i++) { // BER_OBJECT_ID: Extension OID cb = EncodeObjId(NULL, aExt[i].pszObjId); if (-1 == cb) { _JumpError(-1, error, "EncodeObjId"); } acbExt[i] = cb; if (aExt[i].fCritical) { // BER_BOOL: fCritical acbExt[i] += 1 + EncodeLength(NULL, 1); acbExt[i]++; // boolean value } // BER_OCTET_STRING: Extension octet string value acbExt[i] += 1 + EncodeLength(NULL, aExt[i].Value.cbData); acbExt[i] += aExt[i].Value.cbData; // octet string // BER_SEQUENCE: Extension Sequence cbExtTotal += 1 + EncodeLength(NULL, acbExt[i]); cbExtTotal += acbExt[i]; } // BER_SEQUENCE: Extension Array Sequence acbLen[2] = cbExtTotal; cbExtTotal += 1 + EncodeLength(NULL, cbExtTotal); // BER_SET: Attribute Value acbLen[1] = cbExtTotal; cbExtTotal += 1 + EncodeLength(NULL, cbExtTotal); // BER_OBJECT_ID: Attribute OID cb = EncodeObjId(NULL, szOID_CERT_EXTENSIONS); if (-1 == cb) { _JumpError(-1, error, "EncodeObjId"); } cbExtTotal += cb; // BER_SEQUENCE: Attribute Array Sequence acbLen[0] = cbExtTotal; cbExtTotal += 1 + EncodeLength(NULL, cbExtTotal); // Allocate memory and encode the extensions pb = (BYTE *) LocalAlloc(LMEM_FIXED, cbExtTotal); if (NULL == pb) { cbExtTotal = -1; _JumpError(-1, error, "LocalAlloc"); } *ppbExtensions = pb; *pb++ = BER_SEQUENCE; // Attribute Array Sequence pb += EncodeLength(pb, acbLen[0]); pb += EncodeObjId(pb, szOID_CERT_EXTENSIONS); *pb++ = BER_SET; // Attribute Value pb += EncodeLength(pb, acbLen[1]); *pb++ = BER_SEQUENCE; // Extension Array Sequence pb += EncodeLength(pb, acbLen[2]); CSASSERT(*ppbExtensions + cbExtTotal >= pb); for (i = 0; i < cExt; i++) { CSASSERT(*ppbExtensions + cbExtTotal > pb); *pb++ = BER_SEQUENCE; // Extension Sequence pb += EncodeLength(pb, acbExt[i]); // BER_OBJECT_ID: Extension OID pb += EncodeObjId(pb, aExt[i].pszObjId); if (aExt[i].fCritical) { *pb++ = BER_BOOL; // fCritical pb += EncodeLength(pb, 1); *pb++ = 0xff; } *pb++ = BER_OCTET_STRING; // Extension octet string value pb += EncodeLength(pb, aExt[i].Value.cbData); CopyMemory(pb, aExt[i].Value.pbData, aExt[i].Value.cbData); pb += aExt[i].Value.cbData; } CSASSERT(*ppbExtensions + cbExtTotal == pb); error: if (NULL != acbExt) { LocalFree(acbExt); } return(cbExtTotal); } long EncodeExtensions( IN WCHAR const *pwszCertType, OUT BYTE **ppbExtensions) { LONG cbExt; BYTE *pbExt = NULL; CERT_EXTENSION aExt[1]; DWORD cExt = 0; DWORD i; // Allocate memory and construct the CertType extension: aExt[cExt].pszObjId = szOID_ENROLL_CERTTYPE_EXTENSION; aExt[cExt].fCritical = FALSE; aExt[cExt].Value.cbData = AllocEncodeUnicodeString( pwszCertType, &aExt[cExt].Value.pbData); //DumpHex(aExt[cExt].Value.pbData, aExt[cExt].Value.cbData); cExt++; cbExt = AllocEncodeExtensionArray(cExt, aExt, ppbExtensions); if (-1 == cbExt) { _JumpError(-1, error, "AllocEncodeExtensionArray"); } error: for (i = 0; i < cExt; i++) { if (NULL != aExt[i].Value.pbData) { LocalFree(aExt[i].Value.pbData); } } return(cbExt); } HRESULT EncodeRequest( IN PctPrivateKey const *pKey, IN NAMETABLE const *pNameTable, OUT BYTE **ppbRequest, OUT DWORD *pcbRequest) { HRESULT hr; BYTE *pbRequest0Alloc = NULL; BYTE *pbRequest1Alloc = NULL; BYTE *pbSigAlloc = NULL; BYTE *pbRequest0; BYTE *pbSave; BYTE *pbEncoded; BYTE *pbExt; LONG cbExt; LONG cbResult; LONG cbEncoded; BYTE bZero; LONG cbDN; DWORD cbRequest0; DWORD cbRequest1; LONG cbLenRequest; BSAFE_PRV_KEY *pk = (BSAFE_PRV_KEY *) pKey->pKey; cbExt = EncodeExtensions(L"User", &pbExt); if (-1 == cbExt) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); _JumpError(hr, error, "EncodeExtensions"); } //DumpHex(pbExt, cbExt); cbDN = EncodeDN(NULL, pNameTable); if (-1 == cbDN) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); _JumpError(hr, error, "EncodeDN"); } cbRequest0 = pk->datalen + 32 + cbDN + 16 + cbExt + 3; pbRequest0Alloc = (BYTE *) LocalAlloc(LMEM_FIXED, cbRequest0); if (NULL == pbRequest0Alloc) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "LocalAlloc"); } pbRequest0 = pbRequest0Alloc; pbEncoded = pbRequest0; // Encode BER_SEQUENCE: Version+Subject+Key+Attributes Sequence cbLenRequest = EncodeHeader(pbEncoded, cbRequest0); pbEncoded += cbLenRequest; pbSave = pbEncoded; // Save pointer past sequence length // Encode integer 0: Version 1 PKCS10 bZero = (BYTE) CERT_REQUEST_V1; pbEncoded += EncodeInteger(pbEncoded, &bZero, sizeof(bZero)); // Encode sequence of names cbResult = EncodeDN(pbEncoded, pNameTable); if (0 > cbResult) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); _JumpError(hr, error, "EncodeDN"); } pbEncoded += cbResult; cbResult = EncodeSubjectPubKeyInfo(pKey, pbEncoded); if (0 > cbResult) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); _JumpError(hr, error, "EncodeSubjectPubKeyInfo"); } pbEncoded += cbResult; // Encode attributes: // BER_OPTIONAL | 0: Attribute Field cbResult = EncodeAttributeHeader(pbEncoded, cbExt); pbEncoded += cbResult; CopyMemory(pbEncoded, pbExt, cbExt); pbEncoded += cbExt; // Encode BER_SEQUENCE: Version+Subject+Key+Attributes Sequence (again) cbEncoded = SAFE_SUBTRACT_POINTERS(pbEncoded, pbSave); cbResult = EncodeHeader(pbRequest0, cbEncoded); // If the header sequence length takes up less space than we anticipated, // add the difference to the base pointer and encode the header again, // right before the encoded data. if (cbResult != cbLenRequest) { CSASSERT(cbResult < cbLenRequest); pbRequest0 += cbLenRequest - cbResult; // Encode BER_SEQUENCE: Version+Subject+Key+Attributes Sequence (again) cbResult = EncodeHeader(pbRequest0, cbEncoded); } cbRequest0 = cbResult + SAFE_SUBTRACT_POINTERS(pbEncoded, pbSave); //DumpHex(pbRequest0, cbRequest0); // How much space do we need? cbRequest1 = cbRequest0 + pk->datalen + 32; pbRequest1Alloc = (BYTE *) LocalAlloc(LMEM_FIXED, cbRequest1); if (NULL == pbRequest1Alloc) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "LocalAlloc"); } pbEncoded = pbRequest1Alloc; // Encode BER_SEQUENCE: outer Request Sequence cbLenRequest = EncodeHeader(pbEncoded, cbRequest1); pbEncoded += cbLenRequest; pbSave = pbEncoded; // Save pointer past outer sequence length CopyMemory(pbEncoded, pbRequest0, cbRequest0); pbEncoded += cbRequest0; cbResult = EncodeAlgorithm(pbEncoded, ALGTYPE_SIG_RSA_MD5); pbEncoded += cbResult; //DumpHex(pbRequest1Alloc, cbRequest1); cbResult = pk->datalen + 16; pbSigAlloc = (BYTE *) LocalAlloc(LMEM_FIXED, cbResult); if (NULL == pbSigAlloc) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "LocalAlloc"); } if (!SigRSAMD5Sign( pbRequest0, cbRequest0, pbSigAlloc, (DWORD *) &cbResult, pKey)) { hr = E_FAIL; _JumpError(hr, error, "SigRSAMD5Sign"); } pbEncoded += EncodeBitString(pbEncoded, pbSigAlloc, cbResult); cbEncoded = SAFE_SUBTRACT_POINTERS(pbEncoded, pbSave); cbResult = EncodeHeader(pbRequest1Alloc, cbEncoded); cbRequest1 = cbResult + cbEncoded; if (cbResult != cbLenRequest) { if (cbResult > cbLenRequest) { // The chunk has actually grown from the estimate. BYTE *pbT; pbT = (BYTE *) LocalAlloc(LMEM_FIXED, cbRequest1); if (NULL == pbT) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "LocalAlloc"); } EncodeHeader(pbT, cbEncoded); CopyMemory( pbT + cbResult, pbSave, cbEncoded); LocalFree(pbRequest1Alloc); pbRequest1Alloc = pbT; } else { cbResult = EncodeHeader(pbRequest1Alloc, cbEncoded); MoveMemory( pbRequest1Alloc + cbResult, pbRequest1Alloc + cbLenRequest, cbEncoded); } } *ppbRequest = pbRequest1Alloc; *pcbRequest = cbRequest1; pbRequest1Alloc = NULL; //DumpHex(*ppbRequest, *pcbRequest); hr = S_OK; error: if (NULL != pbSigAlloc) { LocalFree(pbSigAlloc); } if (NULL != pbRequest1Alloc) { LocalFree(pbRequest1Alloc); } if (NULL != pbRequest0Alloc) { LocalFree(pbRequest0Alloc); } if (NULL != pbExt) { LocalFree(pbExt); } return(hr); } HRESULT GetAndCompareProperty( CERT_NAME_INFO *pNameInfo, char const *pszObjId, char const *pszValue, BYTE **pbProp, DWORD *pcbProp, BOOL *pfMatch) { HRESULT hr; CERT_RDN_ATTR *prdnaT; CERT_RDN *prdn; CERT_RDN *prdnEnd; *pfMatch = FALSE; prdnaT = NULL; for ( prdn = pNameInfo->rgRDN, prdnEnd = &prdn[pNameInfo->cRDN]; prdn < prdnEnd; prdn++) { CERT_RDN_ATTR *prdna; CERT_RDN_ATTR *prdnaEnd; for ( prdna = prdn->rgRDNAttr, prdnaEnd = &prdna[prdn->cRDNAttr]; prdna < prdnaEnd; prdna++) { if (0 == strcmp(prdna->pszObjId, pszObjId)) { prdnaT = prdna; if (prdnaT->Value.cbData == strlen(pszValue) && 0 == memcmp(pszValue, prdnaT->Value.pbData, prdnaT->Value.cbData)) { *pfMatch = TRUE; } else if (g_fAllowDups) { continue; } prdn = prdnEnd; // exit outer for loop, too. break; } } } if (NULL == prdnaT) { hr = CERTSRV_E_PROPERTY_EMPTY; _JumpError2(hr, error, "Missing Property", CERTSRV_E_PROPERTY_EMPTY); } *pbProp = prdnaT->Value.pbData; *pcbProp = prdnaT->Value.cbData; hr = S_OK; error: return(hr); } HRESULT CheckProperties( DWORD ReqId, NAMETABLE *pNameTable, DWORD CertNumber, CERT_NAME_INFO *pNameInfo) { HRESULT hr; BYTE *pbProp; DWORD cbProp; DWORD i; DWORD dwCount = 0; BOOL fMatch; if (g_fPrintProperties) { wprintf( L"Properties for Certificate %u, RequestId %u:\n", CertNumber, ReqId); } for (i = 0; i < pNameTable->cnt; i++) { NAMEENTRY *pNameEntry; RDNENTRY *prdn; pNameEntry = &pNameTable->pNameEntry[i]; prdn = &g_ardnSubject[pNameEntry->iRDN]; hr = GetAndCompareProperty( pNameInfo, prdn->pszObjId, (char const *) pNameEntry->pbData, &pbProp, &cbProp, &fMatch); if (CERTSRV_E_PROPERTY_EMPTY == hr) { //_PrintError(hr, "GetAndCompareProperty"); pbProp = NULL; hr = S_OK; } _JumpIfError(hr, error, "GetAndCompareProperty"); if (NULL != pbProp && !fMatch) { wprintf( L"Property doesn't match: Expected %hs=\"%hs\", pbProp = \"%hs\"\n", prdn->pszShortName, pNameEntry->pbData, pbProp); hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); _JumpError(hr, error, "GetAndCompareProperty: no match"); } if (g_fPrintProperties) { DWORD ccol; #define CCOL_OID 10 ccol = strlen(prdn->pszObjId) + 1; if (ccol < CCOL_OID) { ccol = CCOL_OID - ccol; } else { ccol = 0; } wprintf( L" %u: %hs: %*s%hs=%hs%hs%hs\n", i, prdn->pszObjId, ccol, "", prdn->pszShortName, NULL == pbProp? "" : "\"", NULL == pbProp? " -- MISSING --" : (char const *) pbProp, NULL == pbProp? "" : "\""); } } if (g_fPrintProperties) { wprintf(L"\n"); } hr = S_OK; error: return(hr); } HRESULT EncodeRenewal( IN BYTE const *pbRequest, IN DWORD cbRequest, OUT BYTE **ppbRenewal, OUT DWORD *pcbRenewal) { HRESULT hr; *ppbRenewal = (BYTE *) LocalAlloc(LMEM_FIXED, cbRequest); if (NULL == *ppbRenewal) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "LocalAlloc"); } CopyMemory(*ppbRenewal, pbRequest, cbRequest); *pcbRenewal = cbRequest; hr = S_OK; error: return(hr); } HRESULT SubmitRequest( OPTIONAL IN DISPATCHINTERFACE *pdiRequest, IN DWORD Flags, IN BYTE const *pbRequest, IN DWORD cbRequest, IN WCHAR const *pwszAttributes, IN WCHAR const *pwszConfig, OUT DWORD *pRequestIdOut, OUT DWORD *pDisposition, OUT HRESULT *phrLastStatus, OUT WCHAR **ppwszDisposition, OUT BYTE **ppbCert, OUT DWORD *pcbCert) { HRESULT hr; WCHAR *pwszRequest = NULL; BSTR strCert = NULL; BSTR strCertChain = NULL; BSTR strDisposition = NULL; BYTE *pbCert = NULL; DWORD cbCert; BYTE const *pbChain; DWORD cbChain; CERTSERVERENROLL *pcsEnroll = NULL; WCHAR *pwszServer = NULL; WCHAR *pwszAuthority = NULL; WCHAR *pwszDisposition; *phrLastStatus = S_OK; *ppwszDisposition = NULL; *ppbCert = NULL; *pRequestIdOut = 0; if (NULL == pdiRequest) { hr = mySplitConfigString(pwszConfig, &pwszServer, &pwszAuthority); _JumpIfError(hr, error, "mySplitConfigString"); // CertServerSubmitRequest can only handle binary requests; // pass the request in binary form, and pass Flags to so indicate. hr = CertServerSubmitRequest( CR_IN_BINARY | Flags, pbRequest, cbRequest, pwszAttributes, pwszServer, pwszAuthority, &pcsEnroll); _JumpIfError(hr, error, "CertServerSubmitRequest"); *phrLastStatus = pcsEnroll->hrLastStatus; _PrintIfError2( *phrLastStatus, "pcsEnroll->hrLastStatus Real Status", HRESULT_FROM_WIN32(ERROR_INVALID_DATA)); pwszDisposition = pcsEnroll->pwszDispositionMessage; *pDisposition = pcsEnroll->Disposition; *pRequestIdOut = pcsEnroll->RequestId; } else { hr = myCryptBinaryToString( pbRequest, cbRequest, CRYPT_STRING_BASE64REQUESTHEADER, &pwszRequest); _JumpIfError(hr, error, "myCryptBinaryToString"); if (g_fRPC) { Flags |= CR_IN_RPC; } hr = Request_Submit( pdiRequest, CR_IN_BASE64HEADER | Flags, pwszRequest, sizeof(WCHAR) * wcslen(pwszRequest), pwszAttributes, pwszConfig, (LONG *) pDisposition); if (S_OK != hr) { _PrintError2( hr, "Request_Submit", HRESULT_FROM_WIN32(ERROR_INVALID_DATA)); // Collect the RequestId for potential error reporting: Request_GetRequestId(pdiRequest, (LONG *) pRequestIdOut); hr = Request_GetLastStatus(pdiRequest, phrLastStatus); _JumpIfError(hr, error, "Request_GetLastStatus"); if (FAILED(*phrLastStatus)) { hr = *phrLastStatus; } _JumpError2( hr, error, "Request_GetLastStatus Real Status", HRESULT_FROM_WIN32(ERROR_INVALID_DATA)); } hr = Request_GetLastStatus(pdiRequest, phrLastStatus); _JumpIfError(hr, error, "Request_GetLastStatus"); _PrintIfError(*phrLastStatus, "Request_GetLastStatus Real Status"); hr = Request_GetDispositionMessage(pdiRequest, &strDisposition); _JumpIfError(hr, error, "Request_GetDispositionMessage"); hr = Request_GetRequestId(pdiRequest, (LONG *) pRequestIdOut); _JumpIfError(hr, error, "Request_GetrequestId"); pwszDisposition = strDisposition; } if (CR_DISP_ISSUED == *pDisposition) { if (NULL == pdiRequest) { cbCert = pcsEnroll->cbCert; pbCert = (BYTE *) LocalAlloc(LMEM_FIXED, cbCert); if (NULL == pbCert) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "LocalAlloc"); } CopyMemory(pbCert, pcsEnroll->pbCert, cbCert); } else { hr = Request_GetCertificate( pdiRequest, CR_OUT_BASE64HEADER, &strCert); _JumpIfError(hr, error, "Request_GetCertificate"); hr = myCryptStringToBinary( strCert, wcslen(strCert), CRYPT_STRING_BASE64HEADER, &pbCert, &cbCert, NULL, NULL); _JumpIfError(hr, error, "myCryptStringToBinary"); } if (g_fSave) { hr = EncodeToFileW( L"test.crt", pbCert, cbCert, DECF_FORCEOVERWRITE | CRYPT_STRING_BINARY); _JumpIfError(hr, error, "EncodeToFileW"); if (NULL == pdiRequest) { pbChain = pcsEnroll->pbCertChain; cbChain = pcsEnroll->cbCertChain; } else { hr = Request_GetCertificate( pdiRequest, CR_OUT_BINARY | CR_OUT_CHAIN, &strCertChain); _JumpIfError(hr, error, "Request_GetCertificate"); pbChain = (BYTE const *) strCertChain; cbChain = SysStringByteLen(strCertChain); } hr = EncodeToFileW( L"testchain.crt", pbChain, cbChain, DECF_FORCEOVERWRITE | CRYPT_STRING_BINARY); _JumpIfError(hr, error, "EncodeToFileW"); } } if (NULL != pwszDisposition) { *ppwszDisposition = (WCHAR *) LocalAlloc( LMEM_FIXED, (wcslen(pwszDisposition) + 1) * sizeof(WCHAR)); if (NULL == *ppwszDisposition) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "LocalAlloc"); } wcscpy(*ppwszDisposition, pwszDisposition); } *pcbCert = cbCert; *ppbCert = pbCert; pbCert = NULL; hr = S_OK; error: if (NULL != pwszServer) { LocalFree(pwszServer); } if (NULL != pwszAuthority) { LocalFree(pwszAuthority); } if (NULL != pbCert) { LocalFree(pbCert); } if (NULL != pcsEnroll) { CertServerFreeMemory(pcsEnroll); } if (NULL != strCertChain) { SysFreeString(strCertChain); } if (NULL != strCert) { SysFreeString(strCert); } if (NULL != strDisposition) { SysFreeString(strDisposition); } if (NULL != pwszRequest) { LocalFree(pwszRequest); } return(hr); } HRESULT TestOneRequest( IN PctPrivateKey const *pKey, OPTIONAL IN DISPATCHINTERFACE *pdiRequest, IN WCHAR const *pwszConfig, IN DWORD CertNumber, OUT DWORD *pRequestId, OUT DWORD *pTimeOneRequest) { HRESULT hr; HRESULT hrLastStatus; NAMETABLE NameTable; BOOL fTableAllocated; BYTE *pbRequest; DWORD cbRequest; BYTE *pbCert; DWORD cbCert; CERT_CONTEXT const *pCertContext; DWORD RequestIdOut = 0; DWORD Disposition; WCHAR *pwszDisposition = NULL; CERT_NAME_INFO *pNameInfo; DWORD cbNameInfo; CERT_INFO const *pCertInfo; LONG Flags; WCHAR wszAttributes[MAX_PATH]; fTableAllocated = FALSE; pbRequest = NULL; pbCert = NULL; pCertContext = NULL; pNameInfo = NULL; if (g_fDebug) { hr = GenerateTestNameTable(&NameTable); _JumpIfError(hr, error, "GenerateTestNameTable"); } else { hr = GenerateNameTable(&NameTable); _JumpIfError(hr, error, "GenerateNameTable"); } fTableAllocated = TRUE; hr = EncodeRequest(pKey, &NameTable, &pbRequest, &cbRequest); _JumpIfError(hr, error, "EncodeRequest"); Flags = CR_IN_PKCS10; if (g_fRenewal) { BYTE *pbTmp; hr = EncodeRenewal(pbRequest, cbRequest, &pbTmp, &cbRequest); _JumpIfError(hr, error, "EncodeRenewal"); LocalFree(pbRequest); pbRequest = pbTmp; Flags = CR_IN_PKCS7; } if (g_fSave) { hr = EncodeToFileW( L"test.req", pbRequest, cbRequest, DECF_FORCEOVERWRITE | CRYPT_STRING_BINARY); _JumpIfError(hr, error, "EncodeToFileW"); } *pTimeOneRequest = 0 - GetTickCount(); wsprintf( wszAttributes, L"\n" L" attrib 1 end : value 1 end \t\r\n" L"\tattrib 2 end:value_2_end\n" L" \tattrib3:value-3-end\r\n" L"Version:3\n" L"RequestType:CertGen\n" L"CertGenSequence:%u\n", CertNumber); hr = SubmitRequest( pdiRequest, Flags, pbRequest, cbRequest, wszAttributes, pwszConfig, &RequestIdOut, &Disposition, &hrLastStatus, &pwszDisposition, &pbCert, &cbCert); *pTimeOneRequest += GetTickCount(); if (S_OK != hr) { _JumpError2( hr, error, "SubmitRequest", HRESULT_FROM_WIN32(ERROR_INVALID_DATA)); } if (CR_DISP_ISSUED != Disposition) { hr = hrLastStatus; if (S_OK == hr) { hr = E_FAIL; } wprintf( L"SubmitRequest disposition=%x hr=%x (%ws)\n", Disposition, hr, NULL == pwszDisposition? L"???" : pwszDisposition); if (g_fIgnoreError) { hr = S_OK; } if (CR_DISP_DENIED == Disposition && g_fIgnoreAccessDenied) { hr = S_OK; } _JumpError(hr, error, "Cert not issued!"); } pCertContext = CertCreateCertificateContext( X509_ASN_ENCODING, pbCert, cbCert); if (NULL == pCertContext) { hr = myHLastError(); _JumpError(hr, error, "CertCreateCertificateContext"); } pCertInfo = pCertContext->pCertInfo; if (!myDecodeName( X509_ASN_ENCODING, X509_NAME, pCertInfo->Subject.pbData, pCertInfo->Subject.cbData, CERTLIB_USE_LOCALALLOC, &pNameInfo, &cbNameInfo)) { hr = myHLastError(); _JumpError(hr, error, "myDecodeName"); } hr = CheckProperties(RequestIdOut, &NameTable, CertNumber, pNameInfo); _JumpIfError(hr, error, "CheckProperties"); error: *pRequestId = RequestIdOut; if (NULL != pwszDisposition) { LocalFree(pwszDisposition); } if (NULL != pNameInfo) { LocalFree(pNameInfo); } if (NULL != pCertContext) { CertFreeCertificateContext(pCertContext); } if (NULL != pbCert) { LocalFree(pbCert); } if (NULL != pbRequest) { LocalFree(pbRequest); } if (fTableAllocated) { FreeLocalMemory(&NameTable); } return(hr); } HRESULT TestMain() { HRESULT hr; PctPrivateKey *pKey = NULL; DWORD TimeStartTest; DWORD TimeStartLastN; DWORD TimeRequestTotal = 0; DWORD TimeRequestLastN = 0; DWORD TimeElapsedTotal; DWORD TotalCount = 0; DISPATCHINTERFACE diRequest; DISPATCHINTERFACE *pdiRequest = NULL; BOOL fCoInit = FALSE; DWORD RequestId = 0; WCHAR const *pwszConfig; BSTR strConfig = NULL; g_crdnMax = g_crdnSubject; hr = GetPrivateKeyStuff(&pKey); _JumpIfError(hr, error, "GetPrivateKeyStuff"); hr = CoInitialize(NULL); if (S_OK != hr && S_FALSE != hr) { _JumpError(hr, error, "CoInitialize"); } fCoInit = TRUE; if (1 >= g_fRPC) { hr = Request_Init(g_DispatchFlags, &diRequest); _JumpIfError(hr, error, "Request_Init"); pdiRequest = &diRequest; } pwszConfig = g_pwszConfig; if (NULL == pwszConfig) { hr = ConfigGetConfig(g_DispatchFlags, CC_LOCALACTIVECONFIG, &strConfig); _JumpIfError(hr, error, "ConfigGetConfig"); pwszConfig = strConfig; } TimeStartLastN = TimeStartTest = GetTickCount(); while (TotalCount < g_MaximumCount) { DWORD TimeOneRequest; DWORD TimeRequestEnd; DWORD TimeElapsedLastN; hr = TestOneRequest( pKey, pdiRequest, pwszConfig, TotalCount + 1, &RequestId, &TimeOneRequest); if (S_OK != hr) { WCHAR const *pwszMsg; pwszMsg = myGetErrorMessageText(hr, TRUE); CONSOLEPRINT3(( DBG_SS_CERTREQ, "RequestId %u: %hs%ws\n", RequestId, HRESULT_FROM_WIN32(ERROR_INVALID_DATA) == hr? "Ignoring 7f length encoding: " : "", NULL != pwszMsg? pwszMsg : L"Message retrieval Error")); if (NULL != pwszMsg) { LocalFree(const_cast(pwszMsg)); } } if (HRESULT_FROM_WIN32(ERROR_INVALID_DATA) != hr || !g_fIgnoreError) { _JumpIfError(hr, error, "TestOneRequest"); } TimeRequestEnd = GetTickCount(); TimeRequestTotal += TimeOneRequest; TimeRequestLastN += TimeOneRequest; TimeElapsedTotal = TimeRequestEnd - TimeStartTest; TimeElapsedLastN = TimeRequestEnd - TimeStartLastN; TotalCount++; if (g_fTime) { if (0 == g_IntervalCount || 0 == (TotalCount % g_IntervalCount)) { DWORD count; count = g_IntervalCount; if (0 == count) { count = TotalCount; } TimeElapsedLastN = TimeRequestEnd - TimeStartLastN; wprintf( L"RequestId %u: %u/%u Certs in %u/%u seconds (ave=%u/%u ms)\n", RequestId, count, TotalCount, MSTOSEC(TimeElapsedLastN), MSTOSEC(TimeElapsedTotal), TimeElapsedLastN/count, TimeElapsedTotal/TotalCount); if (0 != g_IntervalCount) { TimeRequestLastN = 0; TimeStartLastN = GetTickCount(); } } } } error: if (NULL != pKey) { LocalFree(pKey); } if (NULL != g_pRSAPublicKey) { LocalFree(g_pRSAPublicKey); } if (NULL != pdiRequest) { Request_Release(pdiRequest); } if (NULL != strConfig) { SysFreeString(strConfig); } if (fCoInit) { CoUninitialize(); } if (0 != TotalCount) { wprintf( L"\n%u Total Certificates in %u/%u seconds (request/elapsed time)\n", TotalCount, MSTOSEC(TimeRequestTotal), MSTOSEC(TimeElapsedTotal)); wprintf( L"Certificates required average of %u/%u milliseconds " L"(request/elapsed time)\n", TimeRequestTotal/TotalCount, TimeElapsedTotal/TotalCount); } return(hr); } void Usage(TCHAR *pwszError) { wprintf(L"%ws\n", pwszError); wprintf(L"%ws\n", wszUsage); exit(1); } extern "C" int __cdecl wmain(int argc, WCHAR *argv[]) { HRESULT hr; while (1 < argc && ('-' == argv[1][0] || '/' == argv[1][0])) { WCHAR *pwsz = argv[1]; while (NULL != pwsz && *++pwsz != '\0') { switch (*pwsz) { case 'a': case 'A': g_fIgnoreAccessDenied++; break; case 'c': case 'C': if (0 == lstrcmpi(pwsz, L"config")) { if (1 >= argc) { Usage(TEXT("Missing -config argument")); } g_pwszConfig = argv[2]; } else { if (2 >= argc || !iswdigit(argv[2][0]) || '\0' != pwsz[1]) { Usage(TEXT("Missing numeric -c argument")); } g_MaximumCount = _wtoi(argv[2]); } argc--; argv++; pwsz = NULL; break; case 'd': case 'D': g_fDebug++; break; case 'i': case 'I': g_fIgnoreError++; break; case 'r': case 'R': if (0 == lstrcmpi(pwsz, L"renewal")) { g_fRenewal++; pwsz = NULL; } else if (0 == lstrcmpi(pwsz, L"rpc")) { g_fRPC++; if (0 == lstrcmp(pwsz, L"RPC")) { g_fRPC++; } pwsz = NULL; } else { g_fSave++; } break; case 'p': case 'P': g_fPrintProperties++; break; case 't': case 'T': g_fTime++; g_IntervalCount = 10; if (2 < argc && iswdigit(argv[2][0])) { if ('\0' != pwsz[1]) { Usage(TEXT("Missing numeric -t argument")); } g_IntervalCount = _wtoi(argv[2]); argc--; argv++; pwsz = NULL; } break; case 'z': case 'Z': g_fAllowDups++; g_crdnMax *= 5; break; case 'm': case 'M': g_fShowTime++; break; case 'h': case 'H': default: Usage(TEXT("CertGen Usage")); } } argc--; argv++; } if (argc != 1) { Usage(TEXT("Extra arguments")); } if (g_fShowTime) { SYSTEMTIME st; GetSystemTime(&st); wprintf(L"Start time: %2i:%2i:%2i:%i\n", st.wHour, st.wMinute, st.wSecond, st.wMilliseconds); } hr = TestMain(); if (g_fShowTime) { SYSTEMTIME st; GetSystemTime(&st); wprintf(L"End time: %2i:%2i:%2i:%i\n", st.wHour, st.wMinute, st.wSecond, st.wMilliseconds); wprintf(L"type any key to finish->"); _getch(); wprintf(L"\n"); } myRegisterMemDump(); return((int) hr); } // We need this to include RSA library extern "C" BOOL GenRandom(ULONG huid, BYTE *pbBuffer, size_t dwLength) { wprintf(L"Error GenRandom called\n"); ExitProcess(0); return(TRUE); }