//+------------------------------------------------------------------------- // // Microsoft Windows // // Copyright (C) Microsoft Corporation, 1995 - 1999 // // File: xcertlib.cpp // // Contents: most functions are moved and modofied from certsrv library // // History: 03-2000 xtan created //-------------------------------------------------------------------------- #include #include #include #include #include "xelib.h" // Crypt callback versions: must have certain signatures LPVOID myCryptAlloc_LocalAlloc(size_t cbSize) { return myAlloc(cbSize, CERTLIB_USE_LOCALALLOC); } VOID myCryptAlloc_LocalFree(VOID* pv) { myFree(pv, CERTLIB_USE_LOCALALLOC); } LPVOID myCryptAlloc_CoTaskMemAlloc(size_t cbSize) { return myAlloc(cbSize, CERTLIB_USE_COTASKMEMALLOC); } VOID myCryptAlloc_CoTaskMemFree(VOID* pv) { myFree(pv, CERTLIB_USE_COTASKMEMALLOC); } // give callers an easy way to choose what to call: pick allocator based on allocation type PFN_CRYPT_ALLOC PickAlloc(CERTLIB_ALLOCATOR allocType) { if (allocType == CERTLIB_USE_LOCALALLOC) return myCryptAlloc_LocalAlloc; else if (allocType == CERTLIB_USE_COTASKMEMALLOC) return myCryptAlloc_CoTaskMemAlloc; CSASSERT(!"Bad allocType"); return NULL; } PFN_CRYPT_FREE PickFree(CERTLIB_ALLOCATOR allocType) { if (allocType == CERTLIB_USE_LOCALALLOC) return myCryptAlloc_LocalFree; else if (allocType == CERTLIB_USE_COTASKMEMALLOC) return myCryptAlloc_CoTaskMemFree; CSASSERT(!"Bad allocType"); return NULL; } VOID * myAlloc(IN size_t cbBytes, IN CERTLIB_ALLOCATOR allocType) { void *pv; switch (allocType) { case CERTLIB_USE_LOCALALLOC: pv = LocalAlloc(LMEM_FIXED, cbBytes); break; case CERTLIB_USE_COTASKMEMALLOC: pv = CoTaskMemAlloc(cbBytes); break; default: CSASSERT(FALSE); pv = NULL; break; } if (NULL == pv) { _PrintError(E_OUTOFMEMORY, "myAlloc"); SetLastError((DWORD) E_OUTOFMEMORY); } return(pv); } VOID myFree(IN void *pv, IN CERTLIB_ALLOCATOR allocType) { switch(allocType) { case CERTLIB_USE_LOCALALLOC: LocalFree(pv); break; case CERTLIB_USE_COTASKMEMALLOC: CoTaskMemFree(pv); break; default: CSASSERT(FALSE); break; } } HRESULT myHError(HRESULT hr) { CSASSERT(S_FALSE != hr); if (S_OK != hr && S_FALSE != hr && !FAILED(hr)) { hr = HRESULT_FROM_WIN32(hr); if (0 == HRESULT_CODE(hr)) { // A call failed without properly setting an error condition! hr = E_UNEXPECTED; } CSASSERT(FAILED(hr)); } return(hr); } HRESULT myHLastError(VOID) { return(myHError(GetLastError())); } #ifdef _XENROLL_SRC_ typedef BOOL (WINAPI * PFNCryptEncodeObjectEx) (IN DWORD dwCertEncodingType, IN LPCSTR lpszStructType, IN const void *pvStructInfo, IN DWORD dwFlags, IN OPTIONAL PCRYPT_ENCODE_PARA pEncodePara, OUT void *pvEncoded, IN OUT DWORD *pcbEncoded); typedef BOOL (WINAPI * PFNCryptDecodeObjectEx) (IN DWORD dwCertEncodingType, IN LPCSTR lpszStructType, IN const BYTE *pbEncoded, IN DWORD cbEncoded, IN DWORD dwFlags, IN OPTIONAL PCRYPT_DECODE_PARA pDecodePara, OUT OPTIONAL void *pvStructInfo, IN OUT DWORD *pcbStructInfo); #endif //_XENROLL_SRC_ BOOL myEncodeObject( DWORD dwEncodingType, IN LPCSTR lpszStructType, IN VOID const *pvStructInfo, IN DWORD dwFlags, IN CERTLIB_ALLOCATOR allocType, OUT BYTE **ppbEncoded, OUT DWORD *pcbEncoded) { BOOL b = FALSE; CSASSERT(NULL != ppbEncoded); CRYPT_ENCODE_PARA sAllocator; sAllocator.cbSize = sizeof(sAllocator); sAllocator.pfnAlloc = PickAlloc(allocType); sAllocator.pfnFree = PickFree(allocType); #ifdef _XENROLL_SRC_ PFNCryptEncodeObjectEx pfnCryptEncodeObjectEx = NULL; HMODULE hModule = GetModuleHandle("crypt32.dll"); if (NULL != hModule) { pfnCryptEncodeObjectEx = (PFNCryptEncodeObjectEx) GetProcAddress(hModule, "CryptEncodeObjectEx"); if (NULL != pfnCryptEncodeObjectEx) { b = pfnCryptEncodeObjectEx( dwEncodingType, lpszStructType, const_cast(pvStructInfo), dwFlags|CRYPT_ENCODE_ALLOC_FLAG, &sAllocator, ppbEncoded, pcbEncoded); } } #else b = CryptEncodeObjectEx( dwEncodingType, lpszStructType, const_cast(pvStructInfo), dwFlags|CRYPT_ENCODE_ALLOC_FLAG, &sAllocator, ppbEncoded, pcbEncoded); if (b && 0 == *pcbEncoded) { SetLastError((DWORD) HRESULT_FROM_WIN32(ERROR_INVALID_DATA)); b = FALSE; } #endif //_XENROLL_SRC_ return(b); } BOOL myDecodeObject( IN DWORD dwEncodingType, IN LPCSTR lpszStructType, IN BYTE const *pbEncoded, IN DWORD cbEncoded, IN CERTLIB_ALLOCATOR allocType, OUT VOID **ppvStructInfo, OUT DWORD *pcbStructInfo) { BOOL b = FALSE; CRYPT_DECODE_PARA sAllocator; sAllocator.cbSize = sizeof(sAllocator); sAllocator.pfnAlloc = PickAlloc(allocType); sAllocator.pfnFree = PickFree(allocType); #ifdef _XENROLL_SRC_ PFNCryptDecodeObjectEx pfnCryptDecodeObjectEx = NULL; HMODULE hModule = GetModuleHandle("crypt32.dll"); if (NULL != hModule) { pfnCryptDecodeObjectEx = (PFNCryptDecodeObjectEx) GetProcAddress(hModule, "CryptDecodeObjectEx"); if (NULL != pfnCryptDecodeObjectEx) { b = pfnCryptDecodeObjectEx( dwEncodingType, lpszStructType, pbEncoded, cbEncoded, CRYPT_DECODE_ALLOC_FLAG, // dwFlags &sAllocator, ppvStructInfo, pcbStructInfo); } } #else b = CryptDecodeObjectEx( dwEncodingType, lpszStructType, pbEncoded, cbEncoded, CRYPT_DECODE_ALLOC_FLAG, // dwFlags &sAllocator, ppvStructInfo, pcbStructInfo); if (b && 0 == *pcbStructInfo) { SetLastError((DWORD) HRESULT_FROM_WIN32(ERROR_INVALID_DATA)); b = FALSE; } #endif //_XENROLL_SRC_ return(b); } HRESULT myDecodePKCS7( IN BYTE const *pbIn, IN DWORD cbIn, OPTIONAL OUT BYTE **ppbContents, OPTIONAL OUT DWORD *pcbContents, OPTIONAL OUT DWORD *pdwMsgType, OPTIONAL OUT char **ppszInnerContentObjId, OPTIONAL OUT DWORD *pcSigner, OPTIONAL OUT DWORD *pcRecipient, OPTIONAL OUT HCERTSTORE *phStore, OPTIONAL OUT HCRYPTMSG *phMsg) { HRESULT hr; BYTE *pbContents = NULL; HCERTSTORE hStore = NULL; HCRYPTMSG hMsg = NULL; DWORD cbContents; char *pszInnerContentObjId = NULL; DWORD cb; if (NULL != ppszInnerContentObjId) { *ppszInnerContentObjId = NULL; } if (NULL != pcSigner) { *pcSigner = 0; } if (NULL != pcRecipient) { *pcRecipient = 0; } if (NULL != ppbContents) { *ppbContents = NULL; } if (NULL != phStore) { *phStore = NULL; } if (NULL != phMsg) { *phMsg = NULL; } if (NULL != phStore) { CRYPT_DATA_BLOB blobPKCS7; blobPKCS7.pbData = (BYTE *) pbIn; blobPKCS7.cbData = cbIn; hStore = CertOpenStore( CERT_STORE_PROV_PKCS7, PKCS_7_ASN_ENCODING | X509_ASN_ENCODING, NULL, // hCryptProv 0, // dwFlags &blobPKCS7); if (NULL == hStore) { hr = myHLastError(); _JumpError(hr, error, "CertOpenStore"); // _JumpError2(hr, error, "CertOpenStore", CRYPT_E_ASN1_BADTAG); } } hMsg = CryptMsgOpenToDecode( PKCS_7_ASN_ENCODING | X509_ASN_ENCODING, 0, // dwFlags 0, // dwMsgType NULL, // hCryptProv NULL, // pRecipientInfo NULL); // pStreamInfo if (NULL == hMsg) { hr = myHLastError(); _JumpError(hr, error, "CryptMsgOpenToDecode"); } if (!CryptMsgUpdate(hMsg, pbIn, cbIn, TRUE)) { hr = myHLastError(); _JumpError(hr, error, "CryptMsgUpdate"); } hr = myCryptMsgGetParam( hMsg, CMSG_INNER_CONTENT_TYPE_PARAM, 0, // dwIndex CERTLIB_USE_LOCALALLOC, (VOID **) &pszInnerContentObjId, &cb); _PrintIfError(hr, "myCryptMsgGetParam(inner content type)"); #if 0 DBGPRINT(( DBG_SS_CERTLIBI, "pszInnerContentObjId = %hs\n", pszInnerContentObjId)); #endif //0 cbContents = 0; hr = myCryptMsgGetParam( hMsg, CMSG_CONTENT_PARAM, 0, // dwIndex CERTLIB_USE_LOCALALLOC, (VOID **) &pbContents, &cbContents); _JumpIfError(hr, error, "myCryptMsgGetParam(content)"); if (NULL != pdwMsgType) { cb = sizeof(*pdwMsgType); if (!CryptMsgGetParam( hMsg, CMSG_TYPE_PARAM, 0, pdwMsgType, &cb)) { hr = myHLastError(); _JumpError(hr, error, "CryptMsgGetParam(type)"); } } if (NULL != pcSigner) { cb = sizeof(*pcSigner); if (!CryptMsgGetParam( hMsg, CMSG_SIGNER_COUNT_PARAM, 0, pcSigner, &cb)) { hr = myHLastError(); *pcSigner = 0; if (CRYPT_E_INVALID_MSG_TYPE != hr) { _JumpError(hr, error, "CryptMsgGetParam(signer count)"); } } } if (NULL != pcRecipient) { cb = sizeof(*pcRecipient); if (!CryptMsgGetParam( hMsg, CMSG_RECIPIENT_COUNT_PARAM, 0, pcRecipient, &cb)) { hr = myHLastError(); *pcRecipient = 0; if (CRYPT_E_INVALID_MSG_TYPE != hr) { _JumpError(hr, error, "CryptMsgGetParam(recipient count)"); } } } if (NULL != phMsg) { *phMsg = hMsg; hMsg = NULL; } if (NULL != phStore) { *phStore = hStore; hStore = NULL; } if (NULL != ppszInnerContentObjId) { *ppszInnerContentObjId = pszInnerContentObjId; pszInnerContentObjId = NULL; } if (NULL != pcbContents) { *pcbContents = cbContents; } if (NULL != ppbContents && 0 != cbContents) { *ppbContents = pbContents; pbContents = NULL; } hr = S_OK; error: if (NULL != hMsg) { CryptMsgClose(hMsg); } if (NULL != hStore) { CertCloseStore(hStore, CERT_CLOSE_STORE_CHECK_FLAG); } if (NULL != pbContents) { LocalFree(pbContents); } if (NULL != pszInnerContentObjId) { LocalFree(pszInnerContentObjId); } return(hr); } HRESULT myDupString( IN WCHAR const *pwszIn, IN WCHAR **ppwszOut) { DWORD cb; HRESULT hr; cb = (wcslen(pwszIn) + 1) * sizeof(WCHAR); *ppwszOut = (WCHAR *) LocalAlloc(LMEM_FIXED, cb); if (NULL == *ppwszOut) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "LocalAlloc"); } CopyMemory(*ppwszOut, pwszIn, cb); hr = S_OK; error: return(hr); } HRESULT myAddNameSuffix( IN WCHAR const *pwszValue, IN WCHAR const *pwszSuffix, IN DWORD cwcNameMax, OUT WCHAR **ppwszOut) { HRESULT hr; DWORD cwcValue = wcslen(pwszValue); DWORD cwcSuffix = wcslen(pwszSuffix); WCHAR *pwszOut; *ppwszOut = NULL; pwszOut = (WCHAR *) LocalAlloc( LMEM_FIXED, sizeof(WCHAR) * (1 + cwcValue + cwcSuffix)); if (NULL == pwszOut) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "LocalAlloc"); } CSASSERT(cwcNameMax > cwcSuffix); if (cwcValue > cwcNameMax - cwcSuffix) { cwcValue = cwcNameMax - cwcSuffix; } wcscpy(pwszOut, pwszValue); wcscpy(&pwszOut[cwcValue], pwszSuffix); *ppwszOut = pwszOut; hr = S_OK; error: return(hr); } #define OCTECTSPACES VOID MultiByteStringSize( IN BOOL fOctetString, IN BYTE const *pbIn, IN OUT DWORD *pcbIn, OUT DWORD *pcbString) { DWORD cbIn = *pcbIn; DWORD cbString; if (!fOctetString) { while (1 < cbIn && 0 == pbIn[cbIn - 1]) { cbIn--; } } // Two ascii-hex characters per byte, plus the null terminator: cbString = ((2 * cbIn) + 1) * sizeof(WCHAR); #ifdef OCTECTSPACES // Allow for separating spaces after each byte except the last: if (fOctetString && 1 < cbIn) { cbString += (cbIn - 1) * sizeof(WCHAR); } #endif // OCTECTSPACES *pcbIn = cbIn; *pcbString = cbString; } __inline WCHAR NibbleToAscii( IN BYTE b) { return(L"0123456789abcdef"[b & 0x0f]); } // MultiByteIntegerToWszBuf - convert a little-endian integer blob to // a big endian null-terminated ascii-hex encoded WCHAR string of even length. HRESULT MultiByteIntegerToWszBuf( IN BOOL fOctetString, IN DWORD cbIn, IN BYTE const *pbIn, IN OUT DWORD *pcbOut, OPTIONAL OUT WCHAR *pwszOut) { HRESULT hr = S_OK; DWORD cbOut; BYTE const *pbEnd = &pbIn[cbIn]; MultiByteStringSize(fOctetString, pbIn, &cbIn, &cbOut); if (NULL != pwszOut) { BYTE const *pb; if (cbOut > *pcbOut) { hr = TYPE_E_BUFFERTOOSMALL; _JumpError(hr, error, "MultiByteIntegerToWsz: buffer overflow"); } if (fOctetString) { for (pb = pbIn; pb < pbEnd; pb++) { *pwszOut++ = NibbleToAscii(*pb >> 4); *pwszOut++ = NibbleToAscii(*pb); #ifdef OCTECTSPACES if (pb + 1 < pbEnd) { *pwszOut++ = L' '; } #endif // OCTECTSPACES } } else { for (pb = pbEnd; pb-- > pbIn; ) { *pwszOut++ = NibbleToAscii(*pb >> 4); *pwszOut++ = NibbleToAscii(*pb); } } *pwszOut = L'\0'; CSASSERT( (SAFE_SUBTRACT_POINTERS(pwszOut, pwsz) + 1) * sizeof(WCHAR) == cbOut); } *pcbOut = cbOut; error: return(hr); } // MultiByteIntegerToBstr - convert a little-endian integer blob to // a big endian null-terminated ascii-hex encoded BSTR of even length. // If fOctetString is TRUE, preserve endian order, as in a hex dump HRESULT MultiByteIntegerToBstr( IN BOOL fOctetString, IN DWORD cbIn, IN BYTE const *pbIn, OUT BSTR *pstrOut) { HRESULT hr = S_OK; BSTR str = NULL; DWORD cbOut; MultiByteStringSize(fOctetString, pbIn, &cbIn, &cbOut); str = SysAllocStringByteLen(NULL, cbOut - sizeof(WCHAR)); if (NULL == str) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "SysAllocStringLen"); } hr = MultiByteIntegerToWszBuf(fOctetString, cbIn, pbIn, &cbOut, str); _JumpIfError(hr, error, "MultiByteIntegerToWszBuf"); CSASSERT((wcslen(str) + 1) * sizeof(WCHAR) == cbOut); CSASSERT(SysStringByteLen(str) + sizeof(WCHAR) == cbOut); if (NULL != *pstrOut) { SysFreeString(*pstrOut); } *pstrOut = str; str = NULL; error: if (NULL != str) { SysFreeString(str); } return(hr); } BOOL myCryptExportPublicKeyInfo( IN HCRYPTPROV hCryptProv, IN DWORD dwKeySpec, // AT_SIGNATURE | AT_KEYEXCHANGE IN CERTLIB_ALLOCATOR allocType, OUT CERT_PUBLIC_KEY_INFO **ppPubKey, OUT DWORD *pcbPubKey) { BOOL b; *ppPubKey = NULL; *pcbPubKey = 0; for (;;) { b = CryptExportPublicKeyInfo( hCryptProv, dwKeySpec, X509_ASN_ENCODING, *ppPubKey, pcbPubKey); if (b && 0 == *pcbPubKey) { SetLastError((DWORD) HRESULT_FROM_WIN32(ERROR_INVALID_DATA)); b = FALSE; } if (!b) { if (NULL != *ppPubKey) { myFree(*ppPubKey, allocType); *ppPubKey = NULL; } break; } if (NULL != *ppPubKey) { break; } *ppPubKey = (CERT_PUBLIC_KEY_INFO *) myAlloc(*pcbPubKey, allocType); if (NULL == *ppPubKey) { b = FALSE; break; } } return(b); } VOID myMakeExprDateTime( IN OUT FILETIME *pft, IN LONG lDelta, IN enum ENUM_PERIOD enumPeriod) { LONGLONG llDelta; BOOL fSysTimeDelta; llDelta = lDelta; fSysTimeDelta = FALSE; switch (enumPeriod) { case ENUM_PERIOD_WEEKS: llDelta *= CVT_WEEKS; break; case ENUM_PERIOD_DAYS: llDelta *= CVT_DAYS; break; case ENUM_PERIOD_HOURS: llDelta *= CVT_HOURS; break; case ENUM_PERIOD_MINUTES: llDelta *= CVT_MINUTES; break; case ENUM_PERIOD_SECONDS: break; //case ENUM_PERIOD_MONTHS: //case ENUM_PERIOD_YEARS: default: // Avoid side effect of round trip SYSTEMTIME conversion // (avoid truncating microseconds) if lDelta is zero. if (0 != lDelta) { fSysTimeDelta = TRUE; } break; } if (fSysTimeDelta) { SYSTEMTIME SystemTime; FileTimeToSystemTime(pft, &SystemTime); switch (enumPeriod) { case ENUM_PERIOD_MONTHS: if (0 > lDelta) { DWORD dwDelta = (DWORD) -lDelta; SystemTime.wYear -= (WORD) (dwDelta / 12) + 1; SystemTime.wMonth += 12 - (WORD) (dwDelta % 12); } else { SystemTime.wMonth = (WORD) (SystemTime.wMonth + lDelta); } if (12 < SystemTime.wMonth) { SystemTime.wYear += (SystemTime.wMonth - 1) / 12; SystemTime.wMonth = ((SystemTime.wMonth - 1) % 12) + 1; } break; case ENUM_PERIOD_YEARS: SystemTime.wYear = (WORD) (SystemTime.wYear + lDelta); break; default: SystemTime.wYear += 1; break; } DoConvert: if (!SystemTimeToFileTime(&SystemTime, pft)) { if (GetLastError() != ERROR_INVALID_PARAMETER) { CSASSERT(!"Unable to do time conversion"); return; } // In some cases we'll convert to an invalid month-end // only one month changes length from year to year if (SystemTime.wMonth == 2) { // > 29? try leap year if (SystemTime.wDay > 29) { SystemTime.wDay = 29; goto DoConvert; } // == 29? try non-leap year else if (SystemTime.wDay == 29) { SystemTime.wDay = 28; goto DoConvert; } } // sept (9), apr(4), jun(6), nov(11) all have 30 days else if ((SystemTime.wMonth == 9) || (SystemTime.wMonth == 4) || (SystemTime.wMonth == 6) || (SystemTime.wMonth == 11)) { if (SystemTime.wDay > 30) { SystemTime.wDay = 30; goto DoConvert; } } // should never get here CSASSERT(!"Month/year processing: inaccessible code"); return; } } else { *(LONGLONG UNALIGNED *) pft += llDelta * CVT_BASE; } } BOOL myCryptSignCertificate( IN HCRYPTPROV hCryptProv, IN DWORD dwKeySpec, IN DWORD dwEncodingType, IN BYTE const *pbEncodedToBeSigned, IN DWORD cbEncodedToBeSigned, IN CRYPT_ALGORITHM_IDENTIFIER const *pSignatureAlgorithm, IN CERTLIB_ALLOCATOR allocType, OUT BYTE **ppbSignature, OUT DWORD *pcbSignature) { BOOL b; *ppbSignature = NULL; *pcbSignature = 0; for (;;) { b = CryptSignCertificate( hCryptProv, dwKeySpec, dwEncodingType, pbEncodedToBeSigned, cbEncodedToBeSigned, const_cast(pSignatureAlgorithm), NULL, // pvHashAuxInfo *ppbSignature, pcbSignature); if (b && 0 == *pcbSignature) { SetLastError((DWORD) HRESULT_FROM_WIN32(ERROR_INVALID_DATA)); b = FALSE; } if (!b) { if (NULL != *ppbSignature) { myFree(*ppbSignature, allocType); *ppbSignature = NULL; } break; } if (NULL != *ppbSignature) { break; } *ppbSignature = (BYTE *) myAlloc(*pcbSignature, allocType); if (NULL == *ppbSignature) { b = FALSE; break; } } return(b); } HRESULT myEncodeSignedContent( IN HCRYPTPROV hProv, IN DWORD dwCertEncodingType, IN char const *pszObjIdSignatureAlgorithm, IN BYTE *pbToBeSigned, IN DWORD cbToBeSigned, IN CERTLIB_ALLOCATOR allocType, OUT BYTE **ppbSigned, OUT DWORD *pcbSigned) { HRESULT hr; CERT_SIGNED_CONTENT_INFO csci; ZeroMemory(&csci, sizeof(csci)); csci.SignatureAlgorithm.pszObjId = (char *) pszObjIdSignatureAlgorithm; csci.ToBeSigned.cbData = cbToBeSigned; csci.ToBeSigned.pbData = pbToBeSigned; *ppbSigned = NULL; if (!myCryptSignCertificate( hProv, AT_SIGNATURE, dwCertEncodingType, csci.ToBeSigned.pbData, csci.ToBeSigned.cbData, &csci.SignatureAlgorithm, CERTLIB_USE_LOCALALLOC, &csci.Signature.pbData, &csci.Signature.cbData)) { hr = myHLastError(); _JumpError(hr, error, "myCryptSignCertificate"); } // if (!myEncodeCert( if (!myEncodeObject( dwCertEncodingType, X509_CERT, &csci, 0, allocType, ppbSigned, pcbSigned)) { hr = myHLastError(); // _JumpError(hr, error, "myEncodeCert"); _JumpError(hr, error, "myEncodeObject"); } hr = S_OK; error: if (NULL != csci.Signature.pbData) { LocalFree(csci.Signature.pbData); } return(hr); } HRESULT myGetPublicKeyHash( OPTIONAL IN CERT_INFO const *pCertInfo, IN CERT_PUBLIC_KEY_INFO const *pPublicKeyInfo, OUT BYTE **ppbData, OUT DWORD *pcbData) { HRESULT hr; CRYPT_DATA_BLOB *pBlob = NULL; DWORD cb; BYTE const *pb; BYTE abHash[CBMAX_CRYPT_HASH_LEN]; *ppbData = NULL; if (NULL == pPublicKeyInfo) { hr = E_POINTER; _JumpError(hr, error, "parm NULL"); } pb = NULL; cb = 0; if (NULL != pCertInfo) { CERT_EXTENSION const *pExt; CERT_EXTENSION const *pExtEnd; pExtEnd = &pCertInfo->rgExtension[pCertInfo->cExtension]; for (pExt = pCertInfo->rgExtension; pExt < pExtEnd; pExt++) { if (0 == strcmp(szOID_SUBJECT_KEY_IDENTIFIER, pExt->pszObjId)) { if (!myDecodeObject( X509_ASN_ENCODING, X509_OCTET_STRING, pExt->Value.pbData, pExt->Value.cbData, CERTLIB_USE_LOCALALLOC, (VOID **) &pBlob, &cb)) { hr = myHLastError(); _JumpError(hr, error, "myDecodeObject"); } pb = pBlob->pbData; cb = pBlob->cbData; break; } } } if (NULL == pb) { cb = sizeof(abHash); if (!CryptHashPublicKeyInfo( NULL, // hCryptProv CALG_SHA1, 0, // dwFlags, X509_ASN_ENCODING, const_cast(pPublicKeyInfo), abHash, &cb)) { hr = myHLastError(); _JumpError(hr, error, "CryptHashPublicKeyInfo"); } pb = abHash; } *ppbData = (BYTE *) LocalAlloc(LMEM_FIXED, cb); if (NULL == *ppbData) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "LocalAlloc"); } *pcbData = cb; CopyMemory(*ppbData, pb, cb); hr = S_OK; error: if (NULL != pBlob) { LocalFree(pBlob); } return(hr); } HRESULT myCreateSubjectKeyIdentifierExtension( IN CERT_PUBLIC_KEY_INFO const *pPubKey, OUT BYTE **ppbEncoded, OUT DWORD *pcbEncoded) { HRESULT hr; CRYPT_DATA_BLOB KeyIdentifier; KeyIdentifier.pbData = NULL; hr = myGetPublicKeyHash( NULL, // pCertInfo pPubKey, &KeyIdentifier.pbData, &KeyIdentifier.cbData); _JumpIfError(hr, error, "myGetPublicKeyHash"); // Issuer's KeyId: if (!myEncodeObject( X509_ASN_ENCODING, X509_OCTET_STRING, &KeyIdentifier, 0, CERTLIB_USE_LOCALALLOC, ppbEncoded, pcbEncoded)) { hr = myHLastError(); _JumpError(hr, error, "myEncodeObject"); } hr = S_OK; error: if (NULL != KeyIdentifier.pbData) { LocalFree(KeyIdentifier.pbData); } return(hr); } HRESULT myCalculateKeyArchivalHash( IN const BYTE *pbEncryptedKey, IN DWORD cbEncryptedKey, OUT BYTE **ppbHash, OUT DWORD *pcbHash) { HRESULT hr; HCRYPTPROV hProv = NULL; HCRYPTHASH hHash = NULL; BYTE* pbHash = NULL; DWORD cbHash = 0; DWORD dwSize; if (NULL == pbEncryptedKey || NULL == ppbHash || NULL == pcbHash) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER); _JumpError(hr, error, "Invalid parameters"); } //init *ppbHash = NULL; *pcbHash = 0; if (!CryptAcquireContext( &hProv, NULL, // pszContainer NULL, // pszProvider PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) { hr = myHLastError(); _JumpError(hr, error, "CryptAcquireContext"); } //create a hash object if (!CryptCreateHash(hProv, CALG_SHA1, 0, 0, &hHash)) { hr = myHLastError(); _JumpError(hr, error, "CryptCreateHash"); } //hash the data if (!CryptHashData( hHash, pbEncryptedKey, cbEncryptedKey, 0)) { hr = myHLastError(); _JumpError(hr, error, "CryptHashData"); } //get the hash size dwSize = sizeof(cbHash); if (!CryptGetHashParam( hHash, HP_HASHSIZE, (BYTE*)&cbHash, &dwSize, 0)) { hr = myHLastError(); _JumpError(hr, error, "CryptGetHashParam"); } //allocate for hash buffer pbHash = (BYTE*)LocalAlloc(LMEM_FIXED, cbHash); if (NULL == pbHash) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "LocalAlloc"); } dwSize = cbHash; //get the hash if (!CryptGetHashParam( hHash, HP_HASHVAL, (BYTE*)pbHash, &dwSize, 0)) { hr = myHLastError(); _JumpError(hr, error, "CryptGetHashParam"); } //should be the same CSASSERT(dwSize == cbHash); //return *ppbHash = pbHash; *pcbHash = cbHash; pbHash = NULL; hr = S_OK; error: if (NULL != hHash) { CryptDestroyHash(hHash); } if (NULL != hProv) { CryptReleaseContext(hProv, 0); } if (NULL != pbHash) { LocalFree(pbHash); } return hr; } //-------------------------------------------------------------------- // Escapes any characters unsuitable for a URL. Returns a new string. HRESULT myInternetCanonicalizeUrl( IN WCHAR const *pwszIn, OUT WCHAR **ppwszOut) { HRESULT hr; WCHAR *pwsz = NULL; CSASSERT(NULL != pwszIn); if (0 == _wcsnicmp(L"file:", pwszIn, 5)) { hr = myDupString(pwszIn, &pwsz); _JumpIfError(hr, error, "myDupString"); } else { // Calculate required buffer size by passing a very small buffer // The call will fail, and tell us how big the buffer should be. WCHAR wszPlaceHolder[1]; DWORD cwc = ARRAYSIZE(wszPlaceHolder); BOOL bResult; bResult = InternetCanonicalizeUrlW( pwszIn, // lpszUrl wszPlaceHolder, // lpszBuffer &cwc, // lpdwBufferLength 0); // dwFlags CSASSERT(!bResult); // This will always fail hr = myHLastError(); if (HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER) != hr) { // unexpected error _JumpError(hr, error, "InternetCanonicalizeUrl"); } // NOTE: InternetCanonicalizeUrl counts characters, not bytes as doc'd // cwc includes trailing L'0' pwsz = (WCHAR *) LocalAlloc(LMEM_FIXED, cwc * sizeof(WCHAR)); if (NULL == pwsz) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "LocalAlloc"); } // canonicalize if (!InternetCanonicalizeUrlW( pwszIn, // lpszUrl pwsz, // lpszBuffer &cwc, // lpdwBufferLength 0)) // dwFlags { hr = myHLastError(); _JumpError(hr, error, "InternetCanonicalizeUrl"); } } *ppwszOut = pwsz; pwsz = NULL; hr = S_OK; error: if (NULL != pwsz) { LocalFree(pwsz); } return(hr); } // Inverse of InternetCanonicalizeUrl -- Convert "%20" sequences to " ", etc. HRESULT myInternetUncanonicalizeURL( IN WCHAR const *pwszURLIn, OUT WCHAR **ppwszURLOut) { HRESULT hr; URL_COMPONENTSW urlcomp; WCHAR wszScheme[10]; // L"ldap" WCHAR wszHost[MAX_PATH]; WCHAR wszURL[MAX_PATH]; WCHAR wszExtra[MAX_PATH]; WCHAR *pwszURL = NULL; DWORD cURL; DWORD cwcURLAlloc; *ppwszURLOut = NULL; ZeroMemory(&urlcomp, sizeof(urlcomp)); urlcomp.dwStructSize = sizeof(urlcomp); urlcomp.lpszScheme = wszScheme; urlcomp.dwSchemeLength = ARRAYSIZE(wszScheme); urlcomp.lpszHostName = wszHost; urlcomp.dwHostNameLength = ARRAYSIZE(wszHost); urlcomp.lpszUrlPath = wszURL; urlcomp.dwUrlPathLength = ARRAYSIZE(wszURL); urlcomp.lpszExtraInfo = wszExtra; urlcomp.dwExtraInfoLength = ARRAYSIZE(wszExtra); // Decode escape sequemces if (!InternetCrackUrlW(pwszURLIn, 0, ICU_ESCAPE, &urlcomp)) { hr = myHLastError(); _JumpError(hr, error, "InternetCrackUrl"); } cURL = 0; for (;;) { // InternetCreateUrl is spec'd strangely: // // When called with a NULL input pointer or an insufficient buffer // size, the returned count is the number of bytes required, including // the trailing L'\0'. // // When called with a non-NULL input pointer of adequate size, the // returned count is the count of chars, excluding the trailing L'\0'. // // This is just so wierd! if (!InternetCreateUrlW(&urlcomp, 0, pwszURL, &cURL)) { hr = myHLastError(); if (HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER) != hr || NULL != pwszURL) { _JumpError(hr, error, "InternetCreatUrl"); } } if (NULL != pwszURL) { CSASSERT(wcslen(pwszURL) == cURL); CSASSERT(cwcURLAlloc == cURL + 1); break; } pwszURL = (WCHAR *) LocalAlloc(LMEM_FIXED, cURL); if (NULL == pwszURL) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "LocalAlloc"); } cURL /= sizeof(WCHAR); cwcURLAlloc = cURL; } *ppwszURLOut = pwszURL; pwszURL = NULL; hr = S_OK; error: if (NULL != pwszURL) { LocalFree(pwszURL); } return(hr); } BOOL ConvertWszToMultiByte( OUT CHAR **ppsz, IN UINT CodePage, IN WCHAR const *pwc, IN LONG cwc) { HRESULT hr; LONG cch = 0; *ppsz = NULL; for (;;) { cch = WideCharToMultiByte( CodePage, 0, // dwFlags pwc, cwc, // cchWideChar, -1 => null terminated *ppsz, cch, NULL, NULL); if (0 >= cch && (0 != cch || (0 != cwc && (MAXLONG != cwc || L'\0' != *pwc)))) { hr = myHLastError(); _PrintError(hr, "WideCharToMultiByte"); if (NULL != *ppsz) { LocalFree(*ppsz); *ppsz = NULL; } break; } if (NULL != *ppsz) { (*ppsz)[cch] = '\0'; hr = S_OK; break; } *ppsz = (CHAR *) LocalAlloc(LMEM_FIXED, cch + 1); if (NULL == *ppsz) { hr = E_OUTOFMEMORY; break; } } if (S_OK != hr) { SetLastError(hr); } return(S_OK == hr); } BOOL myConvertWszToUTF8( OUT CHAR **ppsz, IN WCHAR const *pwc, IN LONG cwc) { return(ConvertWszToMultiByte(ppsz, CP_UTF8, pwc, cwc)); } BOOL myConvertWszToSz( OUT CHAR **ppsz, IN WCHAR const *pwc, IN LONG cwc) { return(ConvertWszToMultiByte(ppsz, GetACP(), pwc, cwc)); } BOOL myConvertMultiByteToWsz( OUT WCHAR **ppwsz, IN UINT CodePage, IN CHAR const *pch, IN LONG cch) { HRESULT hr; LONG cwc = 0; *ppwsz = NULL; for (;;) { cwc = MultiByteToWideChar(CodePage, 0, pch, cch, *ppwsz, cwc); if (0 >= cwc) { hr = myHLastError(); _PrintError(hr, "MultiByteToWideChar"); if (NULL != *ppwsz) { LocalFree(*ppwsz); *ppwsz = NULL; } break; } if (NULL != *ppwsz) { (*ppwsz)[cwc] = L'\0'; hr = S_OK; break; } *ppwsz = (WCHAR *) LocalAlloc(LMEM_FIXED, (cwc + 1) * sizeof(WCHAR)); if (NULL == *ppwsz) { hr = E_OUTOFMEMORY; break; } } if (S_OK != hr) { SetLastError(hr); } return(S_OK == hr); } BOOL myConvertUTF8ToWsz( OUT WCHAR **ppwsz, IN CHAR const *pch, IN LONG cch) { return(myConvertMultiByteToWsz(ppwsz, CP_UTF8, pch, cch)); } BOOL myConvertSzToWsz( OUT WCHAR **ppwsz, IN CHAR const *pch, IN LONG cch) { return(myConvertMultiByteToWsz(ppwsz, GetACP(), pch, cch)); }