# Vérifier un certificat de réponse IA en ligne et hors ligne

> Pour vérifier un certificat de réponse IA Subsidia, collez le fichier .json sur la page /certificat : votre navigateur recalcule l'empreinte SHA-256 du contenu et contrôle la signature Ed25519 avec la clé publique, sans compte. Hors ligne, le paquet @protypa/subsidia-verify (sans dépendance) ou le script en ligne de commande font le même contrôle, y compris la signature ML-DSA-65 et l'ancrage horodaté que la page ne vérifie pas.

Source : https://subsidia.protypa.fr/guides/verifier-un-certificat-de-reponse-ia · mis à jour le 2026-10-25 · Protypa

## La réponse courte

Il y a deux façons de vérifier un certificat Subsidia, et elles se complètent :

- **En ligne, en une minute** : la page [/certificat](/certificat) recalcule l'empreinte et contrôle la signature Ed25519 dans votre navigateur. Aucun compte, aucune installation.
- **Hors ligne, de façon complète** : le paquet `@protypa/subsidia-verify`, un script en ligne de commande ou une action GitHub contrôlent aussi la double signature, la chaîne et l'ancrage horodaté.

Dans les deux cas, vous n'avez pas à nous croire sur parole : le calcul est reproductible avec la clé publique, qui est ouverte.

## Ce qu'il faut avoir en main

| Pièce | D'où elle vient |
|---|---|
| Le certificat (fichier `.json`) | La fiche de preuve d'une réponse, téléchargeable dans Subsidia ; `certificat.json` dans le dossier de contrôle du Vérificateur ; `attestation.json` dans l'export du registre ; ou `GET /v1/proofs/:id` par l'API |
| La clé publique | `GET /v1/gateway/public-key` (clé Ed25519 courante) ou `GET /v1/gateway/keys` (trousseau complet). Les deux sont publiques, sans authentification |
| Pour une installation sur site | La clé publique de cette installation, à demander à celui qui vous a remis le certificat |

## Méthode 1 : la page /certificat

1. **Ouvrez [/certificat](/certificat).** La page est publique et ne demande aucun compte.
2. **Collez le certificat**, ou déposez le fichier. Le fichier doit faire 256 Ko au plus. Une archive `.zip` est refusée avec un message : ouvrez-la et déposez `certificat.json` (rapport du Vérificateur) ou `attestation.json` (registre).
3. **Choisissez la clé.** Par défaut, la clé de Subsidia (chargée depuis la route publique, avec son empreinte affichée). Pour un certificat émis par une installation sur site (application de bureau, Subsidia Box), choisissez « Clé d'une installation sur site » et collez sa clé publique au format PEM.
4. **Cliquez sur Vérifier.**
5. **Lisez le résultat.** Il existe trois issues :

| Résultat affiché | Ce que cela veut dire |
|---|---|
| Certificat authentique | L'empreinte recalculée est identique à celle du certificat, et la signature est valide pour cette clé |
| Certificat modifié | Au moins un caractère du contenu a changé depuis la signature ; le certificat ne prouve plus rien |
| Signature inconnue pour cette clé | Le contenu est intact mais la signature ne correspond pas à la clé choisie : autre émetteur, autre installation, ou certificat forgé |

6. **Facultatif : comparer le texte.** Pour un certificat de réponse, ou de rapport du Vérificateur, un encart permet de coller le texte exact. Son empreinte est calculée dans le navigateur et comparée à celle que le certificat engage. Un espace en trop suffit à obtenir « différent ». Cette comparaison suppose une empreinte SHA-256 simple : si le certificat porte un engagement à clé (champ `commit`), elle ne peut pas aboutir sans la clé de l'espace de travail, et c'est `checkCompletion` avec `commitKey` qui fait alors le rapprochement.
7. **Facultatif : interroger le registre.** Pour un certificat valide avec la clé de Subsidia, un bouton demande au serveur si le certificat est toujours inscrit dans la chaîne et si le maillon précédent est intact. Le serveur reçoit alors le certificat, répond, et ne garde rien.

Pour vous entraîner, la page propose des exemples fictifs signés par une clé de démonstration dont la clé privée a été détruite après signature, et un bouton « Retoucher un chiffre » qui montre la vérification échouer après une modification.

Si votre navigateur ne sait pas vérifier une signature Ed25519, la page demande le même calcul au serveur de Subsidia, qui ne garde rien, et l'indique dans le résultat.

## Méthode 2 : hors ligne, avec le paquet

Le paquet `@protypa/subsidia-verify` ne dépend de rien : ni clé d'API, ni appel à la passerelle une fois les pièces en main.

```ts
import { verifyCertificate, checkCompletion } from '@protypa/subsidia-verify'

const { keys } = await (await fetch('https://api.subsidia.protypa.fr/v1/gateway/keys')).json()

const check = await verifyCertificate(certificate, { keyring: keys })

const bound = await checkCompletion(certificate, {
  model: 'pulse-auto',   // le modèle que vous avez envoyé
  messages,              // les messages que vous avez envoyés
  content: answerText,   // la réponse reçue
})
```

Deux appels, deux questions différentes. `verifyCertificate` dit que le certificat a été émis par cette clé et n'a pas été modifié. `checkCompletion` dit que ce certificat parle **de cette réponse-là**, celle que vous avez sous les yeux. Faites les deux.

### Épingler la clé

Une clé récupérée auprès de la passerelle que vous auditez prouve peu. Relevez une fois son empreinte par un autre canal, et exigez-la :

```ts
const pinned = await publicKeyFingerprint(pem) // à stocker
await verifyCertificate(cert, { publicKey, expectedFingerprint: pinned })
```

Toute autre clé donne alors un résultat invalide.

### Exiger la signature post-quantique

Un certificat v2 porte deux signatures : Ed25519 et ML-DSA-65. L'option `requirePostQuantum: true` refuse tout certificat sans signature ML-DSA-65 valide. C'est la seule règle qui protège d'un attaquant qui aurait cassé Ed25519, car la liste des algorithmes exigés se trouve dans des octets que cette signature Ed25519 couvre. La vérification de ML-DSA-65 demande Node 24 ou plus, ou un environnement WebCrypto qui le gère ; sinon le résultat est « invalide, ce runtime ne sait pas vérifier ML-DSA-65 », jamais un succès silencieux.

### Vérifier une chaîne et une ancre

`verifyChain` contrôle que chaque certificat est authentique et que les voisins sont reliés ; un certificat supprimé apparaît comme un index manquant (`gaps`). Sur un sous-ensemble, des trous sont normaux, car la chaîne est partagée par tous les enregistrements du Gateway : ils n'ont de sens que dans un export qui se prétend exhaustif. `verifyAnchor` contrôle le jeton d'horodatage RFC 3161 et l'appartenance du certificat au lot horodaté ; sans racines d'autorité fournies, le jeton est cohérent mais l'identité de l'autorité reste à votre appréciation. Le principe est détaillé dans [le guide sur l'horodatage](/guides/horodatage-rfc-3161-reponses-ia).

## Méthode 3 : sans écrire de code

- **Ligne de commande** : `node tools/proof/verify.mjs <fichiers> --keyring <fichier|url> --pin-kid <kid>`, avec `--require-post-quantum` si vous voulez la règle stricte. L'outil refuse de tourner sans clé épinglée, sauf demande explicite (`--trust-fetched-keyring`). Codes de sortie : 0 valide, 1 non valide, 2 erreur d'usage ou de confiance.
- **Action GitHub** : `.github/actions/verify-proofs` exécute les mêmes contrôles en intégration continue.
- **Page de vérification autonome** : un fichier HTML unique, sans requête réseau, dans lequel on colle le certificat, le trousseau et les identifiants de clé épinglés. Quand le navigateur ne sait pas vérifier ML-DSA-65, la signature est affichée comme non vérifiée et le verdict comme « partiellement vérifié », jamais comme réussi.

Ces trois sorties affichent un résultat par contrôle (`ok`, `failed`, `not_checked`) et disent ce qu'elles ne prouvent pas.

## Ce que cela ne fait pas

- **La page /certificat ne vérifie que l'empreinte et la signature Ed25519.** Elle ne contrôle ni la signature ML-DSA-65, ni l'ancre d'horodatage. Pour ces deux points, utilisez le paquet, la ligne de commande ou l'action.
- **Un certificat valide ne dit pas que la réponse est vraie.** Il établit l'intégrité, l'origine et l'ordre, pas l'exactitude. Ces outils le rappellent dans leurs résultats.
- **Le contrôle de la chaîne par la page est une question au serveur.** Elle demande à Subsidia, qui voit toute la chaîne, si le certificat y figure. Un contrôle indépendant de la chaîne passe par `verifyChain` sur des certificats que vous détenez.
- **La clé que vous choisissez est la base de la confiance.** Avec la clé de Subsidia, vous faites confiance à l'empreinte publiée. Avec une installation sur site, la clé doit venir d'une source fiable. Épinglez-la.
- **Après une rotation, la clé courante ne suffit plus.** Un certificat signé par une clé retirée se vérifie avec le trousseau complet (`/v1/gateway/keys`), qui conserve les clés publiques retirées. Sur la page /certificat, la clé proposée est la clé courante ; ce cas relève du paquet.
- **Un identifiant seul ne se vérifie pas.** Il faut le fichier du certificat.

## Les erreurs fréquentes

1. **Retoucher le JSON avant de le coller.** Un reformatage qui ne change pas les valeurs est sans effet, mais une valeur modifiée, ne serait-ce que d'un caractère, change l'empreinte.
2. **Utiliser la mauvaise clé.** Un certificat d'une installation sur site se vérifie avec la clé de cette installation.
3. **Confondre signature valide et réponse exacte.** Les deux contrôles sont indépendants.

## Questions fréquentes

### Comment vérifier un certificat Subsidia sans compte ?

Ouvrez la page /certificat, collez le certificat (ou déposez le fichier .json), laissez la clé de Subsidia sélectionnée et cliquez sur Vérifier. Le calcul se fait dans votre navigateur ; le certificat ne quitte pas la page, sauf si vous demandez ensuite au registre s'il y est toujours inscrit.

### Que signifie « Signature inconnue pour cette clé » ?

Le contenu est intact (l'empreinte correspond), mais la signature ne vérifie pas avec la clé choisie. Soit le certificat vient d'une installation sur site qui signe avec sa propre clé, soit il n'a pas été émis par Subsidia, soit il a été signé par une clé retirée depuis. Dans le premier cas, demandez la clé publique de l'installation concernée.

### La page /certificat vérifie-t-elle la signature post-quantique ML-DSA-65 ?

Non. La page contrôle l'empreinte et la signature Ed25519. La signature ML-DSA-65, l'ancre d'horodatage RFC 3161 et la chaîne complète se vérifient avec le paquet @protypa/subsidia-verify, le script en ligne de commande ou l'action GitHub.

### Peut-on vérifier une réponse avec seulement son identifiant ?

Non. Un identifiant ou un lien ne prouve rien et le registre d'un cabinet n'est pas consultable par un tiers. Il faut le certificat lui-même, le fichier .json que Subsidia fait télécharger.

### Comment savoir que la clé publique n'a pas été remplacée ?

Une clé récupérée auprès de la passerelle que l'on audite prouve peu. Relevez une fois son empreinte par un autre canal et épinglez-la : avec le paquet de vérification, toute autre clé donne un résultat invalide.

### Un certificat valide veut-il dire que la réponse est vraie ?

Non. Il établit l'intégrité, l'origine et l'ordre du document, pas l'exactitude de la réponse. Pour confronter un texte à vos pièces, utilisez le Vérificateur.
