
SRO PKCS11 – SSH Agent CNG est un agent Windows souverain, ultra‑léger et zéro‑dépendance qui unifie PKCS#11, SSH-agent, Pageant et CNG/Smartcard dans un seul binaire robuste. Pensé pour les environnements exigeants, il offre une cryptographie matérielle native, une isolation service/userland, un support complet smartcards.
Unification souveraine PKCS#11 + SSH-agent + Pageant + CNG/Smartcard
Un exécutable Windows unique qui unifie quatre fonctions traditionnellement séparées :
Souverain. Aucune dépendance au CRT. Toutes les opérations mémoire passent par RtlCopyMemory, RtlZeroMemory, RtlEqualMemory (FreeCRT.h). Unicode partout (Win32 natif). Aucun malloc, memcpy, strlen, printf.
Sécurisé. Les clés privées ne sont jamais exportées. Aucun PIN ne transite. CNG/KSP gère l'UI PIN native Windows. Isolation stricte service ↔ userland via pipes sécurisés.
Minimaliste. Un seul binaire. Aucune DLL externe. Pas de registry bloat. Installation simple (regsvr32 ou -install).
Polyvalent. Support simultané de PKCS#11, SSH-agent, Pageant, et WSL2 dans le même processus.
┌──────────────────────────────────────────────────────────────┐
│ 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) │
└──────────────────────────────────────────────────────────────┘
Chargé par :
ssh -I ssh-agent.exe user@hostpkcs11-tool --module ssh-agent.exe --list-objectsExpose les exports PKCS#11 standards :
C_Initialize, C_Finalize, C_GetInfoC_GetSlotList, C_GetSlotInfo, C_GetTokenInfoC_GetMechanismList, C_GetMechanismInfoC_OpenSession, C_CloseSession, C_Login, C_LogoutC_FindObjectsInit, C_FindObjects, C_FindObjectsFinalMécanismes supportés (14 au total) :
CKM_RSA_PKCS (raw avec padding)CKM_RSA_X_509 (raw sans padding)CKM_SHA1_RSA_PKCS (legacy 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 (legacy)CKM_ECDSA_SHA256 (ecdsa-sha2-nistp256/384/521)CKM_ECDSA_SHA384ssh-agent.exe
\\.\pipe\openssh-ssh-agent en session utilisateurCompatible avec :
set SSH_AUTH_SOCK=\\.\pipe\openssh-ssh-agent)ssh-agent.exe -install
net start SROSSHAgentCNG
CreateProcessAsUserWAvantages :
ssh-agent.exe -useragent -pipe \\.\pipe\ssh-ksp-helper-{GUID}
Lancé automatiquement par le service :
NCryptSignHash (UI PIN native)regsvr32 ssh-agent.exe
Crée les clés :
HKLM\SOFTWARE\San@sro Inc\PKCS11-SSH-AgentHKCU\SOFTWARE\San@sro Inc\PKCS11-SSH-AgentHKCU\SOFTWARE\Mozilla\Firefox\PKCS11Modules\SROSSHAgentssh-agent.exe -install
net start SROSSHAgentCNG
Ajouter au ~/.bashrc ou ~/.zshrc :
# --- Pont SSH Agent Windows (TCP -> Unix Socket) ---
export SSH_AUTH_SOCK="$HOME/.ssh/agent.sock"
# Vérifier si le socket est déjà géré par un socat actif
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
regsvr32 /u ssh-agent.exe
ssh-agent.exe -remove
Clé : HKLM\SOFTWARE\San@sro Inc\pkcs11-cng ou HKCU\SOFTWARE\San@sro Inc\pkcs11-cng
Exemple :
StoreName = "MY"
StoreLocation = "CurrentUser"
SmartCardOnly = 1
AllowedKSP = "Microsoft Smart Card Key Storage Provider;YubiKey Smart Card Key Storage Provider"
RelaxCheckMode = 0
LogLevel = 2
Requête :
[type=11]
Réponse :
[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]
Requête :
[type=13][len][key_blob][len][data][flags]
Flags :
0x00 : ssh-rsa (SHA-1, legacy)0x02 : rsa-sha2-2560x04 : rsa-sha2-512Réponse :
[type=14][len][signature_blob]
signature_blob :
[len]["rsa-sha2-256"][len][signature_data]
Compatible PuTTY via WM_COPYDATA :
CreateFileMappingWM_COPYDATA à la fenêtre "Pageant"Format mémoire partagée :
[uint32 length][SSH-agent payload]
Listener TCP sur 127.0.0.1:10022 :
handle_ssh_message()Windows gère entièrement le PIN via CNG/KSP et le minidriver de la smartcard.
Le module ne stocke jamais le PIN et ne le voit jamais transiter :
Cache PIN : Géré automatiquement par Windows/minidriver (pas besoin de cache applicatif).
Flags NCrypt :
NCRYPT_SILENT_FLAG (pas d'UI)SILENT_FLAG échoue : Retry automatique avec UICache clés (timeout 4h) :
CNG_KEY_INFO (handle, provider, container)Cache providers (timeout 4h) :
NCRYPT_PROV_HANDLENCryptOpenStorageProvidercng_store_enum_certificates(cfg, callback, user_data);
Filtre :
SmartCardOnly)SmartCardOnly)cng_sign_hash(key_info, mechanism, hash, hash_len, signature, &sig_len);
Mécanisme → Padding :
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 → Pas de padding (signature brute)RSA :
cng_cert_get_public_key(cert, modulus, &mod_len, exponent, &exp_len);
ECDSA :
cng_cert_get_ec_params(cert, params, ¶ms_len); // OID courbe
cng_cert_get_ec_point(cert, point, &point_len); // Point public
Courbes supportées :
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)Support authentification Active Directory :
cng_extract_upn_from_certificate(cert, upn, upn_size);
Extrait l'extension szOID_NT_PRINCIPAL_NAME pour l'utiliser comme commentaire SSH.
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.
Géré exclusivement par Windows (CNG/KSP/minidriver).
Le module ne stocke jamais le PIN et ne le voit jamais transiter :
Mode service (passthrough pur) : Le service stub ne fait QUE du forwarding transparent :
Pipes sécurisés. Chaque pipe interne est :
FILE_FLAG_FIRST_PIPE_INSTANCELe helper userland invoque CNG/KSP dans la session interactive → UI PIN native.
Logs Unicode. Tous les événements sont journalisés via utils_log() :
Emplacement : OutputDebugString + fichier optionnel (utils_set_log_file()).
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 ----
Ordre de priorité pour le commentaire :
TRAY_MODE_USERLAND (vert) :
TRAY_MODE_SERVICE (bleu) :
Tooltip dynamique :
SRO SSH-Agent (Userland)
12 keys, 3 clients
Mise à jour :
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... : Lance le dialogue de sélection et copie au clipboard
Flush & Reload Keys : Vide les caches clés/providers et recharge
Settings... : Affiche configuration actuelle
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 : Arrêt propre (signale g_shutdown_event)
GetMessage/DispatchMessage loopg_tray_ready_event pour synchronisationShell_NotifyIcon(NIM_DELETE))┌─────────────────────────────────────────────┐
│ 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 │
└─────────────────────────────────────────────┘
Sécurité :
g_wsl2_clients[16]CRITICAL_SECTION par slotMode mirrored (Windows 11 22H2+) :
socat UNIX-LISTEN:"$SSH_AUTH_SOCK",fork,unlink-early \
TCP:127.0.0.1:10022 > /dev/null 2>&1 &
Mode NAT classique :
# 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);
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 natif :
ssh-agent.exe via CreateToolhelp32SnapshotPageant :
FindWindowW(L"Pageant", L"Pageant")SRO Userland :
CreateFileW(\\.\pipe\openssh-ssh-agent)SRO Service :
OpenServiceW(L"SROSSHAgentCNG")SERVICE_RUNNINGAffiché au démarrage si conflit détecté :
⚠ 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]
Actions :
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);
Aucune. Le binaire est auto-contenu et ne charge que des DLL système :
kernel32.dll (toujours présent)advapi32.dll (registry, SCM)crypt32.dll (certificats)ncrypt.dll (CNG)bcrypt.dll (hashing)wtsapi32.dll (sessions)shell32.dll (tray icon)ws2_32.dll (Winsock)cryptui.dll (dialogue sélection certificat)Pas de CRT. Toutes les opérations mémoire via RtlCopyMemory, RtlZeroMemory, RtlEqualMemory.
SSH2_AGENTC_*_ENCRYPT).SSH2_AGENTC_ADD_ID_CONSTRAINED.MAX_HELPERS).RelaxCheckMode = 1 pour les utiliser.Les contributions sont les bienvenues ! Merci de :
Ce logiciel est la propriété de San@sro inc.
Il est distribué selon un modèle de Licence de Confiance :
• Usage Personnel & Éducation : Gratuit et encouragé.
• Usage Professionnel / Commercial : Requiert l'achat d'une Licence de Paix Technique.
L'utilisation en entreprise sans licence valide constitue une violation des droits d'auteur,
malgré l'absence volontaire de tout verrou technique.
La redistribution est autorisée à condition que :
• le binaire reste intact,
• la signature Authenticode originale soit préservée.
Ce logiciel est fourni « tel quel », sans garantie d’aucune sorte.
La licence complète (FR + EN), incluant les définitions, conditions de redistribution, durée, résiliation et modalités d’obtention d’une Licence de Paix Technique, est disponible ici :
Pour toute demande de licence professionnelle :
📧 [email protected]
SRO PKCS11 – SSH Agent CNG ne manipule aucun secret sensible :
le PIN, les clés privées et les opérations cryptographiques sont entièrement gérés par Windows (CNG/KSP/minidriver).
Pour signaler un bug, un comportement anormal ou une vulnérabilité potentielle, une politique de divulgation responsable est disponible ici :
Contact sécurité :
📧 [email protected]
SRO PKCS11 – SSH Agent CNG
Souverain. Robuste. Opérationnel.
Un seul binaire pour tout faire.
C_GetAttributeValueC_SignInit, C_SignC_VerifyInit, C_VerifyC_DecryptInit, C_DecryptC_GenerateRandom, C_SeedRandomCKM_ECDSA_SHA512| Valeur | Type | Description |
|---|
StoreName | REG_SZ | "MY", "Root", etc. (défaut: "MY") |
StoreLocation | REG_SZ | "CurrentUser" ou "LocalMachine" |
Mode | REG_SZ | "All" ou "SmartCard" |
SmartCardOnly | REG_DWORD | 1 = filtrer uniquement smartcards |
AllowedKSP | REG_SZ | Liste de KSP autorisés (séparés par ";") |
RelaxCheckMode | REG_DWORD | 1 = désactiver la validation EKU/KeyUsage/dates (YubiKey PIV auto-signé) |
LogLevel | REG_DWORD | 0=off, 1=error, 2=info, 3=debug |
| Environnement | Mode | Status |
|---|
| 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 | ✓ |
| Environnements durcis | Service stub | ✓ |
| SmartCard GIDS | CNG/KSP | ✓ |
| SmartCard PIV | CNG/KSP | ✓ |
| YubiKey | CNG/KSP | ✓ |
| Nitrokey | CNG/KSP | ✓ |