Как рассчитать идентификатор ключа субъекта в СПГ?

Моя цель — заполнить расширение идентификатора ключа субъекта (2.5.29.14) для сертификата с использованием Microsoft CNG. Я сделал это ранее с Microsoft CAPI, но функция, которую я использовал:

CryptHashPublicKeyInfo

https://msdn.microsoft.com/en-us/library/windows/desktop/aa380204(v=vs.85).aspx

Сейчас амортизируется. КПГ не имеет такого метода. Тем не менее, описание CryptHashPublicKeyInfo в приведенной выше ссылке говорит о том, что они выполняют хэш SHA1 для информации открытого ключа. Поэтому я сделал хэш SHA1 байтов открытого ключа в CNG для тех же данных в CryptHashPublicKeyInfo (CAPI), и эти два хэша различны. Мне нужно разрешить эту разницу. Чтобы было ясно, моя логика работает на том же открытом ключе от того же CSR.

Детали в RFC 5280, кажется, подтверждают то, что говорит Microsoft:
https://tools.ietf.org/html/rfc5280#section-4.2.1.2

(1) KeyIdentifier состоит из 160-битного SHA-1 хэша
значение BIT STRING subjectPublicKey (исключая тег,
длина и количество неиспользованных битов).

Купер и соавт. Трек Стандартов
28] Сертификат RFC 5280 PKIX и профиль CRL
Май 2008

  (2) The keyIdentifier is composed of a four-bit type field with
the value 0100 followed by the least significant 60 bits of
the SHA-1 hash of the value of the BIT STRING
subjectPublicKey (excluding the tag, length, and number of
unused bits).

^ Я предполагаю, что Microsoft занимается делом № 1.

Вот мой код CAPI:

//depreciated (CAPI)
//get data for subject key identifier
//get length
HCRYPTPROV hHashProv = NULL;
if (!CryptHashPublicKeyInfo(
hHashProv,
CALG_SHA1, //sha1
0,
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
&serverCertInfo.SubjectPublicKeyInfo,
NULL,
&dwSubjectKeyIdentifier
))
{
throw std::runtime_error("Unable to get length of public key info hash");
}

//alocate data buffer
pbSubjectKeyIdentifier = (LPBYTE)LocalAlloc(0, dwSubjectKeyIdentifier);
//fill data buffer with subject key identifier
if (!CryptHashPublicKeyInfo(
hHashProv,
CALG_SHA1, //sha1
0,
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
&serverCertInfo.SubjectPublicKeyInfo,
pbSubjectKeyIdentifier,
&dwSubjectKeyIdentifier
))
{
throw std::runtime_error("Unable to fill public key info hash");
}

CRYPT_DATA_BLOB skiBlob;
skiBlob.cbData = dwSubjectKeyIdentifier;
skiBlob.pbData = pbSubjectKeyIdentifier;

//encode subject key identifier extension
LPBYTE pbEncodeSKI = NULL;
DWORD dwEncodedSKI;
if (!CryptEncodeObject(
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
szOID_SUBJECT_KEY_IDENTIFIER,
(void*)&skiBlob,
NULL,
&dwEncodedSKI
))
{
throw std::runtime_error("Unable to get length to encode extension: subject key identifier");
}

pbEncodeSKI = (LPBYTE)LocalAlloc(0, dwEncodedSKI);
if (!CryptEncodeObject(
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
szOID_SUBJECT_KEY_IDENTIFIER,
(void*)&skiBlob,
pbEncodeSKI,
&dwEncodedSKI
))
{
throw std::runtime_error("Unable to encode extension: subject key identifier");
}

Это создает это значение в расширении (тот же открытый ключ в запросе сертификата): 9d77f29e4fa15e46237d59a7c00efde9d286b9dc

Это мой код CNG:

NTSTATUS statusBCryptOpenAlgorithmProvider;
NTSTATUS statusBCryptHash;
BCRYPT_ALG_HANDLE hHashAlg;
LPBYTE pbHash;
DWORD dwHash = 20;
LPSTR lpstrPublicKeyEncoded;
DWORD dwPublicKeyEncoded;
CRYPT_DATA_BLOB skiBlob;

if (!CryptBinaryToStringA(
infoPublicKey.PublicKey.pbData,
infoPublicKey.PublicKey.cbData,
CRYPT_STRING_BINARY,
NULL,
&dwPublicKeyEncoded
))
{
throw std::runtime_error("Error getting length of encoded binary string (CryptBinaryToString)");
}

lpstrPublicKeyEncoded = (LPSTR)LocalAlloc(0, dwPublicKeyEncoded);
if (!CryptBinaryToStringA(
infoPublicKey.PublicKey.pbData,
infoPublicKey.PublicKey.cbData,
CRYPT_STRING_BINARY,
lpstrPublicKeyEncoded,
&dwPublicKeyEncoded
))
{
LocalFree(lpstrPublicKeyEncoded);
throw std::runtime_error("Error encoding binary string (CryptBinaryToString)");
}

statusBCryptOpenAlgorithmProvider = BCryptOpenAlgorithmProvider(
&hHashAlg,
BCRYPT_SHA1_ALGORITHM,
MS_PRIMITIVE_PROVIDER,
0
);

if (0 != statusBCryptOpenAlgorithmProvider)
{
LocalFree(lpstrPublicKeyEncoded);
throw std::runtime_error("Error opening SHA1 algorithm provider (BCryptOpenAlgorithmProvider)");
}

pbHash = (LPBYTE)LocalAlloc(0, dwHash);
statusBCryptHash = BCryptHash(
hHashAlg,
NULL,
0,
(BYTE*)lpstrPublicKeyEncoded,
dwPublicKeyEncoded,
pbHash,
dwHash
);

if (0 != statusBCryptHash)
{
LocalFree(lpstrPublicKeyEncoded);
BCryptCloseAlgorithmProvider(hHashAlg, 0);
LocalFree(pbHash);
throw std::runtime_error("Error hashing public key (BCryptHash)");
}

skiBlob.pbData = pbHash;
skiBlob.cbData = dwHash;

BCryptCloseAlgorithmProvider(hHashAlg, 0);
LocalFree(pbHash);
LocalFree(lpstrPublicKeyEncoded);

//encode subject key identifier extension
LPBYTE pbEncodeSKI = NULL;
DWORD dwEncodedSKI;
if (!CryptEncodeObject(
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
szOID_SUBJECT_KEY_IDENTIFIER,
(void*)&skiBlob,
NULL,
&dwEncodedSKI
))
{
throw std::runtime_error("Unable to get length to encode extension: subject key identifier");
}

pbEncodeSKI = (LPBYTE)LocalAlloc(0, dwEncodedSKI);
if (!CryptEncodeObject(
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
szOID_SUBJECT_KEY_IDENTIFIER,
(void*)&skiBlob,
pbEncodeSKI,
&dwEncodedSKI
))
{
throw std::runtime_error("Unable to encode extension: subject key identifier");
}

Это производит это значение SKI (другое): 210816297e8e76879f99ec4762452b5d38967b5b

Любая подсказка, что я делаю неправильно в образце кода CNG? Очевидно, что есть волшебная последовательность звонков, но я не знаю, что это.

0

Решение

Здесь вы идете: оба варианта CNG и CAPI.

HRESULT capiCreateKeyIdentifierFromPublicKey(NCRYPT_KEY_HANDLE hCngKey, CRYPT_DATA_BLOB* outHash)
{
HRESULT                 hr         = S_OK;
BOOL                    bResult    = FALSE;

PCERT_PUBLIC_KEY_INFO   pCertInfo  = NULL;
DWORD                   cbCertInfo = 0;

outHash->pbData = NULL;
outHash->cbData = 0;/* STEP1: Extract public key. */
bResult = CryptExportPublicKeyInfo(hCngKey, 0, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, NULL, &cbCertInfo);
if (!bResult) {
hr = HRESULT_FROM_WIN32(GetLastError());
goto Cleanup;
}

pCertInfo = (PCERT_PUBLIC_KEY_INFO)HeapAlloc(GetProcessHeap(), 0, cbCertInfo);
if (NULL == pCertInfo) {
hr = HRESULT_FROM_WIN32(ERROR_OUTOFMEMORY);
goto Cleanup;
}bResult = CryptExportPublicKeyInfo(hCngKey, 0, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, pCertInfo, &cbCertInfo);
if (!bResult) {
hr = HRESULT_FROM_WIN32(GetLastError());
goto Cleanup;
}/* STEP2: Make hash. */
bResult = CryptHashPublicKeyInfo(NULL, CALG_SHA1, 0, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, pCertInfo, NULL, &outHash->cbData);
if (!bResult) {
hr = HRESULT_FROM_WIN32(GetLastError());
goto Cleanup;
}

outHash->pbData = (BYTE*)HeapAlloc(GetProcessHeap(), 0, outHash->cbData);

bResult = CryptHashPublicKeyInfo(NULL, CALG_SHA1, 0, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, pCertInfo, outHash->pbData, &outHash->cbData);
if (!bResult) {
hr = HRESULT_FROM_WIN32(GetLastError());
goto Cleanup;
}Cleanup:
if (!SUCCEEDED(hr) && NULL != outHash->pbData) {
HeapFree(GetProcessHeap(), 0, outHash->pbData);
outHash->pbData = NULL;
outHash->cbData = 0;
}

if (NULL != pCertInfo) {
HeapFree(GetProcessHeap(), 0, pCertInfo);
pCertInfo = 0;
}

return hr;
}HRESULT cngCreateKeyIdentifierFromPublicKey(NCRYPT_KEY_HANDLE hCngKey, CRYPT_DATA_BLOB* outHash)
{
// @see https://docs.microsoft.com/en-us/windows/desktop/seccng/creating-a-hash-with-cng
HRESULT                 hr           = S_OK;
BOOL                    bResult      = FALSE;

BCRYPT_ALG_HANDLE       hAlg         = NULL;
BCRYPT_HASH_HANDLE      hHash        = NULL;
NTSTATUS                status       = 0;

DWORD                   cbData       = 0;
DWORD                   cbHashObject = 0;
PBYTE                   pbHashObject = NULL;

PCERT_PUBLIC_KEY_INFO   pCertInfo    = NULL;
DWORD                   cbCertInfo   = 0;

BYTE*                   pbEncodedCertInfo = NULL;
ULONG                   cbEncodedCertInfo = 0;

outHash->pbData = NULL;
outHash->cbData = 0;

/* STEP1: Extract public key. */
bResult = CryptExportPublicKeyInfo(hCngKey, 0, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, NULL, &cbCertInfo);
if (!bResult) {
hr = HRESULT_FROM_WIN32(GetLastError());
goto Cleanup;
}

pCertInfo = (PCERT_PUBLIC_KEY_INFO)HeapAlloc(GetProcessHeap(), 0, cbCertInfo);
if (NULL == pCertInfo) {
hr = HRESULT_FROM_WIN32(ERROR_OUTOFMEMORY);
goto Cleanup;
}bResult = CryptExportPublicKeyInfo(hCngKey, 0, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, pCertInfo, &cbCertInfo);
if (!bResult) {
hr = HRESULT_FROM_WIN32(GetLastError());
goto Cleanup;
}/* STEP2: Encode the public key. */
bResult = CryptEncodeObject(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, X509_PUBLIC_KEY_INFO, pCertInfo, pbEncodedCertInfo, &cbEncodedCertInfo);
if (!bResult) {
hr = HRESULT_FROM_WIN32(GetLastError());
goto Cleanup;
}

pbEncodedCertInfo = (BYTE*)HeapAlloc(GetProcessHeap(), 0, cbEncodedCertInfo);

bResult = CryptEncodeObject(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, X509_PUBLIC_KEY_INFO, pCertInfo, pbEncodedCertInfo, &cbEncodedCertInfo);
if (!bResult) {
hr = HRESULT_FROM_WIN32(GetLastError());
goto Cleanup;
}/* STEP3: Open an algorithm handle. */
status = BCryptOpenAlgorithmProvider(
&hAlg,
BCRYPT_SHA1_ALGORITHM,
NULL,
0
);

if (!NT_SUCCESS(status)) {
hr = HRESULT_FROM_NT(status);
goto Cleanup;
}/* STEP4: Calculate the size of the buffer to hold the hash object. */
status = BCryptGetProperty(
hAlg,
BCRYPT_OBJECT_LENGTH,
(PBYTE)&cbHashObject,
sizeof(DWORD),
&cbData,
0
);

if (!NT_SUCCESS(status)) {
hr = HRESULT_FROM_NT(status);
goto Cleanup;
}/* STEP5: Allocate the buffer for hash object on the heap. */
pbHashObject = (PBYTE)HeapAlloc(GetProcessHeap(), 0, cbHashObject);
if (NULL == pbHashObject) {
hr = HRESULT_FROM_WIN32(ERROR_OUTOFMEMORY);
goto Cleanup;
}/* STEP6: Create a hash object (get handle to CNG hash object). */
status = BCryptCreateHash(
hAlg,
&hHash,
pbHashObject,
cbHashObject,
NULL,
0,
0
);

if (!NT_SUCCESS(status)) {
hr = HRESULT_FROM_NT(status);
goto Cleanup;
}/* STEP7: Calculate the length of buffer for result hash. */
status = BCryptGetProperty(
hAlg,
BCRYPT_HASH_LENGTH,
(PBYTE)&outHash->cbData,
sizeof(DWORD),
&cbData,
0
);

if (!NT_SUCCESS(status)) {
hr = HRESULT_FROM_NT(status);
goto Cleanup;
}/* STEP8: Allocate buffer for result hash on the heap. */
outHash->pbData = (PBYTE)HeapAlloc(GetProcessHeap(), 0, outHash->cbData);
if (NULL == outHash->pbData) {
hr = HRESULT_FROM_WIN32(ERROR_OUTOFMEMORY);
goto Cleanup;
}/* STEP9: Hash data. */
status = BCryptHashData(
hHash,
(PBYTE)pbEncodedCertInfo,
cbEncodedCertInfo,
0
);

if (!NT_SUCCESS(status)) {
hr = HRESULT_FROM_NT(status);
goto Cleanup;
}/* STEP10: Close hash object and get result value. */
status = BCryptFinishHash(
hHash,
outHash->pbData,
outHash->cbData,
0
);

if (!NT_SUCCESS(status)) {
hr = HRESULT_FROM_NT(status);
goto Cleanup;
}

Cleanup:
if (!SUCCEEDED(hr) && NULL != outHash->pbData) {
HeapFree(GetProcessHeap(), 0, outHash->pbData);
outHash->pbData = NULL;
outHash->cbData = 0;
}

if (NULL != hHash) {
BCryptDestroyHash(hHash);
hHash = NULL;
}

if (NULL != pbHashObject) {
HeapFree(GetProcessHeap(), 0, pbHashObject);
pbHashObject = NULL;
}

if (NULL != hAlg) {
BCryptCloseAlgorithmProvider(hAlg, 0);
hAlg = NULL;
}

if (NULL != pbEncodedCertInfo) {
HeapFree(GetProcessHeap(), 0, pbEncodedCertInfo);
pCertInfo = 0;
}

if (NULL != pCertInfo) {
HeapFree(GetProcessHeap(), 0, pCertInfo);
pCertInfo = 0;
}

return hr;
}

Использование:

CRYPT_DATA_BLOB subjectKeyIdentifier = { 0 };
NCRYPT_KEY_HANDLE hCngKey = NULL;

HRESULT hr = NCryptOpenStorageProvider(&hProvider, MS_KEY_STORAGE_PROVIDER, 0);
if (hr) {
hr = NCryptOpenKey(hProvider, &hCngKey, wszKeyName, 0, 0);
if (ERROR_SUCCESS == hr) {
hr = cngCreateKeyIdentifierFromPublicKey(hCngKey, &subjectKeyIdentifier);
if (hr) {
// do smth with data
// clear the memory
HeapFree(GetProcessHeap(), 0, subjectKeyIdentifier.pbData);
subjectKeyIdentifier.pbData = NULL;
subjectKeyIdentifier.cbData = 0;
}

}
}

......
2

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]