Teste de autenticação
1. Introdução e Configuração Inicial
Visão Geral
Esta documentação detalha o processo de assinatura e encriptação de cabeçalhos para autenticação segura em requisições à nossa API. O processo garante que as requisições sejam confiáveis e seguras, prevenindo acessos não autorizados e garantindo a integridade dos dados.
Importar Bibliotecas
Aqui, importamos as bibliotecas necessárias ao longo do processo de autenticação.
- 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;
Definir variáveis
Utilizaremos as variáveis base_url, endpoint, method e request_body. Neste exemplo faremos um POST no endpoint "/test".
- 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. Preparação de Dados para Assinatura
Inserir dados de criptografia
As chaves contidas neste exemplo são apenas para fins de demonstração. Por favor, utilize suas próprias chaves.
- 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-----";
Formatar data
O objeto de data e hora informado deve estar em UTC e deve seguir o padrão da norma internacional ISO 8601 ("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");
Definir cabeçalho JWT
Definimos o algoritmo de codificação JWT
- 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" }
};
Construir hash em MD5 para assinatura no cabeçalho JSON
Construir hash em MD5 para assinatura no cabeçalho (header) utilizando o 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();
}
}
O hash md5 de requisições dos métodos GET e DELETE deve ser gerado com o payload vazio
Construir hash em MD5 para assinatura no cabeçalho Arquivo
Construir hash em MD5 para assinatura no cabeçalho (header) utilizando um arquivo
- 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);
}
}
Definir o corpo do JWT
Essas são as infromações necessárias para assinatura do cabeçalho
- 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 }
};
Realizar criptografia do 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);
Montar header assinado
- 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);
Construir a URL da solicitação
- 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. Realizar Requisição
- 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;