Files
adif-api-reverse-engineering/README.md

6.7 KiB

Ingeniería Reversa de la API de Adif (Elcano)

Este proyecto contiene la documentación y herramientas para interactuar con la API no documentada de Adif (sistema Elcano) obtenida mediante ingeniería reversa de la aplicación móvil oficial.

Archivos

  • base.apk - Aplicación móvil original de Adif
  • API_DOCUMENTATION.md - Documentación completa de la API descubierta
  • adif_client.py - Cliente Python para interactuar con la API
  • decompiled/ - Código fuente descompilado de la APK (generado)
  • apk_extracted/ - Contenido extraído de la APK (generado)

Hallazgos Principales

URLs Base

  • Estaciones: https://estaciones.api.adif.es
  • Circulaciones: https://circulacion.api.adif.es
  • Avisa (Incidencias): https://avisa.adif.es

Autenticación

La API usa User-keys en los headers HTTP en lugar de autenticación OAuth tradicional:

Content-Type: application/json;charset=utf-8
User-key: f4ce9fbfa9d721e39b8984805901b5df  # Para circulaciones
User-key: 0d021447a2fd2ac64553674d5a0c1a6f  # Para estaciones

Endpoints Principales

Circulaciones (Trenes)

  • POST /portroyalmanager/secure/circulationpaths/departures/traffictype/ - Salidas
  • POST /portroyalmanager/secure/circulationpaths/arrivals/traffictype/ - Llegadas
  • POST /portroyalmanager/secure/circulationpaths/betweenstations/traffictype/ - Entre estaciones
  • POST /portroyalmanager/secure/circulationpathdetails/onepaths/ - Detalles de ruta

Estaciones

  • GET /portroyalmanager/secure/stations/allstations/reducedinfo/{token}/ - Todas las estaciones
  • POST /portroyalmanager/secure/stations/onestation/ - Detalles de estación

Uso del Cliente Python

Instalación

# Crear y activar entorno virtual
python3 -m venv venv
source venv/bin/activate  # En Linux/Mac
# O en Windows: venv\Scripts\activate

# Instalar dependencias
pip install requests

Ejemplo Básico

from adif_client import AdifClient, TrafficType, State

# Crear cliente
client = AdifClient(debug=True)

# Obtener salidas de una estación
departures = client.get_departures(
    station_code="10200",  # Madrid Atocha
    traffic_type=TrafficType.CERCANIAS,
    size=10
)

# Obtener trenes entre dos estaciones
trains = client.get_between_stations(
    origin_station="10200",  # Madrid Atocha
    destination_station="10302",  # Madrid Chamartín
    traffic_type=TrafficType.ALL
)

# Obtener detalles de una estación
station = client.get_station_details("10200")

Ejecutar el ejemplo

./venv/bin/python adif_client.py

Estructura de la Aplicación

La app está construida con:

  • Kotlin como lenguaje principal
  • Retrofit para las llamadas HTTP
  • Hilt para inyección de dependencias
  • Coroutines para operaciones asíncronas
  • Firebase para analytics

Arquitectura

com.adif.elcanomovil/
├── serviceNetworking/        # Capa de red
│   ├── circulations/         # Servicios de circulaciones
│   ├── stations/             # Servicios de estaciones
│   ├── compositions/         # Composiciones de trenes
│   ├── avisa/                # Sistema de incidencias
│   └── subscriptions/        # Suscripciones
├── repositories/             # Repositorios (patrón Repository)
├── domain/                   # Lógica de negocio
└── ui*/                      # Capas de presentación

Información Técnica

Estados (State Enum)

  • YES - Sí
  • NOT - No
  • BOTH - Ambos

Nota: En BuildConfig aparece como "ALL" pero en el código real es "BOTH"

Tipos de Tráfico (TrafficType)

  • CERCANIAS - Trenes de cercanías
  • MEDIA_DISTANCIA - Media distancia
  • LARGA_DISTANCIA - Larga distancia
  • ALL - Todos los tipos

PageInfo

La paginación solo usa pageNumber (no incluye size):

{
  "page": {
    "pageNumber": 0
  }
}

⚠️ ACTUALIZACIÓN IMPORTANTE: Sistema de Autenticación

Los tests iniciales fallaron porque la API usa un sistema de autenticación HMAC-SHA256 similar a AWS Signature V4.

El Problema Real

La API NO usa simples API keys. Cada petición requiere:

  1. Headers especiales:

    • X-Elcano-Host
    • X-Elcano-Client: AndroidElcanoApp
    • X-Elcano-Date (timestamp ISO UTC)
    • X-Elcano-UserId (ID único)
    • Authorization con firma HMAC-SHA256
  2. Claves secretas almacenadas en librería nativa (libapi-keys.so):

    • accessKey (método nativo)
    • secretKey (método nativo)
  3. Firma de cada petición que incluye:

    • Método HTTP
    • Path y parámetros
    • Payload (body JSON)
    • Headers canónicos
    • Timestamp

Cómo Obtener las Claves

Método recomendado: Frida

# 1. Instalar Frida
pip install frida-tools

# 2. Conectar dispositivo Android / iniciar emulador
adb devices

# 3. Instalar la app
adb install base.apk

# 4. Ejecutar el script de extracción
frida -U -f com.adif.elcanomovil -l frida_extract_keys.js --no-pause

# 5. Interactuar con la app (ver trenes, etc.)
# Las claves aparecerán en la consola

Ver AUTH_EXPLAINED.md para detalles completos del sistema de autenticación.

Limitaciones Conocidas

  1. ⚠️ Sistema de autenticación complejo: Requiere extracción de claves nativas (ver arriba)

  2. Certificate Pinning: La app implementa certificate pinning (bypasseable con Frida)

  3. UserID dinámico: Se genera por instalación, no es fijo

  4. Autenticación Avisa: El sistema Avisa requiere OAuth2 con flujo de password adicional

Códigos de Estación Comunes

  • 10200 - Madrid Puerta de Atocha
  • 10302 - Madrid Chamartín-Clara Campoamor
  • 71801 - Barcelona Sants
  • 50000 - Valencia Nord
  • 11401 - Sevilla Santa Justa

Herramientas Utilizadas

  • jadx - Descompilador de Android APK a código Java
  • unzip - Para extraer contenido de la APK
  • Python requests - Cliente HTTP
  • curl - Pruebas de endpoints

Descompilación

Para descompilar la APK manualmente:

# Descargar jadx
wget https://github.com/skylot/jadx/releases/download/v1.5.0/jadx-1.5.0.zip
unzip jadx-1.5.0.zip -d jadx

# Descompilar
./jadx/bin/jadx -d decompiled base.apk

Próximos Pasos

  • Investigar el formato exacto de los objetos de petición
  • Obtener un token válido para el endpoint de estaciones
  • Implementar autenticación OAuth para Avisa
  • Documentar códigos de estación
  • Crear mappings de respuestas JSON
  • Implementar manejo de errores robusto

Este proyecto es solo para fines educativos y de investigación. La API de Adif es propiedad de ADIF y debe usarse respetando sus términos de servicio. No se debe abusar de la API ni usarla para fines comerciales sin autorización.

Autor

Proyecto de ingeniería reversa educativa.