Есть ли замена ENGINE_load_private_key() при работе с CAPI?

На пути к использованию хранилища ключей Windows для OpenSSL и клиентских сертификатов я нашел эту статью: https://anexdev.blogspot.com/2018/10/how-to-send-client-certificate-with.html

Помимо некоторых опечаток в данном коде, этот пример вдохновил меня на написание кода, использующего ENGINE CAPI для использования ключей. Подводя итог, найдя сертификат в хранилище ( L"MY"), я смог выполнить ENGINE_load_private_key(), который, наконец, вернул новую выделенную структуру EVP_PKEY.

Однако мне не удалось использовать этот ключ в сочетании с сертификатом. После некоторого исследования я выяснил, что структура содержит только версию, модуль и компонент публичного ключа:

RSAPrivateKey ::= SEQUENCE {
  version           Version,
  modulus           INTEGER,  -- n
  publicExponent    INTEGER,  -- e
  privateExponent   INTEGER,  -- d  (this field and everything following is mising)
  prime1            INTEGER,  -- p
  prime2            INTEGER,  -- q
  exponent1         INTEGER,  -- d mod (p-1)
  exponent2         INTEGER,  -- d mod (q-1)
  coefficient       INTEGER,  -- (inverse of q) mod p
  otherPrimeInfos   OtherPrimeInfos OPTIONAL
}

Поэтому я изучил код реализации OpenSSL. И на самом деле, ENGINE_load_private_key(), по крайней мере, в случае механизма CAPI, очевидно, не предназначен для экспорта закрытого ключа.

В openssl/crypto/engine/eng_pkey.c функция вызывает член load_privkey() выбранного движка. В случае CAPI реализация определяется в openssl/engines/ e_capi.c. Эта функция вызывает capi_find_key() для поиска ключа и capi_get_pkey() для его получения.

Более пристальный взгляд на определение static EVP_PKEY *capi_get_pkey(ENGINE *eng, CAPI_KEY *key), кажется, объясняет корень моей проблемы:

   if (!CryptExportKey(key->key, 0, PUBLICKEYBLOB, 0, pubkey, &len)) {
        CAPIerr(CAPI_F_CAPI_GET_PKEY, CAPI_R_PUBKEY_EXPORT_ERROR);
        capi_addlasterror();
        goto err;
    }

Почему capi_get_pkey() звонит CryptExportKey() с PUBLICKEYBLOB? Это делается намеренно, чтобы обойти какие-либо правила Windows и извлечь закрытый ключ в результате известной ошибки Windows, или ENGINE_load_private_key() де-факто не работает в сочетании с CAPI?

Кто-нибудь знает замену ENGINE_load_private_key() для моего приложения?


person Mario    schedule 25.08.2020    source источник


Ответы (1)


Я уперся в ту же стену и провел небольшое исследование. Похоже, это не ошибка, а способ работы Microsoft CryptoAPI. В случае неэкспортируемых ключей невозможно экспортировать ключ из хранилища сертификатов Windows с помощью CryptExportKey и PRIVATEKEYBLOB. Таким образом, вы можете просто получить ссылку, идентификатор пары ключей, которую впоследствии вы можете использовать для выполнения всех криптографических операций через CryptoAPI (не может экспортировать реальный ключ). Таким образом, механизм openssl CAPI пошел по этому пути, они получают только необходимый идентификатор пары ключей, а затем загружают его в CryptoAPI для выполнения необходимых действий.

Я предполагаю, что это также является причиной того, что capi_rsa_priv_enc не реализован (CryptEncrypt всегда выбирает открытый ключ из предоставленного идентификатора пары ключей), и поскольку нет реального экспортированного закрытого ключа (только идентификатор пары ключей), они могут даже не выполнять шифрование с помощью закрытого ключа даже рядом с CryptoAPI. Но это только мое предположение.

person user1770426    schedule 28.01.2021