Может ли Microsoft Cryptographic API разрешить создание ключей ECDSA из потока байтов?

Поэтому я раздумываю, какую криптографическую библиотеку C++ использовать (я уже понял, как сделать эквивалент в C#) для проверки подписанного хэша файла лицензии.

Читая документацию Microsoft для CNG, кажется, что невозможно сгенерировать ключ ECDSA в коде из потока байтов, несмотря на то, что это возможно для ключа RSA (я думаю, не уверен на 100%).

Поскольку я хотел генерировать поток байтов кода, я посмотрел на crypto++, и мне удалось скомпилировать набор тестов, но cryptolib.lib имеет слишком большой размер 90 мегабайт, и я сталкиваюсь с множеством ошибок ссылок, просто пытаясь сделать что-то простое. Так что сейчас я меньше увлекаюсь крипто++.

Итак, я хочу вернуться к использованию чего-то, поставляемого Microsoft в своей настольной ОС Windows, но я вернулся к своей первоначальной проблеме отсутствия ключей, сгенерированных потоком байтов кода.

Может ли эксперт подтвердить, что это действительно невозможно? Кроме того, могут ли они предложить альтернативу, я рад вернуться к RSA с длинным (2048?) ключом.

Следующий код компилируется и запускается для меня. Его можно найти в MSDN. статья - Подписание данных с помощью CNG. Мне было интересно, можно ли его адаптировать. Этот код (а) создает ключ ECDSA на лету (б) подписывает хэш (в) сохраняет ключ в хранилище сертификатов (г) извлекает ключ и проверяет подписанный хэш. Если да, то я думаю, что это демонстрационный код. Что мне нужно, так это пример жестко закодированного ключа ECDSA, а не созданного на лету.

// CngECDSA.cpp : Defines the entry point for the console application.
// Based on https://msdn.microsoft.com/en-us/library/windows/desktop/aa376304(v=vs.85).aspx

#include "stdafx.h"

#include <Windows.h>
#include <stdint.h>
#include <Bcrypt.h>
#include <ncrypt.h>

#pragma comment(lib, "bcrypt")  
#pragma comment(lib, "ncrypt")


#define NT_SUCCESS(Status)          (((NTSTATUS)(Status)) >= 0)
#define STATUS_UNSUCCESSFUL         ((NTSTATUS)0xC0000001L)

static const  BYTE rgbMsg[] =
{
    0x04, 0x87, 0xec, 0x66, 0xa8, 0xbf, 0x17, 0xa6,
    0xe3, 0x62, 0x6f, 0x1a, 0x55, 0xe2, 0xaf, 0x5e,
    0xbc, 0x54, 0xa4, 0xdc, 0x68, 0x19, 0x3e, 0x94,
};

BYTE value[] =
{ 0x02,0x00,0x00,0x00 };

void __cdecl wmain(
    int                      argc,
    __in_ecount(argc) LPWSTR *wargv)
{
    NCRYPT_PROV_HANDLE      hProv = NULL;
    NCRYPT_KEY_HANDLE       hKey = NULL;
    BCRYPT_KEY_HANDLE       hTmpKey = NULL;
    SECURITY_STATUS         secStatus = ERROR_SUCCESS;
    BCRYPT_ALG_HANDLE       hHashAlg = NULL,
        hSignAlg = NULL;
    BCRYPT_HASH_HANDLE      hHash = NULL;
    NTSTATUS                status = STATUS_UNSUCCESSFUL;
    DWORD                   cbData = 0,
        cbHash = 0,
        cbBlob = 0,
        cbSignature = 0,
        cbHashObject = 0;
    PBYTE                   pbHashObject = NULL;
    PBYTE                   pbHash = NULL,
        pbBlob = NULL,
        pbSignature = NULL;

    UNREFERENCED_PARAMETER(argc);
    UNREFERENCED_PARAMETER(wargv);

    //open an algorithm handle
    if (!NT_SUCCESS(status = BCryptOpenAlgorithmProvider(
        &hHashAlg,
        BCRYPT_SHA1_ALGORITHM,
        NULL,
        0)))
    {
        wprintf(L"**** Error 0x%x returned by BCryptOpenAlgorithmProvider\n", status);
        goto Cleanup;
    }

    if (!NT_SUCCESS(status = BCryptOpenAlgorithmProvider(
        &hSignAlg,
        BCRYPT_ECDSA_P256_ALGORITHM,
        NULL,
        0)))
    {
        wprintf(L"**** Error 0x%x returned by BCryptOpenAlgorithmProvider\n", status);
        goto Cleanup;
    }

    //calculate the size of the buffer to hold the hash object
    if (!NT_SUCCESS(status = BCryptGetProperty(
        hHashAlg,
        BCRYPT_OBJECT_LENGTH,
        (PBYTE)&cbHashObject,
        sizeof(DWORD),
        &cbData,
        0)))
    {
        wprintf(L"**** Error 0x%x returned by BCryptGetProperty\n", status);
        goto Cleanup;
    }

    //allocate the hash object on the heap
    pbHashObject = (PBYTE)HeapAlloc(GetProcessHeap(), 0, cbHashObject);
    if (NULL == pbHashObject)
    {
        wprintf(L"**** memory allocation failed\n");
        goto Cleanup;
    }

    //calculate the length of the hash
    if (!NT_SUCCESS(status = BCryptGetProperty(
        hHashAlg,
        BCRYPT_HASH_LENGTH,
        (PBYTE)&cbHash,
        sizeof(DWORD),
        &cbData,
        0)))
    {
        wprintf(L"**** Error 0x%x returned by BCryptGetProperty\n", status);
        goto Cleanup;
    }

    //allocate the hash buffer on the heap
    pbHash = (PBYTE)HeapAlloc(GetProcessHeap(), 0, cbHash);
    if (NULL == pbHash)
    {
        wprintf(L"**** memory allocation failed\n");
        goto Cleanup;
    }

    //create a hash
    if (!NT_SUCCESS(status = BCryptCreateHash(
        hHashAlg,
        &hHash,
        pbHashObject,
        cbHashObject,
        NULL,
        0,
        0)))
    {
        wprintf(L"**** Error 0x%x returned by BCryptCreateHash\n", status);
        goto Cleanup;
    }


    //hash some data
    if (!NT_SUCCESS(status = BCryptHashData(
        hHash,
        (PBYTE)rgbMsg,
        sizeof(rgbMsg),
        0)))
    {
        wprintf(L"**** Error 0x%x returned by BCryptHashData\n", status);
        goto Cleanup;
    }

    //close the hash
    if (!NT_SUCCESS(status = BCryptFinishHash(
        hHash,
        pbHash,
        cbHash,
        0)))
    {
        wprintf(L"**** Error 0x%x returned by BCryptFinishHash\n", status);
        goto Cleanup;
    }

    //open handle to KSP
    if (FAILED(secStatus = NCryptOpenStorageProvider(
        &hProv,
        MS_KEY_STORAGE_PROVIDER,
        0)))
    {
        wprintf(L"**** Error 0x%x returned by NCryptOpenStorageProvider\n", secStatus);
        goto Cleanup;
    }

    //create a persisted key
    if (FAILED(secStatus = NCryptCreatePersistedKey(
        hProv,
        &hKey,
        NCRYPT_ECDSA_P256_ALGORITHM,
        L"my ecc key",
        0,
        0)))
    {
        wprintf(L"**** Error 0x%x returned by NCryptCreatePersistedKey\n", secStatus);
        goto Cleanup;
    }

    //create key on disk
    if (FAILED(secStatus = NCryptFinalizeKey(hKey, 0)))
    {
        wprintf(L"**** Error 0x%x returned by NCryptFinalizeKey\n", secStatus);
        goto Cleanup;
    }

    //sign the hash
    if (FAILED(secStatus = NCryptSignHash(
        hKey,
        NULL,
        pbHash,
        cbHash,
        NULL,
        0,
        &cbSignature,
        0)))
    {
        wprintf(L"**** Error 0x%x returned by NCryptSignHash\n", secStatus);
        goto Cleanup;
    }


    //allocate the signature buffer
    pbSignature = (PBYTE)HeapAlloc(GetProcessHeap(), 0, cbSignature);
    if (NULL == pbSignature)
    {
        wprintf(L"**** memory allocation failed\n");
        goto Cleanup;
    }

    if (FAILED(secStatus = NCryptSignHash(
        hKey,
        NULL,
        pbHash,
        cbHash,
        pbSignature,
        cbSignature,
        &cbSignature,
        0)))
    {
        wprintf(L"**** Error 0x%x returned by NCryptSignHash\n", secStatus);
        goto Cleanup;
    }

    if (FAILED(secStatus = NCryptExportKey(
        hKey,
        NULL,
        BCRYPT_ECCPUBLIC_BLOB,
        NULL,
        NULL,
        0,
        &cbBlob,
        0)))
    {
        wprintf(L"**** Error 0x%x returned by NCryptExportKey\n", secStatus);
        goto Cleanup;
    }

    pbBlob = (PBYTE)HeapAlloc(GetProcessHeap(), 0, cbBlob);
    if (NULL == pbBlob)
    {
        wprintf(L"**** memory allocation failed\n");
        goto Cleanup;
    }

    if (FAILED(secStatus = NCryptExportKey(
        hKey,
        NULL,
        BCRYPT_ECCPUBLIC_BLOB,
        NULL,
        pbBlob,
        cbBlob,
        &cbBlob,
        0)))
    {
        wprintf(L"**** Error 0x%x returned by NCryptExportKey\n", secStatus);
        goto Cleanup;
    }

    if (!NT_SUCCESS(status = BCryptImportKeyPair(
        hSignAlg,
        NULL,
        BCRYPT_ECCPUBLIC_BLOB,
        &hTmpKey,
        pbBlob,
        cbBlob,
        0)))
    {
        wprintf(L"**** Error 0x%x returned by BCryptImportKeyPair\n", status);
        goto Cleanup;
    }

    if (!NT_SUCCESS(status = BCryptVerifySignature(
        hTmpKey,
        NULL,
        pbHash,
        cbHash,
        pbSignature,
        cbSignature,
        0)))
    {
        wprintf(L"**** Error 0x%x returned by BCryptVerifySignature\n", status);
        goto Cleanup;
    }

    wprintf(L"Success!\n");

Cleanup:

    if (hHashAlg)
    {
        BCryptCloseAlgorithmProvider(hHashAlg, 0);
    }

    if (hSignAlg)
    {
        BCryptCloseAlgorithmProvider(hSignAlg, 0);
    }

    if (hHash)
    {
        BCryptDestroyHash(hHash);
    }

    if (pbHashObject)
    {
        HeapFree(GetProcessHeap(), 0, pbHashObject);
    }

    if (pbHash)
    {
        HeapFree(GetProcessHeap(), 0, pbHash);
    }

    if (pbSignature)
    {
        HeapFree(GetProcessHeap(), 0, pbSignature);
    }

    if (pbBlob)
    {
        HeapFree(GetProcessHeap(), 0, pbBlob);
    }

    if (hTmpKey)
    {
        BCryptDestroyKey(hTmpKey);
    }

    if (hKey)
    {
        NCryptDeleteKey(hKey, 0);
    }

    if (hProv)
    {
        NCryptFreeObject(hProv);
    }
}

Просто для ясности: я стремлюсь к 384 битам, а для совместимости с OpenSsl и C# имя кривой — это рекомендованная NIST кривая secp384r1 – {1.3.132.0.34}. И я буду использовать хэш SHA256 дважды (точно так же, как BitCoin).


person S Meaden    schedule 15.08.2017    source источник
comment
Конечно, можно сериализовать ключ EC. Какая у вас проблема?   -  person stark    schedule 15.08.2017
comment
@stark: привет, я добавил код (из MSDN), и пока он работает, он соответствует моему варианту использования. Мне нужно жестко закодировать (только общедоступный) ключ, предположительно, в виде массива байтов.   -  person S Meaden    schedule 16.08.2017


Ответы (1)


Этот код (а) создает ключ ECDSA на лету (б) подписывает хэш (в) сохраняет ключ в хранилище сертификатов (г) извлекает ключ и проверяет подписанный хэш. Если да, то я думаю, что это демонстрационный код.

А) Да.

Б) Да.

С) Да. Он сохраняет ключ как постоянный объект с именем «мой ключ ec». Но поскольку он больше никогда не запрашивал его по имени, можно было передать nullptr/NULL, чтобы сделать его эфемерным ключом. (И он сохранил его в «хранилище ключей», а не в «хранилище сертификатов».

D) Да, чтобы проверить, нет, чтобы получить.


Получение ваших данных publicKey (это результат преобразования openssl ec -in eckey.pem -pubout -outform der | xxd -g1 в массив C) и подписи (в формате IEEE P1363) — это упражнение, оставленное читателю.

static const BYTE data[] =
{
    '1', '2', '3', '4',
};

static const BYTE publicKey[] =
{
    0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86,
    0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a,
    0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03,
    0x42, 0x00, 0x04, 0x23, 0x63, 0xdd, 0x13, 0x1d,
    0xa6, 0x5e, 0x89, 0x9a, 0x2e, 0x63, 0xe9, 0xe0,
    0x5e, 0x50, 0xc8, 0x30, 0xd4, 0x99, 0x46, 0x62,
    0xff, 0xe8, 0x83, 0xdb, 0x2b, 0x9a, 0x76, 0x7d,
    0xcc, 0xab, 0xa2, 0xf0, 0x70, 0x81, 0xb5, 0x71,
    0x1b, 0xe1, 0xde, 0xe9, 0x0d, 0xfc, 0x8d, 0xe1,
    0x79, 0x70, 0xc2, 0xd9, 0x37, 0xa1, 0x6c, 0xd3,
    0x45, 0x81, 0xf5, 0x2b, 0x8d, 0x59, 0xc9, 0xe9,
    0x53, 0x2d, 0x13,
};

static const BYTE signature[] =
{
    // r
    0xc6, 0x4c, 0x14, 0x55, 0xfe, 0xc0, 0x2f, 0xe7,
    0x4a, 0x25, 0x87, 0xe7, 0x0c, 0x10, 0x4e, 0x73,
    0xf0, 0x28, 0x86, 0x18, 0x28, 0xae, 0xef, 0x4f,
    0xe5, 0xa0, 0xcc, 0x7a, 0xa8, 0xe4, 0x1f, 0xbf,

    // s
    0x35, 0x9f, 0x23, 0xfd, 0xc3, 0xd6, 0x33, 0xfb,
    0x52, 0x47, 0x9b, 0xef, 0x2b, 0x2a, 0x48, 0xa8,
    0x6f, 0x37, 0x04, 0xd0, 0x8c, 0xc3, 0x49, 0x04,
    0x21, 0x53, 0xb8, 0x3c, 0x9d, 0x8c, 0x6c, 0xf5,
};

#define NT_SUCCESS(Status)          (((NTSTATUS)(Status)) >= 0)

int main()
{
    NTSTATUS status = NTE_BAD_DATA;
    int exitCode = ERROR_INVALID_FUNCTION;
    BCRYPT_KEY_HANDLE importedPublicKey = nullptr;
    PCERT_PUBLIC_KEY_INFO subjectPublicKeyInfo = (PCERT_PUBLIC_KEY_INFO)LocalAlloc(0, 2048);
    BCRYPT_ALG_HANDLE sha256 = nullptr;
    BCRYPT_HASH_HANDLE hHash = nullptr;
    BYTE dataHash[256 >> 3];

    DWORD structSize = 2048;
    if (!CryptDecodeObject(
        X509_ASN_ENCODING,
        X509_PUBLIC_KEY_INFO,
        publicKey,
        sizeof(publicKey),
        0,
        subjectPublicKeyInfo,
        &structSize))
    {
        wprintf(L"**** Error 0x%x returned by CryptDecodeObject\n", GetLastError());
        goto Cleanup;
    }

    if (!CryptImportPublicKeyInfoEx2(
        X509_ASN_ENCODING,
        subjectPublicKeyInfo,
        0,
        nullptr,
        &importedPublicKey))
    {
        wprintf(L"**** Error 0x%x returned by CryptImportPublicKeyInfoEx2\n", GetLastError());
        goto Cleanup;
    }

    if (!NT_SUCCESS(status = BCryptOpenAlgorithmProvider(&sha256, BCRYPT_SHA256_ALGORITHM, nullptr, 0)))
    {
        wprintf(L"**** Error 0x%x returned by BCryptOpenAlgorithmProvider\n", status);
        goto Cleanup;
    }

    if (!NT_SUCCESS(status = BCryptCreateHash(sha256, &hHash, nullptr, 0, nullptr, 0, 0)))
    {
        wprintf(L"**** Error 0x%x returned by BCryptCreateHash\n", status);
        goto Cleanup;
    }

    if (!NT_SUCCESS(status = BCryptHashData(hHash, (PUCHAR)data, sizeof(data), 0)))
    {
        wprintf(L"**** Error 0x%x returned by BCryptHashData\n", status);
        goto Cleanup;
    }

    if (!NT_SUCCESS(status = BCryptFinishHash(hHash, (PUCHAR)dataHash, sizeof(dataHash), 0)))
    {
        wprintf(L"**** Error 0x%x returned by BCryptFinishHash\n", status);
        goto Cleanup;
    }

    status = BCryptVerifySignature(
        importedPublicKey,
        nullptr,
        (PUCHAR)dataHash,
        sizeof(dataHash),
        (PUCHAR)signature,
        sizeof(signature),
        0);

    switch (status)
    {
        case STATUS_SUCCESS:
            wprintf(L"Signature verified successfully\n");
            exitCode = ERROR_SUCCESS;
            break;
        case STATUS_INVALID_SIGNATURE:
            wprintf(L"Signature did not verify\n");
            exitCode = ERROR_INVALID_DATA;
            break;
        default:
            wprintf(L"**** Error 0x%x returned by BCryptVerifySignature\n", status);
            goto Cleanup;
    }



Cleanup:
    if (hHash != nullptr)
        BCryptDestroyHash(hHash);

    if (sha256 != nullptr)
        BCryptCloseAlgorithmProvider(sha256, 0);

    if (importedPublicKey != nullptr)
        BCryptDestroyKey(importedPublicKey);

    LocalFree(subjectPublicKeyInfo);

    return exitCode;
}
person bartonjs    schedule 16.08.2017
comment
Благодарность! читая ваш профиль и ваши ответы, получившие наибольшее количество голосов, ясно, что вы тот человек, с которым можно связаться! Я сделаю упражнение openssl. Я также рассмотрю криптографию ядра .Net в соответствии с вашими лучшими ответами. - person S Meaden; 16.08.2017