/*++ 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 #include #include #include // 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] [|] [|]\n")); MyPrintf(_T("/s to Sign.\n")); MyPrintf(_T("/v to Verify.\n")); MyPrintf(_T("/cert [|] - use a certificate.\n")); MyPrintf(_T("/key [|] [|] use container with exchange or signature key.\n")); }