Ir al contenido

Resolución de problemas: fallos de firma y marca de tiempo

Estas entradas cubren los fallos de firma que el motor lanza mediante NextPDF\Exception\SignatureException y NextPDF\Security\Signature\Exception\SignatureLevelUnreachableException. Cada entrada nombra el método de fábrica o la clase exacta para poder confirmar la causa a partir del mensaje y de getContext(), en lugar de inferirla.

Nota sobre la redacción: el motor no certifica que una firma sea válida ni que un documento esté protegido. Informa del fallo detectado. Conviene tratar cada resolución como un paso para eliminar una causa reportada.

Entrada: no se puede generar el nivel PAdES «B-LT» o «B-LTA»

Sección titulada «Entrada: no se puede generar el nivel PAdES «B-LT» o «B-LTA»»
  • Síntoma. SignatureException con el final de mensaje nextpdf/enterprise package is required for B-LT/B-LTA signatures.
  • Causa probable. Falta el proveedor de la capacidad de validación a largo plazo. B-LT y B-LTA incrustan material de revocación y una marca de tiempo de archivo; ese proveedor se distribuye en nextpdf/enterprise.
  • Evidencia / diagnóstico. La fábrica SignatureException::ltvCapabilityMissing() produce este mensaje exacto. getContext() devuelve signature_level fijado en el nivel que se intentó.
  • Resolución.
    1. Instalar el proveedor: ejecutar composer require nextpdf/enterprise.
    2. Volver a ejecutar la llamada de firma.
    3. Si no se puede instalar el proveedor, solicitar en su lugar B-B o B-T, que el paquete del núcleo produce.
  • Relacionado. Referencia de excepciones.

Entrada: el nivel de firma es inalcanzable y la llamada se rechaza

Sección titulada «Entrada: el nivel de firma es inalcanzable y la llamada se rechaza»
  • Síntoma. SignatureLevelUnreachableException con un mensaje de la forma PAdES level "<x>" is unreachable (highest achievable: "<y>").
  • Causa probable. El nivel de conformidad solicitado requiere infraestructura que no está disponible en el momento de firmar: normalmente, una autoridad de marca de tiempo para B-T y superiores. El motor falla de forma cerrada: no aplica una degradación silenciosa para después anunciar el nivel superior.
  • Evidencia / diagnóstico. getContext() devuelve requestedLevel, highestAchievableLevel y reason. El campo reason indica la carencia de infraestructura. Este es el comportamiento de fallo cerrado predeterminado, introducido para evitar que un documento afirme un nivel que no cumple.
  • Resolución.
    1. Leer el campo reason para identificar la infraestructura que falta.
    2. Aportar el componente que falta (por ejemplo, configurar una autoridad de marca de tiempo) y volver a ejecutar la llamada.
    3. Para aceptar a propósito un nivel inferior, pasar allowDegradation: true a PadesOrchestrator. La llamada produce entonces highestAchievableLevel e informa del nivel que produjo.
  • Relacionado. Cifrado y permisos.

Entrada: el cliente de la autoridad de marca de tiempo es obligatorio, pero está ausente

Sección titulada «Entrada: el cliente de la autoridad de marca de tiempo es obligatorio, pero está ausente»
  • Síntoma. SignatureException con el final TSA client is required for level <x> but none was provided.
  • Causa probable. Una solicitud B-T, B-LT o B-LTA necesita un cliente de autoridad de marca de tiempo, y no se conectó ninguno al orquestador.
  • Evidencia / diagnóstico. La fábrica SignatureException::tsaRequired() produce este mensaje; getContext() incluye el signature_level que se intentó.
  • Resolución.
    1. Configurar un cliente de autoridad de marca de tiempo y pasarlo al orquestador.
    2. Volver a ejecutar la llamada.
    3. Para producir un nivel que no necesite marca de tiempo, solicitar B-B.
  • Relacionado. Referencia de excepciones.

Entrada: la URL del extremo de la autoridad de marca de tiempo está vacía

Sección titulada «Entrada: la URL del extremo de la autoridad de marca de tiempo está vacía»
  • Síntoma. SignatureException con el final TSA endpoint URL is empty.
  • Causa probable. Se construyó un cliente de autoridad de marca de tiempo con una URL de extremo vacía.
  • Evidencia / diagnóstico. La fábrica SignatureException::tsaUrlEmpty() produce este mensaje. Es un defecto de configuración, no un fallo de red.
  • Resolución.
    1. Definir una URL de extremo no vacía en el cliente de la autoridad de marca de tiempo, por ejemplo https://timestamp.example.com/tsa.
    2. Si la marca de tiempo no es obligatoria en el nivel solicitado, eliminar en su lugar la conexión del cliente de la autoridad de marca de tiempo.
    3. Volver a ejecutar la llamada.
  • Relacionado. Referencia de excepciones.

Entrada: falta el marcador de posición de firma en el búfer

Sección titulada «Entrada: falta el marcador de posición de firma en el búfer»
  • Síntoma. SignatureException con el final no /Contents <…> field found in PDF buffer (signature placeholder missing).
  • Causa probable. La etapa de firma se ejecutó contra un búfer que no contiene un contenedor de firma reservado, por lo que no hay dónde escribir la firma.
  • Evidencia / diagnóstico. La fábrica SignatureException::signatureContentsNotFound() produce este mensaje.
  • Resolución.
    1. Asegurarse de que el campo de firma y su marcador de posición se escriban antes de que se ejecute la etapa de firma.
    2. Volver a ejecutar la canalización para que el marcador de posición exista cuando empiece la firma.
  • Relacionado. Referencia de excepciones.

Entrada: el estado de revocación es desconocido (el respondedor OCSP rechazó la solicitud)

Sección titulada «Entrada: el estado de revocación es desconocido (el respondedor OCSP rechazó la solicitud)»
  • Síntoma. SignatureException con el final OCSP responder returned non-successful OCSPResponseStatus "<status>".
  • Causa probable. El respondedor OCSP no devolvió un estado successful, por lo que no produjo ninguna aserción de revocación. El motor sigue la RFC 6960 §4.2.1, citada en el código fuente: solo se permite un cuerpo de respuesta con contenido para el estado successful (0). El motor se niega a tratar una respuesta rechazada como un resultado positivo de confianza.
  • Evidencia / diagnóstico. La fábrica SignatureException::nonSuccessfulOcspResponseStatus() produce este mensaje y nombra el estado reportado, por ejemplo tryLater o internalError. Un byte de estado reservado o desconocido produce, en cambio, SignatureException::reservedOcspResponseStatus().
  • Resolución.
    1. Identificar el estado en el mensaje. Para un estado transitorio como tryLater, volver a intentar la obtención de la revocación más tarde.
    2. Para unauthorized o malformedRequest, verificar la URL de la solicitud OCSP y el certificado que el respondedor espera.
    3. No suprimir el fallo para obtener un artefacto B-LT o B-LTA; la aserción de revocación forma parte de ese nivel.
  • Relacionado. Referencia de excepciones.

Entrada: una entrada de la cadena de certificados no se puede decodificar

Sección titulada «Entrada: una entrada de la cadena de certificados no se puede decodificar»
  • Síntoma. SignatureException con el final failed to base64-decode PEM body — input is not valid PEM.
  • Causa probable. Una entrada de la cadena de certificados no es PEM válido: normalmente por truncamiento, un carácter extraño o un blob DER binario suministrado donde se esperaba PEM.
  • Evidencia / diagnóstico. La fábrica SignatureException::pemDecodingFailed() produce este mensaje durante el ensamblaje de la cadena.
  • Resolución.
    1. Inspeccionar cada certificado de la cadena en busca de caracteres extraños o truncamiento.
    2. Volver a exportar el certificado afectado en formato PEM.
    3. Volver a ejecutar la llamada de firma.
  • Relacionado. Cifrado y permisos.

Entrada: el tipo de clave privada no coincide con el algoritmo

Sección titulada «Entrada: el tipo de clave privada no coincide con el algoritmo»
  • Síntoma. SignatureException con el final expected private key of type "<x>" for the configured algorithm but got "<y>".
  • Causa probable. La clave privada cargada no coincide con el algoritmo de firma configurado; por ejemplo, una clave RSA con una selección ECDSA.
  • Evidencia / diagnóstico. La fábrica SignatureException::unexpectedKeyType() produce este mensaje y nombra tanto la clase de clave esperada como la real.
  • Resolución.
    1. Verificar que el certificado y el par de claves coincidan con el algoritmo seleccionado.
    2. Cambiar la selección del algoritmo para que coincida con la clave, o cargar la clave que coincida con el algoritmo.
    3. Volver a ejecutar la llamada de firma.
  • Relacionado. Referencia de excepciones.

Entrada: el material de clave o de firma Ed25519 está malformado

Sección titulada «Entrada: el material de clave o de firma Ed25519 está malformado»
  • Síntoma. SignatureException con un final que nombra un desajuste de longitud Ed25519; por ejemplo Ed25519 signature length <n> ≠ expected 64 bytes, o Ed25519 round-trip self-verify failed.
  • Causa probable. La compilación criptográfica del entorno de ejecución devolvió material de clave o de firma con una longitud incorrecta, o una firma recién producida no se verificó con su propia clave pública. El motor cita la RFC 8032 §3.4 en el código fuente, que establece una firma Ed25519 separada en 64 bytes. El motor aborta en lugar de emitir material que no puede verificar por sí mismo.
  • Evidencia / diagnóstico. Las fábricas relevantes son SignatureException::ed25519SignatureMalformed(), ::ed25519RoundTripVerifyFailed(), ::ed25519KeyParseFailed(), ::ed25519SeedInvalid(), ::ed25519SecretKeyMalformed() y ::ed25519PublicKeyInvalid(). Cada una nombra la longitud observada.
  • Resolución.
    1. Reinstalar la extensión de PHP libsodium; una compilación reducida o corrupta es la causa documentada de material con longitud incorrecta.
    2. Confirmar que la clave es una clave Ed25519 y que OpenSSL es 1.1.1 o más reciente.
    3. Volver a ejecutar la llamada de firma.
  • Relacionado. Referencia de excepciones.

Entrada: no se emitió el diccionario de marca de tiempo de archivo

Sección titulada «Entrada: no se emitió el diccionario de marca de tiempo de archivo»
  • Síntoma. SignatureException con el final no /Type /DocTimeStamp dictionary was emitted into the PDF buffer.
  • Causa probable. El bucle de archivado B-LTA se ejecutó, pero el diccionario de marca de tiempo del documento nunca llegó al búfer, por lo que el artefacto sería un B-LTA escrito parcialmente. El motor se niega a devolverlo.
  • Evidencia / diagnóstico. La fábrica SignatureException::documentTimestampNotEmitted() produce este mensaje. Es un fallo de poscondición lanzado en el momento de finalización.
  • Resolución.
    1. Dar la salida por descartada; no distribuir el artefacto parcial.
    2. Volver a ejecutar la canalización B-LTA con una autoridad de marca de tiempo accesible.
    3. Si el fallo se repite, capturar getContext() y la excepción previa encadenada para un informe de defectos.
  • Relacionado. Referencia de excepciones.
  • Estas fábricas fijan cert_info en un sujeto o una huella digital solo cuando está disponible; un cert_info vacío es esperable en los fallos de capacidad y de configuración.
  • Una solicitud B-LT o B-LTA sin un cliente HTTP configurado lanza SignatureException::httpClientMissing(): la obtención de la revocación necesita un cliente PSR-18 proporcionado al orquestador.
  • Un certificado respaldado por un HSM sin una implementación de firmante lanza SignatureException::hsmSignerMissing(); conectar el firmante al certificado antes de firmar.

Glosario: nivel PAdES · aserción de revocación