Ir al contenido

Inspeccionar una firma existente y comprender el límite de confianza

Esta receta utiliza el inspector de Core para detectar si un PDF contiene una firma y, a continuación, delimitar el alcance. Detectar una firma no equivale a verificarla. La verificación criptográfica, la validación de la ruta de confianza y la comprobación de revocación pertenecen a Premium o a herramientas externas.

  • Core instalado: composer require nextpdf/core:^3.
  • Un archivo PDF para inspeccionar.
  1. Leer los bytes del PDF.
  2. Crear un Inspector y llamar a inspect().
  3. Leer InspectResult::$hasSigned. true significa que existe un diccionario de firma en el archivo.
  4. Leer InspectResult::$isEncrypted y los indicadores de riesgo para conocer el contexto asociado.
  5. Enviar el archivo a un verificador para la decisión criptográfica. El inspector informa sobre la presencia, no sobre la validez.

El diagrama siguiente muestra dos cosas: el límite que esta receta deja claro, la presencia no es validez, y el conjunto completo de comprobaciones que una verificación real todavía debe realizar.

false

true

Yes

No

Yes

No / unknown

Incoming PDF

Inspector.inspect — Quick

hasSigned?

No signature present

Signature dictionary PRESENT

NOT yet validity

Recompute byte-range digest

ISO 32000-2 §12.8.1

Validate CMS SignedData

Build & check X.509 path

to a chosen trust anchor

Check revocation OCSP / CRL

Long-term input?

Read DSS validation material

All checks evaluated

Every check passed?

Trustworthy

Not trusted — do not act

Diagram
<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Inspect\Inspector;
use NextPDF\Inspect\InspectConfig;
$pdfData = file_get_contents(__DIR__ . '/incoming.pdf');
if ($pdfData === false || $pdfData === '') {
fwrite(STDERR, "Cannot read incoming.pdf\n");
exit(1);
}
$inspector = new Inspector();
// InspectConfig::quick() selects InspectDepth::Quick. This is the only
// depth that runs offline: a default Inspector has no Spectrum sidecar,
// and without a sidecar Quick is the sole path that returns a result.
// Standard and Full require the sidecar (see the SIDECAR-001 edge case).
$result = $inspector->inspect(
$pdfData,
InspectConfig::quick(),
);
// hasSigned reports the PRESENCE of a signature dictionary.
// It does NOT mean the signature verifies.
if ($result->hasSigned) {
echo "A signature is present in incoming.pdf\n";
echo "Encrypted: " . ($result->isEncrypted ? 'yes' : 'no') . "\n";
echo "Next step: run a cryptographic verifier before trusting it.\n";
} else {
echo "No signature found in incoming.pdf\n";
}

Para una entrada firmada:

A signature is present in incoming.pdf
Encrypted: no
Next step: run a cryptographic verifier before trusting it.

Para una entrada sin firmar:

No signature found in incoming.pdf
  • La presencia no es validez: el límite. InspectResult::$hasSigned indica que en el archivo existe un diccionario de firma. No comprueba la estructura CMS, el resumen del intervalo de bytes, el certificado de firma, la cadena de certificados ni el estado de revocación. Un archivo manipulado aún puede informar de hasSigned = true. Nunca se debe tratar la presencia como prueba de integridad ni de autoría.
  • Lo que necesita una verificación completa. Una decisión completa recalcula el resumen del intervalo de bytes y lo compara (ISO 32000-2 §12.8.1), valida CMS SignedData, construye y comprueba la ruta X.509 hasta un ancla de confianza y comprueba la revocación mediante OCSP o CRL. En entradas a largo plazo, los datos de validación residen en el DSS (ETSI EN 319 142-2 §6.3.1). Estas operaciones se realizan mediante los contratos SignerInterface y LtvManagerInterface; las implementaciones de producción se incluyen en las ediciones Pro y Enterprise. Un validador externo es la otra ruta admitida.
  • Profundidad de la inspección y el sidecar (SIDECAR-001). Un new Inspector() predeterminado no tiene configurado ningún sidecar de Spectrum. Sin un sidecar, solo InspectDepth::Quick devuelve un resultado. InspectDepth::Quick usa el respaldo PHP en proceso. InspectDepth::Standard e InspectDepth::Full requieren ambas el sidecar. Sin un sidecar disponible, lanzan InspectException con el código INSPECT-SIDECAR-001 («Spectrum sidecar is required for Standard/Full depth inspection», retryable = true). El constructor de InspectConfig tiene como profundidad predeterminada Standard. Por lo tanto, un new InspectConfig() sin parámetros (o new InspectConfig(depth: InspectDepth::Standard)) no puede usarse sin conexión. Lanza SIDECAR-001 antes incluso de que hasSigned llegue a leerse. Usar InspectConfig::quick() para la detección de presencia sin conexión, como se hace en esta receta. Ninguna de las profundidades realiza la verificación criptográfica de la firma en Core.
  • Entrada vacía. Una cadena vacía lanza InspectException con el código INSPECT-INPUT-001. Proteger la lectura.
  • Varias firmas. El indicador de presencia no cuenta las firmas ni distingue una firma de aprobación de una marca de tiempo de documento. Usar un verificador específico cuando el recuento o el veredicto por firma sean relevantes.
AfirmaciónEspecificaciónCláusulareference_id
El valor de la firma se almacena en la entrada Contents del diccionario de firma.ISO 32000-2§12.8.1
La verificación recalcula el resumen sobre ByteRange, excluyendo el valor de la firma.ISO 32000-2§12.8.1
Los datos de validación a largo plazo residen en el DSS.ETSI EN 319 142-2§6.3.1

Esta receta detecta una firma. No afirma que una firma sea válida, de confianza o no revocada. Esa decisión corresponde a un verificador criptográfico.