241 lines
6.7 KiB
Markdown
241 lines
6.7 KiB
Markdown
# 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:
|
|
|
|
```http
|
|
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
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```python
|
|
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
|
|
|
|
```bash
|
|
./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`):
|
|
|
|
```json
|
|
{
|
|
"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**
|
|
|
|
```bash
|
|
# 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:
|
|
|
|
```bash
|
|
# 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
|
|
|
|
## Advertencia Legal
|
|
|
|
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.
|