#pragma once
#include <windows.h>
#include <stdio.h>
#include <Wincrypt.h>
#include "argument.h"
#define MY_ENCODING_TYPE (PKCS_7_ASN_ENCODING | X509_ASN_ENCODING)
#define SHA1_DIGEST_SIZE 20 // in bytes
class CertUtils {
public:
static void PrintFileTimeToString(FILETIME ft);
static void SearchCertBySubject(Arguments args);
static void SearchCertByThumbprint(Arguments args);
static HCERTSTORE OpenCertStore();
static void CloseCertStore(HCERTSTORE hSystemStore);
static vector<PCCERT_CONTEXT> GetCertificates(HCERTSTORE hSystemStore, DWORD findType, const void* pvFindData, bool onlyFirstOne = false);
static void FreeCerts(vector<PCCERT_CONTEXT> certs);
static bool CheckCertProperty(PCCERT_CONTEXT cert, DWORD propId);
static void ScanCertProperty(PCCERT_CONTEXT pCertContext);
static void CheckCertProperty(Arguments args);
static void SetCertProperty(Arguments args);
static void RemoveCertProperty(Arguments args);
};
HCERTSTORE CertUtils::OpenCertStore() { HCERTSTORE hSystemStore; // The system store handle. //------------------------------------------------------------------- // Open the certificate store to be searched. if (hSystemStore = CertOpenStore( CERT_STORE_PROV_SYSTEM, 0, // Encoding type not needed // with this PROV. NULL, // Accept the default HCRYPTPROV. CERT_SYSTEM_STORE_LOCAL_MACHINE, //;CERT_SYSTEM_STORE_CURRENT_USER, CERT_SYSTEM_STORE_LOCAL_MACHINE // Set the system store location in // the registry. L"MY")) // Could have used other predefined // system stores // including Trust, CA, or Root. { printf("Opened the MY system store. \n"); } else { printf("Could not open the MY system store.\n"); exit(1); } return hSystemStore; } void CertUtils::CloseCertStore(HCERTSTORE hSystemStore) { if (hSystemStore) CertCloseStore( hSystemStore, CERT_CLOSE_STORE_CHECK_FLAG); } //findType: CERT_FIND_SUBJECT_STR, CERT_FIND_SHA1_HASH vector<PCCERT_CONTEXT> CertUtils::GetCertificates(HCERTSTORE hSystemStore, DWORD findType, const void *pvFindData, bool onlyFirstOne) { vector<PCCERT_CONTEXT> certs; PCCERT_CONTEXT pDesiredCert = NULL; // Set to NULL for the first // call to // CertFindCertificateInStore. //------------------------------------------------------------------- // Get a certificate that has lpszCertSubject as its // subject. if (pDesiredCert = CertFindCertificateInStore( hSystemStore, MY_ENCODING_TYPE, // Use X509_ASN_ENCODING. 0, // No dwFlags needed. findType, // Find a certificate with a // thumbprint that matches the string // in the next parameter. pvFindData, // The Unicode string to be found // in a certificate's subject. NULL)) // NULL for the first call to the // function. In all subsequent // calls, it is the last pointer // returned by the function. { printf("The desired certificate was found. \n"); PrintFileTimeToString(pDesiredCert->pCertInfo->NotAfter); ScanCertProperty(pDesiredCert); certs.push_back(CertDuplicateCertificateContext(pDesiredCert)); } else { printf("Could not find the desired certificate.\n"); } if (!onlyFirstOne) { while (pDesiredCert) { pDesiredCert = CertFindCertificateInStore( hSystemStore, MY_ENCODING_TYPE, // Use X509_ASN_ENCODING. 0, // No dwFlags needed. findType, // Find a certificate with a // thumbprint that matches the string // in the next parameter. pvFindData, // The Unicode string to be found // in a certificate's subject. pDesiredCert); // NULL for the first call to the // function. In all subsequent // calls, it is the last pointer // returned by the function. // If NOT NULL, the CertFindCertificateInStore will also free the object if (pDesiredCert) { printf("The desired certificate was found. \n"); PrintFileTimeToString(pDesiredCert->pCertInfo->NotAfter); ScanCertProperty(pDesiredCert); certs.push_back(CertDuplicateCertificateContext(pDesiredCert)); } } } printf("In total found %d certs.\n", certs.size()); if (pDesiredCert) { // Free the one since the certs only contains duplicated one. CertFreeCertificateContext(pDesiredCert); } return certs; } void CertUtils::FreeCerts(vector<PCCERT_CONTEXT> certs) { for (PCCERT_CONTEXT cert : certs) { if (cert) CertFreeCertificateContext(cert); } } void CertUtils::SearchCertBySubject(Arguments args) { std::wstring subjectStr = StringUtils::S2WS(args.GetValue("subject")); //L"xtest-superadmin.int.rdst-internal.net"; LPCWSTR lpszCertSubject = subjectStr.c_str(); //------------------------------------------------------------------- // Declare and initialize variables. HCERTSTORE hSystemStore; // The system store handle. hSystemStore = CertUtils::OpenCertStore(); // After OpenCertStore is called, then the store looks like is cached in memory, even a cert is removed from cert store, it could still be found with below CertFindCertificateInStore with the 'hSystemStore' returned here. //wcout << "Will sleep 20s" << std::endl; //Sleep(20000); //------------------------------------------------------------------- // Get a certificate that has lpszCertSubject as its // subject. vector<PCCERT_CONTEXT> certs = CertUtils::GetCertificates(hSystemStore, CERT_FIND_SUBJECT_STR, lpszCertSubject); //------------------------------------------------------------------- // Clean up. FreeCerts(certs); CertUtils::CloseCertStore(hSystemStore); } /* D:\ex\C++\Crypt\Debug>Crypt.exe certsearch -subject=xtest-superadmin.int.rdst-internal.net Function name: certsearch Key=subject, Value=xtest-superadmin.int.rdst-internal.net Got argument vaue 'xtest-superadmin.int.rdst-internal.net' for 'subject' Opened the MY system store. The desired certificate was found. szLocalDateSaturday, March 19, 2022 szLocalTime11:51:37 AM The desired certificate was found. szLocalDateThursday, August 19, 2021 szLocalTime11:53:56 AM The desired certificate was found. szLocalDateSaturday, September 7, 2019 szLocalTime4:00:37 AM Could not find the desired certificate. After uninstall the 2nd cert: D:\ex\C++\Crypt\Debug>Crypt.exe certsearch -subject=xtest-superadmin.int.rdst-internal.net Function name: certsearch Key=subject, Value=xtest-superadmin.int.rdst-internal.net Got argument vaue 'xtest-superadmin.int.rdst-internal.net' for 'subject' Opened the MY system store. The desired certificate was found. szLocalDateSaturday, March 19, 2022 szLocalTime11:51:37 AM The desired certificate was found. szLocalDateSaturday, September 7, 2019 szLocalTime4:00:37 AM Could not find the desired certificate. After re-install the 2nd cert, the output sequence still follow previous sequence. Looks like following NotBefore order, but not sure. D:\ex\C++\Crypt\Debug>Crypt.exe certsearch -subject=xtest-superadmin.int.rdst-internal.net Function name: certsearch Key=subject, Value=xtest-superadmin.int.rdst-internal.net Got argument vaue 'xtest-superadmin.int.rdst-internal.net' for 'subject' Opened the MY system store. The desired certificate was found. szLocalDateSaturday, March 19, 2022 szLocalTime11:51:37 AM The desired certificate was found. szLocalDateThursday, August 19, 2021 szLocalTime11:53:56 AM The desired certificate was found. szLocalDateSaturday, September 7, 2019 szLocalTime4:00:37 AM Could not find the desired certificate. */ void CertUtils::SearchCertByThumbprint(Arguments args) { std::string thumbprintStr = args.GetValue("thumbprint"); LPCSTR lpszCertThumbprint = thumbprintStr.c_str(); CRYPT_HASH_BLOB hashBlob = { 0, nullptr }; const void* findPara = nullptr; vector<BYTE> digest; StringUtils::Hex2Bytes(lpszCertThumbprint, digest); hashBlob.pbData = &digest[0]; hashBlob.cbData = SHA1_DIGEST_SIZE; findPara = &hashBlob; //------------------------------------------------------------------- // Declare and initialize variables. HCERTSTORE hSystemStore; // The system store handle. hSystemStore = CertUtils::OpenCertStore(); vector<PCCERT_CONTEXT> certs = CertUtils::GetCertificates(hSystemStore, CERT_FIND_SHA1_HASH, findPara); for (PCCERT_CONTEXT cert : certs) { CheckCertProperty(cert, CERT_SHA1_HASH_PROP_ID); } //------------------------------------------------------------------- // Clean up. FreeCerts(certs); CertUtils::CloseCertStore(hSystemStore); } bool CertUtils::CheckCertProperty(PCCERT_CONTEXT cert, DWORD propId) { bool exist = true; DWORD cbData; DWORD dwError; // if the latest has the renewal property then delete it if (!CertGetCertificateContextProperty(cert, propId, NULL, &cbData)) { dwError = GetLastError(); if (dwError == CRYPT_E_NOT_FOUND) { cout << "Not found property " << propId << std::endl; exist = false; } else { cout << "Failed to find property " << propId << ", last error: " << dwError << std::endl; } } else { BYTE* pbData = new (std::nothrow) BYTE[cbData]; if (!CertGetCertificateContextProperty(cert, propId, pbData, &cbData)) { cout << "Unable to get property " << propId << std::endl; } else { cout << "cbData: " << cbData << std::endl; vector<BYTE> hashBytes(pbData, pbData + cbData); std::string hashStr = StringUtils::Bytes2Hex(hashBytes); cout << "pbData in hex: " << hashStr << std::endl; } } return exist; } void CertUtils::ScanCertProperty(PCCERT_CONTEXT pCertContext) { void* pvData; DWORD cbData; CRYPT_KEY_PROV_INFO* pCryptKeyProvInfo; DWORD dwPropId = 0; // In a loop, find all of the property IDs for the given certificate. // The loop continues until the CertEnumCertificateContextProperties // returns 0. while (dwPropId = CertEnumCertificateContextProperties( pCertContext, // the context whose properties are to be listed. dwPropId)) // number of the last property found. Must be // 0 to find the first property ID. { //-------------------------------------------------------------------- // Each time through the loop, a property ID has been found. // Print the property number and information about the property. printf("Property # %d found->", dwPropId); switch (dwPropId) { case CERT_FRIENDLY_NAME_PROP_ID: { //-------------------------------------------------------------------- // Retrieve the actual display name certificate property. // First, get the length of the property setting the // pvData parameter to NULL to get a value for cbData // to be used to allocate memory for the pvData buffer. printf("FRIENDLY_NAME_PROP_ID "); if (!(CertGetCertificateContextProperty( pCertContext, dwPropId, NULL, &cbData))) { MyHandleError("Call #1 to property length failed."); } //-------------------------------------------------------------------- // The call succeeded. Use the size to allocate memory for the // property. if (!(pvData = (void*)malloc(cbData))) { MyHandleError("Memory allocation failed."); } //-------------------------------------------------------------------- // Allocation succeeded. Retrieve the property data. if (!(CertGetCertificateContextProperty( pCertContext, dwPropId, pvData, &cbData))) { MyHandleError("Call #2 getting the data failed."); } else { printf("\n The display name is -> %s.", pvData); free(pvData); } break; } case CERT_SIGNATURE_HASH_PROP_ID: { printf("Signature hash ID. "); break; } case CERT_KEY_PROV_HANDLE_PROP_ID: { printf("KEY PROVE HANDLE."); break; } case CERT_KEY_PROV_INFO_PROP_ID: { printf("KEY PROV INFO PROP ID."); if (!(CertGetCertificateContextProperty( pCertContext, // A pointer to the certificate // where the property will be set. dwPropId, // An identifier of the property to get. // In this case, // CERT_KEY_PROV_INFO_PROP_ID NULL, // NULL on the first call to get the // length. &cbData))) // The number of bytes that must be // allocated for the structure. { MyHandleError("The property length was not retrieved."); } if (!(pCryptKeyProvInfo = (CRYPT_KEY_PROV_INFO*)malloc(cbData))) { MyHandleError("Error in allocation of memory."); } if (CertGetCertificateContextProperty( pCertContext, dwPropId, pCryptKeyProvInfo, &cbData)) { printf("\n The current key container is %S.", pCryptKeyProvInfo->pwszContainerName); free(pCryptKeyProvInfo); } else { free(pCryptKeyProvInfo); MyHandleError("The property was not retrieved."); } break; } case CERT_SHA1_HASH_PROP_ID: { printf("SHA1 HASH id."); break; } case CERT_MD5_HASH_PROP_ID: { printf("md5 hash id. "); break; } case CERT_KEY_CONTEXT_PROP_ID: { printf("KEY CONTEXT PROP id."); break; } case CERT_KEY_SPEC_PROP_ID: { printf("KEY SPEC PROP id."); break; } case CERT_ENHKEY_USAGE_PROP_ID: { printf("ENHKEY USAGE PROP id."); break; } case CERT_NEXT_UPDATE_LOCATION_PROP_ID: { printf("NEXT UPDATE LOCATION PROP id."); break; } case CERT_PVK_FILE_PROP_ID: { printf("PVK FILE PROP id. "); break; } case CERT_DESCRIPTION_PROP_ID: { printf("DESCRIPTION PROP id. "); break; } case CERT_ACCESS_STATE_PROP_ID: { printf("ACCESS STATE PROP id. "); break; } case CERT_SMART_CARD_DATA_PROP_ID: { printf("SMAART_CARD DATA PROP id. "); break; } case CERT_EFS_PROP_ID: { printf("EFS PROP id. "); break; } case CERT_FORTEZZA_DATA_PROP_ID: { printf("FORTEZZA DATA PROP id."); break; } case CERT_ARCHIVED_PROP_ID: { printf("ARCHIVED PROP id."); break; } case CERT_KEY_IDENTIFIER_PROP_ID: { printf("KEY IDENTIFIER PROP id. "); break; } case CERT_AUTO_ENROLL_PROP_ID: { printf("AUTO ENROLL id. "); break; } } // end switch printf("\n"); } // end the inner while loop. This is the end of the display of // a single property of a single certificate. cout << "end loop" << std::endl; } void CertUtils::CheckCertProperty(Arguments args) { std::string thumbprintStr = args.GetValue("thumbprint"); std::string propertyIdStr = args.GetValue("propid"); DWORD propId = StringUtils::DigitalStringToDWord(propertyIdStr); printf("propid=%ld\n", propId); LPCSTR lpszCertThumbprint = thumbprintStr.c_str(); CRYPT_HASH_BLOB hashBlob = { 0, nullptr }; const void* findPara = nullptr; vector<BYTE> digest; StringUtils::Hex2Bytes(lpszCertThumbprint, digest); hashBlob.pbData = &digest[0]; hashBlob.cbData = SHA1_DIGEST_SIZE; findPara = &hashBlob; //------------------------------------------------------------------- // Declare and initialize variables. HCERTSTORE hSystemStore; // The system store handle. hSystemStore = CertUtils::OpenCertStore(); vector<PCCERT_CONTEXT> certs = CertUtils::GetCertificates(hSystemStore, CERT_FIND_SHA1_HASH, findPara); printf("Cert count=%d\n", certs.size()); PCCERT_CONTEXT cert = certs.front(); CertUtils::CheckCertProperty(cert, propId); cout << "end" << std::endl; //------------------------------------------------------------------- // Clean up. FreeCerts(certs); CertUtils::CloseCertStore(hSystemStore); } void CertUtils::SetCertProperty(Arguments args) { std::string thumbprintStr = args.GetValue("thumbprint"); std::string propertyIdStr = args.GetValue("propid"); std::string hexValueStr = args.GetValue("hexvalue"); DWORD propId = StringUtils::DigitalStringToDWord(propertyIdStr); printf("propid=%ld\n", propId); vector<BYTE> values; StringUtils::Hex2Bytes(hexValueStr, values); cout << "Value=" << StringUtils::Bytes2Hex(values) << std::endl; LPCSTR lpszCertThumbprint = thumbprintStr.c_str(); CRYPT_HASH_BLOB hashBlob = { 0, nullptr }; const void* findPara = nullptr; vector<BYTE> digest; StringUtils::Hex2Bytes(lpszCertThumbprint, digest); hashBlob.pbData = &digest[0]; hashBlob.cbData = SHA1_DIGEST_SIZE; findPara = &hashBlob; //------------------------------------------------------------------- // Declare and initialize variables. HCERTSTORE hSystemStore; // The system store handle. hSystemStore = CertUtils::OpenCertStore(); vector<PCCERT_CONTEXT> certs = CertUtils::GetCertificates(hSystemStore, CERT_FIND_SHA1_HASH, findPara); printf("Cert count=%d\n", certs.size()); PCCERT_CONTEXT cert = certs.front(); DWORD cbData; DWORD dwError; bool hasSet = CertUtils::CheckCertProperty(cert, propId); if (!hasSet) { cout << "cert does not have property " << propId << ", will set it to " << hexValueStr << std::endl; CRYPT_HASH_BLOB valueHashBlob = { 0, nullptr }; valueHashBlob.pbData = &values[0]; valueHashBlob.cbData = SHA1_DIGEST_SIZE; if (!CertSetCertificateContextProperty( cert, propId, NULL, &valueHashBlob)) { dwError = GetLastError(); cout << "Failed to set cert property " << propId << ", last error: " << dwError << std::endl; } else { cout << "Successfully set cert property " << propId << std::endl; } } cout << "end" << std::endl; //------------------------------------------------------------------- // Clean up. FreeCerts(certs); CertUtils::CloseCertStore(hSystemStore); } void CertUtils::RemoveCertProperty(Arguments args) { std::string thumbprintStr = args.GetValue("thumbprint"); std::string propertyIdStr = args.GetValue("propid"); DWORD propId = StringUtils::DigitalStringToDWord(propertyIdStr); printf("propid=%ld\n", propId); LPCSTR lpszCertThumbprint = thumbprintStr.c_str(); CRYPT_HASH_BLOB hashBlob = { 0, nullptr }; const void* findPara = nullptr; vector<BYTE> digest; StringUtils::Hex2Bytes(lpszCertThumbprint, digest); hashBlob.pbData = &digest[0]; hashBlob.cbData = SHA1_DIGEST_SIZE; findPara = &hashBlob; //------------------------------------------------------------------- // Declare and initialize variables. HCERTSTORE hSystemStore; // The system store handle. hSystemStore = CertUtils::OpenCertStore(); vector<PCCERT_CONTEXT> certs = CertUtils::GetCertificates(hSystemStore, CERT_FIND_SHA1_HASH, findPara); printf("Cert count=%d\n", certs.size()); PCCERT_CONTEXT cert = certs.front(); DWORD cbData; DWORD dwError; bool hasSet = CertUtils::CheckCertProperty(cert, propId); if (hasSet) { cout << "cert has property " << propId << ", will remove it" << std::endl; if (!CertSetCertificateContextProperty( cert, propId, NULL, NULL)) { dwError = GetLastError(); cout << "Failed to remove cert property " << propId << ", last error: " << dwError << std::endl; } else { cout << "Successfully removed cert property " << propId << std::endl; } } cout << "end" << std::endl; //------------------------------------------------------------------- // Clean up. FreeCerts(certs); CertUtils::CloseCertStore(hSystemStore); } void CertUtils::PrintFileTimeToString(FILETIME ft) { SYSTEMTIME st; wchar_t szLocalDate[256], szLocalTime[256]; FileTimeToLocalFileTime(&ft, &ft); FileTimeToSystemTime(&ft, &st); GetDateFormat(LOCALE_USER_DEFAULT, DATE_LONGDATE, &st, NULL, szLocalDate, 255); GetTimeFormat(LOCALE_USER_DEFAULT, 0, &st, NULL, szLocalTime, 255); wcout << "LocalDate" << szLocalDate << " LocalTime" << szLocalTime << std::endl; }
Argument class:
#pragma once #include <iostream> #include <vector> #include <string> #include <map> #include <algorithm> using namespace std; class Argument { public: Argument(char* arg); std::string Key; std::string Value; }; class Arguments { public: Arguments(int argc, char** argv); std::string GetValue(std::string key); private: std::map<string, string> keyValueMap; }; #include "argument.h" Argument::Argument(char* arg) { std::string argStr = std::string(arg); if (arg[0] != '-') { throw "Invalid argument '" + argStr + "', does not start with '-'"; } int delimeterIndex = argStr.find_first_of('='); if (delimeterIndex < 0) { throw "Failed to parse argument: " + argStr + ", did not find delimeter"; } Key = argStr.substr(1, delimeterIndex - 1); Value = argStr.substr(delimeterIndex + 1); std::cout << "Key=" << Key << ", Value=" << Value << std::endl; } Arguments::Arguments(int argc, char** argv) { for (int i = 0; i < argc; ++i) { char* argStr = argv[i]; Argument arg = Argument(argStr); std::string key = std::string(arg.Key); std::transform(key.begin(), key.end(), key.begin(), ::tolower); if (keyValueMap.find(key) != keyValueMap.end()) { throw "Duplicated argument for " + key; } keyValueMap.insert(pair<string, string>(key, arg.Value)); } } std::string Arguments::GetValue(std::string key) { std::string lowKey = std::string(key); std::transform(lowKey.begin(), lowKey.end(), lowKey.begin(), ::tolower); auto npos = keyValueMap.find(key); if (npos == keyValueMap.end()) { throw "Not found key '" + lowKey + "' in arguments."; } else { std::cout << "Got argument vaue '" << npos->second << "' for '" << key << "'" << std::endl; return npos->second; } }
int main(int argc, char** argv) { if (argc < 2) { throw "Please input the function name"; } std::string funcName = argv[1]; std::cout << "Function name: " << funcName << std::endl; std::transform(funcName.begin(), funcName.end(), funcName.begin(), ::tolower); Arguments arguments = Arguments(argc - 2, argv + 2); if (funcName.compare("/?") == 0 || funcName.compare("help") == 0) { ShowHelp(); }else if(funcName.compare("certsearchsubject") == 0) { CertUtils::SearchCertBySubject(arguments); } else if (funcName.compare("certsearchthumbprint") == 0) { CertUtils::SearchCertByThumbprint(arguments); } else if (funcName.compare("certcheckprop") == 0) { CertUtils::CheckCertProperty(arguments); } else if (funcName.compare("certsetprop") == 0) { CertUtils::SetCertProperty(arguments); } else if (funcName.compare("certremoveprop") == 0) { CertUtils::RemoveCertProperty(arguments); } else { throw "The function name '" + funcName + "' is not supported."; } return 0; } void ShowHelp() { std::vector<std::string> SupportedFuncNames; SupportedFuncNames.push_back("test"); SupportedFuncNames.push_back("h2b/hex2base64"); SupportedFuncNames.push_back("b2h/base642hex"); SupportedFuncNames.push_back("encrypt"); SupportedFuncNames.push_back("decrypt"); SupportedFuncNames.push_back("certencrypt"); SupportedFuncNames.push_back("certdecrypt"); SupportedFuncNames.push_back("certsearch"); std::cout << "Support commands:" << std::endl; for (auto iter = SupportedFuncNames.begin(); iter != SupportedFuncNames.end(); ++iter) { std::cout << "\t" << *iter << std::endl; } }