Notary¶
Servicio de firma digital de documentos PDF con soporte PAdES y firma visual.
| Propiedad | Valor |
|---|---|
| Puerto | :8001 (desarrollo) |
| Framework | FastAPI 2.1.0 |
| Firma criptografica | pyHanko 0.25.2 (PAdES-B-T) |
| Firma visual | ReportLab + PyPDF2 |
| Analisis PDF | PyMuPDF (fitz) |
| Runtime | Python 3.11, Gunicorn 21.2.0 (3 workers) |
| Deploy | Docker |
Que hace¶
Notary recibe un PDF y datos del firmante, aplica una firma digital (PAdES criptografica o visual) y opcionalmente estampa informacion del documento (numero, ciudad, fecha).
flowchart LR
A[Backend] -->|POST /sign-pdf| B[Notary]
B --> C{Certificado .p12?}
C -->|Si| D[Firma PAdES-B-T + Visual]
C -->|No + test| E[Firma Visual]
C -->|No + prd| F[Error 400]
D --> G[PDF firmado]
E --> G
G -->|Response| A
Modos de firma¶
Firma PAdES (recomendado para produccion)¶
Firma digital criptografica embebida en el PDF, cumpliendo el estandar PAdES-B-T (Basic with Time):
- Firma criptografica con certificado PKCS#12 (.p12)
- Timestamp de servidor TSA (Time Stamping Authority)
- Representacion visual con datos del firmante
- Verificable en Adobe Acrobat Reader (clic en la firma)
Firma Visual (solo testing)¶
Firma con elementos visuales (texto y lineas) sin componente criptografico:
- Nombre, cargo, departamento, entidad del firmante
- Timestamp de servidor
- No es verificable criptograficamente
Comportamiento por ambiente¶
| Ambiente | ENVIRONMENT |
FALLBACK_TO_VISUAL |
Si no hay certificado |
|---|---|---|---|
| Desarrollo | test |
true (default) |
Usa firma visual |
| Produccion | prd |
false (default) |
Error 400 |
Estructura del proyecto¶
GDI-Notary/
├── app/
│ ├── main.py # Endpoints FastAPI
│ ├── config.py # Constantes y configuracion
│ ├── auth.py # Autenticacion API Key
│ ├── layout.py # Algoritmo posicionamiento 2 columnas
│ ├── signature_inserter.py # Firma visual (ReportLab + PyPDF2)
│ ├── document_stamper.py # Estampado primera/ultima pagina
│ ├── validators.py # Validaciones de entrada
│ ├── certificate_loader.py # Carga certificados .p12 por tenant
│ └── pades_signer.py # Firma PAdES con pyHanko
├── certs/
│ ├── {tenant_id}.p12 # Certificados por tenant
│ └── passwords.json # Mapeo tenant -> password
├── fonts/
│ └── Roboto-Bold.ttf # Fuente para firma visual
├── scripts/
│ └── generate_test_cert.py # Genera certificados de prueba
├── gunicorn_conf.py # Config produccion
├── Dockerfile
└── requirements.txt
Endpoints¶
| Endpoint | Metodo | Auth | Descripcion |
|---|---|---|---|
/sign-pdf |
POST | Si | Firma PDF (PAdES o visual) |
/health |
GET | No | Health check |
/certificate/{tenant_id} |
GET | Si | Info de certificado |
/certificates |
GET | Si | Lista certificados |
Variables de entorno¶
| Variable | Test | Produccion | Descripcion |
|---|---|---|---|
ENVIRONMENT |
test |
prd |
Ambiente de ejecucion |
API_KEY |
miapikey |
(secreto) | API Key de autenticacion |
CERTS_DIR |
./certs |
./certs |
Directorio de certificados |
TSA_URL |
http://timestamp.digicert.com |
(igual) | Servidor de timestamp |
FALLBACK_TO_VISUAL |
true |
false |
Firma visual si no hay cert |
GUNICORN_WORKERS |
3 |
3 |
Workers de Gunicorn |
GUNICORN_TIMEOUT |
90 |
90 |
Timeout en segundos |
Flujo completo de firma¶
sequenceDiagram
participant B as Backend
participant N as Notary
participant TSA as Timestamp Authority
B->>N: POST /sign-pdf (PDF + datos firmante)
N->>N: Validar PDF (formato, tamano)
N->>N: Validar parametros firma
N->>N: Validar tenant_id (path traversal check)
alt Certificado existe
N->>N: Cargar .p12 + password
N->>N: Validar vigencia certificado
N->>N: Calcular posicion (layout 2 columnas)
N->>N: Estampar documento (si aplica)
N->>TSA: Solicitar timestamp
TSA-->>N: Timestamp token
N->>N: Firma PAdES-B-T + visual
else Sin certificado + test
N->>N: Calcular posicion (layout 2 columnas)
N->>N: Estampar documento (si aplica)
N->>N: Firma visual (ReportLab)
else Sin certificado + prd
N-->>B: Error 400 CERTIFICATE_NOT_FOUND
end
N-->>B: PDF firmado (X-Signature-Type header)