Fonctionnement général
L'authentification sur l'API EXPLORE fonctionne en temps normal selon un principe de clé d'authentification (CleClient) et de contrôle IP. Lorsque ce dernier n'est pas possible, un système de chiffrement est mis en place. Tous les webservices génériques d'EXPLORE disposent d'un équivalent dit "signé" permettant ces appels.
Seuls deux paramètres d'entrée sont nécessaires :
- Votre clé client (CleClient)
- La chaîne de paramètres cryptée (Signature)
Le paramètre « Signature » est crypté selon une méthode de chiffrement basée sur un algorithme fiable ; AES (Advanced Encryption Standard). Pour plus d'informations sur AES, vous pouvez vous référer à la page suivante.
Votre organisation ainsi qu'EXPLORE disposent ainsi d'une clé secrète permettant l'échange confidentiel des données en cryptant et décryptant cette chaîne de paramètres. Toute personne ne possédant pas cette clé sera incapable de révéler le contenu de la signature.
L'URL obtenue par le biais du chiffrement, pour des raisons de sécurité, n'est valable que le jour même de sa génération. Il convient ainsi de rajouter la date du jour à la clé secrète comme dans l'exemple ci-dessous (en C#) :
string cleCrypteeClient = DateTime.Today.ToString("yyyyMMdd") + CleSecrete;
Le contenu du paramètre « Signature » correspond à la chaîne de paramètre « normale » du web service. Ainsi, pour le web service Amenagements de la rubrique générique Opportunites, une chaîne à crypter a pour format « CleClient=[Votre clé client]&DateDebut=[yyyy-MM-dd]&DateFin=[yyyy-MM-dd]&MAJ=[false / true] ».
Fonctions de cryptage
La chaîne « Signature » peut être cryptée grâce aux fonctions suivantes :
C# .NET 6
public static string Encrypt(string toEncrypt, string cKey)
{
byte[] plainText = Encoding.UTF8.GetBytes(toEncrypt);
Rfc2898DeriveBytes rfcDb = new(cKey, Encoding.UTF8.GetBytes(cKey));
byte[] key = rfcDb.GetBytes(16);
byte[] iv = rfcDb.GetBytes(16);
Aes? aes = Aes.Create("AesManaged");
if (aes == null)
throw new NotImplementedException();
ICryptoTransform encryptor = aes.CreateEncryptor(key, iv);
byte[]? cipherBytes = null;
using (MemoryStream ms = new())
{
using CryptoStream cs = new(ms, encryptor, CryptoStreamMode.Write);
cs.Write(plainText, 0, plainText.Length);
cs.FlushFinalBlock();
cipherBytes = ms.ToArray();
}
return Convert.ToBase64String(cipherBytes).Replace("+", "-").Replace("/", "_");
}
C# .NET Framework 4.x
public static string Encrypt(string toEncrypt, string ckey)
{
byte[] plainText = Encoding.UTF8.GetBytes(toEncrypt);
Rfc2898DeriveBytes rfcDb = new Rfc2898DeriveBytes(ckey, Encoding.UTF8.GetBytes(ckey));
byte[] key = rfcDb.GetBytes(16);
byte[] iv = rfcDb.GetBytes(16);
RijndaelManaged rijndael = new RijndaelManaged
{
Mode = CipherMode.CBC;
};
ICryptoTransform encryptor = rijndael.CreateEncryptor(key, iv);
byte[] cipherBytes = null;
using (MemoryStream ms = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write));
{
cs.Write(plainText, 0, plainText.Length);
cs.FlushFinalBlock();
cipherBytes = ms.ToArray();
}
}
return Convert.ToBase64String(cipherBytes).Replace("+", "-").Replace("/", "_");
}
C# cas particulier : CRM SalesForce
public static string Encrypt(string toEncrypt, string cKey)
{
byte[] encrypted = null;
using (AesCryptoServiceProvider aes = new AesCryptoServiceProvider())
{
char[] keyChars = cKey.ToCharArray();
byte[] key = new byte[16];
byte[] iv = new byte[16];
for (int i = 0; i < 16; i++)
key[i] = (byte)key[i];
for (int i = 16; i < 32; i++)
iv[i - 16] = (byte)key[i];
aes.Key = key;
aes.IV = iv;
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.PKCS7;
ICryptoTransform enc = aes.CreateEncryptor(aes.Key, aes.IV);
using (MemoryStream ms = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream(ms, enc, CryptoStreamMode.Write))
{
using (StreamWriter sw = new StreamWriter(cs))
{
sw.Write(toEncrypt);
}
encrypted = ms.ToArray();
}
}
}
return Convert.ToBase64String(encrpted).Replace("+", "-").Replace("/", "_");
}
Java
public static String encrypt(String toEncrypt, String cKey) throws Exception
{
try
{
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
PBEKeySpec pbeKeySpec = new PBEKeySpec(cKey.toCharArray(), Arrays.copyOf(cKey.getBytes("UTF-8"), cKey.getBytes("UTF-8").length), 1000, 384);
byte[] secretKeyBytes = factory.generateSecret(pbeKeySpec).getEncoded();
IvParameterSpec ivSpec = new IvParameterSpec(Arrays.copyOfRange(secretKeyBytes, 16, 32));
SecretKeySpec secretKeySpec = new SecretKeySpec(Arrays.copyOfRange(secretKeyBytes, 0, 16), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivSpec);
byte[] encrypted = cipher.doFinal(toEncrypt.getBytes("UTF-8"));
return Base64.getEncoder().encodeToString(encrypted).replace("+", "-").replace("/", "_");
}
catch (Exception ex)
{
throw ex;
}
}
PHP (version < 5.5.0)
<?php
function Encrypt($toEncrypt, $cKey)
{
require 'PBKDF2.class.php';
$pbkdf = new PBKDF2();
$rfcdb = $pbkdf->hash("sha1", $cKey, $cKey, 1000, 32, true);
$key = substr($rfcdb, 0, 16);
$iv = substr($rfcdb, 16, 16);
$cipherTextRaw = openssl_encrypt($toEncrypt, "AES-128-CBC", $key, 1, $iv);
$cipherTextBase64 = str_replace(array("+", "/"), array("-", "_"), base64_encode($cipherTextRaw));
return $cipherTextBase64;
}
?>
PHP (version ≥ 5.5.0)
<?php
function Encrypt($toEncrypt, $cKey)
{
$rfcdb = hash_pbkdf2("sha1", $cKey, $cKey, 1000, 32, true);
$key = substr($rfcdb, 0, 16);
$iv = substr($rfcdb, 16, 16);
$cipherTextRaw = openssl_encrypt($toEncrypt, "AES-128-CBC", $key, 1, $iv);
$cipherTextBase64 = str_replace(array("+", "/"), array("-", "_"), base64_encode($cipherTextRaw));
return $cipherTextBase64;
}
?>
Python
import base64
import hashlib
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
def encrypt(to_encrypt, cKey):
padded = pad(to_encrypt.encode('utf-8'), AES.block_size)
salt = cKey.encode('utf-8')
key = hashlib.pbkdf2_hmac('sha1', cKey.encode('utf-8'), salt, 1000, dklen=32)
iv = key[16:32]
key = key[:16]
cipher = AES.new(key, AES.MODE_CBC, iv)
encrypted = cipher.encrypt(padded)
encoded = base64.urlsafe_b64encode(encrypted).decode().replace("+", "-").replace("/", "_")
return encoded
Exemple d'implémentation (C#)
DateTime dateDebut = DateTime.Today.AddDays(-6);
DateTime dateFin = DateTime.Today;
string cKey = DateTime.Today.ToString("yyyyMMdd") + CleSecrete;
string parameters = string.Concat("CleClient=", CleClient, "&DateDebut=", dateDebut.ToString("yyyy-MM-dd"), "&DateFin=", dateFin.ToString("yyyy-MM-dd"));
string signature = Encrypt(parameters, cKey);
string apiUri = "https://extranet.explore.fr/ExploreAPI/api/Opportunites/MarchesPublics?CleClient=" + CleClient + "&Signature=" + signature;