2025-11-28 00:35:46 +09:00

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"));
}