I'm trying to verify that the certificate from a signature chains back to a particular root certificate, which is not trusted by Windows (it's a private certificate for the app).
My current attempt to do this involves creating a chaining engine which only trusts the specific certificate I want as the root, so that no other chains can be generated.
HCERTSTORE hPrivateRootStore = CertOpenStore(CERT_STORE_PROV_FILENAME, dwEncoding,
NULL, CERT_STORE_OPEN_EXISTING_FLAG | CERT_STORE_READONLY_FLAG,
_T("C:\\Test\\PrivateRoot.cer"));
CERT_CHAIN_ENGINE_CONFIG config;
memset(&config, 0, sizeof(config));
config.cbSize = sizeof(config);
config.hRestrictedTrust = hPrivateRootStore;
config.dwFlags = CERT_CHAIN_CACHE_ONLY_URL_RETRIEVAL | CERT_CHAIN_ENABLE_SHARE_STORE;
HCERTCHAINENGINE hEngine;
CertCreateCertificateChainEngine(&config, &hEngine);
CERT_CHAIN_PARA params;
memset(¶ms, 0, sizeof(params));
params.cbSize = sizeof(params);
PCCERT_CHAIN_CONTEXT chains = NULL;
if (CertGetCertificateChain(hEngine, pCertContext, NULL, hStore, ¶ms,
0, NULL, &chains))
...
(error checking omitted for clarity; pCertContext
and hStore
came from CryptQueryObject
extracting the signature and related certificates from a signed binary file.)
Unfortunately, this doesn't seem to work; despite using a custom chaining engine it still seems to be searching the OS store, and either doesn't find a chain or finds one to a different root (which is trusted by the OS). I can only get the chain I want by adding my private root certificate to the OS trusted store.
I've also tried setting config.hRestrictedOther
to an empty memory store, since the docs suggest that having hRestrictedTrust
non-NULL will bring in the system stores again, but that doesn't make any difference.
Is there something I'm missing, or a better way to do this?
Edit: just to give a bit more context, I'm trying to do something similar to the driver signing certificates, where the signing certificate chains back to two different roots: one standard CA root trusted by the OS and one internal root (which in drivers is also trusted by the OS but in my case will only be trusted by my a开发者_StackOverflow中文版pp). The cross occurs somewhere mid-way up the "main" chain; there could potentially be a number of different files all signed with different "real" CAs but still chained back to my internal certificate.
I've found a half-baked workaround now; it's a little ugly but it does kinda work. I got the basic idea from Chromium's test suite; it involves installing a hook into Crypt32 such that when it tries to open the system stores to build the chain it gets my custom store instead, containing only my desired certificate.
The good is that this seems to force CertGetCertificateChain
to go "past" the real CA certificate and chain all the way to my custom certificate, instead of stopping at the CA certificate (which is what it usually does when that is trusted).
The bad is that it doesn't seem to stop it from building chains to and trusting any other CA certificate. I can work around that by explicitly verifying that the root of the chain is the certificate I wanted, but it's less than ideal, and I'm not sure if there are situations which will trip it up.
Still looking for any better solutions; I'm definitely getting the impression that I'm taking the wrong path somewhere.
Ok, new plan. I'm now just walking the chain manually (since I know that all of the certificates I care about will be in the hStore
extracted from the signed .exe), with basic structure like this:
- Use
WinVerifyTrust
to do basic "is it not tampered" authentication. - Use
CryptQueryObject
to obtain certificate storehStore
from the .exe - Use
CryptMsgGetParam
andCertFindCertificateInStore
to find the signing certificate fromhStore
. - Starting from the signing certificate, use
CertFindCertificateInStore
withCERT_FIND_SUBJECT_NAME
in a loop to find the potential issuer certificates; keep walking back until I hit a self-signed certificate or find my desired root (checking for a match viaCertComparePublicKeyInfo
). - Abandon a particular branch if
CertVerifySubjectCertificateContext
says that the signatures don't match.
Seems cleaner than the previous approaches I was trying. Thoughts/comments/alternatives?
(Something which in some ways does seem to make more sense would be to add an additional custom countersignature [similar to the timestamping] instead of trying to chain certificates like this, but I can't find any information on doing that, or what the pros/cons are.)
精彩评论