2025-11-28 00:35:46 +09:00

597 lines
20 KiB
C++

//<SnippetMusicBundleSig_cppValidateWholePage>
/*****************************************************************************
*
* File: Validate.cpp
*
* Description:
* This sample is a simple application that might be used as a starting-point
* for an application that uses the Packaging API. This sample demonstrates
* signature generation and validation using a sample signing policy described
* in Sign.h
*
* ------------------------------------
*
* This file is part of the Microsoft Windows SDK Code Samples.
*
* Copyright (C) Microsoft Corporation. All rights reserved.
*
* This source code is intended only as a supplement to Microsoft
* Development Tools and/or on-line documentation. See these other
* materials for detailed information regarding Microsoft code samples.
*
* THIS CODE AND INFORMATION ARE 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.
*
****************************************************************************/
#include <stdio.h>
#include <windows.h>
#include <shlobj.h>
#include <msopc.h> // For OPC APIs
#include <WinCrypt.h> // For certificate stuff
#include <CryptDlg.h> // For certificate selection dialog
#include <strsafe.h>
#include <new>
#include "Util.h"
#include "Sign.h"
#include "Validate.h"
// Validates a signature and finds the certificate of the signer.
//
// Parameters:
// opcDigSigManager - the digital signature manager from which we get the signature.
// signature - the signature we want to validate.
// isValid - output. When the function returns successfully, it tells whether the
// signature is valid.
// signerCert - output. When the function returns successfully and isValid is true,
// it holds the certificate of the signer.
HRESULT
ValidateSignature(
IOpcDigitalSignatureManager* opcDigSigManager,
IOpcDigitalSignature* signature,
BOOL* isValid,
PCCERT_CONTEXT* signerCert
)
{
*isValid = FALSE;
*signerCert = NULL;
IOpcCertificateEnumerator * certEnumerator = NULL;
HRESULT hr = signature->GetCertificateEnumerator(&certEnumerator);
UINT32 certCount = 0;
BOOL hasNext = TRUE;
// There may be multiple certificates related to the signature. Only one is the signer's
// certificate. Others should be in a chain to a trusted root certificate.
// We need to iterate through the certificate enumerator and try using each certificate
// to validate the signature until one is successful (the signature is valid against the
// certificate). Then the certificate must be the signer's certificate.
//
// In production code, the application may build a certificate chain starting from the
// signer's certificate to a trusted root certificate (see CertGetCertificateChain for more
// details). If such a chain cannot be built, then the signer's certificate is not trustable.
// The other certificates in the certificate enumerator are used to facilitate chain building.
// The application can create a memory certificate store (see CertOpenStore) and put all these
// certificates to the store, then provide the store as an additional store to the
// CertGetCertificateChain function to search for the certificate chain. Without providing the
// additional store, if the user's machine does not have all the needed certificates, it will
// need to download them from Internet, if possible, or fail to build the chain.
//
// In this sample code we are not doing certificate chain building and validation for
// simplicity.
while (
SUCCEEDED(hr)
&&
SUCCEEDED(hr = certEnumerator->MoveNext(&hasNext))
&&
hasNext
)
{
OPC_SIGNATURE_VALIDATION_RESULT result;
PCCERT_CONTEXT cert = NULL;
certCount++;
// The contract of GetCurrent ensures that if the method fails,
// it will not return a valid CERT_CONTEXT. If it succeeds,
// CertFreeCertificateContext is called to free the CERT_CONTEXT;
// unless it is being returned to the caller in signerCert.
hr = certEnumerator->GetCurrent(&cert);
if (FAILED(hr))
{
// Filter with known possible errors that we can still continue safely.
UINT32 facilityCode = HRESULT_FACILITY(hr);
if (facilityCode == FACILITY_SECURITY || facilityCode == FACILITY_OPC)
{
// The error may be due to a corrupted certificate, bad certificate relationship or
// missing certificate part. We should give a warning to the end user and continue to
// search for the next certificate available.
fwprintf(
stdout,
L"Warning: Found corrupted certificate in the package. "
L"IOpcCertificateEnumerator::GetCurrent() returns hr=0x%x\n",
hr
);
hr = S_OK; // reset the hr so we can continue with the loop.
continue;
}
else
{
// For other types of errors, we will not continue. This way, we will not try to
// continue if the error is critical like E_OUTOFMEMORY.
break;
}
}
// Verify that the digital signature is valid. It means that all the content
// that was signed has not been modified.
hr = opcDigSigManager->Validate(signature, cert, &result);
if (SUCCEEDED(hr))
{
if (result == OPC_SIGNATURE_VALID)
{
// Found the signer certificate and the signature is valid.
*isValid = TRUE;
*signerCert = cert;
break;
}
}
else
{
// The failure HRESULT from IOpcDigitalSignatureManager::Validate() has information on
// why the validation failed.
UINT32 facilityCode = HRESULT_FACILITY(hr);
if (facilityCode == FACILITY_SECURITY)
{
// The error may be due to an incorrect certificate provided to the validation.
// We should try the next certificate available.
fwprintf(
stdout,
L"Warning: Tried to validate signature with certificate %u and it failed with hr=0x%x. \n",
certCount,
hr
);
hr = S_OK; // reset the hr so we can continue with the loop.
}
// For other types of errors, we will not continue. This way, we will not try to
// continue if the error is critical like E_OUTOFMEMORY.
}
// Free the CERT_CONTEXT we got from the certificate enumerator.
CertFreeCertificateContext(cert);
}
if (SUCCEEDED(hr) && certCount == 0)
{
// OPC spec does not require certificate of a signature to be stored in the package.
// If it is not stored in the package, the application validating the signature should have
// the knowledge of what certificate to use. For example, a person A signs a package without
// storing his/her certificate in the package. Person B gets the package and is told the
// package was signed by A. B has A's certificate (with public key only). So B can explicitly
// use the certificate to verify whether the package is really signed by A. In this sample we
// don't have such a scenario and require signer's certificate must store with the signature
// in the package.
fwprintf(
stderr,
L"There is no certificate associated with the signature. Cannot validate the signature.\n"
);
}
// Release resources
if (certEnumerator)
{
certEnumerator->Release();
certEnumerator = NULL;
}
return hr;
}
HRESULT
PrintRelationshipReferenceInfo(
IOpcSignatureRelationshipReference* relsReference
)
{
IOpcUri * sourceUri = NULL;
BSTR uriString = NULL;
OPC_RELATIONSHIPS_SIGNING_OPTION relsSigningOption = OPC_RELATIONSHIP_SIGN_USING_SELECTORS;
HRESULT hr = relsReference->GetSourceUri(&sourceUri);
if (SUCCEEDED(hr))
{
hr = sourceUri->GetDisplayUri(&uriString);
}
if (SUCCEEDED(hr))
{
fwprintf(stdout, L"Source Uri: %s\n", uriString);
}
if (SUCCEEDED(hr))
{
hr = relsReference->GetRelationshipSigningOption(&relsSigningOption);
}
if (SUCCEEDED(hr))
{
if (relsSigningOption == OPC_RELATIONSHIP_SIGN_PART)
{
// Note that in this sample we don't sign relationships part in whole
// so we should not hit this line of code.
fwprintf(stdout, L"The whole relationships part is signed.\n");
}
else // relsSigningOption == OPC_RELATIONSHIP_SIGN_USING_SELECTORS
{
IOpcRelationshipSelectorEnumerator * selectorEnumerator = NULL;
BOOL hasNext = FALSE;
hr = relsReference->GetRelationshipSelectorEnumerator(&selectorEnumerator);
while(
SUCCEEDED(hr)
&&
SUCCEEDED(hr = selectorEnumerator->MoveNext(&hasNext))
&&
hasNext
)
{
IOpcRelationshipSelector * selector = NULL;
LPCWSTR token = NULL;
LPWSTR criterion = NULL;
hr = selectorEnumerator->GetCurrent(&selector);
OPC_RELATIONSHIP_SELECTOR selectorType = OPC_RELATIONSHIP_SELECT_BY_ID;
if (SUCCEEDED(hr))
{
hr = selector->GetSelectorType(&selectorType);
}
switch (selectorType)
{
case OPC_RELATIONSHIP_SELECT_BY_ID:
token = L"Id";
fwprintf(stdout, L"Select by relationship Id.\n");
break;
case OPC_RELATIONSHIP_SELECT_BY_TYPE:
token = L"Type";
fwprintf(stdout, L"Select by relationship Type.\n");
break;
default:
fwprintf(stderr, L"Invalid OPC_RELATIONSHIP_SELECTOR value.\n");
hr = E_UNEXPECTED;
}
if (SUCCEEDED(hr))
{
hr = selector->GetSelectionCriterion(&criterion);
}
if (SUCCEEDED(hr))
{
fwprintf(stdout, L"%s = %s\n", token, criterion);
}
if (selector)
{
selector->Release();
selector = NULL;
}
CoTaskMemFree(static_cast<LPVOID>(criterion));
}
if (selectorEnumerator)
{
selectorEnumerator->Release();
selectorEnumerator = NULL;
}
}
}
// Release resources
if (sourceUri)
{
sourceUri->Release();
sourceUri = NULL;
}
SysFreeString(uriString);
return hr;
}
HRESULT
PrintSigningInfo(
IOpcDigitalSignature* signature
)
{
IOpcSignaturePartReferenceEnumerator * signedPartsEnumerator = NULL;
IOpcSignatureRelationshipReferenceEnumerator * signedRelationshipsEnumerator = NULL;
BOOL hasNext = FALSE;
HRESULT hr = signature->GetSignaturePartReferenceEnumerator(&signedPartsEnumerator);
if (SUCCEEDED(hr))
{
fwprintf(stdout, L"\nThe signature signed the following parts:\n");
}
while(
SUCCEEDED(hr)
&&
SUCCEEDED(hr = signedPartsEnumerator->MoveNext(&hasNext))
&&
hasNext
)
{
IOpcSignaturePartReference * partReference = NULL;
IOpcPartUri * partName = NULL;
BSTR nameString = NULL;
hr = signedPartsEnumerator->GetCurrent(&partReference);
if (SUCCEEDED(hr))
{
hr = partReference->GetPartName(&partName);
}
if (SUCCEEDED(hr))
{
hr = partName->GetDisplayUri(&nameString);
}
if (SUCCEEDED(hr))
{
fwprintf(stdout, nameString);
fwprintf(stdout, L"\n");
}
// Release resources
if (partReference)
{
partReference->Release();
partReference = NULL;
}
if (partName)
{
partName->Release();
partName = NULL;
}
SysFreeString(nameString);
}
if (SUCCEEDED(hr))
{
hr = signature->GetSignatureRelationshipReferenceEnumerator(&signedRelationshipsEnumerator);
fwprintf(stdout, L"\nThe signature signed the following relationships:\n");
}
while(
SUCCEEDED(hr)
&&
SUCCEEDED(hr = signedRelationshipsEnumerator->MoveNext(&hasNext))
&&
hasNext
)
{
IOpcSignatureRelationshipReference * relsReference = NULL;
hr = signedRelationshipsEnumerator->GetCurrent(&relsReference);
if (SUCCEEDED(hr))
{
hr = PrintRelationshipReferenceInfo(relsReference);
}
fwprintf(stdout, L"\n");
// Release resources
if (relsReference)
{
relsReference->Release();
relsReference = NULL;
}
}
// Release resources
if (signedPartsEnumerator)
{
signedPartsEnumerator->Release();
signedPartsEnumerator = NULL;
}
if (signedRelationshipsEnumerator)
{
signedRelationshipsEnumerator->Release();
signedRelationshipsEnumerator = NULL;
}
return hr;
}
HRESULT
ValidateMusicBundleSignature(
IOpcFactory* opcFactory
)
{
IOpcPackage * opcPackage = NULL;
IOpcDigitalSignatureManager * opcDigSigManager = NULL;
IOpcDigitalSignatureEnumerator * signatureEnumerator = NULL;
BOOL hasNext = FALSE;
UINT32 count = 0;
// Load the signed music bundle package.
HRESULT hr = ReadPackageFromFile(g_signedFilePath, opcFactory, &opcPackage);
// Create a digital signature manager for the package.
if (SUCCEEDED(hr))
{
hr = opcFactory->CreateDigitalSignatureManager(opcPackage, &opcDigSigManager);
}
if (SUCCEEDED(hr))
{
hr = opcDigSigManager->GetSignatureEnumerator(&signatureEnumerator);
}
// We report all the signatures (if there are more than one) in the music bundle and validate
// all of them. Note that in the signing part of this sample we only created 1 signature but
// the validation sample is written for generic handling of a signed package.
while (
SUCCEEDED(hr)
&&
SUCCEEDED(hr = signatureEnumerator->MoveNext(&hasNext))
&&
hasNext
)
{
count++;
IOpcDigitalSignature * signature = NULL;
BOOL isValid = FALSE;
PCCERT_CONTEXT signerCert = NULL;
hr = signatureEnumerator->GetCurrent(&signature);
if (SUCCEEDED(hr))
{
fwprintf(stdout, L"Found Signature %u:\n", count);
hr = ValidateSignature(opcDigSigManager, signature, &isValid, &signerCert);
if (SUCCEEDED(hr))
{
if (isValid)
{
WCHAR certFileName[MAX_PATH];
fwprintf(stdout, L"Signature %u is valid.\n", count);
// In production code, the application should check whether the signature is compliant to
// the signing policy: all the required parts and relationships are signed properly (please
// refer to the sample signing policy in Sign.h to see the details).
// In this sample, for simplicity, we just print out such information for user to verify.
PrintSigningInfo(signature);
hr = StringCchPrintf(certFileName, MAX_PATH, L"Signer%u.cer", count);
if (SUCCEEDED(hr))
{
hr = SaveCertificateToFile(signerCert, certFileName);
if (SUCCEEDED(hr))
{
// In production code, the application may verify the certificate by building a certificate
// chain to a trusted root certificate (see CertGetCertificateChain for more details).
// In this sample, for simplicity, we store the signer certificate to a file so the user
// can double-click it and open it in the certificate UI and verify it.
fwprintf(
stdout,
L"The signer of signature %u is identified by the certificate stored in %s. "
L"Please inspect the certificate to see whether you trust the signer "
L"(you can double click on the certificate file to open it).\n",
count,
certFileName
);
}
else
{
fwprintf(
stderr,
L"Failed to save the certificate to file. \n"
);
}
}
CertFreeCertificateContext(signerCert);
}
else
{
fwprintf(stdout, L"Signature %u is invalid.\n", count);
}
}
}
else
{
// Filter with known possible errors that are continuable, and we should be
// able to continue to check the next signature if there is any.
UINT32 facilityCode = HRESULT_FACILITY(hr);
if (facilityCode == FACILITY_OPC
||
facilityCode == FACILITY_SECURITY
||
facilityCode == FACILITY_WEBSERVICES)
{
// These types of error indicate that the signature has problems such as
// the signature part is missing, the signature XML is mal-formatted or corrupted, etc.
// End user may be able to get a clue of what went wrong in the signature by
// looking up the error code.
fwprintf(
stdout,
L"Warning: Found Signature %u but it is corrupted. "
L"IOpcDigitalSignatureEnumerator::GetCurrent() returns hr=0x%x\n",
count,
hr
);
hr = S_OK; // reset the hr so we can continue with the loop.
}
}
if (signature)
{
signature->Release();
signature = NULL;
}
}
if (SUCCEEDED(hr) && count == 0)
{
fwprintf(stderr, L"Music bundle [%s] has no signature.\n", g_signedFilePath);
hr = E_UNEXPECTED;
}
// Release resources
if (opcPackage)
{
opcPackage->Release();
opcPackage = NULL;
}
if (opcDigSigManager)
{
opcDigSigManager->Release();
opcDigSigManager = NULL;
}
if (signatureEnumerator)
{
signatureEnumerator->Release();
signatureEnumerator = NULL;
}
return hr;
}
//</SnippetMusicBundleSig_cppValidateWholePage>