500 lines
12 KiB
C++
Raw Blame History

//----------------------------------------------------------------------------
//
// Copyright (C) 2003 Intel Corporation
//
// File: CertificateOperations.cpp
//
// Contents: Certificates handling functions for an Intel<65> AMT Audit-Log client.
//
//----------------------------------------------------------------------------
#include "CertificateOperations.h"
#include <string.h>
using namespace std;
#ifdef _WIN32
#include "AccessMonitorUtils.h"
#include <atlbase.h>
#include <atlconv.h>
// 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<unsigned char> 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<char*>(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<char*>(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