
SRO PKCS11 – SSH Agent CNG — это суверенный, сверхлегкий и независимый от зависимостей агент Windows, который объединяет PKCS#11, SSH-agent, Pageant и CNG/Smartcard в одном надежном бинарном файле. Разработанный для требовательных сред, он обеспечивает собственную аппаратную криптографию, изоляцию service/userland и полную поддержку смарт-карт.
Суверенное объединение PKCS#11 + SSH-agent + Pageant + CNG/Smartcard
Один уникальный исполняемый файл Windows, объединяющий четыре традиционно разделённые функции:
Суверенный. Нет зависимости от CRT. Все операции с памятью выполняются через RtlCopyMemory, RtlZeroMemory, RtlEqualMemory (FreeCRT.h). Unicode везде (родной Win32). Нет malloc, memcpy, strlen, printf.
Безопасный. Закрытые ключи никогда не экспортируются. Никакой PIN не передаётся. CNG/KSP управляет родным интерфейсом PIN Windows. Строгая изоляция службы ↔ пользовательского режима через защищённые каналы.
Минималистичный. Один единственный бинарный файл. Нет внешних DLL. Нет раздувания реестра. Простая установка (regsvr32 или -install).
Универсальный. Одновременная поддержка PKCS#11, SSH-agent, Pageant и WSL2 в одном процессе.
┌──────────────────────────────────────────────────────────────┐ │ Clients (Git, VS, WSL, OpenSSH, PuTTY, Firefox) │ └────────────────────────┬─────────────────────────────────────┘ │ ┌───────────────┼───────────────┬─────────────────┐ │ │ │ │ SSH-agent Pageant (WM_COPYDATA) PKCS#11 WSL2 (TCP) │ │ │ │ v v v v ┌──────────────────────────────────────────────────────────────┐ │ Service Stub (session 0, SYSTEM) │ │ - Accepte connexions sur \.\pipe\openssh-ssh-agent │ │ - Crée pipe interne par client (GUID unique) │ │ - Lance helper userland avec token interactif │ │ - Forwarde messages sans manipuler de secrets │ └────────────────────────┬─────────────────────────────────────┘ │ lancé par le service v ┌──────────────────────────────────────────────────────────────┐ │ Helper Userland (session interactive) │ │ - Connecte au pipe interne │ │ - Décode protocole SSH-agent/Pageant │ │ - Invoque CNG/KSP pour signature │ │ - UI PIN native Windows (pas de relay) │ │ - Renvoie signature au service │ │ - Fenêtre Pageant cachée pour WM_COPYDATA │ │ - Listener TCP 127.0.0.1:10022 pour WSL2 │ │ - Tray icon avec menu contextuel │ └────────────────────────┬─────────────────────────────────────┘ │ v ┌──────────────────────────────────────────────────────────────┐ │ CNG/KSP Backend │ │ - NCryptSignHash avec PKCS#1/PSS padding │ │ - Enumération certificats Windows Store │ │ - Filtrage SmartCardOnly / AllowedKSP │ │ - Support RSA + ECDSA (P-256, P-384, P-521) │ │ - Support EdDSA (Ed25519, Ed448) │ │ - Support Brainpool (P256r1, P384r1, P512r1) │ │ - Cache clés + providers (4h timeout) │ └──────────────────────────────────────────────────────────────┘
---
## Режимы выполнения
### 1. Режим PKCS#11 (автоматический)
Загружается через:
- `ssh -I ssh-agent.exe user@host`
- Firefox (Security Devices → Load PKCS#11 Module)
- `pkcs11-tool --module ssh-agent.exe --list-objects`
Предоставляет стандартные экспорты PKCS#11:
- `C_Initialize`, `C_Finalize`, `C_GetInfo`
- `C_GetSlotList`, `C_GetSlotInfo`, `C_GetTokenInfo`
- `C_GetMechanismList`, `C_GetMechanismInfo`
- `C_OpenSession`, `C_CloseSession`, `C_Login`, `C_Logout`
- `C_FindObjectsInit`, `C_FindObjects`, `C_FindObjectsFinal`
- `C_GetAttributeValue`
- `C_SignInit`, `C_Sign`
- `C_VerifyInit`, `C_Verify`
- `C_DecryptInit`, `C_Decrypt`
- `C_GenerateRandom`, `C_SeedRandom`
**Поддерживаемые механизмы (всего 14):**
- `CKM_RSA_PKCS` (raw с дополнением)
- `CKM_RSA_X_509` (raw без дополнения)
- `CKM_SHA1_RSA_PKCS` (устаревший ssh-rsa)
- `CKM_SHA256_RSA_PKCS` (rsa-sha2-256)
- `CKM_SHA384_RSA_PKCS` (rsa-sha2-384)
- `CKM_SHA512_RSA_PKCS` (rsa-sha2-512)
- `CKM_SHA256_RSA_PKCS_PSS` (RSA-PSS SHA-256)
- `CKM_SHA384_RSA_PKCS_PSS` (RSA-PSS SHA-384)
- `CKM_SHA512_RSA_PKCS_PSS` (RSA-PSS SHA-512)
- `CKM_ECDSA` (raw)
- `CKM_ECDSA_SHA1` (устаревший)
- `CKM_ECDSA_SHA256` (ecdsa-sha2-nistp256/384/521)
- `CKM_ECDSA_SHA384`
- `CKM_ECDSA_SHA512`
### 2. Режим пользовательского агента (автономный)```bash
ssh-agent.exe
\\.\pipe\openssh-ssh-agent в сеансе пользователяСовместимо с:
set SSH_AUTH_SOCK=\\.\pipe\openssh-ssh-agent)ssh-agent.exe -install net start SROSSHAgentCNG
- Работает в сеансе 0 (SYSTEM)
- Принимает соединения через глобальный канал (pipe)
- Создаёт внутренний канал для каждого клиента (защищённый SID)
- Запускает вспомогательный процесс userland с помощью `CreateProcessAsUserW`
- Пересылает сообщения, не затрагивая секреты
- Пул вспомогательных процессов с таймаутом 4 часа (автоматическое повторное использование)
- Вытеснение LRU при заполнении пула
**Преимущества:**
- ПИН-код в пользовательском сеансе (не в сеансе 0)
- Совместимость с усиленными средами
- Строгая изоляция службы ↔ криптография
- Мультиплексирование нескольких пользователей
### 4. Режим вспомогательного крипто-процесса в пользовательском пространстве```bash
ssh-agent.exe -useragent -pipe \\.\pipe\ssh-ksp-helper-{GUID}
Lancé automatiquement par le service :
NCryptSignHash (UI PIN native)regsvr32 ssh-agent.exe
Создаёт ключи :
- `HKLM\SOFTWARE\San@sro Inc\PKCS11-SSH-Agent`
- `HKCU\SOFTWARE\San@sro Inc\PKCS11-SSH-Agent`
- `HKCU\SOFTWARE\Mozilla\Firefox\PKCS11Modules\SROSSHAgent`
### Установка службы Windows```bash
ssh-agent.exe -install
net start SROSSHAgentCNG
Добавить в ~/.bashrc или ~/.zshrc :```bash
export SSH_AUTH_SOCK="$HOME/.ssh/agent.sock"
if ! pgrep -u $USER socat > /dev/null || [ ! -S "$SSH_AUTH_SOCK" ]; then # Nettoyage préventif rm -f "$SSH_AUTH_SOCK"
# Lancement du bridge en arrière-plan
# Note: Utiliser 127.0.0.1 si mode 'mirrored'
# sinon l'IP du host (ex: 192.168.99.x)
socat UNIX-LISTEN:"$SSH_AUTH_SOCK",fork,unlink-early \
TCP:127.0.0.1:10022 > /dev/null 2>&1 &
fi
### Удаление```bash
regsvr32 /u ssh-agent.exe
ssh-agent.exe -remove
Ключ: HKLM\SOFTWARE\San@sro Inc\pkcs11-cng или HKCU\SOFTWARE\San@sro Inc\pkcs11-cng
Пример:``` StoreName = "MY" StoreLocation = "CurrentUser" SmartCardOnly = 1 AllowedKSP = "Microsoft Smart Card Key Storage Provider;YubiKey Smart Card Key Storage Provider" RelaxCheckMode = 0 LogLevel = 2
---
## Поддерживаемые протоколы
### SSH-Agent
#### SSH2_AGENTC_REQUEST_IDENTITIES (11)
Запрос :```
[type=11]
Ответ:``` [type=12][count][key_blob_1][comment_1][key_blob_2][comment_2]...
**key_blob RSA :**```
[len]["ssh-rsa"][len][exponent][len][modulus]
key_blob ECDSA :``` [len]["ecdsa-sha2-nistp256"][len]["nistp256"][len][point]
**key_blob EdDSA :**```
[len]["ssh-ed25519"][len][point]
Запрос :``` [type=13][len][key_blob][len][data][flags]
**Флаги :**
- `0x00` : ssh-rsa (SHA-1, устаревшее)
- `0x02` : rsa-sha2-256
- `0x04` : rsa-sha2-512
Ответ :```
[type=14][len][signature_blob]
signature_blob :``` [len]["rsa-sha2-256"][len][signature_data]
### Pageant
Совместимость с PuTTY через `WM_COPYDATA` :
1. Клиент создаёт разделяемую память через `CreateFileMapping`
2. Записывает запрос SSH-агента в стандартном формате
3. Отправляет `WM_COPYDATA` в окно "Pageant"
4. Читает ответ из разделяемой памяти
Формат разделяемой памяти :```
[uint32 length][SSH-agent payload]
Listener TCP на 127.0.0.1:10022:
handle_ssh_message()Windows полностью управляет PIN-кодом через CNG/KSP и мини-драйвер смарт-карты.
Модуль никогда не хранит PIN-код и не видит его в процессе передачи:
Кэш PIN: Управляется автоматически Windows/мини-драйвером (кэш приложения не требуется).
Флаги NCrypt:
NCRYPT_SILENT_FLAG (без UI)SILENT_FLAG не срабатывает: Автоматический повтор с UIКэш ключей (тайм-аут 4 часа):
CNG_KEY_INFO (дескриптор, провайдер, контейнер)Кэш провайдеров (тайм-аут 4 часа):
NCRYPT_PROV_HANDLENCryptOpenStorageProvidercng_store_enum_certificates(cfg, callback, user_data);
Фильтр:
- Доступные закрытые ключи
- Разрешенные KSP (если `SmartCardOnly`)
- Неэкспортируемые ключи (если `SmartCardOnly`)
### Подпись```c
cng_sign_hash(key_info, mechanism, hash, hash_len, signature, &sig_len);
Механизм → Дополнение :
CKM_RSA_PKCS → BCRYPT_PAD_PKCS1CKM_SHA256_RSA_PKCS → BCRYPT_PAD_PKCS1 + BCRYPT_SHA256_ALGORITHMCKM_SHA256_RSA_PKCS_PSS → BCRYPT_PAD_PSS + salt size = hash sizeCKM_ECDSA_SHA256 → Без дополнения (сырая подпись)RSA :```c cng_cert_get_public_key(cert, modulus, &mod_len, exponent, &exp_len);
**ECDSA :**```c
cng_cert_get_ec_params(cert, params, ¶ms_len); // OID courbe
cng_cert_get_ec_point(cert, point, &point_len); // Point public
Поддерживаемые кривые:
nistp256 (OID: 1.2.840.10045.3.1.7), nistp384 (1.3.132.0.34), nistp521 (1.3.132.0.35)brainpoolP256r1, brainpoolP384r1, brainpoolP512r1ed25519 (OID: 1.3.101.112), ed448 (1.3.101.113)Поддержка аутентификации Active Directory :```c cng_extract_upn_from_certificate(cert, upn, upn_size);
Extrait l'extension `szOID_NT_PRINCIPAL_NAME` pour l'utiliser comme commentaire SSH → Извлекает расширение `szOID_NT_PRINCIPAL_NAME` для использования в качестве комментария SSH
---
## Sécurité → Безопасность
### Clés privées → Приватные ключи
**Jamais exportées.** Toutes les opérations cryptographiques sont déléguées à CNG/KSP. `NCryptSignHash` est appelé avec le handle de clé, jamais avec la clé elle-même. → **Никогда не экспортируются.** Все криптографические операции делегируются CNG/KSP. `NCryptSignHash` вызывается с дескриптором ключа, но никогда с самим ключом.
### PIN → PIN
**Géré exclusivement par Windows (CNG/KSP/minidriver).** → **Управляется исключительно Windows (CNG/KSP/minidriver).**
Le module **ne stocke jamais le PIN** et **ne le voit jamais transiter** : → Модуль **никогда не хранит PIN** и **никогда не видит его передачу** :
- Le PIN n'est jamais transmis au module PKCS#11 → PIN никогда не передается модулю PKCS#11
- L'UI PIN est affichée par le minidriver de la smartcard → Интерфейс PIN отображается мини-драйвером смарт-карты
- Le cache PIN est géré automatiquement par Windows/minidriver → Кэш PIN автоматически управляется Windows/minidriver
- En mode service : le helper userland (session interactive) reçoit l'UI PIN → В режиме службы: вспомогательный процесс userspace (интерактивная сессия) получает интерфейс PIN
**Mode service (passthrough pur) :** → **Режим службы (чистый passthrough):**
Le service stub ne fait QUE du forwarding transparent : → Служба-заглушка выполняет ТОЛЬКО прозрачную пересылку:
- Client → Service → Helper (forward message SSH-agent) → Клиент → Служба → Помощник (пересылка сообщения SSH-agent)
- Helper → Service → Client (forward réponse SSH-agent) → Помощник → Служба → Клиент (пересылка ответа SSH-agent)
- Le service ne parse jamais le contenu → Служба никогда не анализирует содержимое
- Le service ne voit jamais : PIN, hash, signature, clé → Служба никогда не видит: PIN, хеш, подпись, ключ
### Isolation service ↔ userland → Изоляция службы ↔ userspace
**Pipes sécurisés.** Chaque pipe interne est : → **Безопасные каналы.** Каждый внутренний канал:
- Généré avec un GUID unique → Создается с уникальным GUID
- Créé avec `FILE_FLAG_FIRST_PIPE_INSTANCE` → Создается с `FILE_FLAG_FIRST_PIPE_INSTANCE`
- DACL permettant uniquement l'utilisateur courant → DACL, разрешающий доступ только текущему пользователю
Le helper userland invoque CNG/KSP dans la session interactive → UI PIN native. → Вспомогательный процесс userspace вызывает CNG/KSP в интерактивной сессии → нативный интерфейс PIN.
### Audit → Аудит
**Logs Unicode.** Tous les événements sont journalisés via `utils_log()` : → **Журналы Unicode.** Все события регистрируются через `utils_log()`:
- Connexions client → Подключения клиентов
- Énumération clés → Перечисление ключей
- Requêtes signature → Запросы подписи
- Erreurs CNG/KSP → Ошибки CNG/KSP
- Conflits agents → Конфликты агентов
**Emplacement :** OutputDebugString + fichier optionnel (`utils_set_log_file()`). → **Расположение:** OutputDebugString + опциональный файл (`utils_set_log_file()`).
---
## Compatibilité → Совместимость
| Среда | Режим | Статус |
|------------------------------|------------------------|--------|
| OpenSSH for Windows | Standalone / Service | ✓ |
| Git for Windows | Standalone / Service | ✓ |
| Visual Studio | Standalone / Service | ✓ |
| WSL (npiperelay) | Standalone / Service | ✓ |
| WSL2 (TCP) | Standalone / Service | ✓ |
| PuTTY / plink / pscp | Pageant | ✓ |
| Firefox | PKCS#11 | ✓ |
| OpenSC / pkcs11-tool | PKCS#11 | ✓ |
| ssh -I (OpenSSH) | PKCS#11 | ✓ |
| Усиленные среды | Service stub | ✓ |
| Смарт-карта GIDS | CNG/KSP | ✓ |
| Смарт-карта PIV | CNG/KSP | ✓ |
| YubiKey | CNG/KSP | ✓ |
| Nitrokey | CNG/KSP | ✓ |
---
## Export de clés publiques → Экспорт открытых ключей
### Commande CLI → Команда CLI```bash
ssh-agent.exe -exportkey [output.pub]
CryptUIDlgSelectCertificateFromStoreOpenSSH:``` ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC5... [email protected]
**RFC4716 :**```
---- BEGIN SSH2 PUBLIC KEY ----
Comment: "[email protected]"
AAAAB3NzaC1yc2EAAAADAQABAAABAQC5ABCDEF...
---- END SSH2 PUBLIC KEY ----
Порядок приоритета для комментария:
TRAY_MODE_USERLAND (зелёный) :
TRAY_MODE_SERVICE (синий) :
Динамический tooltip :``` SRO SSH-Agent (Userland) 12 keys, 3 clients
Обновление :
- Каждые 5 секунд
- При каждом подключении/отключении клиента
- При сбросе кэша
### Контекстное меню
**Show Keys...** : Диалог, перечисляющий все доступные ключи```
═══════════════════════════════════════════════
SRO SSH-Agent - Available Keys
═══════════════════════════════════════════════
[01] RSA-2048 - [email protected]
[02] ECDSA-nistp256 - [email protected]
[03] EdDSA-Ed25519 - [email protected]
═══════════════════════════════════════════════
Total: 3 keys
💡 Tip: Use 'Export Public Key' to copy SSH format
Export Public Key... : Запускает диалог выбора и копирует в буфер обмена
Flush & Reload Keys : Очищает кэши ключей/провайдеров и перезагружает
Settings... : Отображает текущую конфигурацию``` Current Configuration:
Store Name: MY Store Location: CurrentUser SmartCard Only: Yes Relax Key Usage Check Mode: No Log Level: 2
Edit registry to change: HKLM\SOFTWARE\San@sro Inc\pkcs11-cng
**Выход** : Чистое завершение (сигнализирует `g_shutdown_event`)
### Выделенный UI поток
- Скрытое окно с очередью сообщений
- `GetMessage/DispatchMessage` цикл
- Событие `g_tray_ready_event` для синхронизации
- Автоматическая очистка (`Shell_NotifyIcon(NIM_DELETE)`)
---
## Поддержка WSL2
### Архитектура```
┌─────────────────────────────────────────────┐
│ WSL2 (Linux) │
│ - socat UNIX-LISTEN → TCP:127.0.0.1:10022 │
└─────────────────────────────────────────────┘
│
│ TCP
v
┌─────────────────────────────────────────────┐
│ Windows Host │
│ - ssh-agent.exe (listener 127.0.0.1:10022)│
│ - CNG/KSP → Smartcard │
└─────────────────────────────────────────────┘
Безопасность:
g_wsl2_clients[16]CRITICAL_SECTION на слотРежим mirrored (Windows 11 22H2+):```bash
socat UNIX-LISTEN:"$SSH_AUTH_SOCK",fork,unlink-early
TCP:127.0.0.1:10022 > /dev/null 2>&1 &
**Классический режим NAT:**```bash
# Récupérer l'IP du host Windows
HOST_IP=$(ip route | grep default | awk '{print $3}')
socat UNIX-LISTEN:"$SSH_AUTH_SOCK",fork,unlink-early \
TCP:$HOST_IP:10022 > /dev/null 2>&1 &
BOOL wsl2_network_start(WORD port, HANDLE shutdown_event); void wsl2_network_stop(void); BOOL wsl2_network_is_running(void); DWORD wsl2_network_get_client_count(void);
## Обнаружение конфликтов
### Обнаруженные агенты```c
typedef enum {
AGENT_NONE = 0,
AGENT_OPENSSH_NATIVE, // OpenSSH for Windows (ssh-agent.exe)
AGENT_PAGEANT, // PuTTY Pageant (fenêtre "Pageant")
AGENT_SRO_USERLAND, // SRO SSH-Agent userland
AGENT_SRO_SERVICE, // SRO SSH-Agent service Windows
AGENT_UNKNOWN // Agent inconnu détecté
} AGENT_TYPE;
OpenSSH от Microsoft:
ssh-agent.exe с помощью CreateToolhelp32SnapshotPageant:
FindWindowW(L"Pageant", L"Pageant")SRO Userland:
CreateFileW(\\.\pipe\openssh-ssh-agent)SRO Service:
OpenServiceW(L"SROSSHAgentCNG")SERVICE_RUNNINGОтображается при запуске в случае обнаружения конфликта:``` ⚠ SSH Agent Conflict Detected
The following SSH agents are already running: • OpenSSH Native (ssh-agent.exe) • PuTTY Pageant
Running multiple agents may cause conflicts.
Do you want to continue anyway?
[Continue] [Stop conflicting agents] [Exit]
**Действия:**
- **Continue** : Всё равно запустить (риск конфликта)
- **Stop** : Попытка остановить агентов (если возможно)
- **Exit** : Выйти без запуска
### Публичная функция```c
BOOL detect_running_agents(AGENT_TYPE* detected_agents, DWORD* count);
BOOL show_agent_conflict_dialog(const AGENT_TYPE* agents, DWORD count);
const WCHAR* agent_type_to_string(AGENT_TYPE agent);
Нет. Бинарный файл самодостаточен и загружает только системные DLL:
kernel32.dll (всегда присутствует)advapi32.dll (реестр, SCM)crypt32.dll (сертификаты)ncrypt.dll (CNG)bcrypt.dll (хеширование)wtsapi32.dll (сеансы)shell32.dll (иконка в трее)ws2_32.dll (Winsock)cryptui.dll (диалог выбора сертификата)Нет CRT. Все операции с памятью через RtlCopyMemory, RtlZeroMemory, RtlEqualMemory.
SSH2_AGENTC_*_ENCRYPT).SSH2_AGENTC_ADD_ID_CONSTRAINED.MAX_HELPERS).RelaxCheckMode = 1 для их использования.Вклад приветствуется! Пожалуйста:
Это программное обеспечение является собственностью San@sro inc.
Оно распространяется по модели Доверительной лицензии:
• Личное использование и образование: бесплатно и приветствуется.
• Профессиональное / коммерческое использование: требуется покупка Лицензии на техническое спокойствие.
Использование на предприятии без действующей лицензии является нарушением авторских прав,
несмотря на добровольное отсутствие каких-либо технических блокировок.
Распространение разрешено при условии, что: • двоичный файл остается нетронутым, • оригинальная подпись Authenticode сохранена.
Это программное обеспечение предоставляется «как есть», без каких-либо гарантий.
Полная лицензия (FR + EN), включая определения, условия распространения, срок действия, прекращение и порядок получения Лицензии на техническое спокойствие, доступна здесь:
Для запросов профессиональной лицензии:
📧 [email protected]
SRO PKCS11 – SSH Agent CNG не обрабатывает никакие конфиденциальные секреты:
PIN-код, закрытые ключи и криптографические операции полностью управляются Windows (CNG/KSP/minidriver).
Для сообщения об ошибке, аномальном поведении или потенциальной уязвимости политика ответственного раскрытия доступна здесь:
Контакт для вопросов безопасности:
📧 [email protected]
SRO PKCS11 – SSH Agent CNG
Суверенный. Надежный. Работоспособный.
Один бинарный файл для всего.
| Значение | Тип | Описание |
|---|
StoreName | REG_SZ | "MY", "Root" и т.д. (по умолчанию: "MY") |
StoreLocation | REG_SZ | "CurrentUser" или "LocalMachine" |
Mode | REG_SZ | "All" или "SmartCard" |
SmartCardOnly | REG_DWORD | 1 = фильтровать только смарт-карты |
AllowedKSP | REG_SZ | Список разрешенных KSP (через ";") |
RelaxCheckMode | REG_DWORD | 1 = отключить проверку EKU/KeyUsage/дат (самоподписанный YubiKey PIV) |
LogLevel | REG_DWORD | 0=выкл, 1=ошибка, 2=инфо, 3=отладка |