625 lines
17 KiB
C++
625 lines
17 KiB
C++
/*++
|
|
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.
|
|
*/
|
|
|
|
#define UNICODE
|
|
#define _UNICODE
|
|
|
|
#include <windows.h>
|
|
#include <tchar.h>
|
|
#include <stdio.h>
|
|
#include <strsafe.h> // for proper buffer handling
|
|
|
|
#define ENCODING (X509_ASN_ENCODING | PKCS_7_ASN_ENCODING)
|
|
|
|
const int BUFFER_SIZE = 4096;
|
|
|
|
#define CheckAndLocalFree(ptr) \
|
|
if (ptr != NULL) \
|
|
{ \
|
|
LocalFree(ptr); \
|
|
ptr = NULL; \
|
|
}
|
|
|
|
void __cdecl MyPrintf(LPCTSTR lpszFormat, ...);
|
|
void PrintUsage();
|
|
BOOL SignVerifyFile(HCRYPTPROV hProv,
|
|
HCRYPTKEY hPubKey,
|
|
DWORD dwKeySpec,
|
|
ALG_ID HashAlgId,
|
|
LPTSTR szFileToSign,
|
|
LPTSTR szSigFile,
|
|
BOOL fSign);
|
|
BOOL GetRSAKeyFromCert(PCCERT_CONTEXT pCertContext,
|
|
BOOL fSign,
|
|
HCRYPTPROV *hProv,
|
|
HCRYPTKEY *hPubKey,
|
|
DWORD *dwKeySpec,
|
|
BOOL *fFreeProv);
|
|
BOOL GetRSAKeyFromContainer(LPTSTR szContainerName,
|
|
DWORD dwAcquireFlags,
|
|
DWORD dwKeySpec,
|
|
HCRYPTPROV *hProv,
|
|
HCRYPTKEY *hPubKey);
|
|
PCCERT_CONTEXT GetCertificateContextFromName(
|
|
LPTSTR lpszCertificateName,
|
|
LPTSTR lpszCertificateStoreName,
|
|
DWORD dwCertStoreOpenFlags);
|
|
|
|
void _tmain(int argc, TCHAR *argv[])
|
|
{
|
|
HCRYPTPROV hProv = NULL;
|
|
HCRYPTKEY hPubKey = NULL;
|
|
PCCERT_CONTEXT pCertContext = NULL;
|
|
LPTSTR szCertificateName = NULL;
|
|
LPTSTR szStoreName = NULL;
|
|
LPTSTR szContainerName = NULL;
|
|
LPTSTR szFileToSign = NULL;
|
|
LPTSTR szSigFile = NULL;
|
|
DWORD dwOpenFlags = CERT_SYSTEM_STORE_CURRENT_USER;
|
|
DWORD dwAcquireFlags = 0;
|
|
DWORD dwKeySpec = AT_SIGNATURE;
|
|
ALG_ID AlgId;
|
|
BOOL fSign = FALSE;
|
|
BOOL fResult = FALSE;
|
|
BOOL fUseCert = FALSE;
|
|
BOOL fFreeProv = FALSE;
|
|
|
|
if (argc != 9)
|
|
{
|
|
PrintUsage();
|
|
return;
|
|
}
|
|
|
|
__try
|
|
{
|
|
// Determine hash algorithm
|
|
if (lstrcmpi(argv[1], _T("sha1")) == 0)
|
|
{
|
|
AlgId = CALG_SHA1;
|
|
}
|
|
else if (lstrcmpi(argv[1], _T("md5")) == 0)
|
|
{
|
|
AlgId = CALG_MD5;
|
|
}
|
|
else
|
|
{
|
|
PrintUsage();
|
|
return;
|
|
}
|
|
|
|
/* Check whether the action to be performed is to sign or verify */
|
|
if (lstrcmpi(argv[2], _T("/s")) == 0)
|
|
{
|
|
fSign = TRUE;
|
|
}
|
|
else if (lstrcmpi(argv[2], _T("/v")) == 0)
|
|
{
|
|
fSign = FALSE;
|
|
}
|
|
else
|
|
{
|
|
PrintUsage();
|
|
return;
|
|
}
|
|
|
|
szFileToSign = argv[3];
|
|
szSigFile = argv[4];
|
|
|
|
// check to see if user wants to use a certificate
|
|
if (lstrcmpi(argv[5], _T("/cert")) == 0)
|
|
{
|
|
fUseCert = TRUE;
|
|
|
|
szCertificateName = argv[6];
|
|
szStoreName = argv[7];
|
|
|
|
// Determine if we have to use user or machine store
|
|
if (lstrcmpi(argv[8], _T("u")) == 0)
|
|
{
|
|
dwOpenFlags = CERT_SYSTEM_STORE_CURRENT_USER;
|
|
}
|
|
else if (lstrcmpi(argv[8], _T("m")) == 0)
|
|
{
|
|
dwOpenFlags = CERT_SYSTEM_STORE_LOCAL_MACHINE;
|
|
}
|
|
else
|
|
{
|
|
PrintUsage();
|
|
return;
|
|
}
|
|
}
|
|
else if (lstrcmpi(argv[5], _T("/key")) == 0)
|
|
{
|
|
fUseCert = FALSE;
|
|
|
|
szContainerName = argv[6];
|
|
|
|
if (lstrcmpi(argv[7], _T("u")) == 0)
|
|
{
|
|
dwAcquireFlags = 0;
|
|
}
|
|
else if (lstrcmpi(argv[7], _T("m")) == 0)
|
|
{
|
|
dwAcquireFlags = CRYPT_MACHINE_KEYSET;
|
|
}
|
|
else
|
|
{
|
|
PrintUsage();
|
|
return;
|
|
}
|
|
|
|
// Use exchange key or signature key
|
|
if (lstrcmpi(argv[8], _T("x")) == 0)
|
|
{
|
|
dwKeySpec = AT_KEYEXCHANGE;
|
|
}
|
|
else if (lstrcmpi(argv[8], _T("s")) == 0)
|
|
{
|
|
dwKeySpec = AT_SIGNATURE;
|
|
}
|
|
else
|
|
{
|
|
PrintUsage();
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
PrintUsage();
|
|
return;
|
|
}
|
|
|
|
if (fUseCert)
|
|
{
|
|
pCertContext = GetCertificateContextFromName(szCertificateName,
|
|
szStoreName,
|
|
dwOpenFlags);
|
|
if (!pCertContext) __leave;
|
|
|
|
fResult = GetRSAKeyFromCert(pCertContext,
|
|
fSign,
|
|
&hProv,
|
|
&hPubKey,
|
|
&dwKeySpec,
|
|
&fFreeProv);
|
|
if (!fResult) __leave;
|
|
}
|
|
else
|
|
{
|
|
fResult = GetRSAKeyFromContainer(szContainerName,
|
|
dwAcquireFlags,
|
|
dwKeySpec,
|
|
&hProv,
|
|
&hPubKey);
|
|
if (!fResult) __leave;
|
|
|
|
fFreeProv = TRUE;
|
|
}
|
|
|
|
fResult = SignVerifyFile(hProv,
|
|
hPubKey,
|
|
dwKeySpec,
|
|
AlgId,
|
|
szFileToSign,
|
|
szSigFile,
|
|
fSign);
|
|
if (!fResult) __leave;
|
|
|
|
if (fSign)
|
|
{
|
|
MyPrintf(_T("File %s hashed and signed successfully!\n"), szFileToSign);
|
|
}
|
|
else
|
|
{
|
|
MyPrintf(_T("File %s verified successfully!\n"), szSigFile);
|
|
}
|
|
}
|
|
__finally
|
|
{
|
|
// Clean up
|
|
if (hPubKey != NULL) CryptDestroyKey(hPubKey);
|
|
if (fFreeProv && (hProv != NULL)) CryptReleaseContext(hProv, 0);
|
|
if (pCertContext != NULL) CertFreeCertificateContext(pCertContext);
|
|
}
|
|
}
|
|
|
|
BOOL SignVerifyFile(HCRYPTPROV hProv,
|
|
HCRYPTKEY hPubKey,
|
|
DWORD dwKeySpec,
|
|
ALG_ID HashAlgId,
|
|
LPTSTR szFileToSign,
|
|
LPTSTR szSigFile,
|
|
BOOL fSign)
|
|
{
|
|
HCRYPTHASH hHash = NULL;
|
|
HANDLE hDataFile = INVALID_HANDLE_VALUE;
|
|
HANDLE hSigFile = INVALID_HANDLE_VALUE;
|
|
LPBYTE pbSignature = NULL;
|
|
DWORD dwSignature;
|
|
BYTE pbBuffer[BUFFER_SIZE];
|
|
DWORD dwBytesRead, dwBytesWritten;
|
|
BOOL fResult;
|
|
BOOL fFinished = FALSE;
|
|
BOOL fReturn = FALSE;
|
|
|
|
__try
|
|
{
|
|
// Open Data file
|
|
hDataFile = CreateFile(szFileToSign,
|
|
GENERIC_READ,
|
|
0,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL);
|
|
if (hDataFile == INVALID_HANDLE_VALUE)
|
|
{
|
|
MyPrintf(_T("CreateFile failed with %d\n"), GetLastError());
|
|
__leave;
|
|
}
|
|
|
|
// Open/Create signature file
|
|
hSigFile = CreateFile(szSigFile,
|
|
GENERIC_READ|GENERIC_WRITE,
|
|
0,
|
|
NULL,
|
|
OPEN_ALWAYS,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL);
|
|
if (hSigFile == INVALID_HANDLE_VALUE)
|
|
{
|
|
MyPrintf(_T("CreateFile failed with %d\n"), GetLastError());
|
|
__leave;
|
|
}
|
|
|
|
// Create Hash
|
|
fResult = CryptCreateHash(hProv, HashAlgId, 0, 0, &hHash);
|
|
if (!fResult)
|
|
{
|
|
MyPrintf(_T("CryptCreateHash failed with %x\n"), GetLastError());
|
|
__leave;
|
|
}
|
|
|
|
// Loop through file and hash file contents
|
|
do
|
|
{
|
|
dwBytesRead = 0;
|
|
|
|
fResult = ReadFile(hDataFile, pbBuffer, BUFFER_SIZE, &dwBytesRead, NULL);
|
|
|
|
if (dwBytesRead == 0) break;
|
|
|
|
if (!fResult)
|
|
{
|
|
MyPrintf(_T("ReadFile failed with %d\n"), GetLastError());
|
|
__leave;
|
|
}
|
|
|
|
fFinished = (dwBytesRead < BUFFER_SIZE);
|
|
|
|
fResult = CryptHashData(hHash, pbBuffer, dwBytesRead, 0);
|
|
if (!fResult)
|
|
{
|
|
MyPrintf(_T("CryptHashData failed with %x\n"), GetLastError());
|
|
__leave;
|
|
}
|
|
|
|
} while (fFinished == FALSE);
|
|
|
|
if (fSign)
|
|
{
|
|
// Get Signature size
|
|
fResult = CryptSignHash(hHash, dwKeySpec, NULL, 0, NULL, &dwSignature);
|
|
if (!fResult)
|
|
{
|
|
MyPrintf(_T("CryptSignHash failed with %x\n"), GetLastError());
|
|
__leave;
|
|
}
|
|
|
|
// Allocate signature bytes
|
|
pbSignature = (LPBYTE)LocalAlloc(LPTR, dwSignature);
|
|
if (!pbSignature)
|
|
{
|
|
MyPrintf(_T("LocalAlloc failed with %d\n"), GetLastError());
|
|
__leave;
|
|
}
|
|
|
|
// Sign and get back signature
|
|
fResult = CryptSignHash(hHash, dwKeySpec, NULL, 0, pbSignature, &dwSignature);
|
|
if (!fResult)
|
|
{
|
|
MyPrintf(_T("CryptSignHash failed with %x\n"), GetLastError());
|
|
__leave;
|
|
}
|
|
|
|
// Write signature to file
|
|
fResult = WriteFile(hSigFile, pbSignature, dwSignature, &dwBytesWritten, NULL);
|
|
if (!fResult)
|
|
{
|
|
MyPrintf(_T("WriteFile failed with %d\n"), GetLastError());
|
|
__leave;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Get size of signature file
|
|
dwSignature = GetFileSize(hSigFile, NULL);
|
|
if (dwSignature == INVALID_FILE_SIZE)
|
|
{
|
|
MyPrintf(_T("GetFileSize failed with %d\n"), GetLastError());
|
|
__leave;
|
|
}
|
|
|
|
// Allocate signature bytes
|
|
pbSignature = (LPBYTE)LocalAlloc(LPTR, dwSignature);
|
|
if (!pbSignature)
|
|
{
|
|
MyPrintf(_T("LocalAlloc failed with %d\n"), GetLastError());
|
|
__leave;
|
|
}
|
|
|
|
// Read Signature
|
|
fResult = ReadFile(hSigFile, pbSignature, dwSignature, &dwBytesRead, NULL);
|
|
if (!fResult)
|
|
{
|
|
MyPrintf(_T("ReadFile failed with %d\n"), GetLastError());
|
|
__leave;
|
|
}
|
|
|
|
// Verify Signature
|
|
fResult = CryptVerifySignature(hHash, pbSignature, dwSignature, hPubKey, NULL, 0);
|
|
if (!fResult)
|
|
{
|
|
MyPrintf(_T("CryptVerifySignature failed with %x\n"), GetLastError());
|
|
__leave;
|
|
}
|
|
}
|
|
|
|
fReturn = TRUE;
|
|
}
|
|
__finally
|
|
{
|
|
// Clean up
|
|
if (hHash != NULL) CryptDestroyHash(hHash);
|
|
if (hDataFile != INVALID_HANDLE_VALUE) CloseHandle(hDataFile);
|
|
if (hSigFile != INVALID_HANDLE_VALUE) CloseHandle(hSigFile);
|
|
CheckAndLocalFree(pbSignature);
|
|
}
|
|
|
|
return fReturn;
|
|
}
|
|
|
|
PCCERT_CONTEXT GetCertificateContextFromName(
|
|
LPTSTR lpszCertificateName,
|
|
LPTSTR lpszCertificateStoreName,
|
|
DWORD dwCertStoreOpenFlags)
|
|
{
|
|
PCCERT_CONTEXT pCertContext = NULL;
|
|
HCERTSTORE hCertStore = NULL;
|
|
LPSTR szStoreProvider;
|
|
DWORD dwFindType;
|
|
|
|
#ifdef UNICODE
|
|
szStoreProvider = (LPSTR)CERT_STORE_PROV_SYSTEM_W;
|
|
#else
|
|
szStoreProvider = (LPSTR)CERT_STORE_PROV_SYSTEM_A;
|
|
#endif
|
|
|
|
// Open the specified certificate store
|
|
hCertStore = CertOpenStore(szStoreProvider,
|
|
0,
|
|
NULL,
|
|
dwCertStoreOpenFlags|
|
|
CERT_STORE_READONLY_FLAG,
|
|
lpszCertificateStoreName);
|
|
if (hCertStore == NULL)
|
|
{
|
|
MyPrintf(_T("CertOpenStore failed with %X\n"), GetLastError());
|
|
return pCertContext;
|
|
}
|
|
|
|
#ifdef UNICODE
|
|
dwFindType = CERT_FIND_SUBJECT_STR_W;
|
|
#else
|
|
dwFindType = CERT_FIND_SUBJECT_STR_A;
|
|
#endif
|
|
|
|
// Find the certificate by CN.
|
|
pCertContext = CertFindCertificateInStore(hCertStore,
|
|
ENCODING,
|
|
0,
|
|
dwFindType,
|
|
lpszCertificateName,
|
|
NULL);
|
|
if (pCertContext == NULL)
|
|
{
|
|
MyPrintf(_T("CertFindCertificateInStore failed with %X\n"), GetLastError());
|
|
}
|
|
|
|
CertCloseStore(hCertStore, 0);
|
|
|
|
return pCertContext;
|
|
}
|
|
|
|
BOOL GetRSAKeyFromCert(PCCERT_CONTEXT pCertContext,
|
|
BOOL fSign,
|
|
HCRYPTPROV *hProv,
|
|
HCRYPTKEY *hPubKey,
|
|
DWORD *dwKeySpec,
|
|
BOOL *fFreeProv)
|
|
{
|
|
BOOL fResult;
|
|
BOOL fReturn = FALSE;
|
|
|
|
__try
|
|
{
|
|
if (hProv == NULL || hPubKey == NULL ||
|
|
dwKeySpec == NULL || fFreeProv == NULL)
|
|
{
|
|
__leave;
|
|
}
|
|
|
|
*hProv = 0;
|
|
*hPubKey = 0;
|
|
*fFreeProv = FALSE;
|
|
|
|
if (fSign)
|
|
{
|
|
// Acquire the certificate's private key
|
|
fResult = CryptAcquireCertificatePrivateKey(pCertContext,
|
|
CRYPT_ACQUIRE_USE_PROV_INFO_FLAG|
|
|
CRYPT_ACQUIRE_COMPARE_KEY_FLAG,
|
|
NULL,
|
|
hProv,
|
|
dwKeySpec,
|
|
fFreeProv);
|
|
if (!fResult)
|
|
{
|
|
MyPrintf(_T("CryptAcquireCertificatePrivateKey failed with %x\n"), GetLastError());
|
|
__leave;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fResult = CryptAcquireContext(hProv,
|
|
NULL,
|
|
MS_DEF_PROV,
|
|
PROV_RSA_FULL,
|
|
CRYPT_VERIFYCONTEXT);
|
|
if (!fResult)
|
|
{
|
|
MyPrintf(_T("CryptAcquireContext failed with %x\n"), GetLastError());
|
|
__leave;
|
|
}
|
|
|
|
*fFreeProv = TRUE;
|
|
|
|
// Import the public key from the certificate so we can verify
|
|
fResult = CryptImportPublicKeyInfo(*hProv,
|
|
ENCODING,
|
|
&(pCertContext->pCertInfo->SubjectPublicKeyInfo),
|
|
hPubKey);
|
|
if (!fResult)
|
|
{
|
|
MyPrintf(_T("CryptImportPublicKeyInfo failed with %x\n"), GetLastError());
|
|
__leave;
|
|
}
|
|
}
|
|
|
|
fReturn = TRUE;
|
|
}
|
|
__finally
|
|
{
|
|
if (!fReturn)
|
|
{
|
|
if (*hPubKey != NULL)
|
|
{
|
|
CryptDestroyKey(*hPubKey);
|
|
*hPubKey = NULL;
|
|
}
|
|
|
|
if ((*fFreeProv == TRUE) && (*hProv != NULL))
|
|
{
|
|
CryptReleaseContext(*hProv, 0);
|
|
*hProv = NULL;
|
|
*fFreeProv = FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return fReturn;
|
|
}
|
|
|
|
BOOL GetRSAKeyFromContainer(LPTSTR szContainerName,
|
|
DWORD dwAcquireFlags,
|
|
DWORD dwKeySpec,
|
|
HCRYPTPROV *hProv,
|
|
HCRYPTKEY *hPubKey)
|
|
{
|
|
BOOL fResult;
|
|
BOOL fReturn = FALSE;
|
|
|
|
__try
|
|
{
|
|
if (hProv == NULL || hPubKey == NULL)
|
|
{
|
|
__leave;
|
|
}
|
|
|
|
// acquire crypto context using container name
|
|
fResult = CryptAcquireContext(hProv,
|
|
szContainerName,
|
|
MS_DEF_PROV,
|
|
PROV_RSA_FULL,
|
|
dwAcquireFlags);
|
|
if (!fResult)
|
|
{
|
|
MyPrintf(_T("CryptAcquireContext failed with %x\n"), GetLastError());
|
|
__leave;
|
|
}
|
|
|
|
// Get the key handle for verification
|
|
fResult = CryptGetUserKey(*hProv, dwKeySpec, hPubKey);
|
|
if (!fResult)
|
|
{
|
|
MyPrintf(_T("CryptGetUserKey failed with %x\n"), GetLastError());
|
|
__leave;
|
|
}
|
|
|
|
fReturn = TRUE;
|
|
}
|
|
__finally
|
|
{
|
|
if (!fReturn)
|
|
{
|
|
if (*hPubKey != NULL)
|
|
{
|
|
CryptDestroyKey(*hPubKey);
|
|
*hPubKey = NULL;
|
|
}
|
|
|
|
if (*hProv != NULL)
|
|
{
|
|
CryptReleaseContext(*hProv, 0);
|
|
*hProv = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
return fReturn;
|
|
}
|
|
|
|
void __cdecl MyPrintf(LPCTSTR lpszFormat, ...)
|
|
{
|
|
TCHAR szOutput[2048];
|
|
va_list v1 = NULL;
|
|
HRESULT hr = S_OK;
|
|
|
|
va_start(v1, lpszFormat);
|
|
hr = StringCbVPrintf(szOutput, sizeof(szOutput), lpszFormat, v1);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
OutputDebugString(szOutput);
|
|
_tprintf(szOutput);
|
|
}
|
|
else
|
|
{
|
|
_tprintf(_T("StringCbVPrintf failed with %X\n"), hr);
|
|
}
|
|
}
|
|
|
|
void PrintUsage()
|
|
{
|
|
MyPrintf(_T("SignHash [md5|sha1] [</s>|</v>] <DataFile> <SigFile> [</cert>|</key>]\n"));
|
|
MyPrintf(_T("/s to Sign.\n"));
|
|
MyPrintf(_T("/v to Verify.\n"));
|
|
MyPrintf(_T("/cert <CertName> <StoreName> [<u>|<m>] - use a certificate.\n"));
|
|
MyPrintf(_T("/key <ContainerName> [<u>|<m>] [<x>|<s>] use container with exchange or signature key.\n"));
|
|
}
|