主权统一 PKCS#11 + SSH-agent + Pageant + CNG/智能卡
一个独特的 Windows 可执行文件,统一了传统上分离的四种功能:
主权。 不依赖 CRT。所有内存操作均通过 RtlCopyMemory、RtlZeroMemory、RtlEqualMemory(FreeCRT.h)。全局 Unicode(原生 Win32)。无 malloc、memcpy、strlen、printf。
安全。 私钥从不导出。PIN 从不传输。CNG/KSP 管理 Windows 原生 PIN UI。通过安全管道实现严格的服务 ↔ 用户态隔离。
极简。 单一二进制文件。无外部 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(安全设备 → 加载 PKCS#11 模块)
- `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`(原始带填充)
- `CKM_RSA_X_509`(原始无填充)
- `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`(原始)
- `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
- Tourne en session 0 (SYSTEM) -> 在会话0(SYSTEM)中运行
- Accepte les connexions sur pipe global -> 接受全局管道上的连接
- Crée un pipe interne par client (sécurisé par SID) -> 为每个客户端创建内部管道(通过SID保护)
- Lance un helper userland avec `CreateProcessAsUserW` -> 使用 `CreateProcessAsUserW` 启动用户态助手
- Forwarde les messages sans toucher aux secrets -> 转发消息而不触及秘密
- Pool de helpers avec timeout 4h (réutilisation automatique) -> 助手池,超时4小时(自动重用)
- Éviction LRU si pool plein -> 如果池满则LRU逐出
**Avantages :**
- UI PIN dans la session utilisateur (pas en session 0) -> 用户会话中的UI PIN(而非会话0)
- Compatible environnements durcis -> 兼容加固环境
- Isolation stricte service ↔ crypto -> 服务与加密的严格隔离
- Multiplexage multi-utilisateurs -> 多用户复用
### 4. Mode helper crypto userland -> ### 4. 用户态加密助手模式```bash
ssh-agent.exe -useragent -pipe \\.\pipe\ssh-ksp-helper-{GUID}
由服务自动启动:
NCryptSignHash(本地 PIN 界面)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, legacy)
- `0x02` : rsa-sha2-256
- `0x04` : rsa-sha2-512
响应:```
[type=14][len][signature_blob]
signature_blob :``` [len]["rsa-sha2-256"][len][signature_data]
### Pageant
通过 `WM_COPYDATA` 与 PuTTY 兼容:
1. 客户端通过 `CreateFileMapping` 创建共享内存
2. 以标准格式写入 SSH-agent 请求
3. 向 "Pageant" 窗口发送 `WM_COPYDATA`
4. 从共享内存中读取响应
共享内存格式:```
[uint32 length][SSH-agent payload]
监听 TCP 127.0.0.1:10022:
handle_ssh_message()Windows 完全管理 PIN,通过 CNG/KSP 和智能卡迷你驱动程序。
模块从不存储 PIN,也从未看到其传输:
NCryptSignHash 触发原生 PIN 界面NCryptSignHash,PIN 界面在用户会话中显示PIN 缓存: 由 Windows/迷你驱动程序自动管理(无需应用缓存)。
NCrypt 标志:
NCRYPT_SILENT_FLAG(无界面)SILENT_FLAG 失败:自动重试并显示界面密钥缓存(超时 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.
---
## 安全性
### 私钥
**从不导出。** 所有加密操作都委托给 CNG/KSP。`NCryptSignHash` 使用密钥句柄调用,从不直接使用密钥本身。
### PIN
**完全由 Windows (CNG/KSP/minidriver) 管理。**
该模块**从不存储 PIN** 且**从不看到 PIN 传输**:
- PIN 从不传输给 PKCS#11 模块
- PIN 界面由智能卡 minidriver 显示
- PIN 缓存由 Windows/minidriver 自动管理
- 在服务模式下:用户态辅助程序(交互式会话)接收 PIN 界面
**服务模式(纯透传):**
服务 stub 仅进行透明转发:
- 客户端 → 服务 → 辅助程序(转发 SSH-agent 消息)
- 辅助程序 → 服务 → 客户端(转发 SSH-agent 响应)
- 服务从不解析内容
- 服务从不看到:PIN、哈希、签名、密钥
### 服务与用户态隔离
**安全的管道。** 每个内部管道:
- 使用唯一 GUID 生成
- 使用 `FILE_FLAG_FIRST_PIPE_INSTANCE` 创建
- DACL 仅允许当前用户
用户态辅助程序在交互式会话中调用 CNG/KSP → 原生 PIN 界面。
### 审计
**Unicode 日志。** 所有事件均通过 `utils_log()` 记录:
- 客户端连接
- 密钥枚举
- 签名请求
- CNG/KSP 错误
- 代理冲突
**位置:** OutputDebugString + 可选文件 (`utils_set_log_file()`)。
---
## 兼容性
| 环境 | 模式 | 状态 |
|-------------------------------|------------------------|------|
| OpenSSH for Windows | 独立 / 服务 | ✓ |
| Git for Windows | 独立 / 服务 | ✓ |
| Visual Studio | 独立 / 服务 | ✓ |
| WSL (npiperelay) | 独立 / 服务 | ✓ |
| WSL2 (TCP) | 独立 / 服务 | ✓ |
| 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 | ✓ |
---
## 公钥导出
### 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(蓝色):
动态工具提示:``` SRO SSH-Agent (Userland) 12 keys, 3 clients
Mise à jour :
- Toutes les 5 secondes
- À chaque connexion/déconnexion client
- Au flush du cache
### Menu contextuel
**Show Keys...** : Dialogue listant toutes les clés disponibles```
═══════════════════════════════════════════════
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
**Exit** : 干净退出 (信号 `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镜像模式(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:
CreateToolhelp32Snapshot 搜索 ssh-agent.exe 进程Pageant:
FindWindowW(L"Pageant", L"Pageant")SRO 用户态:
CreateFileW(\\.\pipe\openssh-ssh-agent)SRO 服务:
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]
**操作:**
- **继续** : 仍然启动(存在冲突风险)
- **停止** : 尝试停止代理(如果可能)
- **退出** : 退出而不启动
### 公共函数```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 签名。
本软件按“原样”提供,不提供任何形式的保证。
完整许可(法文+英文),包括定义、重新分发条件、有效期、终止以及获取技术和平许可证的方式,可在此处获取:
如需专业许可证申请:
📧 [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=调试 |