Primer paso de la investigacion. Se aportan el .apk, las carpetas con el apk extraido y el apk descompilado. El archivo API_DOCUMENTATION.md es un archivo donde se anotaran los descubrimientos del funcionamiento de la API, y los .py son scripts para probar la funcionalidad de la API con los métodos que vayamos encontrando. Finalmente, los archivos .js son scripts de Frida para extraer informacion de la APP durante la ejecucion.
This commit is contained in:
171
api_testing_scripts/adif_auth.py
Normal file
171
api_testing_scripts/adif_auth.py
Normal file
@@ -0,0 +1,171 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Sistema de autenticación HMAC-SHA256 para API de Adif
|
||||
Basado en ElcanoAuth.java
|
||||
"""
|
||||
|
||||
import hmac
|
||||
import hashlib
|
||||
import json
|
||||
from datetime import datetime
|
||||
from typing import Dict
|
||||
|
||||
class AdifAuthenticator:
|
||||
"""Autenticador para la API de Adif usando HMAC-SHA256"""
|
||||
|
||||
def __init__(self, access_key: str, secret_key: str, user_id: str):
|
||||
self.access_key = access_key
|
||||
self.secret_key = secret_key
|
||||
self.user_id = user_id
|
||||
self.client = "AndroidElcanoApp"
|
||||
|
||||
def _format_payload(self, payload: str) -> str:
|
||||
"""Formatear payload (eliminar espacios, saltos de línea)"""
|
||||
return payload.replace(" ", "").replace("\n", "").replace("\r", "")
|
||||
|
||||
def _to_hex(self, data: str) -> str:
|
||||
"""Calcular SHA256 hash en hexadecimal"""
|
||||
return hashlib.sha256(data.encode('utf-8')).hexdigest()
|
||||
|
||||
def _hmac_sha256(self, key: bytes, message: str) -> bytes:
|
||||
"""Calcular HMAC-SHA256"""
|
||||
return hmac.new(key, message.encode('utf-8'), hashlib.sha256).digest()
|
||||
|
||||
def _get_signature_key(self, date_simple: str) -> bytes:
|
||||
"""Derivar clave de firma"""
|
||||
# kDate = HMAC-SHA256(secret_key, date)
|
||||
k_date = self._hmac_sha256(self.secret_key.encode('utf-8'), date_simple)
|
||||
|
||||
# kClient = HMAC-SHA256(kDate, client)
|
||||
k_client = self._hmac_sha256(k_date, self.client)
|
||||
|
||||
# kSigning = HMAC-SHA256(kClient, "elcano_request")
|
||||
k_signing = self._hmac_sha256(k_client, "elcano_request")
|
||||
|
||||
return k_signing
|
||||
|
||||
def _prepare_canonical_request(self, method: str, path: str, params: str,
|
||||
host: str, date: str, payload: str) -> tuple:
|
||||
"""Preparar canonical request"""
|
||||
# Headers canónicos (deben estar en orden)
|
||||
canonical_headers = (
|
||||
f"content-type:application/json;charset=utf-8\n"
|
||||
f"x-elcano-client:{self.client}\n"
|
||||
f"x-elcano-date:{date}\n"
|
||||
f"x-elcano-host:{host}\n"
|
||||
f"x-elcano-userid:{self.user_id}\n"
|
||||
)
|
||||
|
||||
signed_headers = "content-type;x-elcano-client;x-elcano-date;x-elcano-host;x-elcano-userid"
|
||||
|
||||
# Formatear payload
|
||||
formatted_payload = self._format_payload(payload)
|
||||
payload_hash = self._to_hex(formatted_payload)
|
||||
|
||||
# Canonical request
|
||||
canonical_request = (
|
||||
f"{method}\n"
|
||||
f"{path}\n"
|
||||
f"{params}\n"
|
||||
f"{canonical_headers}"
|
||||
f"{signed_headers}\n"
|
||||
f"{payload_hash}"
|
||||
)
|
||||
|
||||
return canonical_request, signed_headers
|
||||
|
||||
def _prepare_string_to_sign(self, canonical_request: str, date: str, date_simple: str) -> str:
|
||||
"""Preparar string to sign"""
|
||||
canonical_hash = self._to_hex(canonical_request)
|
||||
|
||||
string_to_sign = (
|
||||
f"HMAC-SHA256\n"
|
||||
f"{date}\n"
|
||||
f"{date_simple}/{self.client}/{self.user_id}/elcano_request\n"
|
||||
f"{canonical_hash}"
|
||||
)
|
||||
|
||||
return string_to_sign
|
||||
|
||||
def _calculate_signature(self, string_to_sign: str, date_simple: str) -> str:
|
||||
"""Calcular firma"""
|
||||
signing_key = self._get_signature_key(date_simple)
|
||||
signature = self._hmac_sha256(signing_key, string_to_sign)
|
||||
return signature.hex()
|
||||
|
||||
def sign_request(self, method: str, host: str, path: str,
|
||||
params: str = "", payload: str = "") -> Dict[str, str]:
|
||||
"""
|
||||
Firmar una petición HTTP
|
||||
|
||||
Args:
|
||||
method: Método HTTP (GET, POST, etc.)
|
||||
host: Host (ej: circulacion.api.adif.es)
|
||||
path: Path de la petición
|
||||
params: Query parameters (vacío si no hay)
|
||||
payload: Body JSON (vacío para GET)
|
||||
|
||||
Returns:
|
||||
Dict con todos los headers necesarios
|
||||
"""
|
||||
# Timestamps
|
||||
now = datetime.utcnow()
|
||||
date = now.strftime("%Y%m%dT%H%M%SZ")
|
||||
date_simple = now.strftime("%Y%m%d")
|
||||
|
||||
# Canonical request
|
||||
canonical_request, signed_headers = self._prepare_canonical_request(
|
||||
method, path, params, host, date, payload
|
||||
)
|
||||
|
||||
# String to sign
|
||||
string_to_sign = self._prepare_string_to_sign(canonical_request, date, date_simple)
|
||||
|
||||
# Signature
|
||||
signature = self._calculate_signature(string_to_sign, date_simple)
|
||||
|
||||
# Authorization header
|
||||
authorization = (
|
||||
f"HMAC-SHA256 "
|
||||
f"Credential={self.access_key}/{date_simple}/{self.client}/{self.user_id}/elcano_request,"
|
||||
f"SignedHeaders={signed_headers},"
|
||||
f"Signature={signature}"
|
||||
)
|
||||
|
||||
return {
|
||||
"X-Elcano-Host": host,
|
||||
"Content-type": "application/json;charset=utf-8",
|
||||
"X-Elcano-Client": self.client,
|
||||
"X-Elcano-Date": date,
|
||||
"X-Elcano-UserId": self.user_id,
|
||||
"Authorization": authorization
|
||||
}
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Test con las claves extraídas
|
||||
auth = AdifAuthenticator(
|
||||
access_key="and20210615",
|
||||
secret_key="Jthjtr946RTt",
|
||||
user_id="0c8c32dce47f8512"
|
||||
)
|
||||
|
||||
# Ejemplo de firma
|
||||
payload = json.dumps({
|
||||
"stationCode": "10200",
|
||||
"commercialService": "BOTH",
|
||||
"commercialStopType": "BOTH",
|
||||
"page": {"pageNumber": 0},
|
||||
"trafficType": "CERCANIAS"
|
||||
})
|
||||
|
||||
headers = auth.sign_request(
|
||||
method="POST",
|
||||
host="circulacion.api.adif.es",
|
||||
path="/portroyalmanager/secure/circulationpaths/departures/traffictype/",
|
||||
payload=payload
|
||||
)
|
||||
|
||||
print("Headers generados:")
|
||||
for key, value in headers.items():
|
||||
print(f"{key}: {value}")
|
||||
Reference in New Issue
Block a user