13 KiB
PKI API
API Go complète pour gérer une Infrastructure à Clé Publique (PKI) avec hiérarchie de certificats et persistance MongoDB.
Caractéristiques
- ✅ Authentification JWT : Tous les endpoints protégés par JWT
- ✅ Hiérarchie CA : Support des Root CA, Intermediate CA (Sub-CA) et certificats finaux
- ✅ Signature de certificats : Certificats auto-signés ou signés par une CA
- ✅ Gestion des révocations : Révocation et CRL (Certificate Revocation List)
- ✅ Stockage pluggable : MemoryStore (développement) ou MongoDB (production)
- ✅ Cryptographie : X.509, RSA 2048-bit, signatures HS256 pour JWT
Architecture
pkiapi/
├── cmd/main.go # Point d'entrée avec config
├── internal/
│ ├── api/
│ │ ├── router.go # Routage Gin
│ │ ├── auth.go # Login endpoint
│ │ ├── ca.go # Handlers CAs
│ │ └── certificates.go # Handlers certificats
│ ├── auth/
│ │ ├── jwt.go # JWT manager
│ │ └── middleware.go # Middleware d'authentification
│ ├── config/
│ │ └── config.go # Configuration centralisée
│ ├── pki/
│ │ ├── certificate.go # Logique certificats X.509
│ │ └── errors.go # Erreurs PKI
│ └── storage/
│ ├── interface.go # Interface CertificateStore
│ ├── store.go # MemoryStore (en mémoire)
│ ├── mongo.go # MongoStore (persistance)
│ ├── util.go # Helpers sérialisation
│ └── errors.go # Erreurs storage
└── go.mod
Démarrage rapide
1. Installer et compiler
go mod download
go build -o pkiapi ./cmd/main.go
2. Démarrer le serveur
Mode développement (MemoryStore):
export STORAGE_TYPE=memory
export PORT=8080
./pkiapi
# Serveur lancé sur http://localhost:8080
Mode production (MongoDB):
export STORAGE_TYPE=mongodb
export MONGO_URI=mongodb://mongodb-server:27017
export MONGO_DB=pkiapi-prod
export JWT_SECRET_KEY=super-secret-key
./pkiapi
3. Obtenir un token JWT
curl -X POST http://localhost:8080/api/v1/login \
-H "Content-Type: application/json" \
-d '{"username":"admin","password":"admin"}'
# Réponse:
# {
# "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
# "expires_in": 86400
# }
API Endpoints
🔐 Authentification (Public)
POST /api/v1/login
Obtient un token JWT pour accéder aux autres endpoints.
Requête :
curl -X POST http://localhost:8080/api/v1/login \
-H "Content-Type: application/json" \
-d '{
"username": "admin",
"password": "admin"
}'
Réponse :
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"expires_in": 86400
}
🔑 Autorités de Certification (Authentifiés)
GET /api/v1/ca
Liste toutes les autorités de certification.
Requête :
TOKEN="<your_token>"
curl -H "Authorization: Bearer $TOKEN" \
http://localhost:8080/api/v1/ca
Réponse :
{
"cas": [
{
"id": "16de28da-f25e-49cd-81de-a929d34dfe08",
"subject": "CN=Root CA,O=Example,C=FR",
"issuer": "CN=Root CA,O=Example,C=FR",
"not_before": "2025-12-06T22:52:48Z",
"not_after": "2035-12-04T22:52:48Z",
"serial_number": "574847517"
}
]
}
POST /api/v1/ca
Crée une nouvelle autorité de certification auto-signée.
Requête :
TOKEN="<your_token>"
curl -X POST http://localhost:8080/api/v1/ca \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"subject": "CN=Root CA,O=Example Inc,C=FR",
"validity_days": 3650
}'
Réponse :
{
"ca": {
"id": "ff3ac5c5-08d1-401b-9e83-f18eda4c538b",
"subject": "CN=Root CA,O=Example Inc,C=FR",
"not_before": "2025-12-06T21:45:01Z",
"not_after": "2035-12-04T21:45:01Z",
"serial_number": "546965196",
"certificate": "MIIC5zCCAc+gAwIBAgIDCkUz...",
"is_ca": true
},
"created_by": "admin"
}
GET /api/v1/ca/:id
Récupère une autorité de certification par ID.
Requête :
TOKEN="<your_token>"
CA_ID="ff3ac5c5-08d1-401b-9e83-f18eda4c538b"
curl -H "Authorization: Bearer $TOKEN" \
http://localhost:8080/api/v1/ca/$CA_ID
Réponse :
{
"ca": {
"id": "ff3ac5c5-08d1-401b-9e83-f18eda4c538b",
"subject": "CN=Root CA,O=Example Inc,C=FR",
"not_before": "2025-12-06T21:45:01Z",
"not_after": "2035-12-04T21:45:01Z",
"serial_number": "546965196",
"certificate": "MIIC5zCCAc+gAwIBAgIDCkUz...",
"is_ca": true
}
}
POST /api/v1/ca/sign
Crée une CA intermédiaire (Sub-CA) signée par une CA parent.
Requête :
TOKEN="<your_token>"
PARENT_CA_ID="ff3ac5c5-08d1-401b-9e83-f18eda4c538b"
curl -X POST http://localhost:8080/api/v1/ca/sign \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d "{
\"parent_ca_id\": \"$PARENT_CA_ID\",
\"subject\": \"CN=Intermediate CA,O=Example Inc,C=FR\",
\"validity_days\": 1825
}"
Réponse :
{
"ca": {
"id": "b2350d39-53c2-469a-802c-acc39707e352",
"subject": "CN=Intermediate CA,O=Example Inc,C=FR",
"not_before": "2025-12-06T21:45:09Z",
"not_after": "2030-12-05T21:45:09Z",
"serial_number": "576310632",
"certificate": "MIIDOTCCAiGgAwIBAgIEIlnNaD...",
"is_ca": true
},
"created_by": "admin",
"signed_by": "ff3ac5c5-08d1-401b-9e83-f18eda4c538b"
}
📜 Certificats (Authentifiés)
GET /api/v1/certificates
Liste tous les certificats.
Requête :
TOKEN="<your_token>"
curl -H "Authorization: Bearer $TOKEN" \
http://localhost:8080/api/v1/certificates
Réponse :
{
"certificates": [
{
"id": "e12e08a9-adeb-404c-a7b7-a613b77dfe66",
"subject": "CN=server.example.com,O=Example Inc,C=FR",
"issuer": "CN=Intermediate CA,O=Example Inc,C=FR",
"not_before": "2025-12-06T21:45:09Z",
"not_after": "2026-12-06T21:45:09Z",
"serial_number": "46798982",
"revoked": false
}
]
}
POST /api/v1/certificates
Crée un certificat auto-signé.
Requête :
TOKEN="<your_token>"
curl -X POST http://localhost:8080/api/v1/certificates \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"subject": "CN=example.com,O=Example Inc,C=FR",
"validity_days": 365
}'
Réponse :
{
"certificate": {
"id": "8e77050f-d19f-4607-8c49-b974c5f9cb08",
"subject": "CN=example.com,O=Example Inc,C=FR",
"issuer": "CN=example.com,O=Example Inc,C=FR",
"not_before": "2025-12-06T21:41:38Z",
"not_after": "2026-12-06T21:41:38Z",
"serial_number": "673075",
"certificate": "MIIC5zCCAc+gAwIBAgIDCkUzMA0GCSq...",
"revoked": false
},
"created_by": "admin"
}
POST /api/v1/certificates/sign
Signe un certificat avec une CA.
Requête :
TOKEN="<your_token>"
CA_ID="b2350d39-53c2-469a-802c-acc39707e352"
curl -X POST http://localhost:8080/api/v1/certificates/sign \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d "{
\"ca_id\": \"$CA_ID\",
\"subject\": \"CN=server.example.com,O=Example Inc,C=FR\",
\"validity_days\": 365
}"
Réponse :
{
"certificate": {
"id": "e12e08a9-adeb-404c-a7b7-a613b77dfe66",
"subject": "CN=server.example.com,O=Example Inc,C=FR",
"issuer": "CN=Intermediate CA,O=Example Inc,C=FR",
"not_before": "2025-12-06T21:45:09Z",
"not_after": "2026-12-06T21:45:09Z",
"serial_number": "46798982",
"certificate": "MIIDFDCCAfygAwIBAgIEAsoYhjANBg...",
"revoked": false
},
"created_by": "admin",
"signed_by": "b2350d39-53c2-469a-802c-acc39707e352"
}
GET /api/v1/certificates/:id
Récupère un certificat par ID.
Requête :
TOKEN="<your_token>"
CERT_ID="e12e08a9-adeb-404c-a7b7-a613b77dfe66"
curl -H "Authorization: Bearer $TOKEN" \
http://localhost:8080/api/v1/certificates/$CERT_ID
Réponse :
{
"certificate": {
"id": "e12e08a9-adeb-404c-a7b7-a613b77dfe66",
"subject": "CN=server.example.com,O=Example Inc,C=FR",
"issuer": "CN=Intermediate CA,O=Example Inc,C=FR",
"not_before": "2025-12-06T21:45:09Z",
"not_after": "2026-12-06T21:45:09Z",
"serial_number": "46798982",
"certificate": "MIIDFDCCAfygAwIBAgIEAsoYhjANBg...",
"revoked": false
}
}
POST /api/v1/revoke
Révoque un certificat.
Requête :
TOKEN="<your_token>"
CERT_ID="e12e08a9-adeb-404c-a7b7-a613b77dfe66"
curl -X POST http://localhost:8080/api/v1/revoke \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d "{
\"certificate_id\": \"$CERT_ID\",
\"reason\": \"Compromised key\"
}"
Réponse :
{
"message": "certificat révoqué",
"id": "e12e08a9-adeb-404c-a7b7-a613b77dfe66",
"reason": "Compromised key"
}
GET /api/v1/crl
Récupère la Certificate Revocation List (liste des certificats révoqués).
Requête :
TOKEN="<your_token>"
curl -H "Authorization: Bearer $TOKEN" \
http://localhost:8080/api/v1/crl
Réponse :
{
"crl": [
{
"serial_number": "46798982",
"subject": "CN=server.example.com,O=Example Inc,C=FR"
}
],
"version": 1
}
Variables d'Environnement
JWT_SECRET_KEY: Secret pour signer les tokens JWT (défaut:your-secret-key-change-in-prod)
export JWT_SECRET_KEY="your-secure-secret-key"
./pkiapi
Exemple de flux complet
# 1. Obtenir un token
TOKEN=$(curl -s -X POST http://localhost:8080/api/v1/login \
-H "Content-Type: application/json" \
-d '{"username":"admin","password":"admin"}' | jq -r '.token')
echo "Token: $TOKEN"
# 2. Créer une Root CA
ROOT_CA=$(curl -s -X POST http://localhost:8080/api/v1/ca \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"subject":"CN=Root CA,O=Example,C=FR","validity_days":3650}')
ROOT_CA_ID=$(echo $ROOT_CA | jq -r '.ca.id')
echo "Root CA ID: $ROOT_CA_ID"
# 3. Créer une Sub-CA
SUB_CA=$(curl -s -X POST http://localhost:8080/api/v1/ca/sign \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d "{\"parent_ca_id\":\"$ROOT_CA_ID\",\"subject\":\"CN=Intermediate CA,O=Example,C=FR\",\"validity_days\":1825}")
SUB_CA_ID=$(echo $SUB_CA | jq -r '.ca.id')
echo "Sub-CA ID: $SUB_CA_ID"
# 4. Signer un certificat avec la Sub-CA
CERT=$(curl -s -X POST http://localhost:8080/api/v1/certificates/sign \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d "{\"ca_id\":\"$SUB_CA_ID\",\"subject\":\"CN=app.example.com,O=Example,C=FR\",\"validity_days\":365}")
CERT_ID=$(echo $CERT | jq -r '.certificate.id')
echo "Certificate ID: $CERT_ID"
# 5. Lister toutes les CAs
curl -s -H "Authorization: Bearer $TOKEN" http://localhost:8080/api/v1/ca | jq .
# 6. Lister tous les certificats
curl -s -H "Authorization: Bearer $TOKEN" http://localhost:8080/api/v1/certificates | jq .
# 7. Révoquer le certificat
curl -s -X POST http://localhost:8080/api/v1/revoke \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d "{\"certificate_id\":\"$CERT_ID\",\"reason\":\"Test\"}" | jq .
# 8. Voir la CRL
curl -s -H "Authorization: Bearer $TOKEN" http://localhost:8080/api/v1/crl | jq .
Structure du projet
pkiapi/
├── cmd/main.go # Point d'entrée
├── internal/
│ ├── api/
│ │ ├── router.go # Routes Gin
│ │ ├── auth.go # Login
│ │ ├── ca.go # Handlers CA
│ │ └── certificates.go # Handlers certificats
│ ├── auth/
│ │ ├── jwt.go # JWT manager
│ │ └── middleware.go # Middleware JWT
│ ├── pki/
│ │ ├── certificate.go # Logique X.509
│ │ └── errors.go # Erreurs PKI
│ └── storage/
│ ├── store.go # Store thread-safe
│ └── errors.go # Erreurs storage
├── go.mod
├── go.sum
├── Makefile
├── README.md
└── .gitignore
Conventions de code
- Gestion des erreurs : Propagation simple sans wrapper
- Concurrence :
sync.RWMutexpour le store - Cryptographie : Stdlib Go (crypto/x509, crypto/rsa, crypto/rand)
- JWT : github.com/golang-jwt/jwt/v5
Future améliorations
- Persistance en base de données (PostgreSQL)
- Support OCSP (Online Certificate Status Protocol)
- Interface web pour gérer les CAs
- Export des certificats (PEM, DER)
- Support des chaînes intermédiaires
- Auditing et logging
- Rate limiting et throttling
Licence
MIT