# Certificado Digital para VeriFactu

## Introducción

VeriFactu requiere autenticación mTLS: la API presenta tu certificado al conectarse con la AEAT. Tanto en pruebas como en producción se usa el mismo certificado real — no existe un certificado especial de pruebas.

## Certificado válido: FNMT Persona Física

El **Certificado de Ciudadano (Persona Física) de la FNMT** es gratuito y válido tanto para el entorno de pruebas como para producción.

**Obtención gratuita — opciones:**

1. **Presencial en oficina AEAT** (recomendado): llevar el DNI a cualquier delegación de la Agencia Tributaria. Trámite en 2 minutos, sin cita previa en la mayoría.
2. **Con DNIe + NFC**: si tu DNI es posterior a 2015, puedes hacer todo el proceso online desde `sede.fnmt.gob.es` usando el chip NFC del DNI con tu móvil o un lector de tarjetas.
3. **Videollamada** (coste 2,99 € + IVA): servicio de identificación remota sin desplazamiento.

> La renovación online es gratuita mientras el certificado esté vigente (hasta 60 días antes de su vencimiento).

## Verificar el certificado (PowerShell)

```powershell
$cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2(".\data\certs\personal.p12", "TU_CONTRASEÑA")
Write-Host "Válido desde:" $cert.NotBefore
Write-Host "Válido hasta:" $cert.NotAfter
Write-Host "Asunto:"       $cert.Subject
Write-Host "Vigente:"      ($cert.NotAfter -gt (Get-Date))
```

El campo `Subject` debe contener `OU=CIUDADANOS` (persona física) y el NIF del titular.

## Registrar Nuevo Certificado

Una vez tengas el archivo .p12:

### 1. Copiar a una ubicación segura

```bash
cp micertificado.p12 ./data/certs/
```

### 2. Actualizar config.yml

```yaml
certificates:
  storage_path: ./data/certs/
  cert_file: ./data/certs/nuevo_cert.p12
  cert_password: TU_CONTRASEÑA
```

### 3. Convertir certificado

La API necesita el certificado en formato PEM:

```bash
python convert_cert.py ./data/certs/nuevo_cert.p12 TU_CONTRASEÑA
```

Esto genera:
- `cert_key.pem` (clave privada)
- `cert_cert.pem` (certificado público)

### 4. Reiniciar API

```bash
# Detener API
taskkill /F /IM main.exe

# Iniciar
go run main.go
```

### 5. Probar

```bash
python test_invoice.py
```

## Estructura de Directorios

```
VerifactuMidAPI/
├── data/
│   └── certs/
│       ├── personal.p12      # Tu certificado
│       ├── cert_key.pem     # Generado automáticamente
│       └── cert_cert.pem   # Generado automáticamente
└── config.yml              # Configuración
```


## Problemas Comunes

### "pkcs12: expected exactly two safe bags in the PFX PDU"
El archivo .p12 contiene más de un certificado (cadena completa con CA intermedias). La librería Go `pkcs12` espera solo el certificado de cliente y la clave privada.

**Solución:** Extraer solo el certificado de cliente y crear un nuevo P12:
```bash
# Extraer clave privada
openssl pkcs12 -in original.p12 -nocerts -nodes -passin pass:PASSWORD -out key.pem

# Extraer solo el certificado de cliente
openssl pkcs12 -in original.p12 -clcerts -nokeys -passin pass:PASSWORD -out client_cert.pem

# Crear nuevo P12 simplificado
openssl pkcs12 -export -out personal.p12 -inkey key.pem -in client_cert.pem \
  -passout pass:PASSWORD -name "personal" -legacy -certpbe PBE-SHA1-3DES -keypbe PBE-SHA1-3DES
```

### "pkcs12: unknown digest algorithm: 2.16.840.1.101.3.4.2.1"
La librería Go `pkcs12` no soporta cifrado SHA-256 en archivos P12. Se necesita usar cifrado legacy (SHA1/3DES).

**Solución:** Recrear el P12 con cifrado legacy (ver comando `-legacy -certpbe PBE-SHA1-3DES -keypbe PBE-SHA1-3DES` arriba).

### "403 Forbidden"
- El certificado no está autorizado en el entorno de pruebas
- O el certificado es de producción y estás en test

### "Certificate expired"
- El certificado ha caducado
- Solicitar uno nuevo

### "Invalid password"
- La contraseña del .p12 es incorrecta

### "tls: failed to find any PEM data"
- Error al convertir el certificado
- Ejecutar `python convert_cert.py` manualmente para ver el error

## Siguientes Pasos

1. Obtener certificado de pruebas FNMT
2. Registrar en la API
3. Probar con `python test_invoice.py`
4. Verificar que devuelve estado "Correcto" (no "Correcto (local)")

## Recursos

- FNMT: https://www.fnmt.es
- Certificados válidos VeriFactu: https://www.sede.fnmt.gob.es/preguntas-frecuentes/certificado-de-representante/-/asset_publisher/eIal9z2VE0Kb/content/certificados-electr%C3%B3nicos-v%C3%A1lidos-para-el-sistema-veri*factu
- Certificado entidad sin personalidad jurídica: https://www.sede.fnmt.gob.es/certificados/certificado-de-representante/entidad-sin-personalidad-juridica
- Portal AEAT Pruebas: https://preportal.aeat.es
- Sede AEAT: https://sede.agenciatributaria.gob.es