I need to enumerate keys in the machine key container. Although this is generally an optional provider function, both MS_STRONG_PROV
and MS_ENH_RSA_AES_PROV
support it. I do not think I am doing anything wrong or unusual: first, acquiring a context handle with CryptAcquireContext(... CRYPT_MACHINE_KEYSET | CRYPT_VERIFYCONTEXT ...)
, then calling CryptGetProvParam(... PP_ENUMCONTAINERS ...)
repeatedly until the enumeration is exhausted:
void enum_keys(HCRYPTPROV hprov) {
BYTE buf[1024]; // Max key name length we support.
for (DWORD first_next = CRYPT_FIRST; 1; first_next = CRYPT_NEXT) {
DWORD buf_len = sizeof buf;
if (!CryptGetProvParam(hprov, PP_ENUMCONTAINERS, buf, &buf_len, first_next)) {
if (GetLastError() == ERROR_NO_MORE_ITEMS) break;
else exit(1);
}
}
}
void do_benchmark(DWORD enum_flags) {
enum_flags |= CRYPT_VERIFYCONTEXT;
HCRYPTPROV hprov;
if (!CryptAcquireContext(&hprov, NULL, MS_ENH_RSA_AES_PROV_A,
PROV_RSA_AES, enum_flags))
exit(1);
int K = 100;
ClockIn(); // Pseudocode
for (int i = 0; i < K; ++i)
enum_keys (hprov);
ClockOut(); // Pseudocode.
printf(" %f ms per pass\n", TimeElapsed() / K);
CryptReleaseContext(hprov, 0);
}
void main() {
printf("--- User key store access performance test... ");
do_benchmark(0);
printf("--- Machine key store access performance test... ");
do_benchmark(CRYPT_MACHINE_KEYSET);
}
To benchmark the enumeration, I am leaving context acquisition and release out of the loop, and clocking only the enumeration, and repeat the enumeration 100 times. What I am noticing is that the enumeration is significantly slower for a normal user than an administrator. When I run the test as myself (member of Administrators with UAC enabled), I am getting
--- User key store access performance test... 3.317211 ms per pass
--- Machine key store access performance test... 78.051593 ms per pass
However, when I run the same test from an elevated prompt, the result is dramatically different:
--- User key store access performance test... 3.279580 ms per pass
--- Machine key store access performance test... 1.499939 ms per pass
Under the hood, more keys are reported to an admin than to non-admin user, but that's expected and normal. What I do not understand is why the enumeration is ~40 times slower for a non-admin user. Any pointers?
I am putting the full source of my test into a Gist. The test is run on a pretty generic Windows 7 machine without any crypto hardware.
Added: on a Server 2012 virtual machine on a Server 2012 HyperV host, the slowdown factor was even greater, over 130: 440 vs 3.3 ms. 440ms is a performance issue for me, indeed.