sgcSign Server : signature de code et signature de documents auto-hébergées via REST

· Composants

La signature de code souffre d'un problème de gestion des clés. La configuration habituelle copie un fichier .pfx, ou branche un token USB, sur chaque agent de build qui doit signer une version. La clé privée finit sur des machines qui exécutent des scripts de build non fiables, il n'existe aucune trace de ce qui a été signé ni par qui, et faire tourner le certificat implique d'intervenir sur chaque agent. sgcSign 2026.6 y répond avec un nouveau serveur de signature : un démon auto-hébergé qui expose la signature via une API REST TLS, pour que vos pipelines et vos développeurs signent à distance pendant que la clé de signature reste à un seul endroit.

La clé ne quitte jamais le serveur. Mieux encore, le serveur peut servir de façade à un token matériel ou à un KMS cloud, de sorte que la clé n'existe jamais sous forme de fichier. Chaque requête est authentifiée par une clé d'API, soumise à une limitation de débit et inscrite dans un journal d'audit infalsifiable. Cet article décrit ce que le serveur peut signer, comment le configurer, un démarrage rapide en cinq minutes, et les deux façons de l'appeler : curl en clair et le client en ligne de commande sgcsign fourni.

Ce qu'il signe

Un serveur, une API, huit formats de signature. La même forme d'endpoint (POST /api/v1/sign/<format>) couvre à la fois les exécutables et les documents :

Chaque format accepte une URL d'horodatage RFC 3161 optionnelle, pour que les signatures restent valides après l'expiration du certificat de signature. Un endpoint compagnon POST /api/v1/verify vérifie une signature existante et renvoie le sujet du signataire, sa validité et l'horodatage.

Fournisseurs de clés enfichables

Un fournisseur est un handle nommé vers une clé de signature. L'appelant nomme un fournisseur à chaque requête ; il ne voit jamais la clé. Vous pouvez en enregistrer autant que nécessaire, et le même serveur peut combiner un PFX local pour les outils internes avec un certificat EV adossé au cloud pour les versions publiques. Neuf types de fournisseurs sont pris en charge :

Les secrets restent en dehors du fichier de configuration. Tout paramètre dont le nom se termine par _env est lu depuis une variable d'environnement, de sorte que le mot de passe PFX, le code PIN du token ou le secret cloud est fourni par l'environnement du service, et non enregistré sur disque.

Configurer le serveur

Le serveur entier est piloté par un seul fichier JSON, sgcSignServer.conf.json (un exemple documenté est fourni sous le nom sgcSignServer.conf.sample.json). La structure de premier niveau se résume à une poignée de blocs :

{
  "server": {
    "listen": "0.0.0.0",
    "port": 8443,
    "tls": {
      "enabled": true,
      "cert_file": "certs/server.crt",
      "key_file":  "certs/server.key"
    },
    "max_upload_mb": 512,
    "firewall": {
      "enabled": true,
      "whitelist": ["10.0.0.0/8"],
      "brute_force": { "enabled": true, "max_attempts": 5, "ban_duration_sec": 900 },
      "rate_limit":  { "enabled": true, "max_connections_per_ip": 30, "time_window_sec": 60 }
    }
  },
  "storage": { "sqlite_path": "data/sgcsignserver.db" },
  "admin": {
    "initial_user": "admin",
    "initial_password_env": "SGCSIGN_ADMIN_INIT_PW",
    "session_timeout_minutes": 60
  },
  "audit": { "retention_days": 365 },
  "providers": [
    {
      "name": "pfx-build",
      "type": "PFX",
      "params": { "file": "certs/build.pfx", "password_env": "SGCSIGN_PFX_PW" }
    },
    {
      "name": "ev-release",
      "type": "AzureTS",
      "params": {
        "tenant_id": "00000000-0000-0000-0000-000000000000",
        "client_id": "00000000-0000-0000-0000-000000000000",
        "client_secret_env": "SGCSIGN_AZURE_TS_SECRET",
        "account": "your-account",
        "certificate_profile": "your-profile",
        "endpoint": "https://eus.codesigning.azure.net/"
      }
    }
  ]
}

Les blocs correspondent directement aux fonctionnalités. server définit l'adresse d'écoute, le port (8443 par défaut), le certificat TLS et le pare-feu intégré (listes d'autorisation et de refus d'IP, verrouillage anti-force-brute, limitation du débit de connexions, protections contre le path-traversal et les charges utiles). storage pointe vers la base de données SQLite qui contient les utilisateurs, les clés d'API, le journal d'audit et la file d'attente des webhooks. admin définit le premier utilisateur et la durée des sessions. audit fixe la fenêtre de rétention. providers est la liste ci-dessus.

Le mot de passe administrateur d'amorçage n'est jamais stocké dans le fichier. Au tout premier démarrage, lorsque la table des utilisateurs est vide, le serveur lit la variable d'environnement nommée par initial_password_env (par défaut SGCSIGN_ADMIN_INIT_PW) et crée le compte administrateur. À chaque démarrage ultérieur, cette variable est ignorée, elle ne sert donc qu'à amorcer la première connexion.

Démarrage rapide en cinq minutes

Installez le serveur (l'assistant d'installation enregistre le service Windows, ou exécutez sgcSignServer.exe --install), puis :

1. Créez un certificat de signature de code de test et exportez-le en PFX. Pour un déploiement réel, vous importez votre certificat émis par une autorité de certification, ou vous sautez cette étape et pointez plutôt un fournisseur vers votre token ou votre KMS cloud.

$cert = New-SelfSignedCertificate -Type CodeSigningCert `
  -Subject "CN=sgcSign Quickstart" -KeyAlgorithm RSA -KeyLength 3072 `
  -CertStoreLocation Cert:\CurrentUser\My -NotAfter (Get-Date).AddYears(1)
$pw = ConvertTo-SecureString "QuickStartPFX!" -Force -AsPlainText
Export-PfxCertificate -Cert $cert -Password $pw `
  -FilePath "C:\Program Files\sgcSign Server\certs\quickstart.pfx"

2. Définissez les secrets comme variables d'environnement machine pour que le compte de service puisse les lire :

setx /M SGCSIGN_ADMIN_INIT_PW "ChangeMeNow!"
setx /M SGCSIGN_PFX_PW        "QuickStartPFX!"

3. Rédigez une configuration minimale avec un seul fournisseur PFX (TLS désactivé pour un test localhost uniquement) :

{
  "server":   { "listen": "127.0.0.1", "port": 8443, "tls": { "enabled": false } },
  "storage":  { "sqlite_path": "data/sgcsignserver.db" },
  "admin":    { "initial_user": "admin", "initial_password_env": "SGCSIGN_ADMIN_INIT_PW" },
  "audit":    { "retention_days": 365 },
  "providers": [
    { "name": "pfx-quickstart", "type": "PFX",
      "params": { "file": "certs/quickstart.pfx", "password_env": "SGCSIGN_PFX_PW" } }
  ]
}

4. Démarrez le service et confirmez qu'il est à l'écoute :

sc start sgcSignServer
sc query sgcSignServer

S'il s'arrête au démarrage, exécutez-le au premier plan avec sgcSignServer.exe --console --config <path> pour voir l'erreur en direct (une variable SGCSIGN_ADMIN_INIT_PW manquante ou un PFX illisible en sont les causes habituelles). Le drapeau --selftest-providers charge la configuration, initialise chaque fournisseur et quitte, ce qui vous permet de valider les clés avant la mise en production.

5. Connectez-vous et créez une clé d'API. Ouvrez http://localhost:8443/admin, connectez-vous en tant que admin avec le mot de passe d'amorçage, et créez une clé d'API. La clé en clair n'est affichée qu'une seule fois, à la création ; copiez-la immédiatement. Les clés portent le préfixe sgcsk_.

6. Signez un exécutable de test avec curl :

curl -X POST http://localhost:8443/api/v1/sign/authenticode ^
  -H "X-API-Key: sgcsk_..." ^
  -F file=@app.exe ^
  -F provider=pfx-quickstart ^
  -F hash=sha256 ^
  --output app-signed.exe

C'est toute la boucle : configurer un fournisseur, émettre une clé, signer. À partir de là, vous remplacez le PFX auto-signé par un vrai certificat, vous activez TLS et vous verrouillez le pare-feu sur le sous-réseau de votre CI.

Signer via REST

Chaque requête de signature est un POST contenant le fichier et quelques champs de formulaire. L'authentification tient en un en-tête, soit X-API-Key: sgcsk_..., soit Authorization: Bearer sgcsk_.... La réponse est l'artefact signé sous forme application/octet-stream, avec le sujet du signataire et la durée de signature renvoyés dans les en-têtes X-Sgcsign-Signer-Subject et X-Sgcsign-Duration-Ms.

Signer un PDF, avec un motif et un emplacement de signature visibles :

curl -X POST https://sign.acme.local:8443/api/v1/sign/pades \
  -H "X-API-Key: sgcsk_..." \
  -F file=@invoice.pdf \
  -F provider=qualified-eu \
  -F tsa_url=http://timestamp.digicert.com \
  -F reason=Approved \
  -F location="Madrid, Spain" \
  -F signer_name="Acme Billing" \
  -o invoice-signed.pdf

Signer une facture XML avec un profil XAdES eIDAS :

curl -X POST https://sign.acme.local:8443/api/v1/sign/xades \
  -H "X-API-Key: sgcsk_..." \
  -F file=@invoice.xml \
  -F provider=qualified-eu \
  -F profile=eidas \
  -F xades_type=enveloped \
  -o invoice-signed.xml

Vérifier une signature renvoie du JSON plutôt qu'un fichier :

curl -X POST https://sign.acme.local:8443/api/v1/verify \
  -H "X-API-Key: sgcsk_..." \
  -F file=@app-signed.exe \
  -F format=authenticode

{ "valid": true, "status": "valid",
  "subject": "CN=Acme Inc., O=Acme, C=US",
  "has_timestamp": true, "timestamp_time": "2026-06-15T13:01:17.000Z" }

Le client en ligne de commande sgcsign

Le serveur est livré avec sgcsign, un petit CLI multi-formats doté de quatre verbes : sign, verify, keys et health. L'URL du serveur, la clé d'API et le fournisseur peuvent provenir de drapeaux ou des variables d'environnement SGCSIGN_SERVER, SGCSIGN_APIKEY et SGCSIGN_PROVIDER, ce qui maintient les secrets hors de la ligne de commande dans la CI :

set SGCSIGN_SERVER=https://sign.acme.local:8443
set SGCSIGN_APIKEY=sgcsk_...

sgcsign sign -f authenticode -p pfx-build -o app-signed.exe app.exe ^
  --tsa http://timestamp.digicert.com --desc "Acme Installer"

sgcsign verify -f authenticode app-signed.exe

Pour Authenticode, il existe une astuce qui économise de la bande passante. Avec le mode pré-hachage (--prehash, activé par défaut), le CLI calcule le hachage du PE localement et n'envoie que le hachage, quelques centaines d'octets au lieu d'un binaire de plusieurs mégaoctets. Le serveur signe le hachage et renvoie un blob PKCS#7 détaché, que le client intègre dans le fichier. Sur un agent de CI à bande passante limitée signant un gros installeur, cela transforme un envoi de plusieurs mégaoctets en un envoi minuscule.

Sécurité et exploitation

Le serveur est conçu pour fonctionner sans surveillance devant une vraie clé de signature, donc la surface opérationnelle compte autant que la cryptographie :

Pourquoi auto-héberger

L'intérêt du serveur est de réduire un parc d'agents de build risqués et porteurs de clés à un unique endpoint durci. La clé de signature ne réside que sur le serveur, ou dans le HSM ou le KMS cloud dont le serveur fait office de façade, jamais sur les machines qui exécutent vos scripts de build. L'accès est contrôlé par des jetons d'API par clé avec quotas, limités à des projets, et chaque signature est enregistrée dans un journal d'audit que vous maîtrisez. Faire tourner un certificat, révoquer la clé d'un agent compromis ou prouver qui a signé quoi deviennent un seul endroit à consulter au lieu de plusieurs.

Disponibilité

Le sgcSign Server fait partie de sgcSign 2026.6.0. Il s'exécute comme service Windows ou comme application console, se configure par le fichier JSON unique présenté ci-dessus, et se pilote depuis curl, le CLI sgcsign fourni, ou tout client HTTP via sa description OpenAPI.

Des questions, des retours ou besoin d'aide pour le mettre en place ? Contactez-nous. Vous obtiendrez une réponse des personnes qui ont écrit le code.