2025-11-28 00:35:46 +09:00

1002 lines
28 KiB
C++

// CAPIWrappers.cpp - Functions for dealing with certificates.
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright (c) Microsoft Corporation. All rights reserved
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
#endif
#include "CAPIWrappers.h"
#pragma comment(lib, "crypt32")
#pragma comment(lib, "rpcrt4")
#ifndef USES
#define USES(x) (x = x)
#endif
#define HRESULT_FROM_RPCSTATUS(x) \
(((x < 0) || (x == RPC_S_OK)) ? \
(HRESULT)x : \
(HRESULT) (((x) & 0x0000FFFF) | (FACILITY_RPC<< 16) | 0x80000000))
#define SECOND_IN_FILETIME (10i64 * 1000 * 1000)
const DWORD SIXTYFOUR_K = 64 * 1024;
const DWORD SIXTEEN_K = 16 * 1024;
const DWORD ONE_K = 1024;
BYTE s_keyDataBuf[SIXTEEN_K] = {0};
BYTE s_certBuf[SIXTEEN_K] = {0};
BYTE s_fileBuf[SIXTYFOUR_K] = {0};
/****************************************************************************++
Description :
This function creates a well known sid using User domain. CreateWellKnownSid requires
domain sid to be provided to generate such sids. This function first gets the domain sid
out of the user information in the token and then generate a well known sid.
Arguments:
hToken - [supplies] The token for which sid has to be generated
sidType - [supplies] The type of well known sid
pSid - [receives] The newly create sid
pdwSidSize - [Supplies/Receives] The size of the memory allocated for ppSid
Returns:
Errors returned by GetTokenInformation
Errors returned by CreateWellKnownSid
E_OUTOFMEMORY In case there is not enough memory
Errors returned by GetWindowsAccountDomainSid
--***************************************************************************/
HRESULT
CreateWellKnownSidForAccount(
__in_opt HANDLE hToken,
__in WELL_KNOWN_SID_TYPE sidType,
__out PSID pSid,
__inout DWORD * pdwSidSize)
{
HRESULT hr = S_OK;
TOKEN_USER * pUserToken = NULL;
DWORD dwTokenLen = 0;
PBYTE pDomainSid[SECURITY_MAX_SID_SIZE];
DWORD dwSidSize = SECURITY_MAX_SID_SIZE;
//
// Get the TokenUser, use this TokenUser to generate a well-known sid that requires a domain
//
if (!GetTokenInformation(
hToken,
TokenUser,
NULL,
dwTokenLen,
&dwTokenLen))
{
DWORD err = GetLastError();
if (err != ERROR_INSUFFICIENT_BUFFER)
{
hr = HRESULT_FROM_WIN32(err);
goto cleanup;
}
}
pUserToken = (TOKEN_USER *) new BYTE[dwTokenLen];
if (pUserToken == NULL)
{
hr = E_OUTOFMEMORY;
goto cleanup;
}
if (!GetTokenInformation(
hToken,
TokenUser,
pUserToken,
dwTokenLen,
&dwTokenLen))
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto cleanup;
}
//
// Now get the domain sid from the TokenUser
//
if (!GetWindowsAccountDomainSid(
pUserToken->User.Sid,
(PSID) pDomainSid,
&dwSidSize))
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto cleanup;
}
if(!CreateWellKnownSid(
sidType,
pDomainSid,
pSid,
pdwSidSize))
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto cleanup;
}
cleanup:
delete [] pUserToken;
return hr;
}
/****************************************************************************++
Routine Description:
Verifies whether specified well-known SID is in the current user token
Arguments:
sid - one of the WELL_KNOWN_SID_TYPE consts
hToken - Optional the token for which we want to test membership
pfMember - [Receives] TRUE if specified sid is a member of the user token, FALSE otherwise
Notes:
-
Return Value:
Errors returned by CreateWellKnownSid
Errors returned by CheckTokenMembership
--*****************************************************************************/
HRESULT
IsMemberOf(
__in WELL_KNOWN_SID_TYPE sid,
__in_opt HANDLE hToken,
__out BOOL * pfMember)
{
HRESULT hr = S_OK;
BOOL fMember = FALSE;
BYTE pSID[SECURITY_MAX_SID_SIZE] = {0};
DWORD dwSIDSize = sizeof(pSID);
//
// create SID for the authenticated users
//
if (!CreateWellKnownSid(
sid,
NULL, // not a domain sid
(SID*)pSID,
&dwSIDSize))
{
hr = HRESULT_FROM_WIN32(GetLastError());
if (FAILED(hr) && (hr != E_INVALIDARG))
{
goto Cleanup;
}
//
// In case of invalid-arg we might need to provide the domain, so create well known sid for domain
//
hr = CreateWellKnownSidForAccount(hToken, sid, pSID, &dwSIDSize);
if (hr == HRESULT_FROM_WIN32(ERROR_NON_ACCOUNT_SID))
{
//
// If it is a non account sid (for example Local Service). Ignore the error.
//
hr = S_OK;
fMember = FALSE;
goto Cleanup;
}
else if (FAILED(hr))
{
goto Cleanup;
}
}
//
// check whether token has this sid
//
if (!CheckTokenMembership(hToken,
(SID*)pSID, // sid for the authenticated user
&fMember))
{
hr = HRESULT_FROM_WIN32(GetLastError());
// just to be on the safe side (as we don't know that CheckTokenMembership
// does not modify fAuthenticated in case of error)
fMember = FALSE;
if (hr == E_ACCESSDENIED && hToken == NULL)
{
// unable to query the thread token. Open as self and try again
if (OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE, &hToken))
{
if (CheckTokenMembership(hToken, (SID*)pSID, &fMember))
{
hr = S_OK;
}
else
{
// stick with the original error code, but ensure that fMember is correct
fMember = FALSE;
}
CloseHandle(hToken);
}
}
goto Cleanup;
}
Cleanup:
*pfMember = fMember;
return hr;
}
// helper used by CreateCryptProv
HRESULT
IsServiceAccount(
OUT BOOL * pfMember)
{
HRESULT hr = S_OK;
BOOL fMember = FALSE;
hr = IsMemberOf(WinLocalServiceSid, NULL, &fMember);
if (FAILED(hr) || fMember)
{
goto Cleanup;
}
hr = IsMemberOf(WinLocalSystemSid, NULL, &fMember);
if (FAILED(hr) || fMember)
{
goto Cleanup;
}
hr = IsMemberOf(WinNetworkServiceSid, NULL, &fMember);
if (FAILED(hr) || fMember)
{
goto Cleanup;
}
Cleanup:
*pfMember = fMember;
return hr;
}
/****************************************************************************++
Routine Description:
Deletes the key container and the keys
Arguments:
pwzContainer -
Notes:
-
Return Value:
- S_OK
- or -
- no other errors are expected
--*****************************************************************************/
HRESULT
DeleteKeys(
IN PCWSTR pwzContainer)
{
HRESULT hr = S_OK;
HCRYPTPROV hCryptProv = NULL;
BOOL fServiceAccount = FALSE;
hr = IsServiceAccount(&fServiceAccount);
if (FAILED(hr))
{
goto Cleanup;
}
//
// this is the most counter-intuitive API that i have seen in my life
// in order to delete the contanier and all the keys in it, i have to call CryptAcquireContext
//
if (!CryptAcquireContextW(&hCryptProv,
pwzContainer,
NULL,
DEFAULT_PROV_TYPE,
fServiceAccount ?
(CRYPT_DELETEKEYSET | CRYPT_MACHINE_KEYSET) :
(CRYPT_DELETEKEYSET)))
{
hr = HRESULT_FROM_WIN32(GetLastError());
}
Cleanup:
return hr;
}
/****************************************************************************++
Routine Description:
Wrapper fro CryptDestroyKey
Arguments:
hKey - handle to the key to destroy
Notes:
-
Return Value:
- VOID
--*****************************************************************************/
VOID
DestroyKey(
IN HCRYPTKEY hKey)
{
if (NULL != hKey)
{
//
// this is quite counter-intuitive API
// CryptDestroyKey just releases the handle to the key, but only in case of
// private/public key pairs.
//
if (!CryptDestroyKey(hKey))
{
HRESULT hr = HRESULT_FROM_WIN32(GetLastError());
USES(hr);
//
// should never happen, unless handle is invalid
//
}
}
}
/****************************************************************************++
Routine Description:
Releases the handle to the CSP
Arguments:
hCryptProv - handle to the CSP
Notes:
- handles the NULL CSP gracefully
Return Value:
- VOID
--*****************************************************************************/
VOID
ReleaseCryptProv(
IN HCRYPTPROV hCryptProv)
{
if (NULL != hCryptProv)
{
if (!CryptReleaseContext(hCryptProv, 0))
{
//
// one reason why this could fail (at least it failed a couple of times already) i s
// that some certifcate store was opened using this CSP, but
// CERT_STORE_NO_CRYPT_RELEASE_FLAG was not specified, so that
// when cert store is released the provider is released as well.
//
// verify that all stores that were ever used, specify this flag
//
HRESULT hr = HRESULT_FROM_WIN32(GetLastError());
USES(hr);
}
}
}
/****************************************************************************++
Routine Description:
Creates a handle to the CSP
Arguments:
pwzContainerName - name of the container to be created. if NULL, GUID is generated
for the name of the container
fCreateNewKeys - forces new keys to be created
phCryptProv - pointer to the location, where handle should be returned
Notes:
-
Return Value:
- S_OK
- or -
- CAPI error returned by CryptAcquireContextW
--*****************************************************************************/
HRESULT
CreateCryptProv(
IN PCWSTR pwzContainerName,
IN BOOL fCreateNewKeys,
OUT HCRYPTPROV* phCryptProv)
{
HRESULT hr = S_OK;
HCRYPTKEY hKey = NULL;
RPC_STATUS status = RPC_S_OK;
BOOL fCreatedContainer = FALSE;
WCHAR* pwzNewContainerName = NULL;
*phCryptProv = NULL;
if (NULL == pwzContainerName)
{
UUID uuid;
BOOL fServiceAccount = FALSE;
//
// generate container name from the UUID
//
status = UuidCreate(&uuid);
hr = HRESULT_FROM_RPCSTATUS(status);
if (FAILED(hr))
{
goto Cleanup;
}
status = UuidToStringW(&uuid, (unsigned short**)&pwzNewContainerName);
hr = HRESULT_FROM_RPCSTATUS(status);
if (FAILED(hr))
{
goto Cleanup;
}
pwzContainerName = pwzNewContainerName;
hr = IsServiceAccount(&fServiceAccount);
if (FAILED(hr))
{
goto Cleanup;
}
//
// open the clean key container
//
// note: CRYPT_NEW_KEYSET is not creating new keys, it just
// creates new key container. duh.
//
if (!CryptAcquireContextW(phCryptProv,
pwzNewContainerName,
NULL, // default provider name
DEFAULT_PROV_TYPE,
fServiceAccount ?
(CRYPT_SILENT | CRYPT_NEWKEYSET | CRYPT_MACHINE_KEYSET) :
(CRYPT_SILENT | CRYPT_NEWKEYSET)))
{
hr = HRESULT_FROM_WIN32(GetLastError());
//
// we are seeing that CryptAcquireContextW returns NTE_FAIL under low
// memory condition, so we just mask the error
//
if (NTE_FAIL == hr)
{
hr = E_OUTOFMEMORY;
}
goto Cleanup;
}
fCreatedContainer = TRUE;
}
else
{
BOOL fServiceAccount = FALSE;
hr = IsServiceAccount(&fServiceAccount);
if (FAILED(hr))
{
goto Cleanup;
}
//
// open the provider first, create the keys too
//
if (!CryptAcquireContextW(phCryptProv,
pwzContainerName,
NULL, // default provider name
DEFAULT_PROV_TYPE,
fServiceAccount ?
(CRYPT_SILENT | CRYPT_MACHINE_KEYSET) :
(CRYPT_SILENT)))
{
hr = HRESULT_FROM_WIN32(GetLastError());
//
// we are seeing that CryptAcquireContextW returns NTE_FAIL under low
// memory condition, so we just mask the error
//
if (NTE_FAIL == hr)
{
hr = E_OUTOFMEMORY;
}
goto Cleanup;
}
}
if (fCreateNewKeys)
{
//
// make sure keys exist
//
if (!CryptGetUserKey(*phCryptProv,
DEFAULT_KEY_SPEC,
&hKey))
{
hr = HRESULT_FROM_WIN32(GetLastError());
// if key does not exist, create it
if (HRESULT_FROM_WIN32((unsigned long)NTE_NO_KEY) == hr)
{
hr = S_OK;
if (!CryptGenKey(*phCryptProv,
DEFAULT_KEY_SPEC,
CRYPT_EXPORTABLE,
&hKey))
{
hr = HRESULT_FROM_WIN32(GetLastError());
//
// we are seeing that CryptGenKey returns ERROR_CANTOPEN under low
// memory condition, so we just mask the error
//
if (HRESULT_FROM_WIN32(ERROR_CANTOPEN) == hr)
{
hr = E_OUTOFMEMORY;
}
goto Cleanup;
}
}
else
{
// failed to get user key by some misterious reason, so bail out
goto Cleanup;
}
}
}
Cleanup:
DestroyKey(hKey);
if (FAILED(hr))
{
//
// release the context
//
ReleaseCryptProv(*phCryptProv);
*phCryptProv = NULL;
//
// delete the keys, if we created them
//
if (fCreatedContainer)
{
DeleteKeys(pwzContainerName);
}
}
if (NULL != pwzNewContainerName)
{
// this always returns RPC_S_OK
status = RpcStringFreeW((unsigned short**)&pwzNewContainerName);
USES(status);
}
return hr;
}
/****************************************************************************++
Routine Description:
Retrieves the name of the CSP container.
Arguments:
hCryptProv - handle to the CSP
pcChars - count of chars in the buffer on input. count of chars used on return
pwzContainerName - pointer to output buffer
Notes:
-
Return Value:
- S_OK
- or -
- NTE_BAD_UID, if hCryptProv handle is not valid
--*****************************************************************************/
HRESULT
GetContainerName(
IN HCRYPTPROV hCryptProv,
__inout ULONG* pcChars,
__out_ecount_opt(*pcChars) LPWSTR pwzContainerName)
{
HRESULT hr = S_OK;
CHAR * pszBuf = new CHAR[*pcChars];
ULONG cbBufSize = sizeof(CHAR) *(*pcChars);
if (NULL == pszBuf)
{
hr = E_OUTOFMEMORY;
goto Cleanup;
}
//
// get the name of the key container
//
if (!CryptGetProvParam(hCryptProv,
PP_CONTAINER,
(BYTE*)pszBuf,
&cbBufSize,
0))
{
hr = HRESULT_FROM_WIN32(GetLastError());
// if buffer is not sufficiently large, just return with this error
if (HRESULT_FROM_WIN32(ERROR_MORE_DATA) == hr)
{
// if buffer was not sufficiently large return the number of characters needed
*pcChars = cbBufSize / sizeof(CHAR);
goto Cleanup;
}
else
{
goto Cleanup;
}
}
//
// convert the string to the wide character, since that's what needed for the key info
//
if (0 == MultiByteToWideChar(CP_ACP,
0, // dwFlags
pszBuf,
-1, // calculate the length
pwzContainerName,
*pcChars))
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto Cleanup;
}
Cleanup:
if (NULL != pszBuf)
{
delete [] pszBuf;
}
return hr;
}
// Read a single cert from a file
HRESULT ReadCertFromFile(LPCWSTR pwzFileName, CERT_CONTEXT** ppCert, HCRYPTPROV* phCryptProv)
{
BOOL bRet = FALSE;
FILE* pFile = NULL;
errno_t err;
// open cert file for local cert
err = _wfopen_s(&pFile, pwzFileName, L"rb");
if (err)
{
return CRYPT_E_FILE_ERROR;
}
// read local cert into *ppLocalCert, allocating memory
fread(s_fileBuf, sizeof(s_fileBuf), 1, pFile);
fclose(pFile);
CRYPT_DATA_BLOB blob;
blob.cbData = sizeof(s_fileBuf);
blob.pbData = s_fileBuf;
HCERTSTORE hCertStore = PFXImportCertStore(&blob, L"DRT Rocks!", CRYPT_EXPORTABLE);
if (NULL == hCertStore)
return HRESULT_FROM_WIN32(GetLastError());
// TODO: does this have to be a c style cast? I get compile errors if I try reinterpret or static cast
// the first cert is always the leaf cert (since we encoded it that way)
CERT_CONTEXT* pCertContext = (CERT_CONTEXT*)CertEnumCertificatesInStore(hCertStore, NULL);
if (NULL == pCertContext)
return HRESULT_FROM_WIN32(GetLastError());
// retreive the crypt provider which has the private key for this certificate
DWORD dwKeySpec = 0;
HCRYPTPROV hCryptProv = NULL;
bRet = CryptAcquireCertificatePrivateKey(pCertContext,
CRYPT_ACQUIRE_SILENT_FLAG | CRYPT_ACQUIRE_COMPARE_KEY_FLAG,
NULL, &hCryptProv, &dwKeySpec, NULL);
if (!bRet)
return HRESULT_FROM_WIN32(GetLastError());
// make sure provider stays around for duration of the test run. We need hCryptProv of root cert to sign local certs
CryptContextAddRef(hCryptProv, NULL, 0);
// everything succeeded, safe to set outparam
*ppCert = pCertContext;
if (NULL != phCryptProv)
*phCryptProv = hCryptProv;
return S_OK;
}
// helper function to write the cert store out to a file
HRESULT WriteStoreToFile(__in HCERTSTORE hCertStore, __in PCWSTR pwzFileName)
{
BOOL bRet = FALSE;
FILE* pFile = NULL;
errno_t err;
CRYPT_DATA_BLOB blob = { sizeof(s_fileBuf), s_fileBuf};
bRet = PFXExportCertStore(hCertStore, &blob, L"DRT Rocks!", EXPORT_PRIVATE_KEYS);
if (!bRet)
{
return HRESULT_FROM_WIN32(GetLastError());
}
err = _wfopen_s(&pFile, pwzFileName, L"wb");
if (err)
{
return CRYPT_E_FILE_ERROR;
}
fwrite(blob.pbData, blob.cbData, 1, pFile);
fclose(pFile);
return S_OK;
}
// helper function used by make certs to encode a name for storage in a cert (modified from drt\test\drtcert\main.cpp)
HRESULT EncodeName(__in PCWSTR pwzName, DWORD* pcbEncodedName, BYTE* pbEncodedName)
{
CERT_NAME_INFO nameInfo = {0};
CERT_RDN rdn = {0};
CERT_RDN_ATTR rdnAttr = {0};
nameInfo.cRDN = 1;
nameInfo.rgRDN = &rdn;
rdn.cRDNAttr = 1;
rdn.rgRDNAttr = &rdnAttr;
rdnAttr.dwValueType = CERT_RDN_UNICODE_STRING;
rdnAttr.pszObjId = szOID_COMMON_NAME;
rdnAttr.Value.pbData = (BYTE*)pwzName;
rdnAttr.Value.cbData = sizeof(WCHAR) * (DWORD)(wcslen(pwzName) + 1);
if (!CryptEncodeObject(DEFAULT_ENCODING, X509_NAME, &nameInfo, pbEncodedName, pcbEncodedName))
{
return HRESULT_FROM_WIN32(GetLastError());
}
return S_OK;
}
// manufacture a single cert and export it to a file
HRESULT MakeAndExportACert(PCWSTR pwzSignerName, PCWSTR pwzCertName, PCWSTR pwzFileName, HCERTSTORE hCertStore,
HCRYPTPROV hCryptProvSigner, HCRYPTPROV hCryptProvThisCert,
CERT_CONTEXT* pIssuerCertContext)
{
HRESULT hr = S_OK;
BOOL bRet = FALSE;
BYTE byteBuf1[ONE_K] = {0};
BYTE byteBuf2[ONE_K] = {0};
DWORD cSignerName = sizeof(byteBuf1);
BYTE* pbSignerName = byteBuf1;
DWORD cCertName = sizeof(byteBuf2);
BYTE* pbCertName = byteBuf2;
CERT_INFO certInfo = {0};
BYTE serialNumberBuf[16];
ULONGLONG ullTime = 0;
XCERT_CONTEXT pCertContext;
ULONG cchContainer = 512;
WCHAR wzContainer[512];
CRYPT_KEY_PROV_INFO keyInfo = {0};
// encode the names for use in a cert
hr = EncodeName(pwzSignerName, &cSignerName, pbSignerName);
if (FAILED(hr))
return hr;
hr = EncodeName(pwzCertName, &cCertName, pbCertName);
if (FAILED(hr))
return hr;
// first retrieve the public key from the hCryptProv (which abstracts the key pair)
DWORD dwSize = sizeof(s_keyDataBuf);
bRet = CryptExportPublicKeyInfo(hCryptProvThisCert, DEFAULT_KEY_SPEC, DEFAULT_ENCODING,
reinterpret_cast<CERT_PUBLIC_KEY_INFO*>(s_keyDataBuf), &dwSize);
if (FALSE == bRet)
return HRESULT_FROM_WIN32(GetLastError());
// set the cert properties
certInfo.dwVersion = CERT_V3;
certInfo.SerialNumber.cbData = sizeof(serialNumberBuf);
certInfo.SerialNumber.pbData = serialNumberBuf;
certInfo.SignatureAlgorithm.pszObjId = DEFAULT_ALGORITHM;
GetSystemTimeAsFileTime((FILETIME*)&ullTime);
ullTime -= SECOND_IN_FILETIME * 60 * 60 * 24;
CopyMemory(&certInfo.NotBefore, &ullTime, sizeof(FILETIME));
ullTime += SECOND_IN_FILETIME * 60 * 60 * 24 * 365;
CopyMemory(&certInfo.NotAfter, &ullTime, sizeof(FILETIME));
certInfo.Issuer.cbData = cSignerName;
certInfo.Issuer.pbData = pbSignerName;
certInfo.Subject.cbData = cCertName;
certInfo.Subject.pbData = pbCertName;
certInfo.SubjectPublicKeyInfo = *(reinterpret_cast<CERT_PUBLIC_KEY_INFO*>(s_keyDataBuf));
// create the cert
DWORD cCertBuf = sizeof(s_certBuf);
bRet = CryptSignAndEncodeCertificate(hCryptProvSigner, // Crypto provider
DEFAULT_KEY_SPEC, // Key spec, we always use the same
DEFAULT_ENCODING, // Encoding type, default
X509_CERT_TO_BE_SIGNED, // Structure type - certificate
&certInfo, // Structure information
&certInfo.SignatureAlgorithm, // Signature algorithm
NULL, // reserved, must be NULL
s_certBuf, // hopefully it will fit in 1K
&cCertBuf);
if (!bRet)
return HRESULT_FROM_WIN32(GetLastError());
// retrieve the cert context. pCertContext gets a pointer into the crypto api heap, we must treat it as read only.
// pCertContext must be freed with CertFreeCertificateContext(p); we use a smart pointer to do the free
pCertContext = (CERT_CONTEXT*)CertCreateCertificateContext(DEFAULT_ENCODING, s_certBuf, cCertBuf);
if (NULL == pCertContext)
return HRESULT_FROM_WIN32(GetLastError());
// next attach the private key
// =================
// retrieve container name
hr = GetContainerName(hCryptProvThisCert, &cchContainer, wzContainer);
if (FAILED(hr))
return hr;
// set up key info struct for CAPI call
keyInfo.pwszContainerName = wzContainer;
keyInfo.pwszProvName = NULL;
keyInfo.dwProvType = DEFAULT_PROV_TYPE;
keyInfo.dwKeySpec = DEFAULT_KEY_SPEC;
// attach private key
bRet = CertSetCertificateContextProperty(pCertContext, CERT_KEY_PROV_INFO_PROP_ID, 0, &keyInfo);
if (!bRet)
return HRESULT_FROM_WIN32(GetLastError());
// put the cert into the store
bRet = CertAddCertificateContextToStore(hCertStore, pCertContext, CERT_STORE_ADD_NEW, NULL);
if (!bRet)
return HRESULT_FROM_WIN32(GetLastError());
// make sure the issuer cert is also in the store, if we have an issuer for the cert (ie, the non self signed case)
if (pIssuerCertContext)
{
bRet = CertAddCertificateContextToStore(hCertStore, pIssuerCertContext, CERT_STORE_ADD_NEW, NULL);
if (!bRet)
return HRESULT_FROM_WIN32(GetLastError());
}
// now export the cert to a file
hr = WriteStoreToFile(hCertStore, pwzFileName);
return hr;
}
// using cryptoApi, make a cert and export it. If there is an existing root cert, this will
// use the existing root cert to sign the local cert.
// export a local cert to the file <currentdir>\LocalCert.cer
// export a root cert to the file <currentDir>\RootCert.cer (if one does not already exist)
HRESULT MakeCert(LPCWSTR pwzLocalCertFileName, LPCWSTR pwzLocalCertName,
LPCWSTR pwzIssuerCertFileName, LPCWSTR pwzIssuerCertName)
{
HRESULT hr = S_OK;
XHCERTSTORE hSelfCertStore;
XHCERTSTORE hSignedCertStore;
XHCRYPTPROV hCryptProvIssuer;
XHCRYPTPROV hCryptProvThis;
XCERT_CONTEXT pIssuerCert;
// If there is an issuer cert, make sure it exists
if (pwzIssuerCertFileName)
{
hr = ReadCertFromFile(pwzIssuerCertFileName, &pIssuerCert, &hCryptProvIssuer);
if (FAILED(hr))
return hr;
}
// create this cert key pair (util function from peernet\common)
hr = CreateCryptProv(NULL, TRUE, &hCryptProvThis);
if (FAILED(hr))
return hr;
// create cert store
hSelfCertStore = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, NULL,
CERT_STORE_CREATE_NEW_FLAG | CERT_STORE_NO_CRYPT_RELEASE_FLAG, NULL);
if (NULL == hSelfCertStore)
return HRESULT_FROM_WIN32(GetLastError());
// Make the self signed cert, and save it to a file
hr = MakeAndExportACert(pwzLocalCertName, pwzLocalCertName, pwzLocalCertFileName, hSelfCertStore, hCryptProvThis, hCryptProvThis, NULL);
if (FAILED(hr))
return hr;
// then, sign it if an issuer name was supplied
// (surprisingly, the same function does both, since it adds a signing record to existing cert)
// FUTURE: this is a bit inefficient, since we write the file twice, we can add a fWrite paramater, and not write the file when it is false
if (pwzIssuerCertFileName)
{
// must create a separate store, or we end up with a single cert and a chain cert in same store, apps can pick wrong cert
hSignedCertStore = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, NULL,
CERT_STORE_CREATE_NEW_FLAG | CERT_STORE_NO_CRYPT_RELEASE_FLAG, NULL);
if (NULL == hSignedCertStore)
return HRESULT_FROM_WIN32(GetLastError());
hr = MakeAndExportACert(pwzIssuerCertName, pwzLocalCertName, pwzLocalCertFileName, hSignedCertStore, hCryptProvIssuer, hCryptProvThis, pIssuerCert);
}
return hr;
}