Authentication test
1. Introduction and Initial Configuration
Overview
This documentation details the process of signing and encrypting headers for secure authentication in requests to our API. The process ensures that requests are reliable and secure, preventing unauthorized access and ensuring data integrity.
Import libraries
Here, we import the necessary libraries throughout the authentication process.
- Python
- PHP
- Node.js
- Java
- C#
import json
import requests
from datetime import datetime
from hashlib import md5
from jose import jwt
use Jose\Component\Core\AlgorithmManager;
use Jose\Component\Signature\JWSTokenSupport;
use Jose\Component\Signature\Algorithm\ES512;
use Jose\Component\Signature\Serializer\CompactSerializer;
use Jose\Component\Signature\JWSBuilder;
use Jose\Component\KeyManagement\JWKFactory;
const jose = require('jose');
const jwt = require('jsonwebtoken');
const crypto = require('crypto');
const axios = require('axios');
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import java.io.IOException;
import java.io.StringReader;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.util.Base64;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import org.bouncycastle.openssl.PEMKeyPair;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Security.Cryptography;
using System.Text;
using Jose;
using Newtonsoft.Json;
Define variables
We will use the variables base_url, endpoint, method, and request_body. In this example, we will make a POST request to the "/test" endpoint.
- Python
- PHP
- Node.js
- Java
- C#
base_url = "https://api-auth.sandbox.qitech.app"
endpoint = "/test"
method = "POST"
request_body = {"name": "QI Tech"}
$base_url = "https://api-auth.sandbox.qitech.app";
$endpoint = "/test";
$method = "POST";
$request_body = ["name" => "QI Tech"];
const base_url = 'https://api-auth.sandbox.qitech.app';
const endpoint = '/test';
const method = 'POST';
const request_body = { name: 'QI Tech' };
private static final String base_url = "https://api-auth.sandbox.qitech.app";
private static final String endpoint = "/test";
private static final String method = "POST";
private static final Map<String, Object> request_body = new HashMap<>();
static {
request_body.put("name", "QI Tech");
}
var base_url = "https://api-auth.sandbox.qitech.app";
var endpoint = "/test";
var method = "POST";
var request_body = new { name = "QI Tech" };
2. Data Preparation for Signature
Insert encryption data
The keys in this example are for demonstration purposes only. Please use your own keys.
- Python
- PHP
- Node.js
- Java
- C#
api_key = "f19c6e62-bd82-4334-9839-020810550c44"
client_private_key = '''-----BEGIN EC PRIVATE KEY-----
MIHbAgEBBEHh1hIeOPE5XNNhn6bxRAmVswsPZ0wZCmzVvP8Tl/LZK9ofVmRVGzll
srU1uezJEyHKYdOHrE2p52xUj+pHzjJvb6AHBgUrgQQAI6GBiQOBhgAEAAofUz1J
hBSOyGHLsnV9Sz0DSWmhl7U+ljqbfa8PKVFWSV3w16I1v2zME5/UzUhHn1gWsjnv
7/ekcLLAQbvqMPNXAfjIhFXLAPzqbB9iCuVua1v0Vgy52rBemOWrJka/Ws2bnKR8
h1N1OxOYeYr6C2jqMygBLktKMAs+282CEiEb4bIv
-----END EC PRIVATE KEY-----'''
$api_key = "f19c6e62-bd82-4334-9839-020810550c44";
$privateKeyString = "-----BEGIN EC PRIVATE KEY-----
MIHbAgEBBEHh1hIeOPE5XNNhn6bxRAmVswsPZ0wZCmzVvP8Tl/LZK9ofVmRVGzll
srU1uezJEyHKYdOHrE2p52xUj+pHzjJvb6AHBgUrgQQAI6GBiQOBhgAEAAofUz1J
hBSOyGHLsnV9Sz0DSWmhl7U+ljqbfa8PKVFWSV3w16I1v2zME5/UzUhHn1gWsjnv
7/ekcLLAQbvqMPNXAfjIhFXLAPzqbB9iCuVua1v0Vgy52rBemOWrJka/Ws2bnKR8
h1N1OxOYeYr6C2jqMygBLktKMAs+282CEiEb4bIv
-----END EC PRIVATE KEY-----";
const api_key = 'f19c6e62-bd82-4334-9839-020810550c44';
const client_private_key = `-----BEGIN EC PRIVATE KEY-----
MIHbAgEBBEHh1hIeOPE5XNNhn6bxRAmVswsPZ0wZCmzVvP8Tl/LZK9ofVmRVGzll
srU1uezJEyHKYdOHrE2p52xUj+pHzjJvb6AHBgUrgQQAI6GBiQOBhgAEAAofUz1J
hBSOyGHLsnV9Sz0DSWmhl7U+ljqbfa8PKVFWSV3w16I1v2zME5/UzUhHn1gWsjnv
7/ekcLLAQbvqMPNXAfjIhFXLAPzqbB9iCuVua1v0Vgy52rBemOWrJka/Ws2bnKR8
h1N1OxOYeYr6C2jqMygBLktKMAs+282CEiEb4bIv
-----END EC PRIVATE KEY-----`;
private static final String clientPrivateKey = "MIHbAgEBBEHh1hIeOPE5XNNhn6bxRAmVswsPZ0wZCmzVvP8Tl/LZK9ofVmRVGzll
srU1uezJEyHKYdOHrE2p52xUj+pHzjJvb6AHBgUrgQQAI6GBiQOBhgAEAAofUz1J
hBSOyGHLsnV9Sz0DSWmhl7U+ljqbfa8PKVFWSV3w16I1v2zME5/UzUhHn1gWsjnv
7/ekcLLAQbvqMPNXAfjIhFXLAPzqbB9iCuVua1v0Vgy52rBemOWrJka/Ws2bnKR8
h1N1OxOYeYr6C2jqMygBLktKMAs+282CEiEb4bIv";
var api_key = "f19c6e62-bd82-4334-9839-020810550c44";
var client_private_key = @"-----BEGIN EC PRIVATE KEY-----
MIHbAgEBBEHh1hIeOPE5XNNhn6bxRAmVswsPZ0wZCmzVvP8Tl/LZK9ofVmRVGzll
srU1uezJEyHKYdOHrE2p52xUj+pHzjJvb6AHBgUrgQQAI6GBiQOBhgAEAAofUz1J
hBSOyGHLsnV9Sz0DSWmhl7U+ljqbfa8PKVFWSV3w16I1v2zME5/UzUhHn1gWsjnv
7/ekcLLAQbvqMPNXAfjIhFXLAPzqbB9iCuVua1v0Vgy52rBemOWrJka/Ws2bnKR8
h1N1OxOYeYr6C2jqMygBLktKMAs+282CEiEb4bIv
-----END EC PRIVATE KEY-----";
Format date
The date and time object provided must be in UTC and must follow the ISO 8601 international standard ("2023-06-26T19:48:32.759844Z").
- Python
- PHP
- Node.js
- Java
- C#
timestamp = datetime.now().strftime("%Y-%m-%dT%H:%M:%S.%fZ")
$timestamp = gmdate('Y-m-d\TH:i:s.u\Z');
const timestamp = new Date().toISOString();
Date now = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSSSS'Z'");
String formattedDate = sdf.format(now);
var timestamp = datetime.now.ToString("yyyy-MM-ddTHH:mm:ss.fffZ");
Define JWT header
We define the JWT encoding algorithm
- Python
- PHP
- Node.js
- Java
- C#
jwt_header = {
"typ": "JWT",
"alg": "ES512"
}
$header = [
"typ" => "JWT",
"alg" => "ES512"
];
const jwt_header = {
typ: 'JWT',
alg: 'ES512'
};
// Não é necessário
var jwt_header = new Dictionary<string, object>
{
{ "typ", "JWT" },
{ "alg", "ES512" }
};
Build MD5 hash for JSON header signature
Build MD5 hash for header signature using the payload
- Python
- PHP
- Node.js
- Java
- C#
json_body = json.dumps(request_body)
md5_hash = md5(json_body.encode()).hexdigest()
$request_body_json = json_encode($request_body);
$md5_hash = md5($request_body_json);
const str_body = JSON.stringify(request_body);
const md5_hash = crypto.createHash('md5').update(str_body).digest('hex');
String payloadMd5 = md5Hash(jsonToString(request_body));
...
private static String jsonToString(Map<String, Object> jsonMap) {
return new com.google.gson.Gson().toJson(jsonMap);
}
...
private static String md5Hash(String text) {
try {
java.security.MessageDigest md = java.security.MessageDigest.getInstance("MD5");
byte[] array = md.digest(text.getBytes());
StringBuilder sb = new StringBuilder();
for (byte b : array) {
sb.append(String.format("%02x", b));
}
return sb.toString();
} catch (java.security.NoSuchAlgorithmException e) {
return null;
}
}
var json_body = JsonConvert.SerializeObject(request_body);
var md5_hash = CalculateMD5Hash(json_body);
.
static string CalculateMD5Hash(string input)
{
using (MD5 md5 = MD5.Create())
{
byte[] inputBytes = Encoding.UTF8.GetBytes(input);
byte[] hashBytes = md5.ComputeHash(inputBytes);
StringBuilder builder = new StringBuilder();
for (int i = 0; i < hashBytes.Length; i++)
{
builder.Append(hashBytes[i].ToString("x2"));
}
return builder.ToString();
}
}
The MD5 hash for GET and DELETE requests must be generated with an empty payload
Build an MD5 hash for the header signature File
Build an MD5 hash for the header signature using a file
- Python
- PHP
- Node.js
- Java
- C#
md5_instance = md5()
for chunk in iter(lambda: file.read(4096), b""):
md5_instance.update(chunk)
file.seek(0)
md5_hash = md5_instance.hexdigest()
$md5_instance = md5_file($file);
$md5_hash = hash_file('md5', $file);
const md5_instance = crypto.createHash('md5');
const readStream = fs.createReadStream(file);
readStream.on('data', (chunk) => {
md5_instance.update(chunk);
});
readStream.on('end', () => {
const md5_hash = md5_instance.digest('hex');
file.seek(0);
MessageDigest md5_instance = MessageDigest.getInstance("MD5");
byte[] buffer = new byte[4096];
int bytesRead;
try (InputStream inputStream = new FileInputStream(file)) {
while ((bytesRead = inputStream.read(buffer)) != -1) {
md5_instance.update(buffer, 0, bytesRead);
}
}
byte[] md5_hashBytes = md5_instance.digest();
StringBuilder md5_hashBuilder = new StringBuilder();
for (byte b : md5_hashBytes) {
md5_hashBuilder.append(String.format("%02x", b));
}
String md5_hash = md5_hashBuilder.toString();
using (var md5_instance = MD5.Create())
{
using (var stream = File.OpenRead(file))
{
byte[] hash = md5_instance.ComputeHash(stream);
string md5_hash = BitConverter.ToString(hash).Replace("-", "").ToLower();
stream.Seek(0, SeekOrigin.Begin);
}
}
Define the JWT body
These are the necessary pieces of information to sign the header
- Python
- PHP
- Node.js
- Java
- C#
jwt_body = {
"payload_md5": md5_hash,
"timestamp": timestamp,
"method": method,
"uri": endpoint
}
$payload = [
"payload_md5" => $md5_hash,
"timestamp" => $timestamp,
"method" => $method,
"uri" => $endpoint
];
const jwt_body = {
payload_md5: md5_hash,
timestamp: timestamp,
method: method,
uri: endpoint
};
Map<String, Object> jwt_body = new HashMap<>();
jwt_body.put("payload_md5", payloadMd5);
jwt_body.put("timestamp", formattedDate);
jwt_body.put("method", method);
jwt_body.put("uri", endpoint);
// Ajuste: Remover espaços e quebras de linha no meio da chave privada
client_private_key = client_private_key.Replace("-----BEGIN EC PRIVATE KEY-----", "")
.Replace("-----END EC PRIVATE KEY-----", "")
.Replace("\n", "")
.Replace("\r", "");
// Converter a chave privada para ECDsa
using (ECDsa ecdsa = ECDsa.Create())
{
ecdsa.ImportECPrivateKey(Convert.FromBase64String(client_private_key), out _);
var jwt_body = new Dictionary<string, object>
{
{ "payload_md5", md5_hash },
{ "timestamp", timestamp },
{ "method", method },
{ "uri", endpoint }
};
Encrypt the header
- Python
- PHP
- Node.js
- Java
- C#
encoded_header_token = jwt.encode(
claims=jwt_body,
key=client_private_key,
algorithm="ES512",
headers=jwt_header
)
$jws = $jwsBuilder
->create()
->withPayload(json_encode($payload))
->addSignature($privateKey, $header)
->build();
$serializer = new CompactSerializer();
$jwt = $serializer->serialize($jws, 0);
const encoded_header_token = jwt.sign(
jwt_body,
client_private_key,
{
algorithm: 'ES512',
header: jwt_header
}
);
PrivateKey privateKey = getPrivateKey(clientPrivateKey);
JwtBuilder jwtBuilder = Jwts.builder().setClaims(jwt_body).signWith(privateKey, SignatureAlgorithm.ES512);
String encodedHeaderToken = jwtBuilder.compact();
...
public static PrivateKey getPrivateKey(final String encodedPvKey) {
try {
final String pvKey = new String(Base64.getDecoder().decode(encodedPvKey));
PEMParser pemParser = new PEMParser(new StringReader(pvKey));
PEMKeyPair pemKeyPair = (PEMKeyPair) pemParser.readObject();
JcaPEMKeyConverter converter = new JcaPEMKeyConverter();
KeyPair kp = converter.getKeyPair(pemKeyPair);
pemParser.close();
return kp.getPrivate();
} catch (IOException e) {
throw new RuntimeException("Couldn't load private key");
}
}
var encoded_header_token = JWT.Encode(jwt_body, ecdsa, JwsAlgorithm.ES512, jwt_header);
Build signed header
- Python
- PHP
- Node.js
- Java
- C#
signed_header = {
"AUTHORIZATION": encoded_header_token,
"API-CLIENT-KEY": api_key
}
$headers = [
'Authorization' => $jwt,
'API-CLIENT-KEY' => $api_key,
];
const signed_header = {
AUTHORIZATION: encoded_header_token,
'API-CLIENT-KEY': api_key
};
Map<String, String> headers = new HashMap<>();
headers.put("AUTHORIZATION", encodedHeaderToken);
headers.put("API-CLIENT-KEY", api_key);
using (var client = new HttpClient())
client.DefaultRequestHeaders.Clear();
client.DefaultRequestHeaders.Add("AUTHORIZATION", encoded_header_token);
client.DefaultRequestHeaders.Add("API-CLIENT-KEY", api_key);
Build the request URL
- Python
- PHP
- Node.js
- Java
- C#
url = f"{base_url}{endpoint}"
$url = $base_url . $endpoint;
const url = `${base_url}${endpoint}`;
String requestUrl = base_url + endpoint;
var url = $"{base_url}{endpoint}";
3. Make the Request
- Python
- PHP
- Node.js
- Java
- C#
post_test_response = requests.post(url=url, headers=signed_header, json=request_body)
$response = \WpOrg\Requests\Requests::post($url, $headers, json_encode($request_body));
axios
.post(url, request_body, { headers: signed_header })
.then(response => {
console.log(response.data);
})
.catch(error => {
console.error(error);
});
OkHttpClient client = new OkHttpClient();
MediaType mediaType = MediaType.parse("application/json");
okhttp3.RequestBody requestBody = RequestBody.create(mediaType, jsonToString(request_body));
Request request = new Request.Builder().url(requestUrl).headers(okhttp3.Headers.of(headers))
.method(method, requestBody).build();
Response response = client.newCall(request).execute();
System.out.println(response.body().string());
var content = new StringContent(json_body, Encoding.UTF8, "application/json");
var post_test_response = client.PostAsync(url, content).Result;