Практическое руководство по хранилищу сертификатов Windows — пользователь или компьютер, куда класть
· Го Комура · Сертификаты, Windows, Безопасность, PKI, TLS, PowerShell, Бизнес-приложения, Информационные системы
«После обновления терминала онлайн-проверки полиса переложили клиентский сертификат на новый ПК — и соединение пропало.» «На машине разработки банковский API открывается, а когда сделали службу Windows, говорит „сертификат не найден“.» «Вообще непонятно, какой сертификат настоящий — тот, что видно в certmgr.msc, или тот, что в certlm.msc.» — когда по заказной разработке делают интеграцию Web API с клиентским сертификатом, такие обращения приходят регулярно.
Онлайн-проверка полиса в медучреждениях, электронная подача, банковские API, EDI с контрагентами. Клиентские сертификаты, которыми когда-то занимались только инфраструктурщики крупных компаний, теперь трогают ИТ малого и среднего бизнеса и разработчики бизнес-приложений. И аварии вокруг сертификатов на деле сводятся к горстке шаблонов. Кладут не туда, забывают права на закрытый ключ, забывают срок — эти три.
Статья рассчитана на разработчиков бизнес-приложений, которые используют клиентские сертификаты, и на ИТ-сотрудников, которым поручают замену сертификатов. Осью решения «хранилище пользователя или хранилище компьютера» за один проход разбираем структуру хранилища сертификатов Windows, выдачу прав на закрытый ключ, инвентаризацию сроков через PowerShell и код использования из .NET. Содержание опирается на первоисточники Microsoft Learn по состоянию на август 2026.
1. Сначала вывод
- Хранилище сертификатов Windows бывает двух систем: «пользователь» (CurrentUser) и «компьютер» (LocalMachine). Хранилище пользователя на каждую учётную запись своё (под HKEY_CURRENT_USER в реестре), хранилище компьютера общее на весь ПК (под HKEY_LOCAL_MACHINE).12
- Средств управления тоже два. certmgr.msc открывает хранилище текущего пользователя, certlm.msc — локального компьютера. Из PowerShell это
Cert:\CurrentUserиCert:\LocalMachine.34 - Куда класть, решает «от чьего имени работает программа, которая сертификат использует». Как правило, приложение интерактивного пользователя — хранилище пользователя; работа без присмотра — служба Windows, IIS, планировщик заданий — хранилище компьютера (таблица решений главы 3).
- Причина «пока разрабатывали — работало, как стало службой — не находится» почти всегда одна. Сертификат, который разработчик положил в своё хранилище пользователя, из CurrentUser службы, работающей под другой учётной записью, невидим (глава 3).
- Сертификат и закрытый ключ — разные вещи. Одного размещения сертификата в хранилище компьютера недостаточно, чтобы учётная запись службы читала закрытый ключ — это обычная ситуация. Выдайте учётной записи выполнения право на чтение через «Управление закрытыми ключами» в certlm.msc.5
- При импорте pfx закрытый ключ по умолчанию нельзя экспортировать.
Import-PfxCertificateпринимает закрытый ключ в форме, из которой его нельзя экспортировать снова, пока не указан-Exportable. Это не авария, а желаемое значение по умолчанию.6 - Истечение предотвращают автоматизацией инвентаризации.
Get-ChildItem Cert:\LocalMachine\My -ExpiringInDays 60механически извлекает сертификаты, которые истекают в пределах заданного числа дней.4 - Отпечаток (thumbprint), зашитый в код или конфигурацию, убивает при каждом обновлении сертификата, потому что у нового сертификата отпечаток всегда другой. Вынести в конфигурацию плюс период параллельной работы старого и нового — базовая конструкция (главы 5 и 7).
Карта знаний этой статьи
Хранилище сертификатов Windows делится на две системы — пользователь (CurrentUser) и компьютер (LocalMachine); куда класть клиентский сертификат, решает «от чьего имени работает программа». Для службы Windows, которая работает без присмотра, хранилище компьютера и выдача прав на закрытый ключ идут в комплекте; истечение сертификата и зашитый отпечаток — классические причины сбоя соединения из-за сертификата.
flowchart LR
accTitle: Карта знаний практического руководства по хранилищу сертификатов Windows
accDescr: Рисунок, показывающий связи хранилища сертификатов (пользователь/компьютер), клиентского сертификата, закрытого ключа, цепочки сертификатов, корневого и промежуточного УЦ, истечения и отпечатка со сбоем соединения, порядка замены, реестра и риска самоподписанного сертификата
certificate_store["хранилище сертификатов"]
client_certificate["клиентский сертификат"]
windows_service["служба Windows"]
localmachine_store["хранилище сертификатов компьютера"]
private_key["закрытый ключ"]
private_key_acl["права доступа к закрытому ключу"]
certificate_chain["цепочка сертификатов"]
intermediate_ca["промежуточный сертификат УЦ"]
root_ca["корневой сертификат УЦ"]
certificate_expiry["истечение сертификата"]
certificate_failure["сбой соединения из-за сертификата"]
thumbprint_hardcode["зашитый отпечаток"]
group_policy["групповая политика"]
intune["Microsoft Intune"]
currentuser_store["хранилище сертификатов пользователя"]
certmgr_msc["certmgr.msc"]
certlm_msc["certlm.msc"]
cert_drive["диск Cert:"]
pfx["файл PFX"]
certutil["certutil"]
code_signing_cert["сертификат подписи кода"]
trusted_publisher_store["хранилище доверенных издателей"]
desktop_app["настольное приложение интерактивного пользователя"]
cert_renewal["замена сертификата"]
cert_ledger["реестр сертификатов"]
self_signed_cert["разовый самоподписанный сертификат"]
trust_anchor_risk["риск злоупотребления точкой доверия"]
cert_selection["выбор (поиск) сертификата"]
personal_store["хранилище «Личные» (My)"]
store_mismatch["путаница хранилищ"]
iis_apppool["пул приложений IIS"]
task_scheduler["необслуживаемый запуск Планировщика заданий"]
exportable_import["импорт с возможностью экспорта"]
key_exfiltration_risk["риск выноса закрытого ключа"]
everyone_full_control["выдача Everyone полного контроля"]
partner_registration["предварительная регистрация сертификата у контрагента"]
cert_switchover["переключение на новый сертификат"]
x509store["класс X509Store"]
validonly_search["поиск сертификата с validOnly"]
key_usage["атрибут использования ключа (Key Usage)"]
config_externalization["вынесение отпечатка в конфигурацию"]
cert_choice_logging["журнал выбранного сертификата"]
windows_service -.->|"использует"| localmachine_store
client_certificate -->|"требует"| private_key
windows_service -.->|"требует"| private_key_acl
certificate_chain -.->|"требует"| intermediate_ca
certificate_chain -->|"требует"| root_ca
certificate_expiry -.->|"может вызвать"| certificate_failure
thumbprint_hardcode -.->|"может вызвать"| certificate_failure
certificate_failure -.->|"проверяется"| certificate_chain
root_ca -.->|"настраивается"| group_policy
root_ca -.->|"настраивается"| intune
currentuser_store -->|"проверяется"| certmgr_msc
localmachine_store -->|"проверяется"| certlm_msc
certificate_store -->|"проверяется"| cert_drive
certificate_expiry -->|"проверяется"| cert_drive
private_key -.->|"хранится в"| pfx
certificate_chain -->|"проверяется"| certutil
code_signing_cert -.->|"хранится в"| trusted_publisher_store
desktop_app -.->|"использует"| currentuser_store
root_ca -.->|"хранится в"| localmachine_store
private_key_acl -.->|"настраивается"| certlm_msc
cert_renewal -.->|"предотвращает"| certificate_failure
cert_ledger -.->|"предотвращает"| certificate_expiry
self_signed_cert -.->|"может вызвать"| trust_anchor_risk
thumbprint_hardcode -->|"не рекомендуется"| cert_selection
self_signed_cert -->|"не рекомендуется"| root_ca
cert_ledger -->|"рекомендуется для"| certificate_expiry
currentuser_store -->|"наследует содержимое"| localmachine_store
client_certificate -->|"хранится в"| personal_store
store_mismatch -->|"может вызвать"| certificate_failure
iis_apppool -.->|"использует"| localmachine_store
iis_apppool -->|"требует"| private_key_acl
task_scheduler -.->|"использует"| localmachine_store
exportable_import -->|"может вызвать"| key_exfiltration_risk
everyone_full_control -->|"не рекомендуется"| private_key_acl
cert_renewal -.->|"требует"| private_key_acl
partner_registration -.->|"должен предшествовать"| cert_switchover
x509store -->|"использует"| certificate_store
validonly_search -.->|"снижает"| certificate_failure
client_certificate -.->|"требует"| key_usage
config_externalization -->|"рекомендуется для"| cert_selection
cert_choice_logging -->|"рекомендуется для"| certificate_failure
На схеме сплошная линия обозначает отношение, которое выполняется всегда, а пунктирная — условное отношение (условия указаны в пояснении к каждому отношению на странице сведений). Полный список отношений (всего 41, с доказательствами и степенью уверенности) и определения основных понятий собраны на странице сведений карты знаний (на японском). Данные: JSON-LD / Turtle
2. Общая картина хранилища сертификатов — два места и логические хранилища
2.1. Две системы: пользователь и компьютер
Хранилище сертификатов Windows в целом делится на два «места».1
- Хранилище сертификатов компьютера (локальный компьютер, LocalMachine): одно на ПК, общее для всех пользователей и служб на этом ПК. Физически живёт под ключом реестра
HKEY_LOCAL_MACHINE\Software\Microsoft\SystemCertificates.2 - Хранилище сертификатов пользователя (текущий пользователь, CurrentUser): на каждую учётную запись пользователя своё. Физически живёт под
HKEY_CURRENT_USER\Software\Microsoft\SystemCertificates, то есть как часть профиля пользователя.2
Есть ещё хранилище на учётную запись службы,3 физически — ключ реестра на имя службы.2 Для практики сначала нужно схватить первые два.
Есть одна важная спецификация. Каждое логическое хранилище хранилища пользователя, кроме «Личные», наследует и показывает содержимое одноимённого хранилища хранилища компьютера.1 Например, если положить сертификат корпоративного УЦ в «Доверенные корневые центры сертификации» хранилища компьютера, он появится и в «Доверенных корневых центрах сертификации» каждого пользователя. Наоборот, наследуется всё, кроме хранилища «Личные», поэтому для клиентского сертификата (того, что кладут в личное) вы сами решаете, «кому нужно его видеть». Эта асимметрия — главный персонаж всей статьи.
flowchart TB
accTitle: Хранилище компьютера и хранилище пользователя
accDescr: Хранилище компьютера одно на ПК и общее для всех пользователей и служб; хранилище пользователя своё на каждую учётную запись, и кроме «Личные» наследует содержимое хранилища компьютера
subgraph LM["Компьютер (LocalMachine)<br/>Одно на ПК, общее для всех пользователей и служб"]
LMMY["Личные (My)"]
LMROOT["Доверенные корневые центры сертификации (Root)"]
LMCA["Промежуточные центры сертификации (CA)"]
LMTP["Доверенные издатели (TrustedPublisher)"]
end
subgraph CU["Пользователь (CurrentUser)<br/>На каждую учётную запись своё"]
CUMY["Личные (My)<br/>не наследуется = место кладёте сами"]
CUROOT["Доверенные корневые центры сертификации (Root)"]
CUCA["Промежуточные центры сертификации (CA)"]
CUTP["Доверенные издатели (TrustedPublisher)"]
end
LMROOT -.->|"содержимое видно через наследование"| CUROOT
LMCA -.->|"наследование"| CUCA
LMTP -.->|"наследование"| CUTP
Рис. 1: Хранилище пользователя, кроме «Личные», наследует содержимое хранилища компьютера; куда класть клиентский сертификат, решаете сами.
2.2. Основные логические хранилища
Внутри каждого места содержимое разделено на логические хранилища по роли. Это папки, которые видно в certmgr.msc / certlm.msc; из PowerShell и командной строки используют английские внутренние имена.24
| Отображаемое имя | Внутреннее имя | Что сюда кладут |
|---|---|---|
| Личные | My | Сертификаты, которыми пользуется этот ПК / этот пользователь. Сюда — клиентские и серверные. Сюда же привязывается закрытый ключ |
| Доверенные корневые центры сертификации | Root | Корневые сертификаты УЦ — точка доверия. Всё под УЦ, положенным сюда, «доверяется» |
| Промежуточные центры сертификации | CA | Промежуточные сертификаты УЦ между корнем и оконечным. Материал построения цепочки |
| Доверенные издатели | TrustedPublisher | Сертификаты, которым доверяют как издателю подписанного ПО (глава 8) |
2.3. Три окна просмотра — certmgr.msc / certlm.msc / диск Cert:
Одно и то же хранилище смотрят тремя средствами.34
- certmgr.msc: консоль управления, открывающая хранилище текущего пользователя.
- certlm.msc: консоль управления, открывающая хранилище локального компьютера.
- Диск PowerShell
Cert:: иерархияCert:\CurrentUser\...иCert:\LocalMachine\...позволяет обращаться с хранилищем как с файловой системой. Сертификаты идентифицируются отпечатком.
Если оснастку сертификатов добавляют в mmc.exe вручную, цель выбирают из трёх видов: «учётная запись пользователя», «учётная запись компьютера», «учётная запись службы». Пользователь без прав администратора может управлять только хранилищем своей учётной записи пользователя.3
Первый шаг расследования сбоя — совместить «какое хранилище смотрит приложение» и «какое хранилище смотрите вы». Пока вы смотрите certmgr.msc и расследуете сбой службы, места разные, и ответ не появится никогда.
3. Куда класть — таблица решений по форме выполнения программы
Критерий один. Под чьей учётной записью работает программа, которая этот сертификат использует.
| Форма выполнения | Учётная запись выполнения | Куда класть | Примечание |
|---|---|---|---|
| Настольное приложение, которое запускает интерактивный пользователь | Сам вошедший пользователь | Пользователь (Cert:\CurrentUser\My) | Нужно внедрять на каждую учётную запись пользователя. Если общим ПК пользуются несколько человек, рассмотрите и хранилище компьютера |
| Служба Windows | LocalSystem / NETWORK SERVICE / выделенная учётная запись службы | Компьютер (Cert:\LocalMachine\My) | Всем, кроме LocalSystem (NETWORK SERVICE, выделенная учётная запись и т. п.), обязательно выдать право читать закрытый ключ (глава 4). LocalSystem читает правом SYSTEM по умолчанию |
| Веб-приложение на IIS | Идентификатор пула приложений | Компьютер | То же |
| Работа без присмотра планировщика заданий (выполнять независимо от входа пользователя) | Учётная запись, указанная заданию | Рекомендуется компьютер | Можно и хранилище пользователя учётной записи выполнения, но растёт только проверка профиля и видимости хранилища, выгоды мало |
| Электронная подача и веб-аутентификация в браузере | Сам вошедший пользователь | Пользователь | Естественно и в смысле «не давать пользоваться никому, кроме того, кому раздали» |
Если сомневаетесь: то, что работает без присмотра, — хранилище компьютера; то, чем управляет человек, — хранилище пользователя.
3.1. Разбор классической аварии — «пока разрабатывали — работало, как стало службой — не находится»
Эту аварию точно воспроизводят следующим порядком.
- Разработчик на своём ПК дважды щёлкает pfx и импортирует. Значение мастера по умолчанию — «текущий пользователь», поэтому сертификат попадает в хранилище пользователя учётной записи разработчика.
- Приложение в разработке запускается из Visual Studio, то есть под учётной записью разработчика, открывает
StoreLocation.CurrentUser— сертификат находится. Работает. - На продакшен-сервере регистрируют как службу Windows. Служба работает под NETWORK SERVICE или выделенной учётной записью.
CurrentUser, который открывает код службы, — хранилище пользователя учётной записи выполнения службы. Оно пусто. «Сертификат не найден».
flowchart TB
accTitle: Пока разрабатывали — работало, как стало службой — не находится
accDescr: Сертификат, положенный в хранилище пользователя разработчика, не виден из CurrentUser службы, которая работает под другой учётной записью
subgraph DEV["Машина разработки"]
D1["Импорт двойным щелчком pfx<br/>значение мастера по умолчанию — «текущий пользователь»"] --> D2["Попадает в хранилище пользователя<br/>учётной записи разработчика"]
D2 --> D3["Запуск из Visual Studio<br/>= работает под учётной записью разработчика"]
D3 --> D4["Открыть CurrentUser — находится<br/>→ работает"]
end
subgraph PROD["Продакшен-сервер"]
P1["Регистрация как службы Windows<br/>учётная запись выполнения — NETWORK SERVICE и т. п."] --> P2["CurrentUser, который открывает код,<br/>— хранилище пользователя учётной записи службы"]
P2 --> P3["Оно пусто<br/>→ «сертификат не найден»"]
end
D4 -.->|"размещают ту же программу"| P1
Рис. 2: Хранилище пользователя разработчика и хранилище пользователя учётной записи службы разные; работа в разработке не гарантирует, что сертификат найдётся в продакшене.
Суть в том, что хранилищ пользователя «столько, сколько учётных записей». Даже если администратор откроет certmgr.msc и скажет «да вот же лежит», это хранилище самого администратора, а не хранилище учётной записи службы. Мера — не копировать по случаю, а положить заново в хранилище компьютера и выровнять код на StoreLocation.LocalMachine. И в комплект входит выдача прав следующей главы.
4. Закрытый ключ и права доступа — вторая классическая авария
4.1. Сертификат и закрытый ключ — разные вещи
В списке хранилища сертификатов виден сертификат (открытые сведения), а не сам закрытый ключ. Для клиентской аутентификации на деле нужна подпись закрытым ключом, поэтому «видно в списке» и «можно пользоваться» — разные проблемы. Если их смешать, получаются плохо читаемые снаружи сбои: «сертификат есть, но TLS-рукопожатие падает», «внутренние ошибки вроде Access Denied».
4.2. Практика импорта pfx — экспортируемость есть решение
Пару сертификат + закрытый ключ передают файлом pfx (PKCS #12) и принимают в хранилище Import-PfxCertificate.6
$pwd = Get-Credential -UserName '(пароль введите ниже)' -Message 'Пароль PFX'
Import-PfxCertificate -FilePath C:\certs\client.pfx `
-CertStoreLocation Cert:\LocalMachine\My -Password $pwd.Password
Важно: пока не указан -Exportable, принятый закрытый ключ нельзя экспортировать снова — поведение по умолчанию.6 Класть всё как экспортируемое «чтобы потом можно было перенести» — добавить ещё один путь выноса закрытого ключа. Оригинал pfx хранят безопасно, а закрытый ключ в хранилище по умолчанию неэкспортируемый — так рекомендуем мы. Оригинал pfx и его пароль как раз часто оставляют открытым текстом. Мышление разобрано в «Хранение секретов в Windows-приложениях — избегаем открытых настроек с помощью DPAPI» и «Безопасная работа с учётными данными в PowerShell».
4.3. Выдача прав на закрытый ключ учётной записи службы
Закрытый ключ сертификата, положенного в хранилище компьютера, по умолчанию обычно не читают ниоткуда, кроме администраторов и SYSTEM. Поэтому служба под LocalSystem читает закрытый ключ как есть, а если работает под NETWORK SERVICE, выделенной учётной записью службы, идентификатором пула приложений IIS и т. п. — всем, кроме этого, явно выдайте учётной записи выполнения право на чтение. Шаги — из UI оснастки сертификатов.5
- Открыть certlm.msc (или оснастку сертификатов с целью «учётная запись компьютера»).
- «Личные» → «Сертификаты», правый щелчок по целевому сертификату, «Все задачи» → «Управление закрытыми ключами».
- На вкладке «Безопасность» добавить учётную запись выполнения (NETWORK SERVICE, выделенная учётная запись службы, идентификатор пула приложений IIS и т. п.) и разрешить «Чтение».5
Полный контроль не нужен. Для подписи достаточно чтения. Наоборот, выдать Everyone полный контроль «потому что не работает» — опустить закрытый ключ до обращения как с открытым паролем; этого абсолютно избегать. Размещение в хранилище компьютера и выдача прав на закрытый ключ всегда в комплекте — одной этой фразой в инструкции этот класс аварий исчезает.
5. Не допустить аварию истечения — инвентаризация, замена, реестр
5.1. Инвентаризация через PowerShell
Срок действия сертификата лежит в свойстве NotAfter. Инвентаризировать механически можно Get-ChildItem по диску Cert:.4
# Личные хранилища компьютера списком по сроку действия
Get-ChildItem Cert:\LocalMachine\My |
Sort-Object NotAfter |
Format-Table Thumbprint, Subject, NotAfter
# Только те, что истекают в пределах 60 дней (0 — уже истёкшие)
Get-ChildItem -Path Cert:\LocalMachine\My -ExpiringInDays 60
-ExpiringInDays возвращает «сертификаты, которые истекают в пределах заданного числа дней»; 0 даёт уже истёкшие.4 Сделайте это ежемесячным заданием планировщика по всем серверам и сводите результат в почту или реестр — и аварии вида «истёк срок, с утра понедельника проверка полиса не проходит» почти исчезают.
5.2. Порядок замены — период параллельной работы и ловушка отпечатка
Обновление сертификата — не «удалить и положить», а «добавить, затем переключить, проверить, затем удалить».
- Импортировать новый сертификат (pfx) в то же хранилище. Отпечатки разные, поэтому старый и новый могут сосуществовать в одном хранилище.
- Выдать права на закрытый ключ нового сертификата (глава 4). При обновлении это чаще всего забывают. Права висят на закрытом ключе каждого сертификата, поэтому после замены сертификата выдачу делают заново.
- Уведомление контрагента (API, где сертификат нужно регистрировать заранее) сначала, продолжая работать на старом сертификате, и обеспечить период, когда принимают и старый, и новый. Если переключить раньше, контрагент отвергнет новый сертификат, и продакшен-обмен встанет.
- Переключить конфигурацию приложения на новый сертификат и проверить работу.
- После достаточного периода удалить старый сертификат.
flowchart LR
accTitle: Обновление сертификата — сначала добавить, затем переключить
accDescr: Новый pfx кладут в то же хранилище, выдают права, заранее регистрируют у контрагента, затем переключают отпечаток и после периода параллельной работы удаляют старый сертификат
I["1. Импорт нового pfx в то же хранилище<br/>(старый и новый сосуществуют)"] --> P["2. Выдать права на закрытый ключ<br/>нового сертификата"]
P --> R["3. Заранее зарегистрировать у контрагента<br/>(продолжать работу на старом)"]
R --> SW["4. Переписать отпечаток в конфигурации,<br/>переключить, проверить работу"]
SW --> DEL["5. После периода параллельной работы<br/>удалить старый сертификат"]
Рис. 3: Порядок замены сертификата — добавить → выдать права → заранее зарегистрировать → сменить конфигурацию → удалить старый; не забудьте обновить отпечаток.
Главная ловушка здесь — отпечаток, записанный в файл конфигурации или в код. Отпечаток уникален на сертификат, при обновлении он обязательно меняется. Если хоть в одном месте остаётся ссылка на старый отпечаток, получается «сертификат обновили, а соединиться нельзя». Надёжно вести в реестре, где отпечаток записан (конфигурация приложения, привязка IIS, скрипты, уведомление контрагента).
5.3. Зачем реестр сертификатов
Реестр — для начала хватит одного листа Excel. Минимум колонок: назначение / издатель / субъект / отпечаток / место (имя сервера + хранилище) / учётные записи с правом на закрытый ключ / срок действия / ссылка на процедуру обновления / ответственный — и сверять с результатом инвентаризации 5.1. Суть аварий с сертификатами — не техника, а «ни у кого нет списка», поэтому реестр действует сильнее всего.
6. Проверка и чтение сбоя — цепочка и раздача корня
6.1. База проверки цепочки и certutil
Ошибки вида «этому сертификату не доверяют» — состояние, в котором цепочка (путь сертификации) от оконечного сертификата до корневого УЦ где-то разорвана. Для разбора удобен certutil.7
flowchart TB
accTitle: Три классические причины разрыва проверки цепочки
accDescr: Ошибка доверия возникает, когда промежуточный УЦ получить нельзя, корень не роздан или оконечный сертификат истёк
LEAF["Оконечный сертификат<br/>(клиентский, серверный)"] --> INT["Промежуточный сертификат УЦ<br/>место: хранилище промежуточных УЦ (CA)"]
INT --> ROOT["Корневой сертификат УЦ<br/>место: доверенные корневые УЦ (Root)"]
INT -.->|"получить нельзя<br/>(ни предъявление, ни AIA, ни хранилище)"| E1["Цепочку построить нельзя<br/>(классическая причина 1)"]
ROOT -.->|"не роздан"| E2["Ошибка «не доверяют»<br/>(классическая причина 2)"]
LEAF -.->|"истёк"| E3["Ошибка срока действия<br/>(классическая причина 3)"]
Рис. 4: Цепочка рвётся, если нет промежуточного УЦ или корня, либо из-за срока действия; слой определяют через certutil.
:: Построить и проверить цепочку файла сертификата (с получением URL отзыва)
certutil -urlfetch -verify client.cer
:: Если целевое приложение использует хранилище пользователя — тот же контекст с -user
certutil -user -urlfetch -verify client.cer
:: Дамп содержимого хранилища (-user — хранилище пользователя)
certutil -store My
certutil -user -store My
certutil -verify проверяет сертификат, CRL и цепочку и, если CACertFile не задан, строит полную цепочку и проверяет её.7 Вывод длинный, но из него читается, на каком уровне оборвалось доверие и получены ли сведения об отзыве. Типичные причины: (1) промежуточный сертификат УЦ получить нельзя (собеседник TLS его не прислал, из сведений AIA сертификата тоже не взять, в хранилище «Промежуточные центры сертификации» его нет); (2) корень корпоративного УЦ не роздан в «Доверенные корневые центры сертификации»; (3) истёк сам сертификат. Промежуточный УЦ решается и предъявлением собеседника, и автоматическим получением через AIA, поэтому размещение в хранилище — «один из способов сделать надёжно».
6.2. Раздача корня корпоративного УЦ и самоподписанного — через GPO/Intune
Если используете корпоративный УЦ или самоподписанный сертификат для проверки, его корневой сертификат нужно раздать на каждый ПК. Не класть вручную по одной машине, а посадить на механизм раздачи.
- Среда Active Directory (GPO): импорт сертификата в «Доверенные корневые центры сертификации» в
Конфигурация компьютера\Политики\Конфигурация Windows\Параметры безопасности\Политики открытого ключараздаёт его на целевые ПК.8 - Среда управления Intune: профилем «Доверенный сертификат» раздают корневой / промежуточный сертификат УЦ. В Windows можно выбрать целевое хранилище (корень / промежуточный компьютера, промежуточный пользователя).9
Как сказано в 2.1, положив в корень хранилища компьютера, доверяют все пользователи.1 Поэтому смотрите и обратный риск. Положить самоподписанный сертификат в «Доверенные корневые центры сертификации» — посадить на этот ПК новую точку доверия. Если его закрытый ключ утечёт, появляется площадка выпускать сертификаты, выдающие себя за любой сайт или ПО. Для постоянной эксплуатации — либо корпоративный УЦ с надлежащей защитой закрытого ключа, либо сертификаты публичного УЦ; самоподписанный корень — принцип «только среда проверки, со сроком».
7. Взгляд разработчика — правильно пользоваться хранилищем из .NET
7.1. Поиск по отпечатку через X509Store
Из .NET хранилище открывают X509Store и получают сертификат через Find.1011
using System.Security.Cryptography.X509Certificates;
static X509Certificate2 GetClientCertificate(string thumbprint)
{
using var store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);
var found = store.Certificates.Find(
X509FindType.FindByThumbprint, thumbprint, validOnly: true);
if (found.Count == 0)
throw new InvalidOperationException(
$"Сертификат не найден: отпечаток={thumbprint}, " +
$"место={store.Location}\\{store.Name}");
var cert = found[0];
if (!cert.HasPrivateKey)
throw new InvalidOperationException(
$"Сертификат есть, но закрытый ключ не связан (импорт " +
$"из .cer и т. п.): отпечаток={thumbprint}, место={store.Location}\\{store.Name}");
return cert;
}
Решение главы 3 сюда стыкуется напрямую. Код, работающий как служба, — StoreLocation.LocalMachine; интерактивное приложение — StoreLocation.CurrentUser. Ещё одно: третий аргумент Find, validOnly. true возвращает только действительные сертификаты, прошедшие проверку.11 Это страховка не схватить истёкший, но тестовый самоподписанный, которому цепочка не доверяет, тоже падает в «не найден» — когда «лежит, а не находится», подозревайте и это. И в сообщение об ошибке, когда не найден, как в примере выше, обязательно включайте, какое хранилище искали. Время расследования аварии главы 3 меняется на порядки.
7.2. Положить клиентский сертификат в HttpClient
Полученный сертификат добавляют в HttpClientHandler.ClientCertificates и предъявляют серверу. Эта коллекция — множество сертификатов, предъявляемых серверу при аутентификации клиента по сертификату.12
var handler = new HttpClientHandler();
handler.ClientCertificates.Add(GetClientCertificate(thumbprint));
var client = new HttpClient(handler);
// дальше — обычный HttpClient
В семействе .NET Core в документации прямо сказано: если у сертификата есть атрибут использования ключа (Key Usage), без «Digital Signature» он не используется при отправке запроса.12 Когда заказываете выпуск клиентского сертификата, правильно передавайте назначение (аутентификация клиента). Кроме того, HttpClient при неверном шаблоне создания даёт исчерпание сокетов и проблемы следования за DNS. Конструкцию с долгоживущим обработчиком разбирали в «Не оборачивайте HttpClient в using».
7.3. Проблема зашитого отпечатка, который умирает при замене
Поиск по отпечатку надёжен, но если отпечаток вшить в код, при каждом обновлении сертификата нужны сборка и выпуск. Мера в проекте — три ступени.
- Минимум: вынести отпечаток в файл конфигурации (appsettings и т. п.), чтобы менять без выпуска. Место конфигурации занести в реестр 5.3.
- Шаг дальше: искать по имени субъекта или издателю и в сочетании с
validOnly: trueвыбирать «из сейчас действительных с этим именем тот, у когоNotAfterдальше всех». В период параллельной работы старого и нового автоматически пересядет на новый сертификат. Но есть риск схватить одноимённый непреднамеренный сертификат, поэтому в комплект — проверка издателя и вывод в журнал. И эта автоматическая пересадка работает только если контрагенту не нужна предварительная регистрация сертификата. В API, где регистрация нужна (5.2), самовольное переключение на только что импортированный незарегистрированный сертификат может остановить обмен — оставайтесь на вынесении в конфигурацию с переключением после подтверждения регистрации. - Закрепить эксплуатацией: при любом способе при запуске писать в журнал, «какой сертификат (отпечаток, срок) выбран». И для расследования сбоя, и для сверки с реестром работает эта одна строка.
8. Связь с сертификатом подписи кода — хранилище «Доверенные издатели»
До сих пор речь шла о сертификатах для обмена (TLS), но в хранилище сертификатов живёт ещё один мир — подпись кода. Точка касания — хранилище «Доверенные издатели (TrustedPublisher)» из таблицы 2.2: место, куда регистрируют сертификат издателя подписанного ПО как доверенный. Есть и в месте пользователя, и в месте компьютера,10 и используется в эксплуатации вроде раздачи издателя внутреннего приложения через GPO в TrustedPublisher каждого ПК.
Если вы на «стороне раздачи» приложения и нужно закрыть подпись кода и предупреждение SmartScreen («Windows защитил ваш компьютер»), это собрано в отдельной статье «Почему Windows показывает сообщение „Windows защитил ваш компьютер“». Знания этой статьи (две системы хранилища, раздача корня) остаются предпосылками как есть.
9. Итог
- Хранилище сертификатов — две системы: пользователь (CurrentUser) и компьютер (LocalMachine). certmgr.msc / certlm.msc / диск
Cert:— три окна на одно и то же. Первый шаг расследования — совместить, «о каком хранилище речь». - Куда класть, решает «от чьего имени работает программа». Работа без присмотра (служба, IIS, задание) — хранилище компьютера; интерактивное приложение — хранилище пользователя, как правило.
- «Пока разрабатывали — работало, в продакшене не находится» — потому что хранилище пользователя разработчика и хранилище пользователя учётной записи службы разные. Выравнивают на хранилище компьютера +
StoreLocation.LocalMachine. - Размещение в хранилище компьютера и выдача права на чтение в «Управлении закрытыми ключами» — всегда в комплекте. Не забудьте выдать заново при обновлении.
- Импорт pfx по умолчанию неэкспортируемый.
-Exportable— только когда действительно нужно. Хранение оригинала pfx и пароля тоже входит в проект. - Истечение предотвращают регулярной инвентаризацией
Get-ChildItem Cert: ... -ExpiringInDaysи реестром сертификатов. Замена — порядок «добавить → переключить → проверить → удалить», следите за пропуском обновления отпечатка в конфигурации. - Разбор цепочки —
certutil -urlfetch -verify. Корень корпоративного УЦ раздают через GPO/Intune; самоподписанный в корень — только среда проверки, со сроком. - В коде отпечаток выносят в конфигурацию и выбранный сертификат пишут в журнал. Одного этого достаточно, чтобы разбор сбоев из-за сертификатов стал другим.
Похожие статьи
- Почему Windows показывает сообщение «Windows защитил ваш компьютер»
- Хранение секретов в Windows-приложениях - избегаем открытых настроек с помощью DPAPI
- Безопасная работа с учётными данными в PowerShell — вывести открытый пароль из скриптов
- Не оборачивайте HttpClient в using ── практика HTTP-взаимодействия в бизнес-приложениях на C#
- Что происходит, когда прикладывают карту медицинского страхования My Number — онлайн-проверка полиса и связь с расчётной системой по исходникам ORCA
Смежные области консультирования
KomuraSoft LLC занимается разработкой бизнес-приложений с интеграцией Web API по клиентскому сертификату (банковские API, онлайн-проверка полиса и т. п.), расследованием сбоев вида «сертификат не найден», «после обновления не соединяется» и наведением порядка в процедуре замены сертификатов. Можно начинать уже с этапа «непонятно, куда в хранилище смотреть».
- Разработка приложений для Windows
- Расследование ошибок и причин
- Технические консультации и ревью дизайна
- Связаться с нами
Справочные ссылки
-
Microsoft Learn, Local Machine and Current User Certificate Stores. О том, что хранилище сертификатов компьютера локально для ПК и общее для всех пользователей, живёт под HKEY_LOCAL_MACHINE; о том, что хранилище сертификатов пользователя своё на учётную запись и живёт под HKEY_CURRENT_USER; о том, что хранилище пользователя наследует содержимое хранилища компьютера, кроме хранилища «Личные» (сертификат, добавленный в «Доверенные корневые центры сертификации» компьютера, появляется и в одноимённом хранилище каждого пользователя). ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, System Store Locations. О положении в реестре CERT_SYSTEM_STORE_CURRENT_USER / CERT_SYSTEM_STORE_LOCAL_MACHINE (соответственно Software\Microsoft\SystemCertificates под HKEY_CURRENT_USER / HKEY_LOCAL_MACHINE); о предопределённых логических хранилищах MY, Root, Trust, CA; о том, что хранилище служб живёт в ключе реестра на имя службы (Software\Microsoft\Cryptography\Services\ServiceName\SystemCertificates); о том, что отдельно существуют хранилища для раздачи групповой политикой. ↩ ↩2 ↩3 ↩4 ↩5
-
Microsoft Learn, How to: View certificates with the MMC snap-in. О том, что certlm.msc управляет сертификатами локального устройства (локального компьютера), certmgr.msc — сертификатами текущего пользователя; о трёх видах цели оснастки сертификатов: «учётная запись компьютера», «учётная запись пользователя», «учётная запись службы»; о том, что пользователь без прав администратора может управлять только сертификатами своей учётной записи пользователя. ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, about_Certificate_Provider. О том, что диск PowerShell Cert: — иерархическое пространство имён с двумя местами хранилища CurrentUser и LocalMachine; о перечислении хранилищ и сертификатов через Get-ChildItem; о том, что параметр -ExpiringInDays возвращает сертификаты, истекающие в пределах заданного числа дней (0 — уже истёкшие); о динамических параметрах вроде -CodeSigningCert; о том, что срок действия хранится в свойстве NotAfter; о том, что сертификаты идентифицируются отпечатком. ↩ ↩2 ↩3 ↩4 ↩5 ↩6
-
Microsoft Learn, How to Modify Private Key Permissions to Support Management Server or Streaming Server. О порядке открыть «Управление закрытыми ключами» (Manage Private Keys) в оснастке сертификатов с целью хранилища локального компьютера и на вкладке «Безопасность» добавить учётной записи выполнения службы (например Network Service) разрешение «Чтение». ↩ ↩2 ↩3
-
Microsoft Learn, Import-PfxCertificate. О том, что Import-PfxCertificate принимает сертификат и закрытый ключ из файла PFX в указанное хранилище; о том, что без переключателя -Exportable принятый закрытый ключ экспортировать нельзя; о синтаксисе и примерах параметров -CertStoreLocation, -Password, -FilePath. ↩ ↩2 ↩3
-
Microsoft Learn, certutil. О том, что certutil -verify проверяет сертификат, CRL и цепочку сертификатов и, если файл сертификата УЦ не задан, строит полную цепочку и проверяет её; о доступности параметра -urlfetch; о том, что certutil -store дампит хранилище сертификатов, а параметр -user обращается к хранилищу пользователя вместо хранилища компьютера. ↩ ↩2
-
Microsoft Learn, Distribute Certificates to Client Computers by Using Group Policy. О порядке импорта сертификата в «Доверенные корневые центры сертификации» в
Конфигурация компьютера\Политики\Конфигурация Windows\Параметры безопасности\Политики открытого ключагрупповой политики и раздачи клиентским компьютерам в домене; о нужных правах (уровень Domain Admins / Enterprise Admins). ↩ -
Microsoft Learn, Create trusted certificate profiles in Microsoft Intune. О том, что профиль Intune «Доверенный сертификат» раздаёт корневой или промежуточный сертификат УЦ на управляемые устройства; о том, что он используется для установления доверия к корневому УЦ как предпосылка профилей сертификатов SCEP/PKCS; о том, что в Windows можно выбрать целевое хранилище: «хранилище сертификатов компьютера — корень», «хранилище сертификатов компьютера — промежуточные», «хранилище сертификатов пользователя — промежуточные». ↩
-
Microsoft Learn, X509Store Class. О том, что X509Store строится с указанием StoreName и StoreLocation (CurrentUser / LocalMachine), открывается методом Open и OpenFlags (ReadOnly, OpenExistingOnly и др.), коллекцию сертификатов получают свойством Certificates; о том, что в стандартные имена хранилищ входят My, Root, CA, TrustedPublisher и др. и хранилище TrustedPublisher есть и в CurrentUser, и в LocalMachine. ↩ ↩2
-
Microsoft Learn, X509Certificate2Collection.Find(X509FindType, Object, Boolean) Method. О том, что метод Find ищет сертификаты по X509FindType (FindByThumbprint и др.) и значению поиска; о том, что при true в третьем аргументе validOnly возвращаются только действительные сертификаты, прошедшие проверку. ↩ ↩2
-
Microsoft Learn, HttpClientHandler.ClientCertificates Property. О том, что свойство ClientCertificates — X509CertificateCollection, предъявляемая серверу при аутентификации клиента по сертификату; о том, что в .NET Core при наличии у сертификата атрибута использования ключа нужно включать «Digital Signature». ↩ ↩2
Похожие статьи
Недавние статьи с теми же тегами помогут подробнее изучить близкие темы.
Брандмауэр Windows и бизнес-приложения — входящие правила регистрируйте из установщика
«На машине разработки работает, у клиента не соединяется» почти всегда упирается в брандмауэр Windows. Статья разбирает поведение «входящ...
Политика аудита безопасности Windows и расследование журнала событий на практике — как стать ИТ-службой, которая умеет читать 4625
Практическое руководство, чтобы ответить на просьбу «посмотрите журналы неудачных входов». Разбирает связь базовой и расширенной политики...
Практическое руководство по Windows LAPS — отказ от общего локального пароля администратора на всех ПК
Общий локальный пароль администратора на всех ПК — питательная среда для атак Pass-the-Hash, когда компрометация одной машины распростран...
OneDrive «Файлы по запросу» и бизнес-приложения — какие допущения ломают заполнители и как с этим жить
CSV с рабочего стола не открывается, или импорт падает с «файл не найден» — причина может быть в Known Folder Move и «Файлах по запросу» ...
Теневое копирование тома (VSS): устройство и практика — почему резервное копирование может скопировать файл, который ещё используется
Занятый файл обычно нельзя скопировать из-за нарушения совместного доступа — как тогда это делает ПО резервного копирования? Статья разби...
Связанные темы
Эти страницы показывают тему статьи в более широком контексте услуг и решений.
Технические темы Windows
Раздел о разработке Windows, расследовании сбоев и использовании существующих активов.
Услуги по этой теме
Статья напрямую связана со следующими услугами.
Разработка приложений для Windows
Бизнес-приложения, интеграция оборудования и средства связи — от требований до разработки.
Частые вопросы
Вопросы, которые часто возникают при консультациях по теме статьи.
- Чем отличаются certmgr.msc и certlm.msc?
- Целевые хранилища разные. certmgr.msc открывает хранилище сертификатов текущего вошедшего пользователя (текущий пользователь, CurrentUser); certlm.msc открывает хранилище сертификатов компьютера (локальный компьютер, LocalMachine). Хранилище компьютера общее для всех пользователей и служб на ПК, для управления нужны права администратора. Пользователь без прав администратора может управлять только своим хранилищем пользователя. Внутри обоих содержимое разделено на логические хранилища вроде «Личные» и «Доверенные корневые центры сертификации»; из PowerShell та же структура видна как Cert:\CurrentUser и Cert:\LocalMachine.
- Клиентский сертификат класть в хранилище пользователя или компьютера?
- Решает, «от чьего имени» работает программа, которая сертификат использует. Для настольного приложения, которое запускает интерактивный пользователь, базовый выбор — хранилище пользователя того, кто им пользуется (Cert:\CurrentUser\My). Для программы, которая работает без присмотра — служба Windows, пул приложений IIS, планировщик заданий — кладите в хранилище компьютера (Cert:\LocalMachine\My) и выдайте учётной записи выполнения право читать закрытый ключ. Хранилище пользователя на каждую учётную запись своё, поэтому сертификат, который разработчик положил в своё хранилище пользователя, службе, работающей под другой учётной записью, невидим. Это классическая причина «пока разрабатывали — работало, в продакшене не находится».
- Что проверять, когда служба Windows не находит сертификат или не может им пользоваться?
- Проверка в два шага. Первое: какое хранилище смотрит. Если код открывает StoreLocation.CurrentUser, это хранилище пользователя учётной записи выполнения службы, а не то, которое администратор видит, открыв certmgr.msc для себя. Перенесите сертификат в хранилище компьютера и выровняйте код на StoreLocation.LocalMachine. Второе: читается ли закрытый ключ. Быть в списке сертификатов и мочь пользоваться ключом — разные вещи: по умолчанию к закрытому ключу в хранилище компьютера обычно имеют доступ только администраторы и SYSTEM. В certlm.msc откройте «Управление закрытыми ключами» у целевого сертификата и выдайте «Чтение» учётной записи выполнения службы (NETWORK SERVICE и т. п.).
- Как заранее ловить истечение сертификата через PowerShell?
- Инвентаризировать можно Get-ChildItem по диску Cert:. Например, Get-ChildItem Cert:\LocalMachine\My | Sort-Object NotAfter | Format-Table Thumbprint, Subject, NotAfter выводит личное хранилище хранилища компьютера в порядке срока действия. Параметр -ExpiringInDays извлекает только «сертификаты, которые истекают в пределах заданного числа дней»; 0 возвращает уже истёкшие. Запускайте это ежемесячно по всем серверам и сверяйте результат с реестром сертификатов — и почти все аварии вида «с утра понедельника не аутентифицируется, потому что сертификат истёк» исчезают.
Об авторе
Страница с профилем автора статьи.
Го Комура
Представитель KomuraSoft LLC
Специализируется на разработке программного обеспечения для Windows, техническом консалтинге и расследовании сбоев, особенно в проектах с унаследованными системами и трудно воспроизводимыми ошибками.