Premier enregistrement avec Gireve - Runbook
Cette procedure est a faire une seule fois par environnement Gireve (Beta-Test, Pre-production, Production). Elle couvre deux sujets separes :
- le certificat client Gireve, via CSR ;
- le premier handshake OCPI
credentials, via token temporaire.
Ce que Gireve demande
Gireve a fourni l'identifiant certificat suivant :
d80fed24-2489-4374-a74a-a3f93a47b03d
Cet identifiant doit etre mis dans le champ Common Name du CSR. Il ne va
pas dans le token OCPI et ne doit pas etre confondu avec party_id.
Pendant le handshake OCPI, Gireve doit aussi pouvoir appeler notre endpoint :
POST https://ocpi.chargemsi.com/ocpi/2.2.1/credentials
Authorization: Token <TOKEN_TEMPORAIRE_CHARGEMSI>
Donc ChargeMSI doit generer un token temporaire, le stocker en base comme
credential Inbound, puis fournir ce meme token a Gireve.
Vue d'ensemble des tokens
| Token | Qui le genere | Qui l'utilise dans Authorization |
Stockage ChargeMSI |
|---|---|---|---|
TOKEN_TEMPORAIRE_CHARGEMSI |
ChargeMSI | Gireve, uniquement pour le premier POST /credentials |
ocpi.Credentials, Direction = Inbound, TokenKind = A |
TOKEN_GIREVE |
Gireve, dans le body du POST /credentials |
ChargeMSI, pour appeler Gireve apres le handshake | ocpi.Credentials, Direction = Outbound, TokenKind = C |
TOKEN_CHARGEMSI_FINAL |
ChargeMSI, dans la reponse au POST /credentials |
Gireve, pour appeler ChargeMSI apres le handshake | ocpi.Credentials, Direction = Inbound, TokenKind = C |
Dans le code, la verification du token entrant se fait dans
Chargemsi.OcpiGateway/Security/OcpiTokenAuthenticationHandler.cs :
le handler lit Authorization: Token ..., cherche exactement cette valeur dans
ocpi.Credentials, avec Direction = Inbound et IsActive = 1, puis attache
le partenaire Gireve au ClaimsPrincipal.
Parcours dans l'application ChargeMSI
Depuis la version 1.2 du Management Portal, le parcours est intégralement
piloté depuis le back-office : écran OCPI > Registry Gireve
(/OcpiRegistry). Il automatise les étapes 2 et 3 (génération du token
temporaire et insertion en base) et expose l'état du handshake en temps réel.
Parcours recommandé (UI) :
- appliquer les migrations OCPI ;
- demarrer
Chargemsi.OcpiGatewayavecOcpi__PublicBaseUrletOcpi__Gireve__VersionsUrl; - se connecter au Management Portal en
Admin, ouvrir **OCPI AdministrationRegistry Gireve** ;
- saisir l'identité partenaire (par défaut FR/GIR, HUB, URL Gireve sandbox) et cliquer sur Générer le token temporaire ;
- copier la valeur affichée (elle ne sera plus affichée ensuite), cocher la case de confirmation et cliquer sur Enregistrer le TOKEN_A en base ;
- transmettre à Gireve l'URL credentials et le token temporaire ;
- surveiller la check-list à l'écran : dès que Gireve appelle
POST /credentials, les indicateurs TOKEN_C Inbound et TOKEN_C Outbound passent à Actif ; - cliquer sur Probe maintenant pour vérifier qu'on peut effectivement
appeler
GET {VersionsUrl}chez Gireve avec le token sortant.
Le parcours SQL ci-dessous reste valable pour les déploiements automatisés (CI / Ansible) ou pour reproduire le handshake en environnement sans portail.
Etape 1 - Generer le CSR Gireve
Le document Gireve demande une cle privee et un CSR par environnement. La cle
privee reste chez ChargeMSI. Seul le fichier .csr est envoye a Gireve.
Linux / macOS / conteneur Linux
mkdir -p partner_key
openssl genrsa -out partner_key/chargemsi.gireve.com.key 2048
openssl req -new \
-key partner_key/chargemsi.gireve.com.key \
-out partner_key/chargemsi.gireve.com.csr \
-subj "/C=FR/ST=France/L=Paris/O=ChargeMSI/OU=ChargeMSI/CN=d80fed24-2489-4374-a74a-a3f93a47b03d/emailAddress=contact@chargemsi.com"
Windows PowerShell
mkdir partner_key
$env:OPENSSL_CONF = "C:\OpenSSL\bin\openssl.cfg"
$env:RANDFILE = ".rnd"
C:\OpenSSL\bin\openssl genrsa `
-out partner_key\chargemsi.gireve.com.key 2048
C:\OpenSSL\bin\openssl req -new `
-key partner_key\chargemsi.gireve.com.key `
-out partner_key\chargemsi.gireve.com.csr `
-subj "/C=FR/ST=France/L=Paris/O=ChargeMSI/OU=ChargeMSI/CN=d80fed24-2489-4374-a74a-a3f93a47b03d/emailAddress=contact@chargemsi.com"
Si OpenSSL demande les champs en interactif, repondre ainsi :
| Champ CSR | Valeur |
|---|---|
| Country Name | FR |
| State or Province | France |
| Locality | Paris |
| Organization | ChargeMSI |
| Organizational Unit | ChargeMSI |
| Common Name | d80fed24-2489-4374-a74a-a3f93a47b03d |
| Email Address | contact@chargemsi.com |
| Challenge password | vide |
| Optional company name | vide |
A envoyer a Gireve : partner_key/chargemsi.gireve.com.csr.
A garder secret : partner_key/chargemsi.gireve.com.key.
Etape 1 bis - Reception du certificat signe par Gireve
Apres traitement du CSR, l'equipe CTM Gireve renvoie par mail :
- le fichier
chargemsi.gireve.com.crt(certificat client signe par leur CA) ; - la liste des IPs sortantes Gireve a whitelister sur notre pare-feu ;
- l'URL
versionsde l'environnement cible (Beta-Test / Pre-Production / Production).
Reponse recue le 2026-05-19 (environnement Pre-Production) :
- IPs Gireve a autoriser en entree (Gireve appelle ChargeMSI) et en sortie (ChargeMSI appelle Gireve) :
34.76.32.171,35.240.33.86,195.21.21.104- URL versions Pre-Production :
https://ocpi-pp-iop.gireve.com/ocpi/versions
Installation du certificat .crt
Sauvegarder le fichier recu a cote de la cle privee genere a l'etape 1 :
partner_key/
chargemsi.gireve.com.key <-- prive, jamais commit
chargemsi.gireve.com.csr <-- envoye a Gireve
chargemsi.gireve.com.crt <-- recu de Gireve, a deployer
Verifier que le couple .key / .crt matche bien :
openssl x509 -in partner_key/chargemsi.gireve.com.crt -noout -modulus | openssl md5
openssl rsa -in partner_key/chargemsi.gireve.com.key -noout -modulus | openssl md5
# Les deux md5 doivent etre identiques.
openssl x509 -in partner_key/chargemsi.gireve.com.crt -noout -subject -issuer -dates
# Verifier :
# subject : CN = d80fed24-2489-4374-a74a-a3f93a47b03d
# issuer : O = Gireve, ...
# notAfter : > date du jour
Construire le PKCS#12 que la gateway charge au demarrage pour l'authentification
mTLS sortante vers Gireve (extension .p12, identique a .pfx) :
openssl pkcs12 -export \
-inkey partner_key/chargemsi.gireve.com.key \
-in partner_key/chargemsi.gireve.com.crt \
-out partner_key/chargemsi.gireve.com.p12 \
-name "ChargeMSI Gireve client" \
-passout pass:$GIREVE_CERT_PASSWORD
Deploiement selon l'environnement :
- Docker / Compose : poser le
.p12dans/etc/chargemsi/gireve/partner_key/preprod/sur l'hote (mode600, owner = compte de service), puis monter ce dossier en lecture seule via la variableGIREVE_CERT_DIRdu compose. Le.p12est alors lu par le gateway sous/certs/gireve/chargemsi.gireve.com.p12. - Windows service / IIS : importer le
.p12dans le storeLocalMachine\Myet donner la lecture de la cle privee au compte de service. - Kubernetes : creer un secret
chargemsi-gireve-certcontenant le.p12- password, monter sous
/var/run/secrets/gireve/.
- password, monter sous
Variables d'environnement attendues par le gateway et le worker (les deux appellent Gireve en sortie) :
Ocpi__Gireve__ClientCertificatePath = /certs/gireve/chargemsi.gireve.com.p12
Ocpi__Gireve__ClientCertificatePassword = <GIREVE_PFX_PASSWORD>
Code (Chargemsi.Core) : ces deux cles sont lues par
OcpiOptions.GireveOptions.ClientCertificatePath/ClientCertificatePassword. LeHttpClienttypedIGireveClientest configure viaConfigurePrimaryHttpMessageHandlerdansOcpiCoreServiceCollectionExtensions.cs: siPathest defini, le.p12est charge avecX509KeyStorageFlags.EphemeralKeySet(la cle privee ne touche jamais le store machine, ce qui est ce qu'on veut dans un conteneur). Au demarrage, le subject et la date d'expiration du certificat sont logges.
Ne JAMAIS commit
.key,.crt,.p12ou.pfx. Le repo doit ignorerpartner_key/(.gitignoredeja en place).
Whitelist reseau Gireve
Sur le pare-feu / WAF / Cloud Armor / Security Group, autoriser :
| Sens | IPs sources | Port destination | Justification |
|---|---|---|---|
Inbound (vers ocpi.chargemsi.com / ocpi-preprod.chargemsi.com) |
34.76.32.171, 35.240.33.86, 195.21.21.104 |
TCP/443 | Gireve appelle nos endpoints OCPI (/ocpi/versions, /credentials, push tokens / commands). |
| Outbound (depuis le gateway + worker) | -> 34.76.32.171, 35.240.33.86, 195.21.21.104 |
TCP/443 | ChargeMSI appelle Gireve (/versions, push locations / sessions / cdrs). |
Si la sortie est filtree par DNS plutot que par IP, autoriser le FQDN
ocpi-pp-iop.gireve.com (PP-IOP) ou ocpi.gireve.com (Production).
Smoke-test apres ouverture (mTLS + token + headers OCPI) :
# 1) mTLS uniquement (sans token, juste pour valider que la chaine
# certificat / IP whitelist / TLS fonctionne) :
curl -v --cert partner_key/chargemsi.gireve.com.crt \
--key partner_key/chargemsi.gireve.com.key \
https://ocpi-pp-iop.gireve.com/ocpi/versions
# Attendu : HTTP 200 si IP whitelistee et cert accepte ; 403 sinon.
# 2) Appel complet mimant exactement ce que le gateway envoie :
TOKEN_OUT="$(echo -n '<TOKEN_GIREVE recu apres handshake>')"
REQ_ID="$(uuidgen)"
curl -v --cert partner_key/chargemsi.gireve.com.crt \
--key partner_key/chargemsi.gireve.com.key \
-H "Authorization: Token $TOKEN_OUT" \
-H "OCPI-from-country-code: FR" \
-H "OCPI-from-party-id: MSI" \
-H "OCPI-to-country-code: FR" \
-H "OCPI-to-party-id: GIR" \
-H "X-Request-ID: $REQ_ID" \
-H "X-Correlation-ID: $REQ_ID" \
https://ocpi-pp-iop.gireve.com/ocpi/2.2.1/credentials
# Attendu : enveloppe OCPI status_code=1000 avec les credentials Gireve actuels.
Etape 2 - Generer le token temporaire pour Gireve
Le token OCPI doit etre une valeur aleatoire et base64. Exemple PowerShell :
$bytes = New-Object byte[] 32
[System.Security.Cryptography.RandomNumberGenerator]::Fill($bytes)
$tokenTemporaire = [Convert]::ToBase64String($bytes)
$tokenTemporaire
Exemple Linux :
openssl rand -base64 32
Copier cette valeur et la transmettre a Gireve avec notre URL credentials :
URL versions : https://ocpi-preprod.chargemsi.com/ocpi/versions
URL credentials : https://ocpi-preprod.chargemsi.com/ocpi/2.2.1/credentials
Authorization : Token <TOKEN_TEMPORAIRE_CHARGEMSI>
Role : CPO
Country code : FR
Party ID : MSI
Etape 3 - Enregistrer le token temporaire cote ChargeMSI
Important : une variable d'environnement seule ne suffit pas pour le premier
appel entrant de Gireve. Le code valide les appels entrants contre la table
ocpi.Credentials. Il faut donc creer le partenaire Gireve et le credential
temporaire en base.
SQL Server
Remplacer <TOKEN_TEMPORAIRE_CHARGEMSI> et verifier VersionsUrl selon
l'environnement Gireve.
DECLARE @TenantID nvarchar(128) = 'default';
DECLARE @PartnerId uniqueidentifier;
DECLARE @Token nvarchar(256) = '<TOKEN_TEMPORAIRE_CHARGEMSI>';
SELECT @PartnerId = Id
FROM ocpi.Partners
WHERE TenantID = @TenantID AND CountryCode = 'FR' AND PartyId = 'GIR';
IF @PartnerId IS NULL
BEGIN
SET @PartnerId = NEWID();
INSERT INTO ocpi.Partners
(Id, TenantID, CreatedDate, PartyId, CountryCode, Role, Environment,
DisplayName, VersionsUrl, CurrentVersion, IsActive, UpdatedUtc)
VALUES
(@PartnerId, @TenantID, SYSUTCDATETIME(), 'GIR', 'FR', 'HUB', 0,
'Gireve', 'https://ocpi-pp-iop.gireve.com/ocpi/versions', '2.2.1',
1, SYSUTCDATETIME());
END;
UPDATE ocpi.Credentials
SET IsActive = 0, RevokedDate = SYSUTCDATETIME()
WHERE PartnerId = @PartnerId
AND Direction = 0
AND TokenKind = 'A'
AND IsActive = 1;
INSERT INTO ocpi.Credentials
(Id, PartnerId, TokenKind, Direction, TokenValue, IsActive, CreatedDate, TenantID)
VALUES
(NEWID(), @PartnerId, 'A', 0, @Token, 1, SYSUTCDATETIME(), @TenantID);
Direction = 0 signifie Inbound dans le code
(CredentialDirection.Inbound). C'est bien le sens voulu : Gireve appelle
ChargeMSI avec ce token.
Verification SQL
SELECT p.DisplayName, c.TokenKind, c.Direction, c.IsActive, c.CreatedDate
FROM ocpi.Credentials c
JOIN ocpi.Partners p ON p.Id = c.PartnerId
WHERE p.PartyId = 'GIR'
ORDER BY c.CreatedDate DESC;
Avant le handshake, on attend au moins :
TokenKind = A, Direction = 0, IsActive = 1
Etape 4 - Stocker les parametres selon l'environnement
Ces valeurs configurent l'application, mais le token temporaire entrant doit quand meme etre en base comme indique a l'etape 3.
Developpement local Windows
cd D:\AWorkspace\OCPPCLAUDE\Chargemsi.OcpiGateway
dotnet user-secrets set "Ocpi:PublicBaseUrl" "https://ocpi.chargemsi.com"
dotnet user-secrets set "Ocpi:Gireve:VersionsUrl" "https://ocpi-pp-iop.gireve.com/ocpi/versions"
Windows service / IIS
[Environment]::SetEnvironmentVariable("Ocpi__PublicBaseUrl", "https://ocpi.chargemsi.com", "Machine")
[Environment]::SetEnvironmentVariable("Ocpi__Gireve__VersionsUrl", "https://ocpi-pp-iop.gireve.com/ocpi/versions", "Machine")
Redemarrer le service ou le pool IIS apres modification.
Docker Linux
docker run \
-e ASPNETCORE_ENVIRONMENT=Production \
-e ConnectionStrings__OcppSqlServerProd="Server=...;Database=...;User ID=...;Password=...;TrustServerCertificate=true;" \
-e Ocpi__PublicBaseUrl="https://ocpi.chargemsi.com" \
-e Ocpi__Gireve__VersionsUrl="https://ocpi-pp-iop.gireve.com/ocpi/versions" \
chargemsi/ocpigateway:latest
Docker Compose
services:
ocpigateway:
image: chargemsi/ocpigateway:latest
environment:
ASPNETCORE_ENVIRONMENT: Production
ConnectionStrings__OcppSqlServerProd: "Server=...;Database=...;User ID=...;Password=...;TrustServerCertificate=true;"
Ocpi__PublicBaseUrl: "https://ocpi.chargemsi.com"
Ocpi__Gireve__VersionsUrl: "https://ocpi-pp-iop.gireve.com/ocpi/versions"
Kubernetes
env:
- name: Ocpi__PublicBaseUrl
value: "https://ocpi.chargemsi.com"
- name: Ocpi__Gireve__VersionsUrl
value: "https://ocpi-pp-iop.gireve.com/ocpi/versions"
Etape 5 - Lancer / accepter le handshake
Dans le scenario demande par Gireve, c'est Gireve qui lance le premier appel vers ChargeMSI. Notre application doit donc simplement etre demarree et joignable.
ChargeMSI expose publiquement :
GET https://ocpi.chargemsi.com/ocpi/versions GET https://ocpi.chargemsi.com/ocpi/2.2.1 POST https://ocpi.chargemsi.com/ocpi/2.2.1/credentialsGireve appelle :
POST /ocpi/2.2.1/credentials Authorization: Token <TOKEN_TEMPORAIRE_CHARGEMSI> Content-Type: application/json { "token": "<TOKEN_GIREVE>", "url": "https://...gireve.../ocpi/versions", "roles": [ { "role": "HUB", "party_id": "GIR", "country_code": "FR", "business_details": { "name": "Gireve" } } ] }OcpiTokenAuthenticationHandlerverifie que<TOKEN_TEMPORAIRE_CHARGEMSI>existe en base avecDirection = Inbound.CredentialsService.RegisterAsync()desactive le token temporaire, stocke<TOKEN_GIREVE>enOutbound, genereTOKEN_CHARGEMSI_FINALenInbound, et repond a Gireve avec nos credentials :{ "data": { "token": "TOKEN_CHARGEMSI_FINAL", "url": "https://ocpi-preprod.chargemsi.com/ocpi/versions", "roles": [ { "role": "CPO", "party_id": "MSI", "country_code": "FR", "business_details": { "name": "ChargeMSI" } } ] }, "status_code": 1000 }
Apres cette etape, Gireve ne doit plus utiliser le token temporaire. Il doit
utiliser TOKEN_CHARGEMSI_FINAL.
Etape 6 - Verifier apres le handshake
SELECT TokenKind, Direction, IsActive, CreatedDate, RevokedDate
FROM ocpi.Credentials
WHERE PartnerId = (
SELECT TOP 1 Id FROM ocpi.Partners
WHERE TenantID = 'default' AND CountryCode = 'FR' AND PartyId = 'GIR'
)
ORDER BY CreatedDate DESC;
Attendu :
TokenKind = C, Direction = 0, IsActive = 1 -- token final que Gireve utilise pour nous appeler
TokenKind = C, Direction = 1, IsActive = 1 -- token Gireve que nous utilisons pour appeler Gireve
TokenKind = A, Direction = 0, IsActive = 0 -- token temporaire revoque
Smoke tests :
Invoke-RestMethod https://ocpi-preprod.chargemsi.com/ocpi/versions
Invoke-RestMethod https://ocpi-preprod.chargemsi.com/ocpi/2.2.1
Invoke-RestMethod https://ocpi-preprod.chargemsi.com/ocpi/cpo/2.2.1/locations `
-Headers @{ Authorization = "Token <TOKEN_CHARGEMSI_FINAL>" }
Si Gireve recoit un 401, verifier :
- le header est exactement
Authorization: Token <valeur>; - la valeur n'a pas d'espace ou de retour ligne copie en trop ;
ocpi.Credentials.Direction = 0;ocpi.Credentials.IsActive = 1;- le gateway pointe vers la bonne base SQL.