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

330 lines
10 KiB
C++

// THIS CODE AND INFORMATION IS 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.
//
// Copyright (c) Microsoft Corporation. All rights reserved.
#include "pch.h"
/*****************************************************************************
HrVerify
This sample demontrates XML signature verification
*****************************************************************************/
HRESULT
HrVerify(
LPCWSTR wszFileIn
)
{
HRESULT hr = S_FALSE;
HCRYPTXML hDoc = NULL;
ULONG i;
const CRYPT_XML_DOC_CTXT *pDoc = NULL;
const CRYPT_XML_SIGNATURE *pSig = NULL;
const CRYPT_XML_REFERENCE *pRef = NULL;
CRYPT_XML_STATUS Status = {0};
BCRYPT_KEY_HANDLE hKey = NULL;
BCRYPT_KEY_HANDLE hKeyAlloc = NULL; // BCryptDestroyKey
PCCERT_CONTEXT pCert = NULL;
CRYPT_XML_DATA_PROVIDER DataProvider = {0};
CRYPT_XML_BLOB Encoded = { CRYPT_XML_CHARSET_AUTO, 0, NULL };
hr = HrLoadFile(
wszFileIn,
&Encoded.pbData,
&Encoded.cbData
);
if( FAILED(hr) )
{
goto CleanUp;
}
hr = CryptXmlOpenToDecode(
NULL, // no custom transforms
0,
NULL,
0,
&Encoded,
&hDoc
);
if( FAILED(hr) )
{
goto CleanUp;
}
hr = CryptXmlGetDocContext(
hDoc,
&pDoc
);
if( FAILED(hr) )
{
goto CleanUp;
}
if( pDoc->cSignature < 1 )
{
wprintf( L"WARNING: The document contains no Signature element.\r\n" );
// No signatures found
hr = S_FALSE;
goto CleanUp;
}
for( i=0; i<pDoc->cSignature; i++ )
{
hKey = NULL;
if( NULL == hKeyAlloc )
{
BCryptDestroyKey( hKeyAlloc );
hKeyAlloc = NULL;
}
//
// Find Signer's certificate
//
for( ULONG ki=0; ki<pDoc->rgpSignature[i]->pKeyInfo->cKeyInfo && NULL == hKey; ki++ )
{
if( CRYPT_XML_KEYINFO_TYPE_X509DATA ==
pDoc->rgpSignature[i]->pKeyInfo->rgKeyInfo[ki].dwType )
{
for( ULONG x=0; x<pDoc->rgpSignature[i]->pKeyInfo->rgKeyInfo[ki].X509Data.cX509Data; x++ )
{
CRYPT_XML_X509DATA_ITEM *pX = &pDoc->rgpSignature[i]->pKeyInfo->rgKeyInfo[ki].X509Data.rgX509Data[x];
if( CRYPT_XML_X509DATA_TYPE_CERTIFICATE == pX->dwType )
{
//
// SAMPLE: Just assume that the first cert is the one ...
// In production code, implement the full logic
// to find the signer's cert, from a set of multiple X.509 data elements
//
pCert = CertCreateCertificateContext(
X509_ASN_ENCODING,
pX->Certificate.pbData,
pX->Certificate.cbData
);
if( NULL != pCert )
{
if( CryptImportPublicKeyInfoEx2(
X509_ASN_ENCODING,
&pCert->pCertInfo->SubjectPublicKeyInfo,
CRYPT_OID_INFO_PUBKEY_SIGN_KEY_FLAG,
NULL,
&hKeyAlloc
))
{
hKey = hKeyAlloc;
break;
}
}
}
}
}
}
if( NULL == hKey )
{
wprintf( L"ERROR: Unable to find signer's key to verify signature [%d] Id='%s'\r\n",
i,
pDoc->rgpSignature[i]->wszId
);
hr = CRYPT_XML_E_SIGNER;
goto CleanUp;
}
//
// SAMPLE: Verify Signer's Trust
//
{
//
// TODO: Use CertGetCertificateChain() to build the chain and verify the trust.
//
//
// TODO: Accept only strong cryptographic algorithms and key strengths.
// Also, place max key length on key size to avoid Denial of Service (DoS)
// attacks on verification key operations.
//
}
hr = CryptXmlVerifySignature(
pDoc->rgpSignature[i]->hSignature,
hKey,
0
);
if( FAILED(hr) )
{
wprintf( L"FAIL: CryptXmlVerifySignature() returned 0x%08x error on signature [%d] Id='%s'\r\n",
hr,
i,
pDoc->rgpSignature[i]->wszId
);
goto CleanUp;
}
wprintf( L"Signature Value on signature [%d] Id='%s' is valid.\r\n",
i,
pDoc->rgpSignature[i]->wszId
);
//
// Verify References
//
hr = CryptXmlGetSignature(
pDoc->rgpSignature[i]->hSignature,
&pSig
);
if( FAILED(hr) )
{
goto CleanUp;
}
for( ULONG r=0; r<pSig->SignedInfo.cReference; r++ )
{
hr = CryptXmlGetReference(
pSig->SignedInfo.rgpReference[r]->hReference,
&pRef
);
if( FAILED(hr) )
{
goto CleanUp;
}
hr = CryptXmlGetStatus(
pSig->SignedInfo.rgpReference[r]->hReference,
&Status
);
if( FAILED(hr) )
{
goto CleanUp;
}
if( 0 != ( Status.dwErrorStatus & CRYPT_XML_STATUS_ERROR_NOT_RESOLVED ))
{
//
// Resolve the external references only.
// The internal references was resolved by CryptXml during CryptXmlVerifySignature
//
if( 0 == ( Status.dwInfoStatus & CRYPT_XML_STATUS_INTERNAL_REFERENCE ))
{
//
// TODO: Verify the scope of the Reference Target URI prior to resolving
//
hr = HrSampleResolveExternalXmlReference(
pRef->wszUri,
&DataProvider
);
if( FAILED(hr) )
{
goto CleanUp;
}
}
if( NULL == DataProvider.pfnRead )
{
hr = CRYPT_XML_E_UNRESOLVED_REFERENCE;
goto CleanUp;
}
//
// Provider must be released by the callee
//
hr = CryptXmlDigestReference(
pSig->SignedInfo.rgpReference[r]->hReference,
0,
&DataProvider
);
ZeroMemory( &DataProvider, sizeof DataProvider );
//
// Get the status to check it again
//
hr = CryptXmlGetStatus(
pSig->SignedInfo.rgpReference[r]->hReference,
&Status
);
}
if( 0 != ( Status.dwErrorStatus & CRYPT_XML_STATUS_ERROR_DIGEST_INVALID ))
{
wprintf( L"Digest Value on reference[%d] Id='%s' is not valid.\r\n",
r,
pSig->SignedInfo.rgpReference[r]->wszId );
}
else
{
if( 0 != ( Status.dwErrorStatus & CRYPT_XML_STATUS_ERROR_NOT_RESOLVED ))
{
wprintf( L"Reference[%d] Id='%s' is not resolved\r\n",
r,
pSig->SignedInfo.rgpReference[r]->wszId );
}
else
{
wprintf( L"Digest Value on reference[%d] Id='%s' is valid\r\n",
r,
pSig->SignedInfo.rgpReference[r]->wszId );
}
}
}
//
// Average status on the Signature
//
hr = CryptXmlGetStatus( pDoc->rgpSignature[i]->hSignature, &Status );
if( FAILED(hr) )
{
goto CleanUp;
}
if( 0 != ( Status.dwErrorStatus & CRYPT_XML_STATUS_ERROR_DIGEST_INVALID ))
{
hr = CRYPT_XML_E_INVALID_DIGEST;
goto CleanUp;
}
if( 0 != ( Status.dwErrorStatus & CRYPT_XML_STATUS_ERROR_NOT_RESOLVED ))
{
hr = CRYPT_XML_E_UNRESOLVED_REFERENCE;
goto CleanUp;
}
}
hr = S_OK;
CleanUp:
if( NULL != Encoded.pbData )
{
LocalFree( Encoded.pbData );
}
if( NULL == hKeyAlloc )
{
BCryptDestroyKey( hKeyAlloc );
}
if( NULL != pCert )
{
CertFreeCertificateContext( pCert );
}
return hr;
}