В чем причина всех различных X509KeyStorageFlags?

Сегодня коллега обнаружил еще одну ошибку, связанную с этим! Раньше я сам находил эти флаги очень разочаровывающими, потому что, если вы ошиблись при создании экземпляров объектов X509Certificate2, их экспорте или сохранении в X509Store, вы можете столкнуться с ситуациями со всевозможными странными ошибками, такими как:

  • неожиданно не может указать NETSH.exe или ASP.net использовать определенный сертификат SSL [по отпечатку пальца], даже если у вас есть этот сертификат в хранилище вашего компьютера
  • неожиданно вы можете экспортировать данные сертификата, но они экспортируются без закрытого ключа с помощью .Export ()
  • неожиданно ваши модульные тесты начинают давать сбой в новой версии Windows, по-видимому, потому, что вы не использовали правильные флаги

Да, они задокументированы и все такое (и некоторые документы кажутся логичными), но почему это должно быть так сложно?


person Tim Lovell-Smith    schedule 10.10.2018    source источник
comment
Безопасность, охрана, охрана. Разные сертификаты служат разным целям, и все флаги предназначены для облегчения различного использования. Вы используете только сертификаты сервера для IIS / ASP.NET, поэтому некоторые флаги / поведение вы обнаруживаете неожиданными. Однако другие могут легко найти их полезными и критическими. Так что подходящего ответа не будет.   -  person Lex Li    schedule 11.10.2018
comment
@Lex Li Ну да, я подумал, что это должно помочь с безопасностью ... Я просто не понимаю, как это сделать. Вероятно, поэтому я склонен угадывать неправильный флаг. Ой, может ли это вообще означать, что я пишу ошибки безопасности, используя неправильные флаги?   -  person Tim Lovell-Smith    schedule 11.10.2018
comment
К сожалению, да. Если вы используете неправильные флаги, могут возникнуть проблемы с безопасностью. Например, некоторые сертификаты должны быть установлены с закрытым ключом, который нельзя экспортировать (в отличие от вашего случая), и если вы установите их с экспортируемым закрытым ключом, повреждения могут произойти прямо у вас под носом. Непросто повторить точные сценарии, но если вы знаете кого-то с опытом работы в области безопасности, вы можете проконсультироваться и узнать больше. Слишком широко для ТАК.   -  person Lex Li    schedule 12.10.2018


Ответы (1)


В основном, это должно быть так сложно сегодня, потому что вчера было так сложно, и никто не придумал ничего проще.

Я не могу придумать здесь линейное повествование, поэтому, пожалуйста, перенесите требуемые колебания взад и вперед.

Что такое файл PFX / PKCS # 12?

Хотя я не могу полностью сказать, каковы истоки PFX, в именах функций Windows есть подсказка для их чтения и записи: PFXImportCertStore и PFXExportCertStore. Они содержат множество отдельных сущностей (сертификатов, закрытых ключей и прочего), которые могут использовать идентификаторы свойств для взаимосвязи. По-видимому, они предназначены для использования в качестве механизма экспорта / импорта для всего хранилища сертификатов, как и все CurrentUser \ My. Но поскольку одним из видов хранилища является «хранилище памяти» (произвольная коллекция), импорт / экспорт .NET имеет смысл, но возникают некоторые сложности (по сравнению с .NET).

Закрытые ключи Windows

Windows поддерживает множество разных мест для закрытых ключей, но для устаревшего криптографического API они сводятся к схеме адресации из 4 частей:

  • Название криптографического провайдера.
  • Имя ключевого контейнера
  • Идентификатор, является ли это машинным или пользовательским ключом.
  • Идентификатор того, является ли это ключом «подписи» или ключом «обмена».

Это было упрощено до трехчастной схемы для CNG:

  • Имя механизма хранения
  • Название ключа
  • Идентификатор, является ли это машинным или пользовательским ключом.

Зачем нужен идентификатор типа "машина или нет"?

CAPI и CNG поддерживают прямое взаимодействие с именованными ключами. Итак, вы создаете ключ с именем «EmailDecryption». Другой пользователь в системе создает ключ с таким же именем. Это должно сработать? Ну, наверное. Так что, да! Отдельные ключи, потому что они хранятся в контексте, привязанном к пользователю, который их создал.

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

Я продолжу и скажу здесь, что слышал, что прямое использование именованных ключей сейчас не рекомендуется; команда CAPI / CNG предпочитает ключи с именами GUID и взаимодействие с ними через хранилища сертификатов. Но это часть эволюции.

Что делает импорт PFX?

PFXImportCertStore копирует все сертификаты из PFX в предоставленное хранилище. Он также импортирует (CryptImportKey или BCryptImportKey, в зависимости от того, что он думает потребности). Затем для каждого из импортированных ключей он находит (через значения свойств в PFX) соответствующий сертификат и устанавливает свойство в представлении хранилища сертификатов для «это мой идентификатор из 4 частей» (ключи CNG просто устанавливают 4-й часть до 0); что на самом деле все, что сертификат знает о своем закрытом ключе.

(PFX - очень сложный формат файла, это описание верно при условии, что ни одна из "странных частей" не используется)

Ключевые сроки жизни

Закрытые ключи Windows живут вечно или до тех пор, пока кто-то их не удалит.

Поэтому, когда PFX импортирует их, они живут вечно. Это имеет смысл, если вы импортировали в CurrentUser \ My. В этом меньше смысла, если вы делаете что-то временное.

.NET меняет отношения / делает их "слишком простой"

Дизайн Windows (в основном) заключается в том, что вы взаимодействуете с хранилищами сертификатов, а из хранилищ сертификатов вы получаете сертификаты. .NET появился позже и (можно предположить, исходя из того, что на самом деле делают приложения) сделал сертификаты объектом верхнего уровня и хранит нечто вроде второстепенного.

Поскольку сертификаты Windows (которые на самом деле являются «элементами сертификата хранилища») «знают», каков их закрытый ключ, сертификаты .NET «знают», каков их закрытый ключ.

О, но диспетчер сертификатов MMC говорит, что он может экспортировать сертификат с его закрытым ключом (в PFX), почему конструктор сертификата не может принять эти байты в дополнение к формату «просто сертификат»? Хорошо, теперь это возможно.

Согласование срока службы

Вы открываете некоторые байты как X509Certificate / X509Certificate2. Это PFX без пароля (любым из возможных способов). Вы видите, что это не тот сертификат, и позволяете сертификату уйти сборщику мусора. Этот закрытый ключ живет вечно, поэтому ваш жесткий диск медленно заполняется, а доступ к хранилищу ключей становится все медленнее и медленнее. Тогда вы сердитесь и переформатируете свой компьютер.

Это кажется плохим, поэтому .NET, когда (поле) сертификата собирает мусор (фактически, завершает работу), он сообщает CAPI (или CNG) удалить ключ. Теперь все работает, как ожидалось, верно? Ну, пока программа не завершится аварийно.

О, вы добавили его в постоянный магазин? Но я собираюсь удалить закрытый ключ после того, как новый объект хранилища сертификатов «узнает», как найти закрытый ключ. Это кажется плохим.

Введите X509KeyStorageFlags.PersistKeySet

PersistKeySet говорит: «Не делайте этого удаления». Это когда вы собираетесь добавить сертификат в X509Store.

Если вам нужно такое же поведение без указания флага, вызовите Environment.FailFast или отключите компьютер от сети после импорта.

Об этом машинном или пользовательском бите

В .NET вы можете легко получить пакет сертификатов в коллекции и вызвать для него Export. Что делать, если у одних есть ключи от машины, а у других - ключи пользователя? На помощь приходит PFXExportCertStore. Когда машинный ключ экспортируется, он записывает идентификатор, который говорит, что это был машинный ключ, поэтому импорт помещает его обратно в то же место.

Ну, обычно. Возможно, вы экспортировали машинный ключ с одной машины и хотите просто проверить его как неадминистратор на другой машине. Хорошо, вы можете указать CRYPT_USER_KEYSET или X509KeyStorageFlags.UserKeySet.

О, вы создали это как пользователь на одной машине, но хотите, чтобы она была машинным ключом на другой? Отлично. CRYPT_MACHINE_KEYSET / X509KeyStorageFlags.MachineKeySet.

Действительно ли мне нужны «временные» файлы?

Если вы просто просматриваете файлы PFX или иным образом хотите работать с ними на временной основе, зачем вообще писать ключ на диск? Хорошо, говорит Windows Vista, мы можем просто загрузить закрытый ключ прямо в объект криптографического ключа, и мы сообщим вам указатель.

PKCS12_NO_PERSIST_KEY / X509KeyStorageFlags.EphemeralKeySet

Я хотел бы думать, что если бы в Windows была эта функция в NT4, то это было бы по умолчанию для .NET. Сейчас это не может быть значением по умолчанию, потому что слишком много вещей зависит от того, как работает «нормальный» импорт, чтобы определить, можно ли использовать закрытый ключ.

А как насчет двух последних?

По умолчанию в PFXImportCertStore закрытые ключи не подлежат повторному экспорту. Чтобы сказать, что это неправильно, вы можете указать CRYPT_EXPORTABLE / X509KeyStorageFlags.Exportable.

CAPI и CNG поддерживают механизм, при котором программные ключи могут требовать согласия или пароля перед использованием закрытого ключа (например, запрос PIN-кода для смарт-карты), но вы должны заявить, что при первом создании (или импорте) ключа . Итак, PFXImportCertStore позволяет вам указывать CRYPT_USER_PROTECTED (а .NET предоставляет его как X509KeyStorageFlags.UserProtected).

Эти последние два действительно имеют смысл только для PFX с «одним закрытым ключом», потому что они применяются ко всем ключам. Они также не охватывают весь спектр опций, которые могли быть у исходных ключей ... и CNG, и CAPI поддерживают «архивируемые» ключи, что означает «экспортируемые один раз». Пользовательские списки ACL для машинных ключей также не поддерживаются в PFX.

Резюме

Для сертификата (или набора сертификатов) все просто. Когда задействованы закрытые ключи, все становится беспорядочным, абстракция над сертификатом (хранилищами) Windows становится немного тонкой, и вам нужно знать о модели сохраняемости и модели хранения.

person bartonjs    schedule 16.10.2018
comment
Вау, это удивительно богатый и подробный ответ. Хотел бы я проголосовать за него больше +1. Большое спасибо за то, что поделились этим со всеми! - person Tim Lovell-Smith; 16.10.2018
comment
Ах да .. щедрости! - person Tim Lovell-Smith; 07.02.2019
comment
Знаете ли вы, что могло привести к удалению закрытых ключей сертификата позже на некоторых машинах при установке файлов pfx с помощью флагов X509KeyStorageFlags.PersistKeySet и X509KeyStorageFlags.MachineKeySet с использованием X509Certificate2.Install ()? Позже - через 2 месяца или 1 день (наверное, после перезагрузки, не уверен). - person axk; 26.03.2021