WindowsXP-SP1/enduser/netmeeting/nmmkcert/nmmkcert.cpp

1269 lines
37 KiB
C++

//+-------------------------------------------------------------------------
//
// NmMkCert - NetMeeting internal certificate generator
//
// Generates NetMeeting default user certificates. The NetMeeting
// root key and certificate are stored as a program resource.
//
// ClausGi 7/29/98 created based on MAKECERT
//
//--------------------------------------------------------------------------
#include "global.h"
#include <oprahcom.h>
#ifdef DEBUG
HDBGZONE ghDbgZone = NULL;
static PTCHAR _rgZonesNmMkCert[] = { TEXT("nmmkcert"), };
#endif /* DEBUG */
//+-------------------------------------------------------------------------
// contants
//--------------------------------------------------------------------------
//allow max 10 extensions per certificate
#define MAX_EXT_CNT 10
//+-------------------------------------------------------------------------
// globals
//--------------------------------------------------------------------------
WCHAR* g_wszSubjectKey = L"_NmMkCert";
WCHAR* g_wszSubjectStore = WSZNMSTORE;
DWORD g_dwSubjectStoreFlag = CERT_SYSTEM_STORE_CURRENT_USER;
DWORD g_dwIssuerKeySpec = AT_SIGNATURE;
DWORD g_dwSubjectKeySpec = AT_KEYEXCHANGE;
WCHAR *g_wszSubjectDisplayName = NULL; // BUGBUG set this?
LPWSTR g_wszIssuerProviderName = NULL;
LPWSTR g_wszSubjectProviderName = NULL;
WCHAR* g_wszSubjectX500Name;
DWORD g_dwProvType = PROV_RSA_FULL;
HMODULE hModule=NULL;
BOOL MakeCert(DWORD dwFlags);
BOOL WINAPI DllMain(HINSTANCE hDllInst, DWORD fdwReason, LPVOID)
{
switch (fdwReason)
{
case DLL_PROCESS_ATTACH:
{
hModule = hDllInst;
ASSERT (hModule != NULL);
DBGINIT(&ghDbgZone, _rgZonesNmMkCert);
DisableThreadLibraryCalls (hDllInst);
DBG_INIT_MEMORY_TRACKING(hDllInst);
break;
}
case DLL_PROCESS_DETACH:
{
DBG_CHECK_MEMORY_TRACKING(hDllInst);
hModule = NULL;
break;
}
default:
break;
}
return (TRUE);
}
//
// X.509 cert strings must be from X.208 printable character set... this
// function enforces that.
//
static const char szPrintable[] = " '()+,-./:=?\""; // along with A-Za-z0-9
VOID MkPrintableString ( LPSTR szString )
{
CHAR * p = szString;
while ( *p )
{
if (!(('a' <= *p && *p <='z') ||
('A' <= *p && *p <='Z') ||
('0' <= *p && *p <='9') ||
_StrChr(szPrintable,*p)))
{
*p = '-';
}
p++;
}
}
DWORD WINAPI NmMakeCert( LPCSTR szFirstName,
LPCSTR szLastName,
LPCSTR szEmailName,
LPCSTR szCity,
LPCSTR szCountry,
DWORD flags)
{
DWORD dwRet = -1;
WARNING_OUT(("NmMakeCert called"));
// Form the unencoded X500 subject string. It would be nice to
// use official constants for the below... CertRDNValueToString?
UINT cbX500Name = ( szFirstName ? lstrlen(szFirstName) : 0 ) +
( szLastName ? lstrlen(szLastName) : 0 ) +
( szEmailName ? lstrlen(szEmailName) : 0 ) +
( szCity ? lstrlen(szCity) : 0 ) +
( szCountry ? lstrlen(szCountry) : 0 ) +
128; // Extra is for RDN OID strings: CN= etc.
char * pX500Name = new char[cbX500Name];
if ( NULL == pX500Name )
{
ERROR_OUT(("couldn't allocate %d bytes for x500 name", cbX500Name));
goto cleanup;
}
ASSERT( ( szFirstName && *szFirstName ) || ( szLastName && *szLastName ) );
wsprintf( pX500Name, "CN=\"%s %s\"", szFirstName ? szFirstName : "", szLastName ? szLastName : "" );
if ( szEmailName && *szEmailName )
wsprintf( pX500Name + lstrlen(pX500Name), ", E=\"%s\"", szEmailName );
if ( szCity && *szCity )
wsprintf( pX500Name + lstrlen(pX500Name), ", S=\"%s\"", szCity );
if ( szCountry && *szCountry )
wsprintf( pX500Name + lstrlen(pX500Name), ", C=\"%s\"", szCountry );
MkPrintableString ( pX500Name );
g_wszSubjectX500Name = AnsiToUnicode ( pX500Name );
ASSERT(g_wszSubjectX500Name);
if ( flags & NMMKCERT_F_LOCAL_MACHINE )
{
// We are being asked to generate a local machine cert...
// change the subject store flag and the key container name
g_dwSubjectStoreFlag = CERT_SYSTEM_STORE_LOCAL_MACHINE;
g_wszSubjectKey = L"_NmMkMchCert";
}
// If we're on NT5 we have to generate the cert using the
// PROV_RSA_SCHANNEL provider, on other platforms this provider type
// doesn't exist.
OSVERSIONINFO osVersion;
ZeroMemory(&osVersion, sizeof(osVersion));
osVersion.dwOSVersionInfoSize = sizeof(osVersion);
GetVersionEx(&osVersion);
if (osVersion.dwPlatformId == VER_PLATFORM_WIN32_NT &&
osVersion.dwMajorVersion >= 5)
{
g_dwProvType = PROV_RSA_SCHANNEL;
}
// Get to work and make the certificate
if (!MakeCert(flags))
{
WARNING_OUT(("NmMakeCert failed."));
}
else
{
dwRet = 1;
}
cleanup:
if ( NULL != g_wszSubjectX500Name )
{
delete g_wszSubjectX500Name;
}
if ( NULL != pX500Name )
{
delete pX500Name;
}
return dwRet;
}
// RUNDLL entry point for certificate uninstall... the prototype is given
// by RUNDLL32.EXE requirements!
void CALLBACK NmMakeCertCleanup ( HWND hwnd, HINSTANCE hinst, LPSTR lpszCmdLine, int nCmdShow )
{
// Clean up exisint certs and private keys
MakeCert(NMMKCERT_F_CLEANUP_ONLY);
g_dwSubjectStoreFlag = CERT_SYSTEM_STORE_LOCAL_MACHINE;
g_wszSubjectKey = L"_NmMkMchCert";
MakeCert(NMMKCERT_F_LOCAL_MACHINE|NMMKCERT_F_CLEANUP_ONLY);
}
//+=========================================================================
// Local Support Functions
//==========================================================================
//+=========================================================================
// MakeCert support functions
//==========================================================================
BOOL VerifyIssuerKey( IN HCRYPTPROV hProv,
IN PCERT_PUBLIC_KEY_INFO pIssuerKeyInfo);
HCRYPTPROV GetSubjectProv(OUT LPWSTR *ppwszTmpContainer);
BOOL GetPublicKey(
HCRYPTPROV hProv,
PCERT_PUBLIC_KEY_INFO *ppPubKeyInfo
);
BOOL EncodeSubject(
OUT BYTE **ppbEncoded,
IN OUT DWORD *pcbEncoded
);
BOOL CreateSpcCommonName(
OUT BYTE **ppbEncoded,
IN OUT DWORD *pcbEncoded
);
BOOL CreateEnhancedKeyUsage(
OUT BYTE **ppbEncoded,
IN OUT DWORD *pcbEncoded
);
BOOL SaveCertToStore(HCRYPTPROV hProv,
HCERTSTORE hStore,
DWORD dwFlag,
BYTE *pbEncodedCert,
DWORD cbEncodedCert,
LPWSTR wszPvk,
DWORD dwKeySpecification,
LPWSTR wszCapiProv,
DWORD dwCapiProvType);
//+-------------------------------------------------------------------------
// Get the root's certificate from the program's resources
//--------------------------------------------------------------------------
PCCERT_CONTEXT GetRootCertContext()
{
PCCERT_CONTEXT pCert = NULL;
HRSRC hRes;
//
// The root certificate is stored as a resource of ours.
// Load it...
//
if (0 != (hRes = FindResource(hModule, MAKEINTRESOURCE(IDR_ROOTCERTIFICATE),
"CER"))) {
HGLOBAL hglobRes;
if (NULL != (hglobRes = LoadResource(hModule, hRes))) {
BYTE *pbRes;
DWORD cbRes;
cbRes = SizeofResource(hModule, hRes);
pbRes = (BYTE *) LockResource(hglobRes);
if (cbRes && pbRes)
pCert = CertCreateCertificateContext(X509_ASN_ENCODING,
pbRes, cbRes);
if ( NULL == pCert )
{
DWORD dwError = GetLastError();
}
UnlockResource(hglobRes);
FreeResource(hglobRes);
}
}
if (pCert == NULL)
{
ERROR_OUT(("Error creating root cert: %x", GetLastError()));
}
return pCert;
}
//+-------------------------------------------------------------------------
// Get the root's private key from the program's resources and create
// a temporary key provider container
//--------------------------------------------------------------------------
HCRYPTPROV GetRootProv(OUT LPWSTR *ppwszTmpContainer)
{
HCRYPTPROV hProv = 0;
HRSRC hRes;
WCHAR wszRootSig[] = L"Root Signature";
*ppwszTmpContainer = NULL;
if (0 != (hRes = FindResource(hModule,MAKEINTRESOURCE(IDR_PVKROOT),"PVK")))
{
HGLOBAL hglobRes;
if (NULL != (hglobRes = LoadResource(hModule, hRes))) {
BYTE *pbRes;
DWORD cbRes;
cbRes = SizeofResource(hModule, hRes);
pbRes = (BYTE *) LockResource(hglobRes);
if (cbRes && pbRes) {
PvkPrivateKeyAcquireContextFromMemory(
g_wszIssuerProviderName,
PROV_RSA_FULL,
pbRes,
cbRes,
NULL, // hwndOwner
wszRootSig,
&g_dwIssuerKeySpec,
&hProv
);
}
UnlockResource(hglobRes);
FreeResource(hglobRes);
}
}
if (hProv == 0)
{
ERROR_OUT(("couldn't create root key provider: %x", GetLastError()));
}
return hProv;
}
//+-------------------------------------------------------------------------
// Make the subject certificate. If the subject doesn't have a private
// key, then, create.
//--------------------------------------------------------------------------
BOOL MakeCert(DWORD dwFlags)
{
BOOL fResult;
HCRYPTPROV hIssuerProv = 0;
LPWSTR pwszTmpIssuerContainer = NULL;
PCCERT_CONTEXT pIssuerCertContext = NULL;
PCERT_INFO pIssuerCert =NULL; // not allocated
HCRYPTPROV hSubjectProv = 0;
LPWSTR pwszTmpSubjectContainer = NULL;
PCERT_PUBLIC_KEY_INFO pSubjectPubKeyInfo = NULL; // not allocated
PCERT_PUBLIC_KEY_INFO pAllocSubjectPubKeyInfo = NULL;
BYTE *pbSubjectEncoded = NULL;
DWORD cbSubjectEncoded =0;
BYTE *pbSpcCommonNameEncoded = NULL;
DWORD cbSpcCommonNameEncoded =0;
BYTE *pbCertEncoded = NULL;
DWORD cbCertEncoded =0;
BYTE *pbEKUEncoded = NULL;
DWORD cbEKUEncoded = 0;
CERT_INFO Cert;
GUID SerialNumber;
HCERTSTORE hStore=NULL;
CERT_EXTENSION rgExt[MAX_EXT_CNT];
DWORD cExt = 0;
CRYPT_ALGORITHM_IDENTIFIER SignatureAlgorithm = {
szOID_RSA_MD5RSA, 0, 0
};
if (0 == (hSubjectProv = GetSubjectProv(&pwszTmpSubjectContainer)))
goto ErrorReturn;
#define TEMP_CLEAN_CODE
#ifdef TEMP_CLEAN_CODE
// open the system store where we used to generate certs
hStore=CertOpenStore(CERT_STORE_PROV_SYSTEM_W,
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
hSubjectProv,
CERT_STORE_NO_CRYPT_RELEASE_FLAG | g_dwSubjectStoreFlag,
L"MY" );
if ( hStore )
{
//
// Delete all old certs
//
PCCERT_CONTEXT pCertContext = NULL;
// Clear out any certificate(s) we may have added before
while ( pCertContext = CertEnumCertificatesInStore(
hStore, (PCERT_CONTEXT)pCertContext ))
{
DWORD dwMagic;
DWORD cbMagic;
cbMagic = sizeof(dwMagic);
if (CertGetCertificateContextProperty(pCertContext,
CERT_FIRST_USER_PROP_ID, &dwMagic, &cbMagic) &&
cbMagic == sizeof(dwMagic) && dwMagic == NMMKCERT_MAGIC )
{
CertDeleteCertificateFromStore(pCertContext);
// Restart the enumeration
pCertContext = NULL;
continue;
}
}
CertCloseStore(hStore,0);
}
#endif // TEMP_CLEAN_CODE
// open a new cert store
hStore=CertOpenStore(CERT_STORE_PROV_SYSTEM_W,
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
hSubjectProv,
CERT_STORE_NO_CRYPT_RELEASE_FLAG | g_dwSubjectStoreFlag,
g_wszSubjectStore);
if(hStore==NULL)
goto ErrorReturn;
// Empty the store
PCCERT_CONTEXT pCertContext;
while ( pCertContext = CertEnumCertificatesInStore ( hStore, NULL ))
{
if ( !CertDeleteCertificateFromStore ( pCertContext ))
{
WARNING_OUT(("Failed to delete certificate: %x", GetLastError()));
break;
}
}
// If NMMKCERT_F_CLEANUP_ONLY is set, we are done
if ( dwFlags & NMMKCERT_F_CLEANUP_ONLY )
{
// We've just deleted the existing certs, now delete the
// private key container and exit.
CryptAcquireContextU(
&hSubjectProv,
g_wszSubjectKey,
g_wszSubjectProviderName,
g_dwProvType,
CRYPT_DELETEKEYSET |
( g_dwSubjectStoreFlag == CERT_SYSTEM_STORE_LOCAL_MACHINE ?
CRYPT_MACHINE_KEYSET : 0 ));
fResult = TRUE;
goto CommonReturn;
}
//
// Get access to the subject's (public) key, creating it if necessary
//
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
//
hIssuerProv= GetRootProv(&pwszTmpIssuerContainer);
if (NULL == (pIssuerCertContext = GetRootCertContext()))
goto ErrorReturn;
pIssuerCert = pIssuerCertContext->pCertInfo;
if (!VerifyIssuerKey(hIssuerProv, &pIssuerCert->SubjectPublicKeyInfo))
goto ErrorReturn;
//
// Update the CERT_INFO
//
ClearStruct(&Cert);
Cert.dwVersion = CERT_V3;
CoCreateGuid(&SerialNumber);
Cert.SerialNumber.pbData = (BYTE *) &SerialNumber;
Cert.SerialNumber.cbData = sizeof(SerialNumber);
Cert.SignatureAlgorithm = SignatureAlgorithm;
Cert.Issuer.pbData = pIssuerCert->Subject.pbData;
Cert.Issuer.cbData = pIssuerCert->Subject.cbData;
{
SYSTEMTIME st;
// Valid starting now...
GetSystemTimeAsFileTime(&Cert.NotBefore);
// Ending in 2039 (arbitrarily)
ClearStruct(&st);
st.wYear = 2039;
st.wMonth = 12;
st.wDay = 31;
st.wHour = 23;
st.wMinute= 59;
st.wSecond= 59;
SystemTimeToFileTime(&st, &Cert.NotAfter);
}
Cert.Subject.pbData = pbSubjectEncoded;
Cert.Subject.cbData = cbSubjectEncoded;
Cert.SubjectPublicKeyInfo = *pSubjectPubKeyInfo;
// Cert Extensions
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 (g_wszSubjectDisplayName) {
if (!CreateSpcCommonName(
&pbSpcCommonNameEncoded,
&cbSpcCommonNameEncoded))
goto ErrorReturn;
rgExt[cExt].pszObjId = szOID_COMMON_NAME;
rgExt[cExt].fCritical = FALSE;
rgExt[cExt].Value.pbData = pbSpcCommonNameEncoded;
rgExt[cExt].Value.cbData = cbSpcCommonNameEncoded;
cExt++;
}
Cert.rgExtension = rgExt;
Cert.cExtension = cExt;
//
// Sign and encode the certificate
//
cbCertEncoded = 0;
CryptSignAndEncodeCertificate(
hIssuerProv,
g_dwIssuerKeySpec,
X509_ASN_ENCODING,
X509_CERT_TO_BE_SIGNED,
&Cert,
&Cert.SignatureAlgorithm,
NULL, // pvHashAuxInfo
NULL, // pbEncoded
&cbCertEncoded
);
if (cbCertEncoded == 0) {
ERROR_OUT(("CryptSignAndEncodeCertificate failed: %x", GetLastError()));
goto ErrorReturn;
}
pbCertEncoded = new BYTE[cbCertEncoded];
if (pbCertEncoded == NULL) goto ErrorReturn;
if (!CryptSignAndEncodeCertificate(
hIssuerProv,
g_dwIssuerKeySpec,
X509_ASN_ENCODING,
X509_CERT_TO_BE_SIGNED,
&Cert,
&Cert.SignatureAlgorithm,
NULL, // pvHashAuxInfo
pbCertEncoded,
&cbCertEncoded
)) {
ERROR_OUT(("CryptSignAndEncodeCertificate(2) failed: %x", GetLastError()));
goto ErrorReturn;
}
// Output the encoded certificate to an cerificate store
ASSERT(g_wszSubjectStore);
ASSERT(AT_KEYEXCHANGE == g_dwSubjectKeySpec);
if((!SaveCertToStore(hSubjectProv,
hStore,
g_dwSubjectStoreFlag,
pbCertEncoded,
cbCertEncoded,
g_wszSubjectKey,
g_dwSubjectKeySpec,
g_wszSubjectProviderName,
g_dwProvType)))
{
ERROR_OUT(("SaveCertToStore failed: %x", GetLastError()));
goto ErrorReturn;
}
fResult = TRUE;
goto CommonReturn;
ErrorReturn:
fResult = FALSE;
CommonReturn:
PvkFreeCryptProv(hSubjectProv, g_wszSubjectProviderName,
g_dwProvType,pwszTmpSubjectContainer);
//free the cert store
if(hStore)
CertCloseStore(hStore, 0);
if (pIssuerCertContext)
CertFreeCertificateContext(pIssuerCertContext);
if (pAllocSubjectPubKeyInfo)
delete (pAllocSubjectPubKeyInfo);
if (pbSubjectEncoded)
delete (pbSubjectEncoded);
if (pbEKUEncoded)
delete (pbEKUEncoded);
if (pbSpcCommonNameEncoded)
delete (pbSpcCommonNameEncoded);
if (pbCertEncoded)
delete (pbCertEncoded);
if (hIssuerProv)
CryptReleaseContext(hIssuerProv,0);
return fResult;
}
//+-------------------------------------------------------------------------
// save the certificate to a certificate store. Attach private key information
// to the certificate
//--------------------------------------------------------------------------
BOOL SaveCertToStore(
HCRYPTPROV hProv,
HCERTSTORE hStore, DWORD dwFlag,
BYTE *pbEncodedCert, DWORD cbEncodedCert,
LPWSTR wszPvk,
DWORD dwKeySpecification,
LPWSTR wszCapiProv, DWORD dwCapiProvType)
{
BOOL fResult=FALSE;
PCCERT_CONTEXT pCertContext=NULL;
CRYPT_KEY_PROV_INFO KeyProvInfo;
HCRYPTPROV hDefaultProvName=NULL;
DWORD cbData=0;
LPSTR pszName=NULL;
LPWSTR pwszName=NULL;
//init
ClearStruct(&KeyProvInfo);
//add the encoded certificate to store
if(!CertAddEncodedCertificateToStore(
hStore,
X509_ASN_ENCODING,
pbEncodedCert,
cbEncodedCert,
CERT_STORE_ADD_REPLACE_EXISTING,
&pCertContext))
goto CLEANUP;
//add properties to the certificate
KeyProvInfo.pwszContainerName=wszPvk;
KeyProvInfo.pwszProvName=wszCapiProv,
KeyProvInfo.dwProvType=dwCapiProvType,
KeyProvInfo.dwKeySpec=dwKeySpecification;
if ( g_dwSubjectStoreFlag == CERT_SYSTEM_STORE_LOCAL_MACHINE )
{
// If this is a local machine cert, set the keyset flags
// indicating that the private key will be under HKLM
KeyProvInfo.dwFlags = CRYPT_MACHINE_KEYSET;
}
ASSERT(AT_KEYEXCHANGE == dwKeySpecification);
//if wszCapiProv is NULL, we get the default provider name
if(NULL==wszCapiProv)
{
//get the default provider
if(CryptAcquireContext(&hDefaultProvName,
NULL,
NULL,
KeyProvInfo.dwProvType,
CRYPT_VERIFYCONTEXT))
{
//get the provider name
if(CryptGetProvParam(hDefaultProvName,
PP_NAME,
NULL,
&cbData,
0) && (0!=cbData))
{
if(pszName= new CHAR[cbData])
{
if(CryptGetProvParam(hDefaultProvName,
PP_NAME,
(BYTE *)pszName,
&cbData,
0))
{
pwszName= AnsiToUnicode(pszName);
KeyProvInfo.pwszProvName=pwszName;
}
}
}
}
}
//free the provider as we want
if(hDefaultProvName)
CryptReleaseContext(hDefaultProvName, 0);
hDefaultProvName=NULL;
//add property related to the key container
if(!CertSetCertificateContextProperty(
pCertContext,
CERT_KEY_PROV_INFO_PROP_ID,
0,
&KeyProvInfo))
goto CLEANUP;
//
// Load the display name from resource and create a blob to
// set the cert friendly name.
//
CHAR szFriendlyName[128];
if (!LoadString(hModule, IDS_DEFNAME, szFriendlyName,
sizeof(szFriendlyName)))
{
ERROR_OUT(("LoadString failed: %d", GetLastError()));
goto CLEANUP;
}
WCHAR *pwszFriendlyName;
pwszFriendlyName = AnsiToUnicode ( szFriendlyName );
if ( NULL == pwszFriendlyName )
{
ERROR_OUT(("AnsiToUnicode failed"));
goto CLEANUP;
}
CRYPT_DATA_BLOB FriendlyName;
FriendlyName.pbData = (PBYTE)pwszFriendlyName;
FriendlyName.cbData = ( lstrlenW(pwszFriendlyName) + 1 ) *
sizeof(WCHAR);
if(!CertSetCertificateContextProperty(
pCertContext,
CERT_FRIENDLY_NAME_PROP_ID,
0,
&FriendlyName))
goto CLEANUP;
//
// Add magic ID
//
CRYPT_DATA_BLOB MagicBlob;
DWORD dwMagic;
dwMagic = NMMKCERT_MAGIC;
MagicBlob.pbData = (PBYTE)&dwMagic;
MagicBlob.cbData = sizeof(dwMagic);
if(!CertSetCertificateContextProperty(
pCertContext,
CERT_FIRST_USER_PROP_ID,
0,
&MagicBlob))
goto CLEANUP;
fResult=TRUE;
CLEANUP:
if (pwszFriendlyName)
delete pwszFriendlyName;
//free the cert context
if(pCertContext)
CertFreeCertificateContext(pCertContext);
if(pszName)
delete (pszName);
if(pwszName)
delete pwszName;
if(hDefaultProvName)
CryptReleaseContext(hDefaultProvName, 0);
return fResult;
}
//+-------------------------------------------------------------------------
// Verify the issuer's certificate. The public key in the certificate
// must match the public key associated with the private key in the
// issuer's provider
//--------------------------------------------------------------------------
BOOL VerifyIssuerKey(
IN HCRYPTPROV hProv,
IN PCERT_PUBLIC_KEY_INFO pIssuerKeyInfo
)
{
BOOL fResult;
PCERT_PUBLIC_KEY_INFO pPubKeyInfo = NULL;
DWORD cbPubKeyInfo;
// Get issuer's public key
cbPubKeyInfo = 0;
CryptExportPublicKeyInfo(
hProv,
g_dwIssuerKeySpec,
X509_ASN_ENCODING,
NULL, // pPubKeyInfo
&cbPubKeyInfo
);
if (cbPubKeyInfo == 0)
{
ERROR_OUT(("CryptExportPublicKeyInfo failed: %x", GetLastError()));
goto ErrorReturn;
}
if (NULL == (pPubKeyInfo = (PCERT_PUBLIC_KEY_INFO) new BYTE[cbPubKeyInfo]))
goto ErrorReturn;
if (!CryptExportPublicKeyInfo(
hProv,
g_dwIssuerKeySpec,
X509_ASN_ENCODING,
pPubKeyInfo,
&cbPubKeyInfo
)) {
ERROR_OUT(("CrypteExportPublicKeyInfo(2) failed: %x", GetLastError()));
goto ErrorReturn;
}
if (!CertComparePublicKeyInfo(
X509_ASN_ENCODING,
pIssuerKeyInfo,
pPubKeyInfo)) {
// BUGBUG:: This might be the test root with an incorrectly
// encoded public key. Convert to the capi representation and
// compare.
BYTE rgProvKey[256]; //BUGBUG needs appropriate constant or calc
BYTE rgCertKey[256]; //BUGBUG needs appropriate constant or calc
DWORD cbProvKey = sizeof(rgProvKey);
DWORD cbCertKey = sizeof(rgCertKey);
if (!CryptDecodeObject(X509_ASN_ENCODING, RSA_CSP_PUBLICKEYBLOB,
pIssuerKeyInfo->PublicKey.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) {
ERROR_OUT(("mismatch: %x", GetLastError()));
goto ErrorReturn;
}
}
fResult = TRUE;
goto CommonReturn;
ErrorReturn:
fResult = FALSE;
CommonReturn:
if (pPubKeyInfo)
delete (pPubKeyInfo);
return fResult;
}
//+-------------------------------------------------------------------------
// Get the subject's private key provider
//--------------------------------------------------------------------------
HCRYPTPROV GetSubjectProv(OUT LPWSTR *ppwszTmpContainer)
{
HCRYPTPROV hProv=0;
WCHAR wszKeyName[40] = L"Subject Key";
int ids;
WCHAR *wszRegKeyName=NULL;
BOOL fResult;
HCRYPTKEY hKey=NULL;
GUID TmpContainerUuid;
//try to get the hProv from the private key container
if(S_OK != PvkGetCryptProv(NULL,
wszKeyName,
g_wszSubjectProviderName,
g_dwProvType,
NULL,
g_wszSubjectKey,
&g_dwSubjectKeySpec,
ppwszTmpContainer,
&hProv))
hProv=0;
//generate the private keys
if (0 == hProv)
{
//now that we have to generate private keys, generate
//AT_KEYEXCHANGE key
// If there is an existing container with the name of the
// one we are about to create, attempt to delete it first so
// that creating it won't fail. This should only happen if the
// container exists but we were unable to acquire a context to
// it previously.
CryptAcquireContextU(
&hProv,
g_wszSubjectKey,
g_wszSubjectProviderName,
g_dwProvType,
CRYPT_DELETEKEYSET |
( g_dwSubjectStoreFlag == CERT_SYSTEM_STORE_LOCAL_MACHINE ?
CRYPT_MACHINE_KEYSET : 0 ));
// Open a new key container
if (!CryptAcquireContextU(
&hProv,
g_wszSubjectKey,
g_wszSubjectProviderName,
g_dwProvType,
CRYPT_NEWKEYSET |
( g_dwSubjectStoreFlag == CERT_SYSTEM_STORE_LOCAL_MACHINE ?
CRYPT_MACHINE_KEYSET : 0 )))
{
ERROR_OUT(("CryptAcquireContext failed: %x", GetLastError()));
goto CreateKeyError;
}
//generate new keys in the key container - make sure its EXPORTABLE
//for SCHANNEL! (Note: remove that when SCHANNEL no longer needs it).
if (!CryptGenKey( hProv, g_dwSubjectKeySpec, CRYPT_EXPORTABLE, &hKey))
{
ERROR_OUT(("CryptGenKey failed: %x", GetLastError()));
goto CreateKeyError;
}
else
CryptDestroyKey(hKey);
//try to get the user key
if (CryptGetUserKey( hProv, g_dwSubjectKeySpec, &hKey))
{
CryptDestroyKey(hKey);
}
else
{
// Doesn't have the specified public key
CryptReleaseContext(hProv, 0);
hProv=0;
}
if (0 == hProv )
{
ERROR_OUT(("sub key error: %x", GetLastError()));
goto ErrorReturn;
}
} //hProv==0
goto CommonReturn;
CreateKeyError:
ErrorReturn:
if (hProv)
{
CryptReleaseContext(hProv, 0);
hProv = 0;
}
CommonReturn:
if(wszRegKeyName)
delete (wszRegKeyName);
return hProv;
}
//+-------------------------------------------------------------------------
// Allocate and get the public key info for the provider
//--------------------------------------------------------------------------
BOOL GetPublicKey(
HCRYPTPROV hProv,
PCERT_PUBLIC_KEY_INFO *ppPubKeyInfo
)
{
BOOL fResult;
PCERT_PUBLIC_KEY_INFO pPubKeyInfo = NULL;
DWORD cbPubKeyInfo;
cbPubKeyInfo = 0;
CryptExportPublicKeyInfo(
hProv,
g_dwSubjectKeySpec,
X509_ASN_ENCODING,
NULL, // pPubKeyInfo
&cbPubKeyInfo
);
if (cbPubKeyInfo == 0) {
ERROR_OUT(("CryptExportPublicKeyInfo failed: %x", GetLastError()));
goto ErrorReturn;
}
if (NULL == (pPubKeyInfo = (PCERT_PUBLIC_KEY_INFO) new BYTE[cbPubKeyInfo]))
goto ErrorReturn;
if (!CryptExportPublicKeyInfo(
hProv,
g_dwSubjectKeySpec,
X509_ASN_ENCODING,
pPubKeyInfo,
&cbPubKeyInfo
)) {
ERROR_OUT(("CryptExportPublicKeyInfo(2) failed: %x", GetLastError()));
goto ErrorReturn;
}
fResult = TRUE;
goto CommonReturn;
ErrorReturn:
fResult = FALSE;
if (pPubKeyInfo) {
delete (pPubKeyInfo);
pPubKeyInfo = NULL;
}
CommonReturn:
*ppPubKeyInfo = pPubKeyInfo;
return fResult;
}
//+-------------------------------------------------------------------------
// Convert and encode the subject's X500 formatted name
//--------------------------------------------------------------------------
BOOL EncodeSubject(
OUT BYTE **ppbEncoded,
IN OUT DWORD *pcbEncoded
)
{
BOOL fResult;
DWORD cbEncodedSubject=0;
BYTE *pbEncodedSubject=NULL;
BYTE *pbEncoded = NULL;
DWORD cbEncoded;
//encode the wszSubjectX500Name into an encoded X509_NAME
if(!CertStrToNameW(
X509_ASN_ENCODING,
g_wszSubjectX500Name,
0,
NULL,
NULL,
&cbEncodedSubject,
NULL))
{
ERROR_OUT(("CertStrToNameW failed: %x", GetLastError()));
goto ErrorReturn;
}
pbEncodedSubject = new BYTE[cbEncodedSubject];
if (pbEncodedSubject == NULL) goto ErrorReturn;
if(!CertStrToNameW(
X509_ASN_ENCODING,
g_wszSubjectX500Name,
0,
NULL,
pbEncodedSubject,
&cbEncodedSubject,
NULL))
{
ERROR_OUT(("CertStrToNameW(2) failed: %x", GetLastError()));
goto ErrorReturn;
}
cbEncoded=cbEncodedSubject;
pbEncoded=pbEncodedSubject;
fResult = TRUE;
goto CommonReturn;
ErrorReturn:
if (pbEncoded) {
delete (pbEncoded);
pbEncoded = NULL;
}
cbEncoded = 0;
fResult = FALSE;
CommonReturn:
*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
//--------------------------------------------------------------------------
BOOL CreateEnhancedKeyUsage(
OUT BYTE **ppbEncoded,
IN OUT DWORD *pcbEncoded
)
{
BOOL fResult = TRUE;
LPBYTE pbEncoded =NULL;
DWORD cbEncoded;
PCERT_ENHKEY_USAGE pUsage =NULL;
//
// Allocate a cert enhanced key usage structure and fill it in
//
pUsage = (PCERT_ENHKEY_USAGE) new BYTE[sizeof(CERT_ENHKEY_USAGE) +
2 * sizeof(LPSTR)];
if ( pUsage != NULL )
{
pUsage->cUsageIdentifier = 2;
pUsage->rgpszUsageIdentifier = (LPSTR *)((LPBYTE)pUsage+sizeof(CERT_ENHKEY_USAGE));
pUsage->rgpszUsageIdentifier[0] = szOID_PKIX_KP_CLIENT_AUTH;
pUsage->rgpszUsageIdentifier[1] = szOID_PKIX_KP_SERVER_AUTH;
}
else
{
fResult = FALSE;
}
//
// Encode the usage
//
if ( fResult == TRUE )
{
fResult = CryptEncodeObject(
X509_ASN_ENCODING,
szOID_ENHANCED_KEY_USAGE,
pUsage,
NULL,
&cbEncoded
);
if ( fResult == TRUE )
{
pbEncoded = new BYTE[cbEncoded];
if ( pbEncoded != NULL )
{
fResult = CryptEncodeObject(
X509_ASN_ENCODING,
szOID_ENHANCED_KEY_USAGE,
pUsage,
pbEncoded,
&cbEncoded
);
}
else
{
fResult = FALSE;
}
}
}
//
// Cleanup
//
delete (pUsage);
if ( fResult == TRUE )
{
*ppbEncoded = pbEncoded;
*pcbEncoded = cbEncoded;
}
else
{
delete (pbEncoded);
}
return( fResult );
}
BOOL CreateSpcCommonName(
OUT BYTE **ppbEncoded,
IN OUT DWORD *pcbEncoded
)
{
BOOL fResult;
BYTE *pbEncoded = NULL;
DWORD cbEncoded;
CERT_NAME_VALUE NameValue;
NameValue.dwValueType = CERT_RDN_UNICODE_STRING;
NameValue.Value.pbData = (BYTE *) g_wszSubjectDisplayName;
NameValue.Value.cbData =0;
cbEncoded = 0;
CryptEncodeObject(X509_ASN_ENCODING, X509_UNICODE_NAME_VALUE,
&NameValue,
NULL, // pbEncoded
&cbEncoded
);
if (cbEncoded == 0) {
ERROR_OUT(("CryptEncodeObject failed: %x", GetLastError()));
goto ErrorReturn;
}
pbEncoded = new BYTE[cbEncoded];
if (pbEncoded == NULL) goto ErrorReturn;
if (!CryptEncodeObject(X509_ASN_ENCODING, X509_UNICODE_NAME_VALUE,
&NameValue,
pbEncoded,
&cbEncoded
)) {
ERROR_OUT(("CryptEncodeObject failed: %x", GetLastError()));
goto ErrorReturn;
}
fResult = TRUE;
goto CommonReturn;
ErrorReturn:
if (pbEncoded) {
delete (pbEncoded);
pbEncoded = NULL;
}
cbEncoded = 0;
fResult = FALSE;
CommonReturn:
*ppbEncoded = pbEncoded;
*pcbEncoded = cbEncoded;
return fResult;
}