NCryptSignHash возвращает NTE_INVALID_PARAMETER (0x80090027)

Я работаю со старым кодом, который подписывал данные с использованием установленного сертификата и CryptoAPI, и мне нужно перенести его на использование CNG. Я попробовал два разных подхода: один с использованием CryptAcquireCertificatePrivateKey(), а другой с использованием NCryptOpenStorageProvider() и NCryptOpenKey(). Оба подхода открывают ключ CNG, и первый вызов NCryptSignHash() правильно возвращает длину подписи как 256 байт.

Второй вызов NCryptOpenKey() для создания данных подписи всегда завершается неудачей с NTE_INVALID_PARAMETER (0x80090027).

Я сформулировал проблему в этом примере:

#define MY_TYPE  (X509_ASN_ENCODING | PKCS_7_ASN_ENCODING)

HRESULT AcquireCertificatePrivateKey (PCCERT_CONTEXT pSignerCert, NCRYPT_PROV_HANDLE* phProvider, NCRYPT_KEY_HANDLE* phKey, DWORD dwFlags)
{
    HRESULT hr;
    DWORD dwSize = 0;
    CRYPT_KEY_PROV_INFO* pKeyProvInfo = NULL;
    NCRYPT_PROV_HANDLE hProvider = NULL;

    CheckIf(NULL == pSignerCert || NULL == phKey, E_INVALIDARG);

    CheckIfGetLastError(!CertGetCertificateContextProperty(pSignerCert, CERT_KEY_PROV_INFO_PROP_ID, NULL, &dwSize));

    pKeyProvInfo = (CRYPT_KEY_PROV_INFO*)malloc(dwSize);
    CheckAlloc(pKeyProvInfo);

    CheckIfGetLastError(!CertGetCertificateContextProperty(pSignerCert, CERT_KEY_PROV_INFO_PROP_ID, pKeyProvInfo, &dwSize));

    Check(NCryptOpenStorageProvider(&hProvider, pKeyProvInfo->pwszProvName, 0));
    Check(NCryptOpenKey(
        hProvider,
        phKey,
        pKeyProvInfo->pwszContainerName,
        pKeyProvInfo->dwKeySpec,
        dwFlags));
    if (phProvider)
    {
        *phProvider = hProvider;
        hProvider = NULL;
    }

Cleanup:
    if (hProvider)
        NCryptFreeObject(hProvider);
    free(pKeyProvInfo);
    return hr;
}

INT main (INT cArgs, __in_ecount(cArgs) PCSTR* ppcszArgs)
{
    HRESULT hr;
    HCERTSTORE hStoreHandle = NULL;
    PCCERT_CONTEXT pSignerCert = NULL;
    PCWSTR pcwzSigner = L"name of my certificate";
    DWORD dwKeySpec = 0, dwSigLen = 0;
    BOOL fCallerFree = FALSE;
    NCRYPT_PROV_HANDLE hProvider = NULL;
    NCRYPT_KEY_HANDLE hKey = NULL;
    BYTE bData[20], *pbSignature = NULL;

    hStoreHandle = CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, NULL, CERT_SYSTEM_STORE_LOCAL_MACHINE | CERT_STORE_READONLY_FLAG | CERT_STORE_OPEN_EXISTING_FLAG /*CERT_SYSTEM_STORE_CURRENT_USER*/, L"Root");
    CheckIfGetLastError(NULL == hStoreHandle);

    pSignerCert = CertFindCertificateInStore(hStoreHandle, MY_TYPE, 0, CERT_FIND_SUBJECT_STR, pcwzSigner, pSignerCert);
    CheckIfGetLastError(NULL == pSignerCert);

    /*
    CheckIfGetLastError(!CryptAcquireCertificatePrivateKey(pSignerCert,
        CRYPT_ACQUIRE_ONLY_NCRYPT_KEY_FLAG, NULL,
        (HCRYPTPROV_OR_NCRYPT_KEY_HANDLE*)&hKey,
        &dwKeySpec, &fCallerFree));
    CheckIf(0 == (dwKeySpec & CERT_NCRYPT_KEY_SPEC), E_FAIL);
    CheckIf(!fCallerFree, E_FAIL);
    */
    Check(AcquireCertificatePrivateKey(pSignerCert, &hProvider, &hKey, NCRYPT_MACHINE_KEY_FLAG));
    fCallerFree = TRUE;

    // The real data is a SHA-1 hash, but this should be fine for testing
    for(int i = 0; i < sizeof(bData); i++)
        bData[i] = i;
    Check(NCryptSignHash(hKey, NULL, bData, sizeof(bData), NULL, 0, &dwSigLen, 0));

    pbSignature = (BYTE*)malloc(dwSigLen);
    CheckAlloc(pbSignature);

    Check(NCryptSignHash(hKey, NULL, bData, sizeof(bData), pbSignature, dwSigLen, &dwSigLen, 0));

Cleanup:
    if (pbSignature)
        free(pbSignature);
    if (hKey && fCallerFree)
        NCryptFreeObject(hKey);
    if (hProvider)
        NCryptFreeObject(hProvider);
    if (hStoreHandle)
        CertCloseStore(hStoreHandle, CERT_CLOSE_STORE_CHECK_FLAG);
    return hr;
}

Когда я попробовал AcquireCertificatePrivateKey(), я подтвердил, что pKeyProvInfo->dwKeySpec содержит AT_KEYEXCHANGE, который также использовался в исходном коде. Я передаю NCRYPT_MACHINE_KEY_FLAG из-за того, как настроен сертификат, и во время тестирования я запускаю Visual Studio и Embarcadero от имени администратора.

Есть идеи?

Спасибо!

В зависимости от типа закрытого ключа вам может потребоваться использовать BCRYPT_PAD_PKCS1 и передать указатель на BCRYPT_PKCS1_PADDING_INFO. запрос NCRYPT_ALGORITHM_GROUP_PROPERTY и, если он NCRYPT_RSA_ALGORITHM_GROU нужен, используйте блокнот. если NCRYPT_ECDSA_ALGORITHM_GROUP - не нужно

RbMm 13.07.2024 23:26
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
1
53
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

вы забываете о параметрах pPaddingInfo и dwFlags NCryptSignHash. оно не всегда может быть 0. Оно может быть 0 только для ECDSA, но не для RSA. вы можете запросить тип ключа по NCryptGetProperty(hKey, NCRYPT_ALGORITHM_GROUP_PROPERTY,) и сравнить его с NCRYPT_RSA_ALGORITHM_GROUP, NCRYPT_ECDSA_ALGORITHM_GROUP и т. д.

union {
    WCHAR Group[16];
    UCHAR bData[32];
};

HRESULT hr = NCryptGetProperty(hKey, NCRYPT_ALGORITHM_GROUP_PROPERTY, (PBYTE)Group, sizeof(Group), &cb, 0);

if (hr)
{
    // hr;
}

BCRYPT_PKCS1_PADDING_INFO pi, *ppi = 0;
DWORD dwFlags = 0;

if (!wcscmp(NCRYPT_RSA_ALGORITHM_GROUP, Group))
{
    pi.pszAlgId = BCRYPT_SHA256_ALGORITHM;
    ppi = &pi;
    dwFlags = BCRYPT_PAD_PKCS1;
}
else if (wcscmp(NCRYPT_ECDSA_ALGORITHM_GROUP, Group))
{
    // hr = NTE_NOT_SUPPORTED;
}

ULONG cb = sizeof(bData);
if (!CryptHashCertificate2(BCRYPT_SHA256_ALGORITHM, 0, 0, *, *, bData, &cb))
{
    // hr = GetLastError();
}

PBYTE pbSignature = 0;
ULONG dwSigLen = 0;

while (NOERROR == (hr = NCryptSignHash(hKey, ppi, bData, cb, pbSignature, dwSigLen, &dwSigLen, dwFlags)))
{
    if (pbSignature)
    {
        break;
    }
    
    pbSignature = (PBYTE)alloca(dwSigLen);
}

Также обратите внимание, что вам не нужно вызывать NCryptSignHash 2 раза в коде src. позвони только один раз

Другие вопросы по теме