Сохранение / восстановление сертификатов с помощью MS CryptoAPI делает недействительным прикрепленный закрытый ключ

#windows #winapi #cryptography #cryptoapi #mscapi

#Windows #winapi #криптография #cryptoapi #mscapi

Вопрос:

Я написал программу, которая должна сохранять и восстанавливать сертификаты пользователей с использованием Windows Crypto API. У меня создалось впечатление, что все работает нормально, но теперь пользователь пожаловался, что закрытый ключ, который был прикреплен к сертификату, недействителен после восстановления сертификата.

Я сохранял сертификаты с помощью:

 HCERTSTORE hCertStore =
    CertOpenStore(CERT_STORE_PROV_PHYSICAL_W,
    0,
    NULL,
    CERT_SYSTEM_STORE_CURRENT_USER |
    CERT_STORE_OPEN_EXISTING_FLAG |
    CERT_STORE_READONLY_FLAG |
    CERT_STORE_UPDATE_KEYID_FLAG,
    (PVOID) storeName.c_str());
  

А затем позже:

 if (!CertSaveStore(hCertStore,
    0,
    CERT_STORE_SAVE_AS_STORE,
    CERT_STORE_SAVE_TO_FILENAME,
    (PVOID) saveFile.c_str(),
    0))
  

Я понимаю, что флаг CERT_STORE_SAVE_AS_STORE должен означать, что весь сертификат должен быть сериализован, включая закрытый ключ. Хотя я отмечаю, что MSDN говорит:

«Свойство CERT_KEY_CONTEXT_PROP_ID и связанные значения CERT_KEY_PROV_HANDLE_PROP_ID и CERT_KEY_SPEC_PROP_ID не сохраняются в сериализованном хранилище».

..который, признаюсь, я действительно не понимаю.

Когда я восстанавливаю сертификат, я использую CertFindCertificateInStore(), чтобы проверить, присутствует ли сертификат уже, и только если это не так, я делаю:

 bOK = CertAddCertificateContextToStore(
    hDestinationStore,
    pCertContext,
    CERT_STORE_ADD_USE_EXISTING,
    NULL);
  

Чтобы добавить сертификат обратно… Итак, мой вопрос в том, почему закрытый ключ не может быть сохранен? Я что-то упускаю?

Ответ №1:

Вы используете неправильный CryptoAPI. Вместо этого вам следует использовать PFXExportCertStoreEx и PFXImportCertStore.

ОБНОВЛЕНО: Функции очень распространены. Конечно, вы не можете экспортировать сертификаты со смарт-карты или другие неэкспортируемые сертификаты. БЛОГ в функции — это не что иное, как содержимое файла PFX. Например, импорт сертификата из файла PFX должен выполнить следующее:

  1. Откройте файл PFX и прочитайте полное содержимое в памяти. Вы, конечно, можете использовать сопоставление файлов вместо этого.
  2. При желании вы можете использовать PFXIsPFXBlob функцию для проверки того, что файл действительно содержит то, что соответствует файлу PFX.
  3. Вы используете PFXImportCertStore для открытия большого двоичного объекта (PFX) в качестве хранилища сертификатов источника.
  4. Вы используете CertOpenStore или какую-либо другую функцию, чтобы открыть хранилище сертификатов назначения, в котором вы хотите сохранить сертификаты из файла PFX.
  5. Используется CertEnumCertificatesInStore для перечисления сертификатов из исходного хранилища сертификатов (PFX) и для всех сертификатов используется CertAddCertificateContextToStore для добавления сертификата из хранилища сертификатов источника в хранилище сертификатов назначения. Сертификаты, имеющие закрытый ключ, будут импортированы вместе с закрытым ключом.
  6. Вы используете CertCloseStore для закрытия обоих открытых хранилищ.

Комментарии:

1. Хм, выглядит многообещающе, будет ли эта функция работать для всех типов сертификатов / cert store?

2. @Benj: Я обновил свой ответ информацией из одной моей старой рабочей программы. Я надеюсь, что этого будет достаточно для вас, чтобы написать свою собственную программу. Экспорт очень прост, но импорт немного сложнее, поэтому я описал импорт более подробно.

3. Кажется, это работает блестяще. Теперь моя программа успешно сохраняет / восстанавливает все сертификаты, включая закрытые ключи.. Большое спасибо!