Contratos / Firma
De un vistazo
Sección titulada «De un vistazo»El dominio de firma contiene seis contratos. Definen cómo producir una firma CMS, aplicar una marca de tiempo RFC 3161, firmar con una clave de hardware y habilitar la validación a largo plazo. El núcleo expone los contratos; las ediciones Pro y Enterprise incluyen las implementaciones de producción.
Instalación
Sección titulada «Instalación»composer require nextpdf/core:^3Panorama conceptual
Sección titulada «Panorama conceptual»Una firma digital de PDF es una estructura CMS SignedData almacenada en el diccionario de firma. La entrada Contents contiene la estructura codificada en DER. La entrada ByteRange identifica los intervalos de bytes cubiertos por el resumen. El resumen cubre todo el archivo y excluye el propio valor de la firma; véase ISO 32000-2 §12.8.1. La estructura CMS sigue RFC 5652 §5.1: una secuencia de versión, algoritmos de resumen, contenido encapsulado e información del firmante.
SignerInterface es el contrato del núcleo. Produce el CMS SignedData para un rango de bytes, aplica una marca de tiempo a un valor de firma e informa si admite la validación a largo plazo. Cubre los niveles base de PAdES desde B-B hasta B-LTA, según se definen en ETSI EN 319 142. Cada nivel superior añade material adicional. B-B contiene la firma base. B-T añade una marca de tiempo de firma. B-LT añade datos de revocación. B-LTA añade una marca de tiempo de archivo.
HsmSignerInterface firma con una clave alojada en un Módulo de Seguridad de Hardware. La clave privada no sale del perímetro del hardware. El contrato devuelve el certificado del firmante y la cadena de certificados en formato DER para que la capa CMS pueda construir la estructura. DeferredSignerInterface extiende SignerInterface para la firma asíncrona. El invocador envía los datos, recibe un identificador de trabajo, consulta periódicamente hasta que se completa y luego recupera el resultado. Usar este contrato cuando un HSM remoto o un servicio de claves en la nube no devuelve un resultado de inmediato.
TimestampProviderInterface solicita un token de marca de tiempo RFC 3161. El protocolo consiste en una solicitud y una respuesta intercambiadas con una Autoridad de Sellado de Tiempo; véase RFC 3161 §2.4. El messageImprint del token es un hash del valor de la firma; RFC 3161 §2.4.2. LtvManagerInterface habilita la validación a largo plazo. Recopila la cadena de certificados, obtiene respuestas OCSP y CRL, construye el Document Security Store y añade una marca de tiempo de documento para B-LTA. CryptoPolicyInterface controla qué algoritmos de hash, firma y cifrado, así como qué fortalezas de clave, se permiten antes de que se ejecute cualquier operación criptográfica.
Superficie de la API
Sección titulada «Superficie de la API»| Tipo | Clase | Miembros clave | Estabilidad | Desde |
|---|---|---|---|---|
SignerInterface | interface | sign(string): SignatureResult, timestamp(string): string, supportsLtv(): bool | estable | 1.0.0 |
HsmSignerInterface | interface | sign(string, string): string, getCertificateDer(), getCertificateChainDer(), getPublicKeyAlgorithm() | estable | 1.0.0 |
DeferredSignerInterface | interface | submitForSigning(string): string, retrieveSignature(string), isComplete(string) (extiende SignerInterface) | experimental | 3.0.0 |
TimestampProviderInterface | interface | getTimestamp(string): string | experimental | 3.0.0 |
LtvManagerInterface | interface | enableLtv(...), addDocumentTimestamp(...) | estable | 1.10.0 |
CryptoPolicyInterface | interface | isHashAlgorithmAllowed(), isSignatureAlgorithmAllowed(), isEncryptionAlgorithmAllowed(), isKeyStrengthAllowed(), getPreferredHashAlgorithm(), getName() | estable | 1.9.0 |
SignerInterface::sign() devuelve un NextPDF\Security\Signature\SignatureResult. La propiedad pública $cmsSignedData contiene el CMS codificado en DER. La propiedad pública $digestHex contiene el resumen SHA-256. La propiedad pública $timestampToken contiene el token TSA opcional. Los métodos de acceso son toHex() / toHexPadded(int) / getSize() / hasTimestamp(). HsmSignerInterface::sign() devuelve bytes codificados en DER para RSA y bytes r‖s en bruto para ECDSA. El argumento de algoritmo utiliza identificadores de OpenSSL.
Ejemplo de código — Inicio rápido
Sección titulada «Ejemplo de código — Inicio rápido»<?php
declare(strict_types=1);
require_once __DIR__ . '/../../vendor/autoload.php';
use NextPDF\Contracts\SignerInterface;
/** * Sign a byte range with any SignerInterface implementation. * * @param SignerInterface $signer A core or Premium signer. * @param string $byteRange The PDF byte range to sign. * * @return string Hex-encoded CMS SignedData for the PDF /Contents field. */function signByteRange(SignerInterface $signer, string $byteRange): string{ $result = $signer->sign($byteRange);
return $result->toHex();}SignerInterface::sign() devuelve un SignatureResult. toHex() produce la cadena hexadecimal que el escritor coloca en el campo /Contents; los bytes DER en bruto están en la propiedad pública $cmsSignedData. La función depende del contrato, no de una clase concreta. Tanto un firmante de prueba del núcleo como un firmante con HSM de Premium satisfacen ese contrato.
Ejemplo de código — Producción
Sección titulada «Ejemplo de código — Producción»<?php
declare(strict_types=1);
require_once __DIR__ . '/../../vendor/autoload.php';
use NextPDF\Contracts\CryptoPolicyInterface;use NextPDF\Contracts\SignerInterface;use NextPDF\Contracts\TimestampProviderInterface;use NextPDF\Exception\NextPdfException;use Psr\Log\LoggerInterface;
final readonly class TimestampedSigningService{ public function __construct( private SignerInterface $signer, private TimestampProviderInterface $timestamps, private CryptoPolicyInterface $policy, private LoggerInterface $logger, ) {}
/** * Sign a byte range, then timestamp the CMS structure. * * @param string $byteRange The PDF byte range to sign. * * @return array{cms: string, digest: string, tst: string} */ public function sign(string $byteRange): array { if (!$this->policy->isHashAlgorithmAllowed($this->policy->getPreferredHashAlgorithm())) { throw new \LogicException('Preferred hash rejected by crypto policy.'); }
try { $signature = $this->signer->sign($byteRange); $token = $this->timestamps->getTimestamp($signature->cmsSignedData);
return [ 'cms' => $signature->toHex(), 'digest' => $signature->digestHex, 'tst' => $token, ]; } catch (NextPdfException $e) { $this->logger->error('Signing failed', [ 'policy' => $this->policy->getName(), 'error' => $e->getMessage(), ]);
throw $e; } }}El servicio inyecta tres contratos. La política criptográfica se consulta antes de la operación de firma. SignatureResult expone los bytes CMS en la propiedad pública $cmsSignedData, el resumen SHA-256 en $digestHex y toHex() para la cadena hexadecimal de /Contents. El proveedor de marca de tiempo recibe los bytes CMS y devuelve un token codificado en DER. El bloque catch registra el nombre de la política y vuelve a lanzar la excepción. En ningún caso silencia el fallo.
Casos límite y trampas
Sección titulada «Casos límite y trampas»- El resumen del rango de bytes debe excluir el valor de la firma. Un resumen que cubre la entrada
Contentsproduce una firma que no puede verificarse nunca; ISO 32000-2 §12.8.1. SignerInterface::supportsLtv()informa sobre la capacidad, no sobre el estado. Un firmante puede admitir la validación a largo plazo y aun así producir una firma B-B cuando no hay configurado ningún servicio de marca de tiempo.DeferredSignerInterface::retrieveSignature()devuelvenullhasta que el trabajo se completa. Conviene consultar primeroisComplete()para evitar transferir la carga útil en cada comprobación. La recuperación es idempotente una vez que existe un resultado.LtvManagerInterface::addDocumentTimestamp()debe ejecutarse después de que se escriba el Document Security Store. Ejecutarlo primero produce una estructura B-LTA no válida.CryptoPolicyInterfacedevuelvetruepara todos los algoritmos cuando no se establece ninguna política. En un entorno regulado, establecer una política explícita; no confiar en el valor predeterminado abierto.HsmSignerInterface::getCertificateChainDer()excluye el certificado del firmante. UsargetCertificateDer()para el certificado hoja del firmante y el método de cadena para los intermedios.
Rendimiento
Sección titulada «Rendimiento»El costo de la firma está dominado por la operación criptográfica y cualquier intercambio de red de ida y vuelta, no por el contrato. Una firma de software local tarda milisegundos de un solo dígito. Una firma con HSM añade el intercambio de ida y vuelta con el dispositivo. Una marca de tiempo añade un intercambio de red de ida y vuelta con la Autoridad de Sellado de Tiempo. La validación a largo plazo añade una obtención de OCSP o CRL por cada certificado de la cadena. El performance_budget de 1500 ms de tiempo de reloj cubre una única firma con marca de tiempo con una TSA remota en una conexión activa. La validación a largo plazo contra un punto de conexión de revocación lento lo supera y debería ejecutarse fuera de la ruta de la solicitud. El perfil de reproducibilidad es structural, no bitwise. Una marca de tiempo incrusta el instante de firma, por lo que dos ejecuciones difieren en los bytes de la marca de tiempo mientras la estructura del documento permanece idéntica.
Notas de seguridad
Sección titulada «Notas de seguridad»Los contratos de firma son el límite criptográfico principal del motor, por lo que el modelo de amenazas es explícito. La custodia de claves es la primera preocupación: HsmSignerInterface mantiene la clave privada dentro del perímetro del hardware, y el contrato nunca expone el material de la clave. La degradación de algoritmos es la segunda: CryptoPolicyInterface bloquea los hashes débiles y las claves cortas antes de la operación, lo que permite que un despliegue aplique un perfil FIPS 140-3 o eIDAS sin bifurcar el motor. La confianza en la marca de tiempo es la tercera: un token RFC 3161 solo es tan confiable como la Autoridad de Sellado de Tiempo, por lo que el contrato del proveedor es inyectable y cada despliegue fija su propia autoridad. La validación a largo plazo es la cuarta: el material de revocación se obtiene en el momento de la firma y se almacena en el Document Security Store para que la verificación sobreviva a la expiración del certificado. Tratar como no confiable toda entrada del firmante. El motor calcula el rango de bytes; nunca se acepta del invocador. Esta página está marcada como export_control_class: legal-review-required porque los contratos rigen la firma criptográfica. La prosa parafrasea todas las fuentes normativas y no cita ninguna, conforme a la higiene de citas.
Conformidad
Sección titulada «Conformidad»| Afirmación | Estándar | Cláusula | Evidencia |
|---|---|---|---|
El valor de la firma se almacena codificado en DER en la entrada Contents del diccionario de firma, como CMS SignedData o un TimeStampToken. | ISO 32000-2 | §12.8.1 | |
El resumen se calcula sobre el rango de bytes definido por la matriz ByteRange y excluye el valor de la firma. | ISO 32000-2 | §12.8.1 | , |
| El material de validación a largo plazo se transporta en el Document Security Store con entradas VRI, OCSP, CRL y de certificado. | ISO 32000-2 | §12.8.4.3 | , |
| La validación a largo plazo de PAdES coloca los datos de validación en los diccionarios DSS y VRI. | ETSI EN 319 142-2 | §6.3 | , |
Un token de marca de tiempo RFC 3161 vincula un hash del valor de la firma a través de messageImprint, intercambiado mediante una solicitud y una respuesta TSA. | RFC 3161 | §2.4.2, §2.4 | , |
| La secuencia CMS SignedData contiene la versión, los algoritmos de resumen, el contenido encapsulado y la información del firmante. | RFC 5652 | §5.1 |
Todas las cláusulas están parafraseadas. NextPDF no reproduce texto normativo. Consultar los estándares publicados para conocer la redacción autorizada.
Contexto comercial
Sección titulada «Contexto comercial»El núcleo expone y congela los contratos de firma. Las implementaciones de producción de HsmSignerInterface, LtvManagerInterface y el firmante diferido se incluyen en las ediciones Pro y Enterprise, junto con la integración de hardware PKCS#11 y PAdES B-LT y B-LTA. El núcleo las resuelve en tiempo de ejecución con class_exists() y las adapta al contrato, de modo que el motor de código abierto no incorpora ninguna dependencia comercial y la API no cambia al actualizar.
Consulta también
Sección titulada «Consulta también»- Contratos: 41 interfaces públicas (SPI) — visión general de la SPI y de los niveles de estabilidad.
- Contratos / Política de seguridad — control de algoritmos y claves de
CryptoPolicyInterface. - Contratos / Extracción — aplicación de PDF/A que se combina con el archivo firmado.
- Seguridad — superficie de implementación de cifrado y firma.
- Auditoría — registro de auditoría de nombres de política y eventos de firma.
- Excepción — jerarquía
NextPdfExceptionlanzada por los firmantes.