500 lines
12 KiB
C++
500 lines
12 KiB
C++
//----------------------------------------------------------------------------
|
||
//
|
||
// 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
|
||
|