# Análisis de Request Bodies - API ADIF

> Ingeniería reversa del paquete `com.adif.elcanomovil.serviceNetworking`
>
> Fecha: 2025-12-04

## Tabla de Contenidos
- [1. Headers de Autenticación](#1-headers-de-autenticación)
- [2. Request Bodies](#2-request-bodies)
- [3. Endpoints y URLs Base](#3-endpoints-y-urls-base)
- [4. Configuración de Red](#4-configuración-de-red)
- [5. Sistema de Autenticación](#5-sistema-de-autenticación)
- [6. Referencias de Código](#6-referencias-de-código)

---

## 1. Headers de Autenticación

### 1.1 Headers Estáticos

**Archivo:** `ServicePaths.java:67-76`

#### Para Circulaciones
```
User-key: f4ce9fbfa9d721e39b8984805901b5df
Content-Type: application/json;charset=utf-8
```

#### Para Estaciones
```
User-key: 0d021447a2fd2ac64553674d5a0c1a6f
Content-Type: application/json;charset=utf-8
```

#### Para AVISA (Login/Refresh)
```
Authorization: Basic YXZpc3RhX2NsaWVudF9hbmRyb2lkOjh5WzZKNyFmSjwhXypmYXE1NyNnOSohNElwa2MjWC1BTg==
```

**Decodificado (Base64):**
```
avista_client_android:8y[6J7!fJ<_*faq57#g9*!4Ipkc#X-AN
```

### 1.2 Headers Dinámicos (Generados por AuthHeaderInterceptor)

**Archivo:** `AuthHeaderInterceptor.java:38-83`

La aplicación genera automáticamente estos headers adicionales:

```
X-CanalMovil-Authentication: <token_generado>
X-CanalMovil-deviceID: <device_id>
X-CanalMovil-pushID: <push_id>
```

**Algoritmo de generación:**
El token se calcula usando la clase `ElcanoClientAuth` con:
- Host del servidor
- Path completo de la URL
- Parámetros de query
- Método HTTP (GET/POST)
- Payload (body serializado sin espacios)
- ID de usuario persistente
- Cliente: "AndroidElcanoApp"

---

## 2. Request Bodies

### 2.1 Circulaciones - Salidas/Llegadas/Entre Estaciones

**Endpoints:**
- `/portroyalmanager/secure/circulationpaths/departures/traffictype/`
- `/portroyalmanager/secure/circulationpaths/arrivals/traffictype/`
- `/portroyalmanager/secure/circulationpaths/betweenstations/traffictype/`

**Modelo:** `TrafficCirculationPathRequest`
**Archivo:** `circulations/model/request/TrafficCirculationPathRequest.java:10-212`

```json
{
  "commercialService": "YES|NOT|BOTH",
  "commercialStopType": "YES|NOT|BOTH",
  "destinationStationCode": "string o null",
  "originStationCode": "string o null",
  "page": {
    "pageNumber": 0
  },
  "stationCode": "string o null",
  "trafficType": "CERCANIAS|AVLDMD|OTHERS|TRAVELERS|GOODS|ALL"
}
```

#### Ejemplo Real
```json
{
  "commercialService": "BOTH",
  "commercialStopType": "BOTH",
  "destinationStationCode": null,
  "originStationCode": null,
  "page": {
    "pageNumber": 0
  },
  "stationCode": "60000",
  "trafficType": "ALL"
}
```

#### Valores Permitidos

**commercialService / commercialStopType** (`CirculationPathRequest.java:65-67`):
- `YES` - Solo servicios/paradas comerciales
- `NOT` - Sin servicios/paradas comerciales
- `BOTH` - Todos los tipos

**trafficType** (`TrafficType.java:16-21`):
- `CERCANIAS` - Trenes de cercanías
- `AVLDMD` - Alta velocidad larga y media distancia
- `OTHERS` - Otros tipos
- `TRAVELERS` - Viajeros
- `GOODS` - Mercancías
- `ALL` - Todos los tipos

---

### 2.2 Circulaciones - Rutas Específicas

**Endpoints:**
- `/portroyalmanager/secure/circulationpathdetails/onepaths/`
- `/portroyalmanager/secure/circulationpathdetails/severalpaths/`

**Modelo:** `OneOrSeveralPathsRequest`
**Archivo:** `circulations/model/request/OneOrSeveralPathsRequest.java:11-140`

```json
{
  "allControlPoints": true/false/null,
  "commercialNumber": "string o null",
  "destinationStationCode": "string o null",
  "launchingDate": 1733356800000,
  "originStationCode": "string o null"
}
```

#### Ejemplo Real
```json
{
  "allControlPoints": true,
  "commercialNumber": "04138",
  "destinationStationCode": "60000",
  "launchingDate": 1733356800000,
  "originStationCode": "71801"
}
```

**Notas importantes:**
- `launchingDate` es un timestamp en **milisegundos** (tipo Long en Java)
- `allControlPoints`: indica si se quieren todos los puntos de control de la ruta
- Todos los campos son opcionales (pueden ser null)

---

### 2.3 Composiciones de Trenes

**Endpoint:** `/portroyalmanager/secure/circulationpaths/compositions/path/`

**Modelo:** `OneOrSeveralPathsRequest` (mismo que rutas)
**Archivo:** `compositions/CompositionsService.java:14-18`

```json
{
  "allControlPoints": true/false/null,
  "commercialNumber": "string o null",
  "destinationStationCode": "string o null",
  "launchingDate": 1733356800000,
  "originStationCode": "string o null"
}
```

---

### 2.4 Estaciones - Detalles de una Estación

**Endpoint:** `/portroyalmanager/secure/stations/onestation/`

**Modelo:** `OneStationRequest`
**Archivo:** `stations/model/OneStationRequest.java:9-93`

```json
{
  "detailedInfo": {
    "extendedStationInfo": true,
    "stationActivities": true,
    "stationBanner": true,
    "stationCommercialServices": true,
    "stationInfo": true,
    "stationServices": true,
    "stationTransportServices": true
  },
  "stationCode": "60000",
  "token": "string"
}
```

**Notas importantes:**
- El objeto `detailedInfo` controla qué información se devuelve en la respuesta
- Todos los campos booleanos por defecto son `true` (ver `DetailedInfoDTO.java:149`)
- El `token` es requerido

#### Campos de DetailedInfo

**Archivo:** `stations/model/DetailedInfoDTO.java:10-151`

| Campo | Tipo | Descripción |
|-------|------|-------------|
| `extendedStationInfo` | boolean | Información extendida de la estación |
| `stationActivities` | boolean | Actividades de la estación |
| `stationBanner` | boolean | Banner/anuncios de la estación |
| `stationCommercialServices` | boolean | Servicios comerciales |
| `stationInfo` | boolean | Información básica |
| `stationServices` | boolean | Servicios disponibles |
| `stationTransportServices` | boolean | Servicios de transporte |

---

### 2.5 Observaciones de Estaciones

**Endpoint:** `/portroyalmanager/secure/stationsobservations/`

**Modelo:** `StationObservationsRequest`
**Archivo:** `stationObservations/model/StationObservationsRequest.java:10-53`

```json
{
  "stationCodes": ["60000", "71801"]
}
```

#### Ejemplo Real
```json
{
  "stationCodes": ["60000", "71801", "79600"]
}
```

**Notas:**
- Array de códigos de estación (strings)
- Campo requerido
- Puede contener múltiples códigos

---

## 3. Endpoints y URLs Base

### 3.1 URLs Base

**Archivo:** `di/NetworkModule.java:73-159`

| Servicio | URL Base | Autenticación |
|----------|----------|---------------|
| **Circulaciones** | `https://circulacion.api.adif.es` | Securizada (con AuthHeaderInterceptor) |
| **Estaciones** | `https://estaciones.api.adif.es` | Securizada (con AuthHeaderInterceptor) |
| **AVISA** | `https://avisa.adif.es` | Básica (sin AuthHeaderInterceptor) |
| **Elcano Web** | `https://elcanoweb.adif.es/api/` | - |

### 3.2 Paths Completos - Estaciones

**Archivo:** `ServicePaths.java:106-112`

```
GET  /portroyalmanager/secure/stations/allstations/reducedinfo/{token}/
POST /portroyalmanager/secure/stations/onestation/
POST /portroyalmanager/secure/stationsobservations/
```

### 3.3 Paths Completos - Circulaciones

**Archivo:** `ServicePaths.java:41-51`

```
POST /portroyalmanager/secure/circulationpaths/departures/traffictype/
POST /portroyalmanager/secure/circulationpaths/arrivals/traffictype/
POST /portroyalmanager/secure/circulationpaths/betweenstations/traffictype/
POST /portroyalmanager/secure/circulationpathdetails/onepaths/
POST /portroyalmanager/secure/circulationpathdetails/severalpaths/
```

### 3.4 Paths Completos - Composiciones

**Archivo:** `ServicePaths.java:55-61`

```
POST /portroyalmanager/secure/circulationpaths/compositions/path/
```

### 3.5 Paths Completos - AVISA

**Archivo:** `ServicePaths.java:82-92` y `ServicePaths.java:29-37`

```
POST /avisa-ws/api/token                    (login)
POST /avisa-ws/api/token                    (refresh)
POST /avisa-ws/api/v1/client                (register)
GET  /avisa-ws/api/v1/station               (stations)
GET  /avisa-ws/api/v1/category              (categories)
GET  /avisa-ws/api/v1/incidence             (incidences list)
GET  /avisa-ws/api/v1/incidence/{id}        (incidence details)
POST /avisa-ws/api/v1/incidence             (create incidence)
```

---

## 4. Configuración de Red

### 4.1 Configuración de OkHttpClient

**Archivo:** `di/NetworkModule.java:100-132`

#### Cliente Básico
```kotlin
OkHttpClient.Builder()
    .certificatePinner(certificatePinner)
    .connectTimeout(60, TimeUnit.SECONDS)
    .readTimeout(60, TimeUnit.SECONDS)
    .build()
```

#### Cliente Securizado (con autenticación)
```kotlin
OkHttpClient.Builder()
    .addInterceptor(AuthHeaderInterceptor(userId))
    .certificatePinner(certificatePinner)
    .connectTimeout(60, TimeUnit.SECONDS)
    .readTimeout(60, TimeUnit.SECONDS)
    .build()
```

**Timeouts:**
- Connect timeout: 60 segundos
- Read timeout: 60 segundos

### 4.2 Servicios que Usan Cliente Securizado

**Archivo:** `di/NetworkModule.java`

- `CirculationService` (línea 73)
- `StationsService` (línea 142)
- `StationObservationsService` (línea 135)
- `CompositionsService` (línea 156)

### 4.3 Servicios que Usan Cliente Básico

- `AvisaLoginService` (línea 50)
- `AvisaStationsService` (línea 57)
- `IncidenceService` (línea 80)
- `SubscriptionsService` (línea 149)

---

## 5. Sistema de Autenticación

### 5.1 AuthHeaderInterceptor

**Archivo:** `interceptors/AuthHeaderInterceptor.java:27-84`

Este interceptor se ejecuta en **todas** las peticiones de los servicios securizados.

#### Proceso de Autenticación

1. **Generación de User ID Persistente**
   - Usa `GeneratePersistentUserIdUseCase`
   - El ID se guarda y reutiliza entre sesiones

2. **Construcción del Token**
   ```java
   ElcanoClientAuth.Builder()
       .host(request.url().host())
       .contentType("application/json;charset=utf-8")
       .path(request.url().encodedPath())
       .params(request.url().encodedQuery())
       .xElcanoClient("AndroidElcanoApp")
       .xElcanoUserId(userId)
       .httpMethodName(request.method())
       .payload(bodyJsonWithoutSpaces)
       .build()
   ```

3. **Generación de Headers**
   - El objeto `ElcanoClientAuth` genera headers de autenticación
   - Se añaden automáticamente a la petición

#### Headers Generados

```
X-CanalMovil-Authentication: <token_calculado>
X-CanalMovil-deviceID: <device_id>
X-CanalMovil-pushID: <push_id>
```

### 5.2 Clase GetKeysHelper

**Archivo:** `AuthHeaderInterceptor.java:44`

Proporciona claves para la autenticación:
- `getKeysHelper.a()` - Primera clave
- `getKeysHelper.b()` - Segunda clave

Estas claves se usan en el algoritmo de firma/autenticación.

### 5.3 Certificate Pinning

**Archivo:** `di/NetworkModule.java:64-70`

La aplicación usa **Certificate Pinning** para prevenir ataques MITM:
- Los certificados SSL esperados están en `PinningRepository`
- Se cargan de forma asíncrona al inicio
- Todas las peticiones verifican el certificado del servidor

---

## 6. Referencias de Código

### 6.1 Archivos Clave

| Archivo | Ubicación | Descripción |
|---------|-----------|-------------|
| `ServicePaths.java` | `serviceNetworking/` | Paths y headers estáticos |
| `AuthHeaderInterceptor.java` | `serviceNetworking/interceptors/` | Generación de auth headers |
| `NetworkModule.java` | `serviceNetworking/di/` | Configuración Retrofit/OkHttp |
| `CirculationService.java` | `serviceNetworking/circulations/` | API de circulaciones |
| `StationsService.java` | `serviceNetworking/stations/` | API de estaciones |
| `StationObservationsService.java` | `serviceNetworking/stationObservations/` | API de observaciones |
| `CompositionsService.java` | `serviceNetworking/compositions/` | API de composiciones |

### 6.2 Modelos de Request

| Modelo | Archivo | Uso |
|--------|---------|-----|
| `TrafficCirculationPathRequest` | `circulations/model/request/` | Departures, Arrivals, BetweenStations |
| `OneOrSeveralPathsRequest` | `circulations/model/request/` | OnePaths, SeveralPaths, Compositions |
| `OneStationRequest` | `stations/model/` | Detalles de estación |
| `DetailedInfoDTO` | `stations/model/` | Configuración de info detallada |
| `StationObservationsRequest` | `stationObservations/model/` | Observaciones de estaciones |

### 6.3 Líneas de Código Importantes

- Headers estáticos: `ServicePaths.java:67-76`
- User-key circulaciones: `ServicePaths.java:67`
- User-key estaciones: `ServicePaths.java:68`
- AVISA login token: `ServicePaths.java:70`
- Auth interceptor: `AuthHeaderInterceptor.java:38-83`
- Base URL circulaciones: `NetworkModule.java:76`
- Base URL estaciones: `NetworkModule.java:145`
- Enum TrafficType: `TrafficType.java:16-21`
- Enum State: `CirculationPathRequest.java:65-67`

---

## 7. Notas Adicionales

### 7.1 Serialización JSON

- **Biblioteca usada:** Moshi (configurado en `NetworkModule.java:87-96`)
- **Formato:** Los nombres de campos en JSON coinciden exactamente con los nombres de propiedades en Java
- **Null handling:** Los campos null se incluyen en el JSON
- **Formato de fecha:** Timestamps en milisegundos (Long)

### 7.2 Consideraciones de Seguridad

1. **User-keys hardcodeadas:** Las claves API están en el código (fáciles de extraer)
2. **Certificate Pinning:** Dificulta interceptar tráfico con proxy
3. **Autenticación dinámica:** Los headers X-CanalMovil requieren conocer el algoritmo
4. **AVISA token:** Credenciales Base64 en el código (pueden decodificarse)

### 7.3 Testing

Para probar estos endpoints:

1. **Extraer el algoritmo de autenticación:**
   - Analizar clase `ElcanoClientAuth` (no incluida en estos archivos)
   - O bien, usar Frida para hookear y capturar headers generados

2. **Bypass Certificate Pinning:**
   - Usar Frida con script de bypass SSL pinning
   - O modificar el APK para deshabilitar pinning

3. **Interceptar tráfico:**
   - mitmproxy con Frida
   - Burp Suite con Frida
   - Captura directa con tcpdump/Wireshark

---

## 8. Próximos Pasos

- [ ] Extraer y analizar clase `ElcanoClientAuth`
- [ ] Reverse engineering del algoritmo de firma
- [ ] Capturar tráfico real con Frida
- [ ] Implementar generador de headers de autenticación
- [ ] Probar endpoints con Postman/curl
- [ ] Documentar respuestas de cada endpoint

---

**Última actualización:** 2025-12-04
**Fuente:** APK decompilado de ADIF El Cano Móvil
**Herramientas:** JADX, análisis manual de código Java
