I need to write a C program for Windows that receives a certificate from network (in PEM format) and validates its signature with a certificate chain file (which is already presented in the application's folder).
Writing such an application is pretty easy and strait forward with openssl library but seems a little complicated with the Windows Crypto API.
Here is what I've tried so far:
First I thought I can create a HCERTSTORE using the certificate-chain file:
HCERTSTORE hFileStoreHandle = CertOpenStore(
CERT_STORE_PROV_FILENAME,
PKCS_7_ASN_ENCODING | X509_ASN_ENCODING, NULL,
(CERT_STORE_OPEN_EXISTING_FLAG | CERT_STORE_READONLY_FLAG),
L"certificate-chain.pem.cert");
Then I figured I can iterate through the store and get PCCERT_CONTEXT struct of certificates:
PCCERT_CONTEXT CAfileContext = NULL;
while(CAfileContext = CertEnumCertificatesInStore(
hCertStore,
CAfileContext)) {
//start verification here
}
I don't know if I am on the right track or not but I'm facing two major problems here.
First is I don't know how to get the received certificate from buffer and convert it to a proper struct in order to validate its signature with certificate-chain file.
Second is I don't know how to verify a certificate signature using the CA chain file.
I'll appreciate all the suggestions and helps.
Here is the sample code for you, hopefully it can help you.
HRESULT hr = E_FAIL;
DWORD dwError = 0;
PCCERT_CONTEXT pCert = NULL;
HCERTSTORE hCertStore = CertOpenStore(
CERT_STORE_PROV_FILENAME,
PKCS_7_ASN_ENCODING | X509_ASN_ENCODING,
NULL,
(CERT_STORE_OPEN_EXISTING_FLAG | CERT_STORE_READONLY_FLAG),
L"certificate-chain.pem.cert");
do
{
if ((pCert = CertEnumCertificatesInStore(hCertStore, pCert)) == NULL)
break;
PCCERT_CONTEXT pcIssuer = NULL;
PCCERT_CONTEXT pcSubject = CertDuplicateCertificateContext(pCert);
for (; NULL != pcSubject;)
{
DWORD dwFlags = 0;
BOOL bret = TRUE;
hr = S_OK;
pcIssuer = CertGetIssuerCertificateFromStore(
hCertStore, pcSubject, NULL, &dwFlags);
if (pcIssuer == NULL)
{
dwError = GetLastError();
if (CRYPT_E_SELF_SIGNED != dwError)
{
hr = E_FAIL;
break;
}
else
{
if ((bret = CertCompareCertificateName(
X509_ASN_ENCODING,
&pcSubject->pCertInfo->Subject,
&pcSubject->pCertInfo->Issuer)) == FALSE)
{
hr = E_FAIL;
break;
}
}
}
HCRYPTPROV hprov = NULL;
bret = CryptAcquireContext(
&hprov, nullptr, nullptr, PROV_RSA_FULL,
CRYPT_SILENT | CRYPT_VERIFYCONTEXT | CRYPT_NEWKEYSET);
if (FALSE == bret)
{
dwError = GetLastError();
if (NTE_EXISTS == dwError)
bret = CryptAcquireContext(
&hprov, nullptr, nullptr, PROV_RSA_FULL,
CRYPT_SILENT | CRYPT_VERIFYCONTEXT);
if (FALSE == bret)
{
hr = E_FAIL;
break;
}
}
if ((bret = CryptVerifyCertificateSignatureEx(
hprov, X509_ASN_ENCODING,
CRYPT_VERIFY_CERT_SIGN_SUBJECT_CERT,
(void*)pcSubject,
CRYPT_VERIFY_CERT_SIGN_SUBJECT_CERT,
(void*)(pcIssuer == nullptr ? pcSubject : pcIssuer),
0, nullptr)) == FALSE)
{
hr = E_FAIL;
break;
}
if (nullptr == pcIssuer)
{
CERT_PUBLIC_KEY_INFO msCert;
msCert.Algorithm =
pcSubject->pCertInfo->SubjectPublicKeyInfo.Algorithm;
msCert.PublicKey.cbData =
sizeof(PETRUSTED_ROOTCERT_PUBKEY);
msCert.PublicKey.pbData =
const_cast<LPBYTE>(PETRUSTED_ROOTCERT_PUBKEY);
msCert.PublicKey.cUnusedBits =
pcSubject->pCertInfo->SubjectPublicKeyInfo.PublicKey.cUnusedBits;
if (FALSE == CertComparePublicKeyInfo(
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
&pcSubject->pCertInfo->SubjectPublicKeyInfo, &msCert))
{
hr = E_FAIL;
break;
}
}
bret = CryptReleaseContext(hprov, 0);
hprov = NULL;
CertFreeCertificateContext(pcSubject);
pcSubject = pcIssuer;
pcIssuer = NULL;
}
if (pcIssuer != NULL)
CertFreeCertificateContext(pcIssuer);
if (pcSubject != NULL)
CertFreeCertificateContext(pcSubject);
if (FAILED(hr))
{
CertFreeCertificateContext(pCert);
break;
}
} while (pCert != NULL);
CertCloseStore(hCertStore, 0);
if (FAILED(hr))
wprintf(L"Failed to verify X509 certification.\n");
else
wprintf(L"Successfully verify X509 certification.\n");
Related
(Hello Everyone) I have some problem with strtok_s. I wrote this code(x64).
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
BOOL TestMD5(CONST WCHAR* MD5_DATABASE_FILE)
{
HANDLE hFile = INVALID_HANDLE_VALUE;
DWORD FileSize = 0;
DWORD dwReaded = 0;
PBYTE pData = NULL;
BOOL bRead = FALSE;
PCHAR token_string = NULL;
PCHAR context = NULL;
CONST PCHAR delimeter = "\r\n";
hFile = CreateFileW(
MD5_DATABASE_FILE,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL
);
if (hFile == INVALID_HANDLE_VALUE)
{
wprintf(L"Can't open md5 database file: ");
return FALSE;
}
FileSize = GetFileSize(hFile, NULL);
if (FileSize == 0 || FileSize == INVALID_FILE_SIZE)
{
CloseHandle(hFile);
return FALSE;
}
pData = (PBYTE)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (SIZE_T)FileSize);
if (pData == NULL)
{
CloseHandle(hFile);
return FALSE;
}
bRead = ReadFile(hFile, pData, FileSize, &dwReaded, NULL);
if (bRead != TRUE || dwReaded != FileSize)
{
HeapFree(GetProcessHeap(), 0, pData);
CloseHandle(hFile);
return FALSE;
}
token_string = (PCHAR)strtok_s(pData, delimeter, &context);
if (token_string == NULL)
{
HeapFree(GetProcessHeap(), 0, pData);
CloseHandle(hFile);
return FALSE;
}
do {
printf("%s\n", token_string);
} while (token_string = (PCHAR)strtok_s(NULL, delimeter, &context));
HeapFree(GetProcessHeap(), 0, pData);
CloseHandle(hFile);
return TRUE;
}
int main(void)
{
WCHAR* MD5_DATABASE_FILE = L"c:\\md5.txt";
TestMD5(MD5_DATABASE_FILE);
}
When I run exe this gives me a incorrect data. Content of md5.txt (DC288E0B39EA16B4E9455F82FF265A67:1213:TestDBG + (\r\n)
output:
D:\repos\TestWindbg\x64\Debug>TestWindbg.exe
DC288E0B39EA16B4E9455F82FF265A67:1213:TestDBG
áááááááááááááááá
I open exe in windbg and I saw while(token_string) is not NULL after first time. But is must?
WinDbg image : "https://i.ibb.co/60nHk5S/Untitled.png"
What is problem? Thanks for reading
Jeffrey Shao - MSFT :Thank you for reply but this is not solution(but I changed my code PBYTE TO PCHAR). The problem is that strtok_s is a string function for this reason you must add NULL byte after buff. Like HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,(SIZE_T)FileSize + 1) #1 for NULL character . HeapAlloc alloc buff size:FileSize and +1 For Null...
Thanks for blabb and Daniel Sęk:
I just change some types of pData and token_string.
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
BOOL TestMD5(CONST WCHAR* MD5_DATABASE_FILE)
{
HANDLE hFile = INVALID_HANDLE_VALUE;
DWORD FileSize = 0;
DWORD dwReaded = 0;
char* pData = NULL;
BOOL bRead = FALSE;
char* token_string = NULL;
PCHAR context = NULL;
CONST PCHAR delimeter = "\r\n";
hFile = CreateFileW(
MD5_DATABASE_FILE,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL
);
if (hFile == INVALID_HANDLE_VALUE)
{
wprintf(L"Can't open md5 database file: ");
return FALSE;
}
FileSize = GetFileSize(hFile, NULL);
if (FileSize == 0 || FileSize == INVALID_FILE_SIZE)
{
CloseHandle(hFile);
return FALSE;
}
pData = (char*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,(SIZE_T)FileSize + 1);
if (pData == NULL)
{
CloseHandle(hFile);
return FALSE;
}
bRead = ReadFile(hFile, pData, FileSize, &dwReaded, NULL);
if (bRead != TRUE || dwReaded != FileSize)
{
HeapFree(GetProcessHeap(), 0, pData);
CloseHandle(hFile);
return FALSE;
}
token_string = strtok_s(pData, delimeter, &context);
if (token_string == NULL)
{
HeapFree(GetProcessHeap(), 0, pData);
CloseHandle(hFile);
return FALSE;
}
do {
printf("%s\n", token_string);
} while (token_string = strtok_s(NULL, delimeter, &context));
HeapFree(GetProcessHeap(), 0, pData);
CloseHandle(hFile);
return TRUE;
}
int main(void)
{
WCHAR* MD5_DATABASE_FILE = L"c:\\md5.txt";
TestMD5(MD5_DATABASE_FILE);
}
Output:
DC288E0B39EA16B4E9455F82FF265A67:1213:TestDBG + (\r\n)
PURPOSE
I'm trying to make a function which will create a given sub key in the HKCU registry hive, or open the sub key if it already exists, then return TRUE.
NOTES
Let RegSidPath represent a fully qualified HKCU registry path with an user SID appended to it such as \\Registry\\User\\S-1-5-20-xxxxxx-xxxxxx-xxxxxxxx-1050
Let KeyToCreate represent a specific registry path such as \\Software\\MyCompany\\MySoftware\\MySubKey
CODE
I have the following function:
BOOL CreateHKCUKey(PWCHAR RegSidPath, PWCHAR KeyToCreate) {
UNICODE_STRING uString;
RtlInitUnicodeString(&uString, RegSidPath);
OBJECT_ATTRIBUTES ObjAttributes;
InitializeObjectAttributes(&ObjAttributes, &uString, OBJ_CASE_INSENSITIVE, 0, 0);
HANDLE BaseKeyHandle = NULL;
NTSTATUS Status = NtOpenKey(&BaseKeyHandle, KEY_CREATE_SUB_KEY, &ObjAttributes);
if (NT_SUCCESS(Status) && Status != STATUS_OBJECT_NAME_NOT_FOUND) {
UNICODE_STRING KeyString = { 0 };
do {
PWCHAR NextSubKey = StrStrW((KeyString.Length == 0 ? KeyToCreate : KeyString.Buffer) + 1, L"\\");
DWORD CurrentKeyLength = lstrlenW(KeyToCreate) - lstrlenW(NextSubKey);
PWCHAR CurrentSubKey = PWCHAR(GlobalAlloc(GPTR, CurrentKeyLength + sizeof(WCHAR)));
if (CurrentSubKey != ERROR) {
memcpy(CurrentSubKey, KeyToCreate, CurrentKeyLength * sizeof(WCHAR));
CurrentSubKey[CurrentKeyLength] = UNICODE_NULL;
RtlInitUnicodeString(&KeyString, CurrentSubKey);
OBJECT_ATTRIBUTES KeyAttributes;
InitializeObjectAttributes(&KeyAttributes, &KeyString, OBJ_CASE_INSENSITIVE, &BaseKeyHandle, 0);
HANDLE CurrentHiveEntry = NULL;
Status = NtOpenKey(&CurrentHiveEntry, KEY_CREATE_SUB_KEY, &KeyAttributes);
if (RtlNtStatusToDosError(Status) == ERROR_BAD_PATHNAME) {
InitializeObjectAttributes(&KeyAttributes, &KeyString, OBJ_CASE_INSENSITIVE, &CurrentHiveEntry, 0);
DWORD DefaultDisposition;
Status = NtCreateKey(&CurrentHiveEntry, KEY_CREATE_SUB_KEY, &KeyAttributes, 0, NULL, REG_OPTION_NON_VOLATILE, &DefaultDisposition);
if (NT_SUCCESS(Status)) {
if (StrCmpNW(KeyString.Buffer + uString.Length, KeyString.Buffer, lstrlenW(KeyToCreate) == 0))
return TRUE;
else continue;
} else break;
} else break;
BaseKeyHandle = CurrentHiveEntry;
}
} while (TRUE);
}
NtClose(BaseKeyHandle);
return FALSE;
}
PROBLEM
Whenever the code gets to this part of the function
Status = NtOpenKey(&CurrentHiveEntry, KEY_CREATE_SUB_KEY, &KeyAttributes);
if (RtlNtStatusToDosError(Status) == ERROR_BAD_PATHNAME) {
The return value is always ERROR_BAD_PATHNAME (161) even if the current sub key already exists.
QUESTION
What is the reason, and what am I doing wrong? Is there anything that I've done which is not correct, and how can I fix it?
NTSTATUS CreateKey(PHANDLE KeyHandle, ACCESS_MASK DesiredAccess, PWCHAR RegSidPath, PWCHAR KeyToCreate, PULONG Disposition)
{
UNICODE_STRING ObjectName;
RtlInitUnicodeString(&ObjectName, RegSidPath);
OBJECT_ATTRIBUTES oa = { sizeof(oa), 0, &ObjectName ,OBJ_CASE_INSENSITIVE };
NTSTATUS status = ZwOpenKey(&oa.RootDirectory, KEY_CREATE_SUB_KEY, &oa);
if (0 <= status)
{
ObjectName.Buffer = KeyToCreate;
do
{
ACCESS_MASK Access;
if (KeyToCreate = wcschr(++ObjectName.Buffer, '\\'))
{
ObjectName.Length = (USHORT)RtlPointerToOffset(ObjectName.Buffer, KeyToCreate);
Access = KEY_CREATE_SUB_KEY;
}
else
{
ObjectName.Length = (USHORT)wcslen(ObjectName.Buffer) * sizeof(WCHAR);
Access = DesiredAccess;
}
ObjectName.MaximumLength = ObjectName.Length;
status = ZwCreateKey(KeyHandle, Access, &oa, 0, 0, 0, Disposition);
NtClose(oa.RootDirectory);
oa.RootDirectory = *KeyHandle;
} while (0 <= status && (ObjectName.Buffer = KeyToCreate));
}
return status;
}
and use as
HANDLE hKey;
NTSTATUS status = CreateKey(&hKey, KEY_ALL_ACCESS,
L"\\REGISTRY\\USER\\S-***",
L"\\Software\\MyCompany\\MySoftware\\MySubKey", 0);
Unless you are writing a driver, use RegCreateKeyEx() instead. It handles all the logic of creating intermediate keys for you if they don't already exist. 1 function call, no looping needed.
HKEY hKey;
DWORD dwDisposition;
LONG lRet = RegCreateKeyExW(HKEY_USERS, L"S-1-5-20-xxxxxx-xxxxxx-xxxxxxxx-1050\\Software\\MyCompany\\MySoftware\\MySubKey", 0, NULL, REG_OPTION_NON_VOLATILE, samDesired, NULL, &hKey, &dwDisposition);
if (lRet == 0)
{
...
RegCloseKey(hKey);
}
However, to access the HKEY_CURRENT_USER hive of a specific user, the preferred solution is to use RegOpenCurrentUser() or LoadUserProfile() instead of accessing HKEY_USERS directly:
// impersonate the desired user first, then...
HKEY hRootKey;
LONG lRet = RegOpenCurrentUser(samDesired, &hRootKey);
if (lRet == 0)
{
HKEY hKey;
DWORD dwDisposition;
lRet = RegCreateKeyExW(hRootKey, L"Software\\MyCompany\\MySoftware\\MySubKey", 0, NULL, REG_OPTION_NON_VOLATILE, samDesired, NULL, &hKey, &dwDisposition);
if (lRet == 0)
{
...
RegCloseKey(hKey);
}
RegCloseKey(hRootKey);
}
// stop impersonating...
// obtain token to desired user first, then...
PROFILEINFO profile = {0};
profile.dwSize = sizeof(profile);
profile.dwFlags = PI_NOUI;
if (LoadUserProfile(hToken, &profile))
{
HKEY hKey;
DWORD dwDisposition;
LONG lRet = RegCreateKeyExW((HKEY)profile.hProfile, L"Software\\MyCompany\\MySoftware\\MySubKey", 0, NULL, REG_OPTION_NON_VOLATILE, samDesired, NULL, &hKey, &dwDisposition);
if (lRet == 0)
{
...
RegCloseKey(hKey);
}
UnloadUserProfile(hToken, profile.hProfile);
}
// release token ...
I was using OpenSSL version 0.9.8h in an Android project. I update it to the 0.9.8.zf version but now it doesn't work.
The two functions that highlight the problem are initialize_client_ctx and initialize_client_ctx. When I call SSL_connect I get an SSL_ERROR_SSL error value. By checking details I retrieve a "bad packet length" error (error:14092073:SSL routines:SSL3_GET_SERVER_HELLO:bad packet length).
The point in the code is indicated in a comment. The code works well with the previous version. I attach also a Wireshark capture file. Any ideas?
SSL_CTX *initialize_client_ctx(const char *keyfile, const char *certfile,
const char *password, int transport)
{
SSL_METHOD *meth = NULL;
X509 *cert = NULL;
SSL_CTX *ctx;
if (transport == IPPROTO_UDP) {
meth = DTLSv1_client_method();
} else if (transport == IPPROTO_TCP) {
meth = TLSv1_client_method();
} else {
return NULL;
}
ctx = SSL_CTX_new(meth);
if (ctx == NULL) {
//print ... Couldn't create SSL_CTX
return NULL;
}
if (password[0] != '\0') {
SSL_CTX_set_default_passwd_cb_userdata(ctx, (void *) password);
SSL_CTX_set_default_passwd_cb(ctx, password_cb);
}
if (tls_client_local_cn_name[0] != '\0') {
cert = _tls_set_certificate(ctx, tls_client_local_cn_name);
}
if (cert==NULL && certfile[0] != '\0') {
//print several warnings....
}
if (cert!=NULL)
{
X509_free(cert);
cert = NULL;
}
/* Load the CAs we trust */
{
char *caFile = 0, *caFolder = 0;
int fd = open(eXosip_tls_ctx_params.root_ca_cert, O_RDONLY);
if (fd >= 0) {
struct stat fileStat;
if (fstat(fd, &fileStat) < 0) {
} else {
if (S_ISDIR(fileStat.st_mode)) {
caFolder = eXosip_tls_ctx_params.root_ca_cert;
} else {
caFile = eXosip_tls_ctx_params.root_ca_cert;
}
}
close(fd);
}
{
int verify_mode = SSL_VERIFY_PEER;
SSL_CTX_set_verify(ctx, verify_mode, &verify_cb);
SSL_CTX_set_verify_depth(ctx, ex_verify_depth + 1);
}
}
SSL_CTX_set_options(ctx, SSL_OP_ALL | SSL_OP_NO_SSLv2 |
SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION |
SSL_OP_CIPHER_SERVER_PREFERENCE);
if(!SSL_CTX_set_cipher_list(ctx,"ALL")) {
//print ... set_cipher_list: cannot set anonymous DH cipher
SSL_CTX_free(ctx);
return NULL;
}
return ctx;
}
static int _tls_tl_ssl_connect_socket(struct socket_tab *sockinfo)
{
X509 *cert;
BIO *sbio;
int res;
if (sockinfo->ssl_ctx == NULL) {
sockinfo->ssl_ctx =
initialize_client_ctx(eXosip_tls_ctx_params.client.priv_key,
eXosip_tls_ctx_params.client.cert,
eXosip_tls_ctx_params.client.priv_key_pw,
IPPROTO_TCP);
sockinfo->ssl_conn = SSL_new(sockinfo->ssl_ctx);
if (sockinfo->ssl_conn == NULL) {
return -1;
}
sbio = BIO_new_socket(sockinfo->socket, BIO_NOCLOSE);
if (sbio == NULL) {
return -1;
}
SSL_set_bio(sockinfo->ssl_conn, sbio, sbio);
}
do {
struct timeval tv;
int fd;
fd_set readfds;
res = SSL_connect(sockinfo->ssl_conn);
res = SSL_get_error(sockinfo->ssl_conn, res);
if (res == SSL_ERROR_NONE) {
//printf... SSL_connect succeeded
break;
}
if (res != SSL_ERROR_WANT_READ && res != SSL_ERROR_WANT_WRITE) {
//<-- here there is a problem res == SSL_ERROR_SSL
//print ERR_reason_error_string(ERR_get_error()));
//print ERR_error_string(ERR_get_error(), NULL));
return -1;
}
tv.tv_sec = SOCKET_TIMEOUT / 1000;
tv.tv_usec = (SOCKET_TIMEOUT % 1000) * 1000;
//retry the connection
fd = SSL_get_fd(sockinfo->ssl_conn);
FD_ZERO(&readfds);
FD_SET(fd, &readfds);
res = select(fd + 1, &readfds, NULL, NULL, &tv);
if (res < 0) {
//print error
return -1;
} else if (res > 0) {
//print...connetrion done!
} else {
//socket timeout, no data to read
return 1;
}
} while (!SSL_is_init_finished(sockinfo->ssl_conn));
if (SSL_is_init_finished(sockinfo->ssl_conn)) {
//print.. SSL_is_init_finished done
} else {
//print.. failed
}
cert = SSL_get_peer_certificate(sockinfo->ssl_conn);
if (cert != 0) {
int cert_err;
tls_dump_cert_info("tls_connect: remote certificate: ", cert);
cert_err = SSL_get_verify_result(sockinfo->ssl_conn);
if (cert_err != X509_V_OK) {
//print... Failed to verify remote certificate
tls_dump_verification_failure(cert_err);
if (eXosip_tls_ctx_params.server.cert[0] != '\0') {
X509_free(cert);
return -1;
} else if (cert_err != X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT
&& cert_err != X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN
&& cert_err != X509_V_ERR_CRL_HAS_EXPIRED
&& cert_err != X509_V_ERR_CERT_HAS_EXPIRED
&& cert_err != X509_V_ERR_CERT_REVOKED
&& cert_err != X509_V_ERR_CERT_UNTRUSTED
&& cert_err != X509_V_ERR_CERT_REJECTED) {
X509_free(cert);
return -1;
}
}
X509_free(cert);
} else {
//print .. No certificate received
/* X509_free is not necessary because no cert-object was created -> cert == NULL */
if (eXosip_tls_ctx_params.server.cert[0] == '\0') {
#ifdef ENABLE_ADH
/* how can we guess a user want ADH... specific APIs.. */
sockinfo->ssl_state = 3;
return 0;
#endif
}
return -1;
}
sockinfo->ssl_state = 3;
return 0;
}
SOLVED Thanks to Eric Tsui that helps me to figure out the problem. The 'hello' that I receive from the server in the handshake has zero length. To solve this I modified the file openssl/ssl/s3_clnt.c in the following way (toggling off the length control):
diff -ur ./s3_clnt.c ./original/s3_clnt.c
--- submodules/externals/openssl/ssl/s3_clnt.c 2015-06-29 14:59:56.723462992 +0200
+++ ../../opensslOrig/s3_clnt.c 2015-06-29 15:00:22.487464221 +0200
## -868,12 +868,14 ##
}
#endif
+#ifndef OPENSSL_NO_TLSEXT
if (p != (d + n)) {
/* wrong packet length */
al = SSL_AD_DECODE_ERROR;
SSLerr(SSL_F_SSL3_GET_SERVER_HELLO, SSL_R_BAD_PACKET_LENGTH);
goto f_err;
}
+#endif
return (1);
f_err:
I have a C application (not C++ or C#) that tests connectivity to our API from a client's perspective using WinHTTP. Here's code that downloads a file.
DWORD Measure(FILE* fout, LPCTSTR lpszUrl) {
int i;
DWORD dwResult = 0;
DWORD dwSize = 0;
DWORD dwDownloaded = 0;
DWORD dwRequestSize = 0;
LPSTR pszOutBuffer;
BOOL bResults = FALSE;
HINTERNET hSession = NULL;
HINTERNET hConnect = NULL;
HINTERNET hRequest = NULL;
WINHTTP_STATUS_CALLBACK status = NULL;
if (0 == dwResult) {
hSession = WinHttpOpen(_T("Test/1.0"), WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);
if (hSession == NULL) {
dwResult = GetLastError();
}
}
if (0 == dwResult) {
status = WinHttpSetStatusCallback(hSession, (WINHTTP_STATUS_CALLBACK)HttpCallback, WINHTTP_CALLBACK_FLAG_ALL_NOTIFICATIONS, NULL);
if (WINHTTP_INVALID_STATUS_CALLBACK == status) {
dwResult = GetLastError();
}
}
if (0 == dwResult) {
hConnect = WinHttpConnect(hSession, lpszUrl, INTERNET_DEFAULT_HTTPS_PORT, 0);
if (hConnect == NULL) {
dwResult = GetLastError();
}
}
if (0 == dwResult) {
hRequest = WinHttpOpenRequest(hConnect, L"GET", NULL, NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, WINHTTP_FLAG_SECURE | WINHTTP_FLAG_REFRESH);
if (hRequest == NULL) {
dwResult = GetLastError();
}
}
if (0 == dwResult) {
bResults = WinHttpSendRequest(hRequest,WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, 0, 0);
}
if (0 == dwResult) {
if(!QueryPerformanceCounter(&liDownloadStart)){
dwResult = GetLastError();
}
}
if (bResults) {
bResults = WinHttpReceiveResponse(hRequest, NULL);
if (!bResults) {
dwResult = GetLastError();
}
}
if (dwResult == 0 && bResults) {
do {
dwSize = 0;
if( !WinHttpQueryDataAvailable( hRequest, &dwSize ) ){
dwResult = GetLastError();
}
if (0 == dwResult) {
dwRequestSize += dwSize;
}
pszOutBuffer = (LPSTR)malloc(dwSize+1);
if (!pszOutBuffer) {
dwResult = ERROR_OUTOFMEMORY;
}
if (dwResult == 0) {
ZeroMemory(pszOutBuffer, dwSize + 1);
if( !WinHttpReadData( hRequest, (LPVOID)pszOutBuffer, dwSize, &dwDownloaded ) ){
dwResult = GetLastError();
}
free(pszOutBuffer);
}
} while (dwSize > 0 && dwResult == 0);
}
if (hRequest) WinHttpCloseHandle(hRequest);
if (hConnect) WinHttpCloseHandle(hConnect);
if (hSession) WinHttpCloseHandle(hSession);
return dwResult;
}
I can enable tracing using the following module:
BOOL enable = TRUE;
WinHttpSetOption(NULL, WINHTTP_OPTION_ENABLETRACING, &enable, sizeof(enable));
Following the instructions in the article Capturing WinHTTP Logs, I could ask the user to enable tracing, including the path where files should be stored.
netsh winhttp set tracing trace-file-prefix="C:\Temp\dpws" level=verbose format=ansi state=enabled max-trace-file-size=1073741824
However, most folks running my application are technically challenged. They may forget to disable it when they are done.
How do I programmatically enable WinHTTP tracing for my process?
I need to verify code signatures of binaries. Microsoft Authenticode I think is the term. Is there a sane way to do this using the Windows API?
Have you looked at WinVerifyTrust ? Since it's not immediately obvious how to use it to verify the signature of a binary, you probably want to look at the sample code specifically for that.
How to find authenticode for drivers:
Disclaimer: I did not write this code.
BOOL VerifyEmbeddedSignature(LPCWSTR pwszSourceFile)
{
LONG lStatus;
GUID WintrustVerifyGuid = WINTRUST_ACTION_GENERIC_VERIFY_V2;
GUID DriverActionGuid = DRIVER_ACTION_VERIFY;
HANDLE hFile;
DWORD dwHash;
BYTE bHash[100];
HCATINFO hCatInfo;
HCATADMIN hCatAdmin;
WINTRUST_DATA wd = { 0 };
WINTRUST_FILE_INFO wfi = { 0 };
WINTRUST_CATALOG_INFO wci = { 0 };
////set up structs to verify files with cert signatures
memset(&wfi, 0, sizeof(wfi));
wfi.cbStruct = sizeof( WINTRUST_FILE_INFO );
wfi.pcwszFilePath = pwszSourceFile;
wfi.hFile = NULL;
wfi.pgKnownSubject = NULL;
memset(&wd, 0, sizeof(wd));
wd.cbStruct = sizeof( WINTRUST_DATA );
wd.dwUnionChoice = WTD_CHOICE_FILE;
wd.pFile = &wfi;
wd.dwUIChoice = WTD_UI_NONE;
wd.fdwRevocationChecks = WTD_REVOKE_NONE;
wd.dwStateAction = 0;
wd.dwProvFlags = WTD_SAFER_FLAG;
wd.hWVTStateData = NULL;
wd.pwszURLReference = NULL;
wd.pPolicyCallbackData = NULL;
wd.pSIPClientData = NULL;
wd.dwUIContext = 0;
lStatus = WinVerifyTrust( NULL, &WintrustVerifyGuid, &wd );
////if failed, try to verify using catalog files
if (lStatus != ERROR_SUCCESS)
{
//open the file
hFile = CreateFileW(pwszSourceFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE)
return FALSE;
dwHash = sizeof(bHash);
if (!CryptCATAdminCalcHashFromFileHandle(hFile, &dwHash, bHash, 0))
{
CloseHandle(hFile);
return FALSE;
}
//Create a string form of the hash (used later in pszMemberTag)
LPWSTR pszMemberTag = new WCHAR[dwHash * 2 + 1];
for ( DWORD dw = 0; dw < dwHash; ++dw )
{
wsprintfW( &pszMemberTag[dw * 2], L"%02X", bHash[dw] );
}
if (!CryptCATAdminAcquireContext(&hCatAdmin, &DriverActionGuid, 0))
{
CloseHandle(hFile);
return FALSE;
}
//find the catalog which contains the hash
hCatInfo = CryptCATAdminEnumCatalogFromHash(hCatAdmin, bHash, dwHash, 0, NULL);
if ( hCatInfo )
{
CATALOG_INFO ci = { 0 };
CryptCATCatalogInfoFromContext( hCatInfo, &ci, 0 );
memset(&wci, 0, sizeof(wci));
wci.cbStruct = sizeof( WINTRUST_CATALOG_INFO );
wci.pcwszCatalogFilePath = ci.wszCatalogFile;
wci.pcwszMemberFilePath = pwszSourceFile;
wci.pcwszMemberTag = pszMemberTag;
memset(&wd, 0, sizeof(wd));
wd.cbStruct = sizeof( WINTRUST_DATA );
wd.dwUnionChoice = WTD_CHOICE_CATALOG;
wd.pCatalog = &wci;
wd.dwUIChoice = WTD_UI_NONE;
wd.fdwRevocationChecks = WTD_STATEACTION_VERIFY;
wd.dwProvFlags = 0;
wd.hWVTStateData = NULL;
wd.pwszURLReference = NULL;
wd.pPolicyCallbackData = NULL;
wd.pSIPClientData = NULL;
wd.dwUIContext = 0;
lStatus = WinVerifyTrust( NULL, &WintrustVerifyGuid, &wd );
CryptCATAdminReleaseCatalogContext( hCatAdmin, hCatInfo, 0 );
}
CryptCATAdminReleaseContext( hCatAdmin, 0 );
delete[] pszMemberTag;
CloseHandle(hFile);
}
if (lStatus != ERROR_SUCCESS)
return false;
else
return true;
}
Here is the working code to verify a file (technically any file type).
#include <stdio.h>
#include <windows.h>
#include <Softpub.h>
#include <wincrypt.h>
#include <wintrust.h>
#include <mscat.h>
#include <atlbase.h>
// Link with the Wintrust.lib file.
#pragma comment (lib, "wintrust")
BOOL VerifySignature(LPCSTR path) //We will receive the char* filepath not wchar*
{
USES_CONVERSION;
LPCWSTR pwszSourceFile = A2W(path); //We convert the char* to wchar*
LONG lStatus;
GUID WintrustVerifyGuid = WINTRUST_ACTION_GENERIC_VERIFY_V2;
GUID DriverActionGuid = DRIVER_ACTION_VERIFY;
HANDLE hFile;
DWORD dwHash;
BYTE bHash[100];
HCATINFO hCatInfo;
HCATADMIN hCatAdmin;
WINTRUST_DATA wd = { 0 };
WINTRUST_FILE_INFO wfi = { 0 };
WINTRUST_CATALOG_INFO wci = { 0 };
////set up structs to verify files with cert signatures
wfi.cbStruct = sizeof(WINTRUST_FILE_INFO);
wfi.pcwszFilePath = pwszSourceFile;
wfi.hFile = NULL;
wfi.pgKnownSubject = NULL;
wd.cbStruct = sizeof(WINTRUST_DATA);
wd.pPolicyCallbackData = NULL;
wd.pSIPClientData = NULL;
wd.dwUIChoice = WTD_UI_NONE;
wd.fdwRevocationChecks = WTD_REVOKE_NONE;
wd.dwUnionChoice = WTD_CHOICE_FILE;
wd.pFile = &wfi;
wd.dwStateAction = WTD_STATEACTION_VERIFY;
wd.hWVTStateData = NULL;
wd.pwszURLReference = NULL;
wd.dwProvFlags |= WTD_CACHE_ONLY_URL_RETRIEVAL;
wd.dwUIContext = 0;
wd.pSignatureSettings = 0;
lStatus = WinVerifyTrust((HWND)INVALID_HANDLE_VALUE, &WintrustVerifyGuid, &wd);
wd.dwStateAction = WTD_STATEACTION_CLOSE;
WinVerifyTrust((HWND)INVALID_HANDLE_VALUE, &WintrustVerifyGuid, &wd); //close hWVTStateData
////if failed, try to verify using catalog files
if (lStatus != ERROR_SUCCESS)
{
//open the file
hFile = CreateFileW(pwszSourceFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE)
return FALSE;
dwHash = sizeof(bHash);
if (!CryptCATAdminCalcHashFromFileHandle(hFile, &dwHash, bHash, 0))
{
CloseHandle(hFile);
return FALSE;
}
//Create a string form of the hash (used later in pszMemberTag)
LPWSTR pszMemberTag = new WCHAR[dwHash * 2 + 1];
for (DWORD dw = 0; dw < dwHash; ++dw)
{
wsprintfW(&pszMemberTag[dw * 2], L"%02X", bHash[dw]);
}
if (!CryptCATAdminAcquireContext(&hCatAdmin, &DriverActionGuid, 0))
{
CloseHandle(hFile);
return FALSE;
}
//find the catalog which contains the hash
hCatInfo = CryptCATAdminEnumCatalogFromHash(hCatAdmin, bHash, dwHash, 0, NULL);
if (hCatInfo)
{
CATALOG_INFO ci = { 0 };
CryptCATCatalogInfoFromContext(hCatInfo, &ci, 0);
memset(&wci, 0, sizeof(wci));
wci.cbStruct = sizeof(WINTRUST_CATALOG_INFO);
wci.pcwszCatalogFilePath = ci.wszCatalogFile;
wci.pcwszMemberFilePath = pwszSourceFile;
wci.hMemberFile = hFile;
wci.pcwszMemberTag = pszMemberTag;
wci.pbCalculatedFileHash = bHash;
wci.cbCalculatedFileHash = dwHash;
wci.hCatAdmin = hCatAdmin;
memset(&wd, 0, sizeof(wd));
wd.cbStruct = sizeof(WINTRUST_DATA);
wd.pPolicyCallbackData = NULL;
wd.pSIPClientData = NULL;
wd.dwUIChoice = WTD_UI_NONE;
wd.fdwRevocationChecks = WTD_REVOKE_NONE;
wd.dwUnionChoice = WTD_CHOICE_CATALOG;
wd.pCatalog = &wci;
wd.dwStateAction = WTD_STATEACTION_VERIFY;
wd.hWVTStateData = NULL;
wd.pwszURLReference = NULL;
wd.dwProvFlags |= WTD_CACHE_ONLY_URL_RETRIEVAL;
wd.dwUIContext = 0;
wd.pSignatureSettings = 0;
lStatus = WinVerifyTrust((HWND)INVALID_HANDLE_VALUE, &WintrustVerifyGuid, &wd);
wd.dwStateAction = WTD_STATEACTION_CLOSE;
WinVerifyTrust((HWND)INVALID_HANDLE_VALUE, &WintrustVerifyGuid, &wd); //close hWVTStateData
CryptCATAdminReleaseCatalogContext(hCatAdmin, hCatInfo, 0);
}
CryptCATAdminReleaseContext(hCatAdmin, 0);
delete[] pszMemberTag;
CloseHandle(hFile);
}
return (lStatus == ERROR_SUCCESS);
}
int main(int argc, char *argv[])
{
if (VerifySignature(argv[1]))
printf("Verified file signature\n");
else
printf("Could not verify file signature\n");
return 0;
}