//---------------------------------------------------------------------------- // // Copyright (C) 2003 Intel Corporation // // File: CertificateOperations.cpp // // Contents: Certificates handling functions for an Intel� AMT Audit-Log client. // //---------------------------------------------------------------------------- #include "CertificateOperations.h" #include using namespace std; #ifdef _WIN32 #include "AccessMonitorUtils.h" #include #include // Certificate Directory. static const char* CERT_DIRECTORY = "PVS\\CERT\\"; /***************************************************************************** * Get Certificate Name that is encoded in the certificate blob encoded in ASN_1 * Arguments: * blob - Certificate blob date. * Length - Data Length. * * Return value: * Certificate name ****************************************************************************/ string GetStringFromASN_1(BYTE *blob, WORD length) { CERT_NAME_BLOB name; name.pbData = blob; name.cbData = length; string ret; DWORD et = X509_ASN_ENCODING | PKCS_7_ASN_ENCODING; DWORD st = CERT_X500_NAME_STR | CERT_NAME_STR_REVERSE_FLAG; char pszNameString[2048]; if (CertNameToStr(et,&name,st,pszNameString,2048) < 1) { ret = ""; } else { ret = pszNameString; } return ret; } /***************************************************************************** * Verify Digital signature (Hash SHA1/SHA256/SHA384) * Arguments: * certContext - Signing Certificate (for Public Key). * data - Data to verify the signature. * dataLength - Data Length. * signature - Digital signature. * signatureLength - Digital signature Length. * signatureMechanism - Digital signature Length signing mechanism. * valid - Output parameter that indicates if the signature is valid * or not. * * Return value: * true - on success * false - on failure (Error) ****************************************************************************/ bool VerifySign(PCCERT_CONTEXT certContext, BYTE *data, DWORD dataLength, BYTE *signature, DWORD signatureLength, WORD signatureMechanism, bool &valid) { if ((certContext == NULL) || (valid == NULL)) { return false; } if ((data == NULL) || (signature == NULL) || (dataLength == 0) || (signatureLength == 0)) { return false; } HCRYPTPROV hProv; if (!CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_AES , 0)) { if (!CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_AES, CRYPT_NEWKEYSET)) { return false; } } HCRYPTPROV hHash = 0; ALG_ID algID; switch(signatureMechanism) { case RSA_SHA1: algID = CALG_SHA1; break; case RSA_SHA256: algID = CALG_SHA_256; break; case RSA_SHA384: algID = CALG_SHA_384; break; default: algID = CALG_SHA1; } if (!CryptCreateHash(hProv, algID, 0, 0, &hHash)) { if (NTE_BAD_ALGID == GetLastError()) { printf("\nError: Hash algorithm not supported. (Probably because running the sample on not supporting OS.)\n"); } CryptReleaseContext(hProv,0); return false; } if (!CryptHashData(hHash,data,dataLength,0)) { CryptDestroyHash(hHash); CryptReleaseContext(hProv,0); return false; } HCRYPTKEY hKey; if (!CryptImportPublicKeyInfo(hProv, X509_ASN_ENCODING, //| PKCS_7_ASN_ENCODING, &(certContext->pCertInfo->SubjectPublicKeyInfo), &hKey)) { CryptDestroyHash(hHash); CryptReleaseContext(hProv,0); return false; } for(unsigned int i=0;i < signatureLength / 2; i++) { swap(signature[i],signature[signatureLength - 1 - i]); } if(CryptVerifySignature(hHash, signature, signatureLength, hKey, NULL, 0)) { valid = true; } else { if (GetLastError() == NTE_BAD_SIGNATURE) { valid = false; } else { CryptDestroyKey(hKey); CryptDestroyHash(hHash); CryptReleaseContext(hProv,0); return false; } } CryptDestroyKey(hKey); CryptDestroyHash(hHash); CryptReleaseContext(hProv,0); return true; } //In real code here we should get the certificate from Active Directory or LDAP /***************************************************************************** * Get Certificate from Active Directory or LDAP * Arguments: * serialNumber - Certificate Serial Number * serialNumberLength - Certificate Serial Number Length. * issuerAsn1 - Issuer * issuerAsn1Length - Issuer length. * * Return value: * Certificate context or NULL for error. ****************************************************************************/ PCCERT_CONTEXT GetCertContext(BYTE *serialNumber, WORD serialNumberLength, BYTE *issuerAsn1, WORD issuerAsn1Length) { using namespace ATL; if (serialNumberLength < 1) { return NULL; } string serialNumberString; for (int i = 0; i < serialNumberLength; i++) { char temp[3]; int index = _snprintf(temp, 2, "%02X", serialNumber[i]); temp[index] = 0; serialNumberString.append(temp); } char serverRootPath[MAX_PATH]; if (GetModuleFileName(NULL,serverRootPath,MAX_PATH) == 0) { return NULL; } string certPath = serverRootPath; string::size_type t = certPath.find_last_of("\\"); if (t == string::npos) { return NULL; } else { certPath = certPath.substr(0,t); } certPath.append("\\..\\"); certPath.append(CERT_DIRECTORY); certPath.append(serialNumberString); certPath.append(".cer"); HCERTSTORE hFileStore = CertOpenStore(CERT_STORE_PROV_FILENAME, PKCS_7_ASN_ENCODING | X509_ASN_ENCODING, NULL, CERT_STORE_READONLY_FLAG, CA2W(certPath.c_str())); if (hFileStore == NULL) { return NULL; } PCCERT_CONTEXT CertContext = CertFindCertificateInStore( hFileStore, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, NULL, CERT_FIND_ANY, NULL, NULL); PCCERT_CONTEXT duplicateCertContext = CertDuplicateCertificateContext(CertContext); CertCloseStore(hFileStore,0); return duplicateCertContext; } /***************************************************************************** * Get Certificate Context from PEM File. * Arguments: * filename - PEM Filename. * * Return value: * PCCERT_CONTEXT - Certificate Context Pointer. ****************************************************************************/ PCCERT_CONTEXT GetCetficateContextFromFile(std::string filename) { PCCERT_CONTEXT context; vector buf; AuditUtils auditUtils; auditUtils.GetTextKeyFromBase64File(filename.c_str(), string(OPEN_CERT_TAG), string(CLOSE_CERT_TAG), buf); if ((context = CertCreateCertificateContext(X509_ASN_ENCODING, buf.data(), buf.size())) == NULL) { printf("Error: Can't Get Certificate Context\n"); return NULL; } return context; } /***************************************************************************** * Validates a Given Certificate Chain. (valid using the Root CA's defined * in the computer). * Arguments: * chain - Array of Certificates (PCCERT_CONTEXT) * valid - Output parameter that indicates if the chain is valid or not * * Return value: * true - on success * false - on failure (Error) ****************************************************************************/ bool ValidCertChain(PCCERT_CONTEXT chain[], bool &valid) { HCERTSTORE certStore = NULL; PCCERT_CONTEXT cert; bool status = true; valid = false; // Initialize data structures. HCERTCHAINENGINE hChainEngine = NULL; CERT_CHAIN_ENGINE_CONFIG ChainConfig; PCCERT_CHAIN_CONTEXT pChainContext = NULL; PCCERT_CONTEXT pCertContext = NULL; CERT_ENHKEY_USAGE EnhkeyUsage; CERT_USAGE_MATCH CertUsage; CERT_CHAIN_PARA ChainPara; DWORD dwFlags = 0; // Open Certificate Store in the Memory. if ((certStore = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, NULL, 0, NULL)) == NULL) { status = false; goto EXIT; } // Load Certificate Chain to the Certificate Store. while((cert = *chain++) != NULL) { if (CertAddCertificateContextToStore(certStore, cert, CERT_STORE_ADD_REPLACE_EXISTING, NULL) == FALSE) { status = false; goto EXIT; } } //--------------------------------------------------------- EnhkeyUsage.cUsageIdentifier = 0; EnhkeyUsage.rgpszUsageIdentifier=NULL; CertUsage.dwType = USAGE_MATCH_TYPE_AND; CertUsage.Usage = EnhkeyUsage; ChainPara.cbSize = sizeof(CERT_CHAIN_PARA); ChainPara.RequestedUsage=CertUsage; ChainConfig.cbSize = sizeof(CERT_CHAIN_ENGINE_CONFIG); ChainConfig.hRestrictedRoot= NULL ; ChainConfig.hRestrictedTrust= NULL ; ChainConfig.hRestrictedOther= NULL ; ChainConfig.cAdditionalStore=0 ; ChainConfig.rghAdditionalStore = NULL ; ChainConfig.dwFlags = CERT_CHAIN_CACHE_END_CERT; ChainConfig.dwUrlRetrievalTimeout= 0 ; ChainConfig.MaximumCachedCertificates=0 ; ChainConfig.CycleDetectionModulus = 0; //--------------------------------------------------------- // Create the nondefault certificate chain engine. if(CertCreateCertificateChainEngine(&ChainConfig, &hChainEngine) == FALSE) { status = false; goto EXIT; } //------------------------------------------------------- // Loop through the certificates in the store, // and create a chain for each. valid = true; while(pCertContext = CertEnumCertificatesInStore(certStore, pCertContext)) { //------------------------------------------------------------------- // Build a chain using CertGetCertificateChain // and the certificate retrieved. if(CertGetCertificateChain( NULL, // use the default chain engine pCertContext, // pointer to the end certificate NULL, // use the default time certStore, // Use our Certificate Store &ChainPara, // use AND logic and enhanced key usage as indicated in the ChainPara data structure dwFlags, NULL, // currently reserved &pChainContext) == FALSE) // return a pointer to the chain created { valid = false; status = false; goto EXIT; } // Check if we have Trust Errors if (pChainContext->TrustStatus.dwErrorStatus != CERT_TRUST_NO_ERROR) { valid = false; break; } CertFreeCertificateChain(pChainContext); pChainContext = NULL; } // end while loop EXIT: if (pChainContext) { CertFreeCertificateChain(pChainContext); pChainContext = NULL; } //--------------------------------------------------------- // Free the chain engine. if (hChainEngine) { CertFreeCertificateChainEngine(hChainEngine); } // Close Certificate Store if (certStore) { CertCloseStore(certStore, CERT_CLOSE_STORE_FORCE_FLAG); } return status; } #else /* Linux */ /* * Converts data to binary base64. using OpenSSL Functions * Arguments: * inStr - data to be converted * inLen - data to be converted length * outStr - [out] binary base64 data, dynamic allocate. (using new) * outLen - [out] binary base64 data length. * Return Value: * none * */ void ToBase64(const char *inStr, unsigned int inLen, char **outStr, unsigned int *outLen) { BIO *b64, *mbio; BUF_MEM *bptr; b64 = BIO_new(BIO_f_base64()); mbio = BIO_new(BIO_s_mem()); mbio = BIO_push(b64, mbio); BIO_write(mbio, inStr, inLen); BIO_flush(mbio); BIO_get_mem_ptr(b64, &bptr); *outLen = bptr->length; if (bptr->length > 0) { *outStr = new char[bptr->length + 1]; if (*outStr != NULL) { memcpy(*outStr, bptr->data, bptr->length); (*outStr)[bptr->length] = 0x00; } } BIO_free_all(mbio); mbio = NULL; b64 = NULL; } /* * Create X509 OpenSSL Object from certificate string. * * Arguments: * certStr - [in] The certificate. * Return Value: * Pointer to X509 OpenSSL Object that represent X509 Certificate. * NULL On Error */ X509* LoadCertificateFromMem(unsigned char *certBuf, unsigned int certLen) { const char CERT_HEADER[] = "-----BEGIN CERTIFICATE-----\n"; const char CERT_FOOTER[] = "-----END CERTIFICATE-----"; BIO *bp = NULL; X509 *cert = NULL; if (certBuf == NULL || certLen == 0) { return NULL; } // Convert to Base64 char *tmpBase64 = NULL; unsigned int tmpBase64Len; ToBase64(reinterpret_cast(certBuf), certLen, &tmpBase64, &tmpBase64Len); if (tmpBase64 == NULL) { return NULL; } string certStr = tmpBase64; delete[] tmpBase64; if (certStr.empty()) { return NULL; } // Inserting Certificate Header and Footer certStr.insert(0, CERT_HEADER, strlen(CERT_HEADER)); certStr.insert(certStr.size(), CERT_FOOTER, strlen(CERT_FOOTER)); char *tmp = const_cast(certStr.c_str()); /* Create BIO File From Memory*/ if ((bp = BIO_new_mem_buf (tmp, -1)) == NULL) { return NULL; } /* Get OpenSSL Certificate X509 Object */ cert = PEM_read_bio_X509(bp, NULL, 0, NULL); if (cert == NULL) { ERR_print_errors_fp (stderr); } BIO_free(bp); return cert; } #endif