Refactor: reorganización completa del proyecto y documentación consolidada
Esta actualización reorganiza el proyecto de reverse engineering de la API de ADIF con los siguientes cambios: Estructura del proyecto: - Movida documentación principal a carpeta docs/ - Consolidados archivos markdown redundantes en CLAUDE.md (contexto completo del proyecto) - Organización de tests en carpeta tests/ con README explicativo - APK renombrado de base.apk a adif.apk para mayor claridad Archivos de código: - Movidos adif_auth.py y adif_client.py a la raíz (antes en api_testing_scripts/) - Eliminados scripts de testing obsoletos y scripts de Frida no utilizados - Nuevos tests detallados: test_endpoints_detailed.py y test_onepaths_with_real_trains.py Descubrimientos: - Documentados nuevos hallazgos en docs/NEW_DISCOVERIES.md - Actualización de onePaths funcionando con commercialNumber real (devuelve 200) - Extraídos 1587 códigos de estación en station_codes.txt Configuración: - Actualizado .gitignore con mejores patrones para Python e IDEs - Eliminados archivos temporales de depuración y logs
This commit is contained in:
482
docs/API_DOCUMENTATION.md
Normal file
482
docs/API_DOCUMENTATION.md
Normal file
@@ -0,0 +1,482 @@
|
||||
# Adif Elcano API - Ingeniería Reversa
|
||||
|
||||
Documentación completa de la API de ADIF (El Cano Móvil) obtenida mediante ingeniería reversa de la aplicación móvil.
|
||||
|
||||
**Estado**: ✅ Proyecto completado con éxito
|
||||
**Última actualización**: 2025-12-05
|
||||
|
||||
## 🎯 Resumen de Funcionalidad
|
||||
|
||||
| Característica | Estado | Notas |
|
||||
|----------------|--------|-------|
|
||||
| Autenticación HMAC-SHA256 | ✅ Implementada | Algoritmo completo y validado |
|
||||
| Endpoints funcionales | ✅ 4/8 (50%) | departures, arrivals, onepaths, stationsobservations |
|
||||
| Endpoints bloqueados | ⚠️ 4/8 | 401 Unauthorized por permisos limitados |
|
||||
| Códigos de estación | ✅ 1587 | Extraídos de `assets/stations_all.json` |
|
||||
| Claves extraídas | ✅ Completas | ACCESS_KEY y SECRET_KEY de `libapi-keys.so` |
|
||||
|
||||
## URLs Base
|
||||
|
||||
```
|
||||
BASE_URL_STATIONS = https://estaciones.api.adif.es
|
||||
BASE_URL_CIRCULATION = https://circulacion.api.adif.es
|
||||
BASE_URL_ELCANOWEB = https://elcanoweb.adif.es/api/
|
||||
BASE_URL_AVISA = https://avisa.adif.es/avisa-ws/api/
|
||||
```
|
||||
|
||||
## Headers de Autenticación
|
||||
|
||||
### Para API de Circulaciones y Composiciones
|
||||
```
|
||||
Content-Type: application/json;charset=utf-8
|
||||
User-key: f4ce9fbfa9d721e39b8984805901b5df
|
||||
```
|
||||
|
||||
### Para API de Estaciones
|
||||
```
|
||||
Content-Type: application/json;charset=utf-8
|
||||
User-key: 0d021447a2fd2ac64553674d5a0c1a6f
|
||||
```
|
||||
|
||||
### Para Avisa Login
|
||||
```
|
||||
Authorization: Basic YXZpc3RhX2NsaWVudF9hbmRyb2lkOjh5WzZKNyFmSjwhXypmYXE1NyNnOSohNElwa2MjWC1BTg==
|
||||
```
|
||||
|
||||
### Para Suscripciones
|
||||
```
|
||||
Authorization: Basic ZGVpbW9zOmRlaW1vc3R0
|
||||
X-CanalMovil-Authentication: <token>
|
||||
X-CanalMovil-deviceID: <device_id>
|
||||
X-CanalMovil-pushID: <push_id>
|
||||
```
|
||||
|
||||
## Tokens
|
||||
|
||||
```
|
||||
REGISTRATION_TOKEN = Bearer b9034774-c6e4-4663-a1a8-74bf7102651b
|
||||
```
|
||||
|
||||
## Endpoints
|
||||
|
||||
### Estaciones
|
||||
|
||||
#### Obtener todas las estaciones
|
||||
```
|
||||
GET /portroyalmanager/secure/stations/allstations/reducedinfo/{token}/
|
||||
Base: https://estaciones.api.adif.es
|
||||
Headers: User-key para estaciones
|
||||
```
|
||||
|
||||
#### Obtener detalles de una estación
|
||||
```
|
||||
POST /portroyalmanager/secure/stations/onestation/
|
||||
Base: https://estaciones.api.adif.es
|
||||
Headers: User-key para estaciones
|
||||
|
||||
Body:
|
||||
{
|
||||
"stationCode": "string"
|
||||
}
|
||||
```
|
||||
|
||||
#### Observaciones de estación
|
||||
```
|
||||
POST /portroyalmanager/secure/stationsobservations/
|
||||
Base: https://estaciones.api.adif.es
|
||||
Headers: User-key para estaciones
|
||||
|
||||
Body:
|
||||
{
|
||||
"stationCodes": ["string"] // Array de códigos de estación (requerido)
|
||||
}
|
||||
|
||||
Ejemplo:
|
||||
{
|
||||
"stationCodes": ["60000", "71801"]
|
||||
}
|
||||
```
|
||||
|
||||
### Circulaciones (Trenes)
|
||||
|
||||
#### Salidas (Departures)
|
||||
```
|
||||
POST /portroyalmanager/secure/circulationpaths/departures/traffictype/
|
||||
Base: https://circulacion.api.adif.es
|
||||
Headers: User-key para circulaciones
|
||||
|
||||
Body:
|
||||
{
|
||||
"commercialService": "YES|NOT|BOTH", // Estado del servicio comercial
|
||||
"commercialStopType": "YES|NOT|BOTH", // Tipo de parada comercial
|
||||
"destinationStationCode": "string|null", // Código estación destino (opcional)
|
||||
"originStationCode": "string|null", // Código estación origen (opcional)
|
||||
"page": {
|
||||
"pageNumber": number // Número de página
|
||||
},
|
||||
"stationCode": "string|null", // Código estación (opcional)
|
||||
"trafficType": "CERCANIAS|AVLDMD|OTHERS|TRAVELERS|GOODS|ALL" // Tipo de tráfico
|
||||
}
|
||||
|
||||
Ejemplo:
|
||||
{
|
||||
"commercialService": "BOTH",
|
||||
"commercialStopType": "BOTH",
|
||||
"destinationStationCode": null,
|
||||
"originStationCode": null,
|
||||
"page": {
|
||||
"pageNumber": 0
|
||||
},
|
||||
"stationCode": "60000",
|
||||
"trafficType": "ALL"
|
||||
}
|
||||
```
|
||||
|
||||
#### Llegadas (Arrivals)
|
||||
```
|
||||
POST /portroyalmanager/secure/circulationpaths/arrivals/traffictype/
|
||||
Base: https://circulacion.api.adif.es
|
||||
Headers: User-key para circulaciones
|
||||
|
||||
Body: Mismo formato que departures (TrafficCirculationPathRequest)
|
||||
```
|
||||
|
||||
#### Entre estaciones
|
||||
```
|
||||
POST /portroyalmanager/secure/circulationpaths/betweenstations/traffictype/
|
||||
Base: https://circulacion.api.adif.es
|
||||
Headers: User-key para circulaciones
|
||||
|
||||
Body: Mismo formato que departures (TrafficCirculationPathRequest)
|
||||
```
|
||||
|
||||
#### Una ruta específica
|
||||
```
|
||||
POST /portroyalmanager/secure/circulationpathdetails/onepaths/
|
||||
Base: https://circulacion.api.adif.es
|
||||
Headers: User-key para circulaciones
|
||||
|
||||
Body:
|
||||
{
|
||||
"allControlPoints": boolean|null, // Todos los puntos de control (opcional)
|
||||
"commercialNumber": "string|null", // Número comercial del tren (opcional)
|
||||
"destinationStationCode": "string|null", // Código estación destino (opcional)
|
||||
"launchingDate": number|null, // Fecha de lanzamiento en timestamp (Long) (opcional)
|
||||
"originStationCode": "string|null" // Código estación origen (opcional)
|
||||
}
|
||||
|
||||
Ejemplo:
|
||||
{
|
||||
"allControlPoints": true,
|
||||
"commercialNumber": "04138",
|
||||
"destinationStationCode": "60000",
|
||||
"launchingDate": 1733356800000,
|
||||
"originStationCode": "71801"
|
||||
}
|
||||
```
|
||||
|
||||
#### Varias rutas
|
||||
```
|
||||
POST /portroyalmanager/secure/circulationpathdetails/severalpaths/
|
||||
Base: https://circulacion.api.adif.es
|
||||
Headers: User-key para circulaciones
|
||||
|
||||
Body: Mismo formato que onepaths (OneOrSeveralPathsRequest)
|
||||
```
|
||||
|
||||
### Composiciones
|
||||
|
||||
#### Composición de tren
|
||||
```
|
||||
POST /portroyalmanager/secure/circulationpaths/compositions/path/
|
||||
Base: https://circulacion.api.adif.es
|
||||
Headers: User-key para circulaciones
|
||||
|
||||
Body: Same as onepaths request
|
||||
```
|
||||
|
||||
### Avisa (Sistema de incidencias)
|
||||
|
||||
#### Login
|
||||
```
|
||||
POST /avisa-ws/api/token
|
||||
Base: https://avisa.adif.es
|
||||
Headers: Basic auth token para Avisa
|
||||
|
||||
Query params:
|
||||
- grant_type: "password"
|
||||
- username: <username>
|
||||
- password: <password>
|
||||
```
|
||||
|
||||
#### Registrar cliente
|
||||
```
|
||||
POST /avisa-ws/api/v1/client
|
||||
Base: https://avisa.adif.es
|
||||
```
|
||||
|
||||
#### Estaciones Avisa
|
||||
```
|
||||
GET /avisa-ws/api/v1/station
|
||||
Base: https://avisa.adif.es
|
||||
```
|
||||
|
||||
#### Categorías de estación
|
||||
```
|
||||
GET /avisa-ws/api/v1/category
|
||||
Base: https://avisa.adif.es
|
||||
```
|
||||
|
||||
#### Incidencias
|
||||
```
|
||||
GET /avisa-ws/api/v1/incidence
|
||||
POST /avisa-ws/api/v1/incidence
|
||||
GET /avisa-ws/api/v1/incidence/{incidenceId}
|
||||
Base: https://avisa.adif.es
|
||||
```
|
||||
|
||||
### Suscripciones
|
||||
|
||||
#### Listar suscripciones
|
||||
```
|
||||
GET /api/subscriptions?platform=300
|
||||
Base: https://elcanoweb.adif.es
|
||||
Headers: Basic auth + X-CanalMovil headers
|
||||
```
|
||||
|
||||
#### Crear suscripción
|
||||
```
|
||||
POST /api/subscriptions
|
||||
Base: https://elcanoweb.adif.es
|
||||
Headers: Basic auth + X-CanalMovil headers
|
||||
```
|
||||
|
||||
#### Silenciar suscripción
|
||||
```
|
||||
PUT /api/subscriptions/{id}/mute
|
||||
Base: https://elcanoweb.adif.es
|
||||
Headers: Basic auth + X-CanalMovil headers
|
||||
```
|
||||
|
||||
## 📊 Estructura de Respuestas
|
||||
|
||||
### Respuesta de Departures/Arrivals
|
||||
|
||||
```json
|
||||
{
|
||||
"commercialPaths": [
|
||||
{
|
||||
"commercialPathInfo": {
|
||||
"timestamp": 1764927847100,
|
||||
"commercialPathKey": {
|
||||
"commercialCirculationKey": {
|
||||
"commercialNumber": "90399",
|
||||
"launchingDate": 1764889200000
|
||||
},
|
||||
"originStationCode": "10620",
|
||||
"destinationStationCode": "60004"
|
||||
},
|
||||
"commercialOriginStationCode": "10620",
|
||||
"commercialDestinationStationCode": "60004",
|
||||
"line": null,
|
||||
"core": null,
|
||||
"observation": null,
|
||||
"trafficType": "CERCANIAS",
|
||||
"opeProComPro": {
|
||||
"operator": "RF",
|
||||
"product": "C",
|
||||
"commercialProduct": " "
|
||||
},
|
||||
"compositionData": {
|
||||
"compositionLenghtType": null,
|
||||
"compositionFloorType": null,
|
||||
"accesible": false
|
||||
},
|
||||
"announceableStations": ["60004"]
|
||||
},
|
||||
"passthroughStep": {
|
||||
"stopType": "NO_STOP",
|
||||
"announceable": false,
|
||||
"stationCode": "10200",
|
||||
"arrivalPassthroughStepSides": null,
|
||||
"departurePassthroughStepSides": {
|
||||
"plannedTime": 1764927902000,
|
||||
"forecastedOrAuditedDelay": 2175,
|
||||
"timeType": "FORECASTED",
|
||||
"plannedPlatform": "2",
|
||||
"sitraPlatform": null,
|
||||
"ctcPlatform": null,
|
||||
"operatorPlatform": null,
|
||||
"resultantPlatform": "PLANNED",
|
||||
"preassignedPlatform": null,
|
||||
"observation": null,
|
||||
"circulationState": "RUNNING",
|
||||
"announceState": "NORMAL",
|
||||
"technicalCirculationKey": {
|
||||
"technicalNumber": "90399",
|
||||
"technicalLaunchingDate": 1764889200000
|
||||
},
|
||||
"visualEffects": {
|
||||
"inmediateDeparture": false,
|
||||
"countDown": false,
|
||||
"showDelay": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Campos importantes**:
|
||||
- `commercialNumber`: Número comercial del tren
|
||||
- `launchingDate`: Fecha de salida en milisegundos (timestamp)
|
||||
- `plannedTime`: Hora planificada en milisegundos
|
||||
- `forecastedOrAuditedDelay`: Retraso en segundos
|
||||
- `circulationState`: Estado del tren (RUNNING, PENDING_TO_CIRCULATE, etc.)
|
||||
- `plannedPlatform`: Andén planificado
|
||||
|
||||
### Respuesta de OnePaths (Ruta Completa)
|
||||
|
||||
```json
|
||||
{
|
||||
"commercialPaths": [
|
||||
{
|
||||
"commercialPathInfo": { /* Igual que en departures */ },
|
||||
"passthroughSteps": [ // ← Array con TODAS las paradas
|
||||
{
|
||||
"stopType": "COMMERCIAL",
|
||||
"announceable": false,
|
||||
"stationCode": "10620",
|
||||
"arrivalPassthroughStepSides": null,
|
||||
"departurePassthroughStepSides": {
|
||||
"plannedTime": 1764918000000,
|
||||
"forecastedOrAuditedDelay": 430,
|
||||
"timeType": "AUDITED",
|
||||
"plannedPlatform": "1",
|
||||
"circulationState": "RUNNING",
|
||||
"showDelay": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"stopType": "NO_STOP",
|
||||
"stationCode": "C1062",
|
||||
"arrivalPassthroughStepSides": { /* ... */ },
|
||||
"departurePassthroughStepSides": { /* ... */ }
|
||||
}
|
||||
// ... más paradas
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Diferencia clave**:
|
||||
- `departures/arrivals` → `passthroughStep` (singular, solo la estación consultada)
|
||||
- `onepaths` → `passthroughSteps` (plural, array con todas las paradas del recorrido)
|
||||
|
||||
### Respuesta de Station Observations
|
||||
|
||||
```json
|
||||
{
|
||||
"stationObservations": [
|
||||
{
|
||||
"stationCode": "10200",
|
||||
"observation": "Texto de la observación"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Status Codes
|
||||
|
||||
| Código | Significado | Causa |
|
||||
|--------|-------------|-------|
|
||||
| 200 | ✅ Success | Petición exitosa con datos |
|
||||
| 204 | ⚠️ No Content | Autenticación correcta pero sin datos disponibles |
|
||||
| 400 | ❌ Bad Request | Payload incorrecto, campo requerido faltante o formato inválido |
|
||||
| 401 | ❌ Unauthorized | Sin permisos (claves con perfil limitado) |
|
||||
|
||||
**Nota importante sobre 204**: Un status 204 NO es un error. Significa que la autenticación y el payload son correctos, pero no hay datos disponibles para esa consulta específica.
|
||||
|
||||
## Tipos de Datos
|
||||
|
||||
### TrafficType (Tipos de tráfico)
|
||||
- `CERCANIAS` - Trenes de cercanías
|
||||
- `AVLDMD` - Alta Velocidad, Larga y Media Distancia
|
||||
- `OTHERS` - Otros tipos de tráfico
|
||||
- `TRAVELERS` - Viajeros
|
||||
- `GOODS` - Mercancías
|
||||
- `ALL` - Todos los tipos
|
||||
|
||||
### State (Estados para comercialService y comercialStopType)
|
||||
- `YES` - Sí
|
||||
- `NOT` - No
|
||||
- `BOTH` - Ambos
|
||||
|
||||
### PageInfoDTO
|
||||
```json
|
||||
{
|
||||
"pageNumber": 0
|
||||
}
|
||||
```
|
||||
|
||||
## Notas de Seguridad
|
||||
|
||||
- La app usa certificate pinning con claves públicas específicas
|
||||
- Los tokens están hardcodeados en la aplicación
|
||||
- Las User-keys son diferentes para cada servicio (estaciones vs circulaciones)
|
||||
- El token de registro `b9034774-c6e4-4663-a1a8-74bf7102651b` está en el código
|
||||
|
||||
## 🗺️ Códigos de Estación
|
||||
|
||||
**Total**: 1587 estaciones disponibles
|
||||
**Archivo**: `station_codes.txt` (raíz del proyecto)
|
||||
**Fuente**: `apk_extracted/assets/stations_all.json`
|
||||
|
||||
### Formato
|
||||
```
|
||||
código nombre tipos_tráfico
|
||||
```
|
||||
|
||||
### Top Estaciones
|
||||
```
|
||||
10200 Madrid Puerta de Atocha AVLDMD
|
||||
10302 Madrid Chamartín-Clara Campoamor AVLDMD
|
||||
71801 Barcelona Sants AVLDMD,CERCANIAS
|
||||
60000 València Nord AVLDMD
|
||||
11401 Sevilla Santa Justa AVLDMD
|
||||
50003 Alacant Terminal AVLDMD,CERCANIAS
|
||||
54007 Córdoba Central AVLDMD
|
||||
79600 Zaragoza Portillo AVLDMD,CERCANIAS
|
||||
03216 València J.Sorolla AVLDMD
|
||||
04040 Zaragoza Delicias AVLDMD,CERCANIAS
|
||||
```
|
||||
|
||||
## Notas de Implementación
|
||||
|
||||
Esta documentación se ha obtenido mediante ingeniería reversa del código decompilado de la aplicación Android de ADIF Elcano.
|
||||
|
||||
**Herramientas utilizadas**:
|
||||
- **Ghidra**: Extracción de claves de `libapi-keys.so`
|
||||
- **JADX**: Decompilación del APK
|
||||
- **Python 3**: Implementación del cliente
|
||||
- **Frida**: Análisis dinámico (opcional)
|
||||
|
||||
**Clases principales analizadas**:
|
||||
- `com.adif.elcanomovil.serviceNetworking.circulations.model.request.TrafficCirculationPathRequest`
|
||||
- `com.adif.elcanomovil.serviceNetworking.circulations.model.request.OneOrSeveralPathsRequest`
|
||||
- `com.adif.elcanomovil.serviceNetworking.stationObservations.model.StationObservationsRequest`
|
||||
- `com.adif.elcanomovil.serviceNetworking.circulations.model.request.CirculationPathRequest` (interface)
|
||||
- `com.adif.elcanomovil.serviceNetworking.circulations.model.request.TrafficType` (enum)
|
||||
- `com.adif.elcanomovil.serviceNetworking.interceptors.auth.ElcanoAuth` (algoritmo HMAC)
|
||||
|
||||
**Archivos clave**:
|
||||
- `apk_extracted/lib/x86_64/libapi-keys.so` - Claves de autenticación
|
||||
- `apk_extracted/assets/stations_all.json` - Base de datos de estaciones
|
||||
- `apk_decompiled/sources/com/adif/elcanomovil/` - Código fuente decompilado
|
||||
|
||||
---
|
||||
|
||||
**Última actualización**: 2025-12-05
|
||||
**Estado**: ✅ Proyecto completado con éxito
|
||||
Reference in New Issue
Block a user