windows-nt/Source/XPSP1/NT/termsrv/remdsk/rds/cert/pvkutil.cpp
2020-09-26 16:20:57 +08:00

424 lines
11 KiB
C++

#include "global.h"
//+-------------------------------------------------------------------------
// Private Key file definitions
//
// The file consists of the FILE_HDR followed by cbEncryptData optional
// bytes used to encrypt the private key and then the private key.
// The private key is encrypted according to dwEncryptType.
//
// The public key is included with the private key.
//--------------------------------------------------------------------------
typedef struct _FILE_HDR {
DWORD dwMagic;
DWORD dwVersion;
DWORD dwKeySpec;
DWORD dwEncryptType;
DWORD cbEncryptData;
DWORD cbPvk;
} FILE_HDR, *PFILE_HDR;
// BUGBUG: enum from pvk.h?
#ifndef ENTER_PASSWORD
#define ENTER_PASSWORD 0
#endif // ENTER_PASSWORD
#define PVK_FILE_VERSION_0 0
#define PVK_MAGIC 0xb0b5f11e
// Private key encrypt types
#define PVK_NO_ENCRYPT 0
#define MAX_PVK_FILE_LEN 4096
typedef BOOL (* PFNREAD)(HANDLE h, void * p, DWORD cb);
extern DWORD g_dwSubjectStoreFlag;
//+-------------------------------------------------------------------------
// Read & Write to memory fucntion
//--------------------------------------------------------------------------
typedef struct _MEMINFO {
BYTE * pb;
DWORD cb;
DWORD cbSeek;
} MEMINFO, * PMEMINFO;
static BOOL ReadFromMemory(
IN HANDLE h,
IN void * p,
IN DWORD cb
)
{
PMEMINFO pMemInfo = (PMEMINFO) h;
if (pMemInfo->cbSeek + cb <= pMemInfo->cb) {
// copy the bytes
memcpy(p, &pMemInfo->pb[pMemInfo->cbSeek], cb);
pMemInfo->cbSeek += cb;
return TRUE;
} else {
SetLastError(ERROR_END_OF_MEDIA);
return FALSE;
}
}
//+-------------------------------------------------------------------------
// 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<cb; i++) {
int b;
b = (*pb & 0xF0) >> 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;
}
#define UUID_WSTR_BYTES ((sizeof(GUID) * 2 + 1) * sizeof(WCHAR))
//-------------------------------------------------------------------------
//
// Call GetLastError and convert the return code to HRESULT
//--------------------------------------------------------------------------
HRESULT WINAPI SignError ()
{
DWORD dw = GetLastError ();
HRESULT hr;
if ( dw <= (DWORD) 0xFFFF )
hr = HRESULT_FROM_WIN32 ( dw );
else
hr = dw;
if ( ! FAILED ( hr ) )
{
// somebody failed a call without properly setting an error condition
hr = E_UNEXPECTED;
}
return hr;
}
static BOOL LoadKeyW(
IN HCRYPTPROV hCryptProv,
IN HANDLE hRead,
IN PFNREAD pfnRead,
IN DWORD cbKeyData,
IN HWND hwndOwner,
IN LPCWSTR pwszKeyName,
IN DWORD dwFlags,
IN OUT OPTIONAL DWORD *pdwKeySpec
)
{
BOOL fResult;
FILE_HDR Hdr;
HCRYPTKEY hKey = 0;
BYTE *pbPvk = NULL;
DWORD cbPvk;
// Read the file header and verify
if (!pfnRead(hRead, &Hdr, sizeof(Hdr)))
{
ERROR_OUT(("can't read in-memory pvk file hdr"));
goto BadPvkFile;
}
ASSERT( Hdr.dwMagic == PVK_MAGIC );
// Treat as a "normal" private key file
cbPvk = Hdr.cbPvk;
if (Hdr.dwVersion != PVK_FILE_VERSION_0 ||
Hdr.cbEncryptData > MAX_PVK_FILE_LEN ||
cbPvk == 0 || cbPvk > MAX_PVK_FILE_LEN)
goto BadPvkFile;
if (pdwKeySpec) {
DWORD dwKeySpec = *pdwKeySpec;
*pdwKeySpec = Hdr.dwKeySpec;
if (dwKeySpec && dwKeySpec != Hdr.dwKeySpec) {
SetLastError(PVK_HELPER_WRONG_KEY_TYPE);
goto ErrorReturn;
}
}
// Allocate and read the private key
if (NULL == (pbPvk = new BYTE[cbPvk]))
goto ErrorReturn;
if (!pfnRead(hRead, pbPvk, cbPvk))
goto BadPvkFile;
ASSERT(Hdr.dwEncryptType == PVK_NO_ENCRYPT);
// Decrypt and import the private key
if (!CryptImportKey(hCryptProv, pbPvk, cbPvk, 0, dwFlags,
&hKey))
goto ErrorReturn;
fResult = TRUE;
goto CommonReturn;
BadPvkFile:
SetLastError(PVK_HELPER_BAD_PVK_FILE);
if (pdwKeySpec)
*pdwKeySpec = 0;
ErrorReturn:
fResult = FALSE;
CommonReturn:
if (pbPvk)
delete (pbPvk);
if (hKey)
CryptDestroyKey(hKey);
return fResult;
}
static BOOL AcquireKeyContextW(
IN LPCWSTR pwszProvName,
IN DWORD dwProvType,
IN HANDLE hRead,
IN PFNREAD pfnRead,
IN DWORD cbKeyData,
IN HWND hwndOwner,
IN LPCWSTR pwszKeyName,
IN OUT OPTIONAL DWORD *pdwKeySpec,
OUT HCRYPTPROV *phCryptProv
)
{
BOOL fResult;
HCRYPTPROV hProv = 0;
GUID TmpContainerUuid;
LPWSTR pwszTmpContainer = NULL;
// Create a temporary keyset to load the private key into
// UuidCreate(&TmpContainerUuid);
if (CoCreateGuid((GUID *)&TmpContainerUuid) != S_OK)
{
goto ErrorReturn;
}
if (NULL == (pwszTmpContainer = (LPWSTR) new BYTE[
6 * sizeof(WCHAR) + UUID_WSTR_BYTES]))
goto ErrorReturn;
LStrCpyW(pwszTmpContainer, L"TmpKey");
BytesToWStr(sizeof(UUID), &TmpContainerUuid, pwszTmpContainer + 6);
if (!CryptAcquireContextU(
&hProv,
pwszTmpContainer,
pwszProvName,
dwProvType,
CRYPT_NEWKEYSET |
( g_dwSubjectStoreFlag == CERT_SYSTEM_STORE_LOCAL_MACHINE ?
CRYPT_MACHINE_KEYSET : 0 )))
goto ErrorReturn;
if (!LoadKeyW(
hProv,
hRead,
pfnRead,
cbKeyData,
hwndOwner,
pwszKeyName,
0, // dwFlags
pdwKeySpec
))
goto DeleteKeySetReturn;
fResult = TRUE;
goto CommonReturn;
DeleteKeySetReturn:
CryptReleaseContext(hProv, 0);
CryptAcquireContextU(
&hProv,
pwszTmpContainer,
pwszProvName,
dwProvType,
CRYPT_DELETEKEYSET
);
hProv = 0;
ErrorReturn:
if (hProv) {
CryptReleaseContext(hProv, 0);
hProv = 0;
}
fResult = FALSE;
CommonReturn:
if (pwszTmpContainer) {
delete (pwszTmpContainer);
}
*phCryptProv = hProv;
return fResult;
}
//+-------------------------------------------------------------------------
// Creates a temporary container in the provider and loads the private key
// from memory.
// For success, returns a handle to a cryptographic provider for the private
// key and the name of the temporary container. PrivateKeyReleaseContext must
// be called to release the hCryptProv and delete the temporary container.
//
// PrivateKeyLoadFromMemory is called to load the private key into the
// temporary container.
//--------------------------------------------------------------------------
BOOL
WINAPI
PvkPrivateKeyAcquireContextFromMemory(
IN LPCWSTR pwszProvName,
IN DWORD dwProvType,
IN BYTE *pbData,
IN DWORD cbData,
IN HWND hwndOwner,
IN LPCWSTR pwszKeyName,
IN OUT OPTIONAL DWORD *pdwKeySpec,
OUT HCRYPTPROV *phCryptProv
)
{
HRESULT hr = S_OK;
if(FAILED(hr))
return FALSE;
MEMINFO MemInfo;
MemInfo.pb = pbData;
MemInfo.cb = cbData;
MemInfo.cbSeek = 0;
BOOL fhr = AcquireKeyContextW(
pwszProvName,
dwProvType,
(HANDLE) &MemInfo,
ReadFromMemory,
cbData,
hwndOwner,
pwszKeyName,
pdwKeySpec,
phCryptProv
);
return fhr;
}
//+-------------------------------------------------------------------------
// Releases the cryptographic provider and deletes the temporary container
// created by PrivateKeyAcquireContext or PrivateKeyAcquireContextFromMemory.
//--------------------------------------------------------------------------
BOOL
WINAPI
PvkPrivateKeyReleaseContext(
IN HCRYPTPROV hCryptProv,
IN LPCWSTR pwszProvName,
IN DWORD dwProvType,
IN LPWSTR pwszTmpContainer
)
{
HRESULT hr = S_OK;
if (hCryptProv)
CryptReleaseContext(hCryptProv, 0);
if (pwszTmpContainer) {
// Delete the temporary container for the private key from
// the provider
//
// Note: for CRYPT_DELETEKEYSET, the returned hCryptProv is undefined
// and must not be released.
CryptAcquireContextU(
&hCryptProv,
pwszTmpContainer,
pwszProvName,
dwProvType,
CRYPT_DELETEKEYSET
);
delete (pwszTmpContainer);
}
return TRUE;
}
//+-------------------------------------------------------------------------
// Get crypto provider to based on either the pvkfile or key container name
//--------------------------------------------------------------------------
HRESULT WINAPI PvkGetCryptProv( IN HWND hwnd,
IN LPCWSTR pwszCaption,
IN LPCWSTR pwszCapiProvider,
IN DWORD dwProviderType,
IN LPCWSTR pwszPvkFile,
IN LPCWSTR pwszKeyContainerName,
IN DWORD *pdwKeySpec,
OUT LPWSTR *ppwszTmpContainer,
OUT HCRYPTPROV *phCryptProv)
{
HANDLE hFile=NULL;
HRESULT hr=E_FAIL;
DWORD dwRequiredKeySpec=0;
//Init
*ppwszTmpContainer=NULL;
*phCryptProv=NULL;
//get the provider handle based on the key container name
if(!CryptAcquireContextU(phCryptProv,
pwszKeyContainerName,
pwszCapiProvider,
dwProviderType,
( g_dwSubjectStoreFlag == CERT_SYSTEM_STORE_LOCAL_MACHINE ?
CRYPT_MACHINE_KEYSET : 0 )))
return SignError();
dwRequiredKeySpec=*pdwKeySpec;
//make sure *pdwKeySpec is the correct key spec
HCRYPTKEY hPubKey;
if (CryptGetUserKey(
*phCryptProv,
dwRequiredKeySpec,
&hPubKey
))
{
CryptDestroyKey(hPubKey);
*pdwKeySpec=dwRequiredKeySpec;
return S_OK;
}
else
{
// Doesn't have the specified public key
hr=SignError();
CryptReleaseContext(*phCryptProv, 0);
*phCryptProv=NULL;
return hr;
}
}
void WINAPI PvkFreeCryptProv(IN HCRYPTPROV hProv,
IN LPCWSTR pwszCapiProvider,
IN DWORD dwProviderType,
IN LPWSTR pwszTmpContainer)
{
if (pwszTmpContainer) {
// Delete the temporary container for the private key from
// the provider
PvkPrivateKeyReleaseContext(hProv,
pwszCapiProvider,
dwProviderType,
pwszTmpContainer);
} else {
if (hProv)
CryptReleaseContext(hProv, 0);
}
}