Deprecated authentication methods
IP address restriction
Authentication to EXPLORE API used to work through an authentication key (CleClient) and IP address restrictions. You then had to add the “CleClient” parameter in the URI (or in the body in case of an HTTP POST request). The list of IP addresses which should be authorized have to be communicated to our technical support team.
URI Encryption
When IP restriction is out of the question, an encryption system can be used in its stead. All EXPLORE API generic webservices can be called in this encrypted way, which we call “signed”.
You only need two parameters then:
- Your authentication key (CleClient)
- The encrypted query string (Signature)
The “Signature” parameter is encrypted via a method based on a reliable algorithm; AES (Advanced Encryption Standard). If you want more information on AES, you can go check this page.
Your team and EXPLORE will then share a secret key that wil allow for decryption and thus secure your call. No one, without this key, will be able to reveal what the encrypted parameter contains.
The URI you get after encryption, for security reasons, only works the same day it has been generated. To implement this, you have to add the date to your secret key as is demonstrated below (in C#):
string cipherKey = DateTime.Today.ToString("yyyyMMdd") + secretKey;
The content of the “Signature” parameter corresponds to the “normal” query string of the parameter. Thus, if you want to send a request to the Amenagement webservice in the Opportunites section, the string you will have to encrypt will look like “CleClient=[your authentication key]&DateDebut=[yyyy-MM-dd]&DateFin=[yyyy-MM-dd]”.
Encryption methods
The “Signature” string can be encrypted using the following methods:
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("/", "_");
}
Special C# case: SalesForce CRM environment
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 (< 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 (≥ 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
Usage example (C#)
DateTime startDate = DateTime.Today.AddDays(-6);
DateTime endDate = DateTime.Today;
string cipherKey = DateTime.Today.ToString("yyyyMMdd") + secretKey;
string parameters = string.Concat("CleClient=", authenticationKey, "&DateDebut=", startDate.ToString("yyyy-MM-dd"), "&DateFin=", endDate.ToString("yyyy-MM-dd"));
string signature = Encrypt(parameters, cipherKey);
string endpoint = "https://extranet.explore.fr/ExploreAPI/api/Opportunites/MarchesPublics?CleClient=" + authenticationKey + "&Signature=" + signature;

