142 lines
4.7 KiB
Markdown
142 lines
4.7 KiB
Markdown
|
|
# 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
|