Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

703 lines
22 KiB

  1. /*++
  2. Copyright (c) 1997 Microsoft Corporation
  3. Module Name:
  4. certupgr.cxx
  5. Abstract:
  6. Functions used in upgrading server certs from K2 [server cert in metabase] to
  7. Avalanche [server cert in CAPI store].
  8. Author:
  9. Alex Mallet (amallet) 07-Dec-1997
  10. Boyd Multerer (boydm) 20-Jan-1998 Converted to be useful in setup
  11. --*/
  12. #include "stdafx.h"
  13. #include <objbase.h>
  14. #ifndef _CHICAGO_
  15. #include "oidenc.h"
  16. // keyring include
  17. //#include "intrlkey.h"
  18. // This stuff below is moved from above include
  19. #define REQUEST_HEADER_K2B2VERSION 0x0101
  20. #define REQUEST_HEADER_IDENTIFIER 'RHDR'
  21. #define REQUEST_HEADER_CURVERSION 0x0101
  22. typedef struct _KeyRequestHeader
  23. {
  24. DWORD Identifier; // must be 'RHDR'
  25. DWORD Version; // version of header record
  26. DWORD cbSizeOfHeader; // byte count of header. Afterwards is the request.
  27. DWORD cbRequestSize; // size of the request that follows
  28. BOOL fReqSentToOnlineCA;
  29. LONG longRequestID;
  30. BOOL fWaitingForApproval;
  31. char chCA[MAX_PATH];
  32. } KeyRequestHeader, *LPREQUEST_HEADER;
  33. ///--- end of #include "intrlkey.h"
  34. //
  35. //Local includes
  36. //
  37. #include "certupgr.h"
  38. //#include "certtools.h"
  39. // The below define is in some interal schannel header file. John Banes
  40. // told me to just redefine it below as such........ - Boyd
  41. LPCSTR SGC_KEY_SALT = "SGCKEYSALT";
  42. // prototypes
  43. BOOL DecodeAndImportPrivateKey( PBYTE pbEncodedPrivateKey IN,
  44. DWORD cbEncodedPrivateKey IN,
  45. PCHAR pszPassword IN,
  46. PWCHAR pszKeyContainerIN,
  47. CRYPT_KEY_PROV_INFO *pCryptKeyProvInfo );
  48. BOOL UpdateCSPInfo( PCCERT_CONTEXT pcCertContext );
  49. BOOL FImportAndStoreRequest( PCCERT_CONTEXT pCert, PVOID pbPKCS10req, DWORD cbPKCS10req );
  50. //-------------------------------------------------------------------------
  51. PCCERT_CONTEXT CopyKRCertToCAPIStore_A( PVOID pbPrivateKey, DWORD cbPrivateKey,
  52. PVOID pbPublicKey, DWORD cbPublicKey,
  53. PVOID pbPKCS10req, DWORD cbPKCS10req,
  54. PCHAR pszPassword,
  55. PCHAR pszCAPIStore)
  56. {
  57. PCCERT_CONTEXT pCert = NULL;
  58. // prep the wide strings
  59. PWCHAR pszwCAPIStore = NULL;
  60. DWORD lenStore = (strlen(pszCAPIStore)+1) * sizeof(WCHAR);
  61. pszwCAPIStore = (PWCHAR)GlobalAlloc( GPTR, lenStore );
  62. if ( !pszwCAPIStore )
  63. goto cleanup;
  64. // convert the strings
  65. MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED, pszCAPIStore, -1, pszwCAPIStore, lenStore );
  66. // do the real call
  67. pCert = CopyKRCertToCAPIStore_W(
  68. pbPrivateKey, cbPrivateKey,
  69. pbPublicKey, cbPublicKey,
  70. pbPKCS10req, cbPKCS10req,
  71. pszPassword,
  72. pszwCAPIStore );
  73. cleanup:
  74. // preserve the last error state
  75. DWORD err = GetLastError();
  76. // clean up the strings
  77. if ( pszwCAPIStore )
  78. GlobalFree( pszwCAPIStore );
  79. // reset the last error state
  80. SetLastError( err );
  81. // return the cert
  82. return pCert;
  83. }
  84. //--------------------------------------------------------------------------------------------
  85. // Copies an old Key-Ring style cert to the CAPI store. This cert comes in as two binaries and a password.
  86. PCCERT_CONTEXT CopyKRCertToCAPIStore_W( PVOID pbPrivateKey, DWORD cbPrivateKey,
  87. PVOID pbPublicKey, DWORD cbPublicKey,
  88. PVOID pbPKCS10req, DWORD cbPKCS10req,
  89. PCHAR pszPassword,
  90. PWCHAR pszCAPIStore)
  91. /*++
  92. Routine Description:
  93. Upgrades K2 server certs to Avalanche server certs - reads server cert out of K2
  94. metabase, creates cert context and stores it in CAPI2 "MY" store and writes
  95. relevant information back to metabase.
  96. Arguments:
  97. pMDObject - pointer to Metabase object
  98. pszOldMBPath - path to where server cert is stored in old MB, relative to SSL_W3_KEYS_MD_PATH
  99. pszNewMBPath - fully qualified path to where server cert info should be stored in new MB
  100. Returns:
  101. BOOL indicating success/failure
  102. --*/
  103. {
  104. BOOL fSuccess = FALSE;
  105. HCERTSTORE hStore = NULL;
  106. PCCERT_CONTEXT pcCertContext = NULL;
  107. LPOLESTR polestr = NULL;
  108. // start by opening the CAPI store that we will be saving the certificate into
  109. hStore = CertOpenStore( CERT_STORE_PROV_SYSTEM,
  110. 0,
  111. NULL,
  112. CERT_SYSTEM_STORE_LOCAL_MACHINE,
  113. pszCAPIStore );
  114. if ( !hStore )
  115. {
  116. // iisDebugOut((_T("Error 0x%x calling CertOpenStore \n"), GetLastError());
  117. goto EndUpgradeServerCert;
  118. }
  119. // at this point we check to see if a certificate was passed in. If none was, then we need
  120. // to create a dummy-temporary certificate that markes the private key as incomplete. That
  121. // way, then the real certificate comes back from verisign the regular tools can be used
  122. // to complete the key.
  123. //CertCreateSelfSignCertificate()
  124. //
  125. //Create cert context to be stored in CAPI store
  126. //
  127. pbPublicKey = (PVOID)((PBYTE)pbPublicKey + CERT_DER_PREFIX);
  128. cbPublicKey -= CERT_DER_PREFIX;
  129. pcCertContext = CertCreateCertificateContext( X509_ASN_ENCODING, (PUCHAR)pbPublicKey, cbPublicKey);
  130. if ( pcCertContext )
  131. {
  132. // the private key gets stored in a seperate location from the certificate and gets referred to
  133. // by the certificate. We should try to pick a unique name so that some other cert won't step
  134. // on it by accident. There is no formal format for this name whatsoever. Some groups use a
  135. // human-readable string, some use a hash of the cert, and some use a GUID string. All are valid
  136. // although for generated certs the hash or the GUID are probably better.
  137. // get the 128 big md5 hash of the cert for the name
  138. DWORD dwHashSize;
  139. BOOL fHash;
  140. BYTE MD5Hash[16]; // give it some extra size
  141. dwHashSize = sizeof(MD5Hash);
  142. fHash = CertGetCertificateContextProperty( pcCertContext,
  143. CERT_MD5_HASH_PROP_ID,
  144. (VOID *) MD5Hash,
  145. &dwHashSize );
  146. // Since the MD5 hash is the same size as a guid, we can use the guid utilities to make a
  147. // nice string out of it.
  148. HRESULT hresult;
  149. hresult = StringFromCLSID( (REFCLSID)MD5Hash, &polestr );
  150. //
  151. // Now decode private key blob and import it into CAPI1 private key
  152. //
  153. CRYPT_KEY_PROV_INFO CryptKeyProvInfo;
  154. if ( DecodeAndImportPrivateKey( (PUCHAR)pbPrivateKey, cbPrivateKey, pszPassword,
  155. polestr, &CryptKeyProvInfo ) )
  156. {
  157. //
  158. // Add the private key to the cert context
  159. //
  160. BOOL f;
  161. f = CertSetCertificateContextProperty( pcCertContext, CERT_KEY_PROV_INFO_PROP_ID,
  162. 0, &CryptKeyProvInfo );
  163. f = UpdateCSPInfo( pcCertContext );
  164. if ( f )
  165. {
  166. //
  167. // Store it in the provided store
  168. //
  169. if ( CertAddCertificateContextToStore( hStore, pcCertContext,
  170. CERT_STORE_ADD_REPLACE_EXISTING, NULL ) )
  171. {
  172. fSuccess = TRUE;
  173. // Write out the original request as a property on the cert
  174. FImportAndStoreRequest( pcCertContext, pbPKCS10req, cbPKCS10req );
  175. }
  176. else
  177. {
  178. // iisDebugOut((_T("Error 0x%x calling CertAddCertificateContextToStore"), GetLastError());
  179. }
  180. }
  181. else
  182. {
  183. // iisDebugOut((_T("Error 0x%x calling CertSetCertificateContextProperty"), GetLastError());
  184. }
  185. }
  186. }
  187. else
  188. {
  189. // iisDebugOut((_T("Error 0x%x calling CertCreateCertificateContext"), GetLastError());
  190. }
  191. //
  192. //Cleanup that's done only on failure
  193. //
  194. if ( !fSuccess )
  195. {
  196. if ( pcCertContext )
  197. {
  198. CertFreeCertificateContext( pcCertContext );
  199. }
  200. pcCertContext = NULL;
  201. }
  202. EndUpgradeServerCert:
  203. // cleanup
  204. if ( hStore )
  205. CertCloseStore ( hStore, 0 );
  206. if ( polestr )
  207. CoTaskMemFree( polestr );
  208. // return the answer
  209. return pcCertContext;
  210. }
  211. //--------------------------------------------------------------------------------------------
  212. BOOL UpdateCSPInfo( PCCERT_CONTEXT pcCertContext )
  213. {
  214. BYTE cbData[1000];
  215. CRYPT_KEY_PROV_INFO* pProvInfo = (CRYPT_KEY_PROV_INFO *) cbData;
  216. DWORD dwFoo = 1000;
  217. BOOL fSuccess = TRUE;
  218. if ( ! CertGetCertificateContextProperty( pcCertContext,
  219. CERT_KEY_PROV_INFO_PROP_ID,
  220. pProvInfo,
  221. &dwFoo ) )
  222. {
  223. fSuccess = FALSE;
  224. // iisDebugOut((_T("Fudge. failed to get property : 0x%x"), GetLastError());
  225. }
  226. else
  227. {
  228. pProvInfo->dwProvType = PROV_RSA_SCHANNEL;
  229. pProvInfo->pwszProvName = NULL;
  230. if ( !CertSetCertificateContextProperty( pcCertContext,
  231. CERT_KEY_PROV_INFO_PROP_ID,
  232. 0,
  233. pProvInfo ) )
  234. {
  235. fSuccess = FALSE;
  236. // iisDebugOut((_T("Fudge. failed to set property : 0x%x"), GetLastError());
  237. }
  238. }
  239. // return success
  240. return fSuccess;
  241. }
  242. //--------------------------------------------------------------------------------------------
  243. BOOL DecodeAndImportPrivateKey( PBYTE pbEncodedPrivateKey IN,
  244. DWORD cbEncodedPrivateKey IN,
  245. PCHAR pszPassword IN,
  246. PWCHAR pszKeyContainer IN,
  247. CRYPT_KEY_PROV_INFO *pCryptKeyProvInfo )
  248. /*++
  249. Routine Description:
  250. Converts the private key stored in the metabase, in Schannel-internal format,
  251. into a key that can be imported via CryptImportKey() to create a CAP1 key blob.
  252. Arguments:
  253. pbEncodedPrivateKey - pointer to [encoded] private key
  254. cbEncodedPrivateKey - size of encoded private key blob
  255. pszPassword - password used to encode private key
  256. pszKeyContainer - container name for private key
  257. pCryptKeyProvInfo - pointer to CRYPT_KEY_PROV_INFO structure filled in on success
  258. Returns:
  259. BOOL indicating success/failure
  260. --*/
  261. {
  262. BOOL fSuccess = FALSE;
  263. DWORD cbPassword = strlen(pszPassword);
  264. PPRIVATE_KEY_FILE_ENCODE pPrivateFile = NULL;
  265. DWORD cbPrivateFile = 0;
  266. MD5_CTX md5Ctx;
  267. struct RC4_KEYSTRUCT rc4Key;
  268. DWORD i;
  269. HCRYPTPROV hProv = NULL;
  270. HCRYPTKEY hPrivateKey = NULL;
  271. DWORD cbDecodedPrivateKey = 0;
  272. PBYTE pbDecodedPrivateKey = NULL;
  273. DWORD err;
  274. //
  275. //HACK HACK HACK - need to make sure Schannel is initialized, so it registers
  276. //its custom decoders, which we make use of in the following code. So, make a
  277. //bogus call to an Schannel function
  278. // Note: on NT5, the AcquireCredentialsHandle operates in the lsass process and
  279. // thus will not properly initialize the stuff we need in our process. Thus we
  280. // call SslGenerateRandomBits instead.
  281. //
  282. DWORD dw;
  283. SslGenerateRandomBits( (PUCHAR)&dw, sizeof(dw) );
  284. // We have to do a little fixup here. Old versions of
  285. // schannel wrote the wrong header data into the ASN
  286. // for private key files, so we must fix the size data.
  287. pbEncodedPrivateKey[2] = (BYTE) (((cbEncodedPrivateKey - 4) & 0xFF00) >> 8); //Get MSB
  288. pbEncodedPrivateKey[3] = (BYTE) ((cbEncodedPrivateKey - 4) & 0xFF); //Get LSB
  289. //
  290. // ASN.1 decode the private key.
  291. //
  292. //
  293. // Figure out the size of the buffer needed
  294. //
  295. if( !CryptDecodeObject(X509_ASN_ENCODING,
  296. szPrivateKeyFileEncode,
  297. pbEncodedPrivateKey,
  298. cbEncodedPrivateKey,
  299. 0,
  300. NULL,
  301. &cbPrivateFile) )
  302. {
  303. err = GetLastError();
  304. // iisDebugOut((_T("Error 0x%x decoding the private key"), err);
  305. goto EndDecodeKey;
  306. }
  307. pPrivateFile = (PPRIVATE_KEY_FILE_ENCODE) LocalAlloc( LPTR, cbPrivateFile );
  308. if(pPrivateFile == NULL)
  309. {
  310. SetLastError( ERROR_OUTOFMEMORY );
  311. goto EndDecodeKey;
  312. }
  313. //
  314. // Actually fill in the buffer
  315. //
  316. if( !CryptDecodeObject( X509_ASN_ENCODING,
  317. szPrivateKeyFileEncode,
  318. pbEncodedPrivateKey,
  319. cbEncodedPrivateKey,
  320. 0,
  321. pPrivateFile,
  322. &cbPrivateFile ) )
  323. {
  324. err = GetLastError();
  325. // iisDebugOut((_T("Error 0x%x decoding the private key"), err);
  326. goto EndDecodeKey;
  327. }
  328. //
  329. // Decrypt the decoded private key using the password.
  330. //
  331. MD5Init(&md5Ctx);
  332. MD5Update(&md5Ctx, (PBYTE) pszPassword, cbPassword);
  333. MD5Final(&md5Ctx);
  334. rc4_key( &rc4Key, 16, md5Ctx.digest );
  335. // memset( &md5Ctx, 0, sizeof(md5Ctx) );
  336. rc4( &rc4Key,
  337. pPrivateFile->EncryptedBlob.cbData,
  338. pPrivateFile->EncryptedBlob.pbData );
  339. //
  340. // Build a PRIVATEKEYBLOB from the decrypted private key.
  341. //
  342. //
  343. // Figure out size of buffer needed
  344. //
  345. if( !CryptDecodeObject( X509_ASN_ENCODING,
  346. szPrivateKeyInfoEncode,
  347. pPrivateFile->EncryptedBlob.pbData,
  348. pPrivateFile->EncryptedBlob.cbData,
  349. 0,
  350. NULL,
  351. &cbDecodedPrivateKey ) )
  352. {
  353. // NOTE: This stuff is complicated!!! The following code came
  354. // from John Banes. Heck this whole routine pretty much came
  355. // from John Banes. -- Boyd
  356. // Maybe this was a SGC style key.
  357. // Re-encrypt it, and build the SGC decrypting
  358. // key, and re-decrypt it.
  359. BYTE md5Digest[MD5DIGESTLEN];
  360. rc4_key(&rc4Key, 16, md5Ctx.digest);
  361. rc4(&rc4Key,
  362. pPrivateFile->EncryptedBlob.cbData,
  363. pPrivateFile->EncryptedBlob.pbData);
  364. CopyMemory(md5Digest, md5Ctx.digest, MD5DIGESTLEN);
  365. MD5Init(&md5Ctx);
  366. MD5Update(&md5Ctx, md5Digest, MD5DIGESTLEN);
  367. MD5Update(&md5Ctx, (PUCHAR)SGC_KEY_SALT, strlen(SGC_KEY_SALT));
  368. MD5Final(&md5Ctx);
  369. rc4_key(&rc4Key, 16, md5Ctx.digest);
  370. rc4(&rc4Key,
  371. pPrivateFile->EncryptedBlob.cbData,
  372. pPrivateFile->EncryptedBlob.pbData);
  373. // Try again...
  374. if(!CryptDecodeObject(X509_ASN_ENCODING,
  375. szPrivateKeyInfoEncode,
  376. pPrivateFile->EncryptedBlob.pbData,
  377. pPrivateFile->EncryptedBlob.cbData,
  378. 0,
  379. NULL,
  380. &cbDecodedPrivateKey))
  381. {
  382. ZeroMemory(&md5Ctx, sizeof(md5Ctx));
  383. err = GetLastError();
  384. goto EndDecodeKey;
  385. }
  386. }
  387. pbDecodedPrivateKey = (PBYTE) LocalAlloc( LPTR, cbDecodedPrivateKey );
  388. if( pbDecodedPrivateKey == NULL )
  389. {
  390. SetLastError( ERROR_OUTOFMEMORY );
  391. goto EndDecodeKey;
  392. }
  393. //
  394. // Actually fill in the buffer
  395. //
  396. if( !CryptDecodeObject( X509_ASN_ENCODING,
  397. szPrivateKeyInfoEncode,
  398. pPrivateFile->EncryptedBlob.pbData,
  399. pPrivateFile->EncryptedBlob.cbData,
  400. 0,
  401. pbDecodedPrivateKey,
  402. &cbDecodedPrivateKey ) )
  403. {
  404. err = GetLastError();
  405. // iisDebugOut((_T("Error 0x%x decoding the private key"), err);
  406. goto EndDecodeKey;
  407. }
  408. // On NT 4 the ff holds true : <- from Alex Mallet
  409. // Although key is going to be used for key exchange, mark it as being
  410. // used for signing, because only 512-bit key exchange keys are supported
  411. // in the non-domestic rsabase.dll, whereas signing keys can be up to
  412. // 2048 bits.
  413. //
  414. // On NT 5, PROV_RSA_FULL should be changed to PROV_RSA_SCHANNEL, and
  415. // aiKeyAlg to CALG_RSA_KEYX, because PROV_RSA_SCHANNEL, which is only
  416. // installed on NT 5, supports 1024-bit private keys for key exchange
  417. //
  418. // On NT4, Schannel doesn't care whether a key is marked for signing or exchange,
  419. // but on NT5 it does, so aiKeyAlg must be set appropriately
  420. //
  421. ((BLOBHEADER *) pbDecodedPrivateKey)->aiKeyAlg = CALG_RSA_KEYX;
  422. //
  423. // Clean out the key container, pszKeyContainer
  424. //
  425. CryptAcquireContext(&hProv,
  426. pszKeyContainer,
  427. NULL,
  428. PROV_RSA_SCHANNEL,
  429. CRYPT_DELETEKEYSET | CRYPT_MACHINE_KEYSET);
  430. //
  431. // Create a CryptoAPI key container in which to store the key.
  432. //
  433. if( !CryptAcquireContext( &hProv,
  434. pszKeyContainer,
  435. NULL,
  436. PROV_RSA_SCHANNEL,
  437. CRYPT_NEWKEYSET | CRYPT_MACHINE_KEYSET))
  438. {
  439. err = GetLastError();
  440. // iisDebugOut((_T("Error 0x%x calling CryptAcquireContext"), err);
  441. goto EndDecodeKey;
  442. }
  443. //
  444. // Import the private key blob into the key container.
  445. //
  446. if( !CryptImportKey( hProv,
  447. pbDecodedPrivateKey,
  448. cbDecodedPrivateKey,
  449. 0,
  450. CRYPT_EXPORTABLE, //so we can export it later
  451. &hPrivateKey ) )
  452. {
  453. err = GetLastError();
  454. // iisDebugOut((_T("Error 0x%x importing PRIVATEKEYBLOB"), err);
  455. goto EndDecodeKey;
  456. }
  457. //
  458. // Fill in the CRYPT_KEY_PROV_INFO structure, with the same parameters we
  459. // used in the call to CryptAcquireContext() above
  460. //
  461. //
  462. // container name in the structure is a unicode string, so we need to convert
  463. //
  464. if ( pszKeyContainer != NULL )
  465. {
  466. // point the key container name to the passed in string
  467. // WARNING: this does not actually copy the string, just the pointer
  468. // to it. So the strings needs to remain valid until the ProvInfo is commited.
  469. pCryptKeyProvInfo->pwszContainerName = pszKeyContainer;
  470. }
  471. else
  472. {
  473. pCryptKeyProvInfo->pwszContainerName = NULL;
  474. }
  475. pCryptKeyProvInfo->pwszProvName = NULL;
  476. pCryptKeyProvInfo->dwProvType = PROV_RSA_FULL;
  477. pCryptKeyProvInfo->dwFlags = 0x20; // allow the cert to be exchanged
  478. pCryptKeyProvInfo->cProvParam = 0;
  479. pCryptKeyProvInfo->rgProvParam = NULL;
  480. pCryptKeyProvInfo->dwKeySpec = AT_KEYEXCHANGE; // allow the cert to be exchanged
  481. fSuccess = TRUE;
  482. EndDecodeKey:
  483. //
  484. // Clean-up that happens regardless of success/failure
  485. //
  486. if ( pPrivateFile )
  487. {
  488. LocalFree( pPrivateFile );
  489. }
  490. if ( pbDecodedPrivateKey )
  491. {
  492. LocalFree( pbDecodedPrivateKey );
  493. }
  494. if ( hPrivateKey )
  495. {
  496. CryptDestroyKey( hPrivateKey );
  497. }
  498. if ( hProv )
  499. {
  500. CryptReleaseContext( hProv, 0 );
  501. }
  502. return fSuccess;
  503. } //DecodeAndImportPrivateKey
  504. //--------------------------------------------------------------------------------------------
  505. /*++
  506. Routine Description:
  507. Takes an incoming PKCS10 request and saves it as a property attached to the key. It also
  508. checks if the request is in the old internal Keyring format or not......
  509. Arguments:
  510. pCert - CAPI certificate context pointer for the cert to save the request on
  511. pbPKCS10req - pointer to the request
  512. cbPKCS10req - size of the request
  513. Returns:
  514. BOOL indicating success/failure
  515. --*/
  516. BOOL FImportAndStoreRequest( PCCERT_CONTEXT pCert, PVOID pbPKCS10req, DWORD cbPKCS10req )
  517. {
  518. BOOL f;
  519. DWORD err;
  520. // if any NULLS are passed in, fail gracefully
  521. if ( !pCert || !pbPKCS10req || !cbPKCS10req )
  522. return FALSE;
  523. // first, check if the incoming request is actually pointing to an old KeyRing internal
  524. // request format. That just means that the real request is actuall slightly into
  525. // the block. The way you tell is by testing the first DWORD to see it
  526. // is REQUEST_HEADER_IDENTIFIER
  527. // start by seeing if this is a new style key request
  528. LPREQUEST_HEADER pHeader = (LPREQUEST_HEADER)pbPKCS10req;
  529. if ( pHeader->Identifier == REQUEST_HEADER_IDENTIFIER )
  530. {
  531. // update the request pointer and data count
  532. pbPKCS10req = (PBYTE)pbPKCS10req + pHeader->cbSizeOfHeader;
  533. cbPKCS10req = pHeader->cbRequestSize;
  534. }
  535. // now save the request onto the key
  536. CRYPT_DATA_BLOB dataBlob;
  537. ZeroMemory( &dataBlob, sizeof(dataBlob) );
  538. dataBlob.pbData = (PBYTE)pbPKCS10req; // pointer to blob data
  539. dataBlob.cbData = cbPKCS10req; // blob length info
  540. f = CertSetCertificateContextProperty(
  541. pCert,
  542. CERTWIZ_REQUEST_PROP_ID,
  543. 0,
  544. &dataBlob
  545. );
  546. err = GetLastError();
  547. /*
  548. HRESULT hRes = CertTool_SetBinaryBlobProp(
  549. pCert, // cert context to set the prop on
  550. pbPKCS10req, // pointer to blob data
  551. cbPKCS10req, // blob length info
  552. CERTWIZ_REQUEST_PROP_ID,// property ID for context
  553. TRUE // the request is already encoded
  554. );
  555. */
  556. return f;
  557. }
  558. #endif //_CHICAGO_