Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/Eseperio/verifactu-php/llms.txt

Use this file to discover all available pages before exploring further.

XmlSignerService applies a XAdES Enveloped digital signature to an XML document using the taxpayer’s digital certificate. AEAT requires every VERI*FACTU SOAP submission to carry a valid ds:Signature element, produced with the RSA-SHA256 algorithm and an Exclusive C14N canonicalisation method. Namespace: eseperio\verifactu\services\XmlSignerService Requires: robrichards/xmlseclibs ^3.0 (declared as a Composer dependency).

What is XAdES Enveloped signing?

XAdES (XML Advanced Electronic Signatures) is a family of W3C-based XML digital signature formats. The Enveloped variant means the <ds:Signature> element is inserted inside the signed XML document — the signature and the signed content travel together in one XML tree. AEAT mandates this approach for VERI*FACTU because:
  1. The signed content is the invoice record (RegistroAlta or the full RegFactuSistemaFacturacion wrapper), not an external reference.
  2. The signature must survive SOAP envelope processing without being detached.
  3. The ds:Signature references the root element via a URI and includes the enveloped-signature transform so the signing algorithm can exclude the signature block itself from the digest.
The library uses robrichards/xmlseclibs to produce a standards-compliant signature (XMLDSig with RSA-SHA256 and EXC-C14N canonicalisation) and embeds the signing certificate’s public key in the ds:KeyInfo / ds:X509Data block.

Method

signXml(string $xmlString, string $certPath, string $certPassword): string|false

Loads the certificate and private key from the specified file, applies an XAdES Enveloped signature to the XML, and returns the signed XML string.
xmlString
string
required
A well-formed XML string to sign. The <ds:Signature> element will be appended as the last child of the document’s root element.
certPath
string
required
Absolute path to the digital certificate file. Supported formats:
  • .pfx / .p12 — PKCS#12 bundle (certificate + private key).
  • .pem — PEM file containing both an X.509 certificate block and a private key block.
certPassword
string
required
Password for the certificate. Pass an empty string '' for unprotected PEM files.
Returns: The signed XML as a string, including the <?xml ...?> declaration. Returns false if signing fails (in practice, exceptions are thrown before this case is reached). Throws:
  • \RuntimeExceptionrobrichards/xmlseclibs library not found; certificate or key loading failure.
  • \Exception — The input string is not valid XML.

Signing algorithm details

PropertyValue
Signature algorithmRSA-SHA256 (XMLSecurityKey::RSA_SHA256)
CanonicalisationExclusive C14N (XMLSecurityDSig::EXC_C14N)
Digest methodSHA-256 (XMLSecurityDSig::SHA256)
Transformenveloped-signature (http://www.w3.org/2000/09/xmldsig#enveloped-signature)
Key infods:X509Certificate (PEM, base64-encoded) + ds:X509SubjectName
Signature placementAppended as last child of the XML root element

When the library calls this automatically

XmlSignerService::signXml() is invoked internally in two places: VerifactuService::registerInvoice() — signs the RegistroAlta DOM document before it is wrapped inside the RegFactuSistemaFacturacion structure. The signature therefore covers only the invoice record element. VerifactuService::cancelInvoice() — signs the fully-wrapped RegFactuSistemaFacturacion XML (after the RegistroAnulacion is embedded). The signature therefore covers the entire submission envelope. In both cases the certificate path and password are read from VerifactuService’s global configuration (certPath / certPassword keys). You do not need to manage the signing step yourself.

When to call manually

Call signXml() directly when you:
  • Need to sign XML outside of a registerInvoice() / cancelInvoice() flow (e.g. event records via EventDispatcherService).
  • Are building a custom submission pipeline and want fine-grained control over when signing occurs.
  • Need to verify that your certificate produces a valid signature before going to production.

Code examples

Basic manual signing

use eseperio\verifactu\services\XmlSignerService;

$xml = <<<XML
<?xml version="1.0" encoding="UTF-8"?>
<RegistroAlta xmlns="https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SuministroInformacion.xsd">
  <IDVersion>1.0</IDVersion>
  <!-- ... other fields ... -->
</RegistroAlta>
XML;

$signedXml = XmlSignerService::signXml(
    $xml,
    '/var/certs/empresa.pfx',
    's3cr3t'
);

// $signedXml now contains a <ds:Signature> element inside <RegistroAlta>
echo $signedXml;

Signing a PEM certificate

$signedXml = XmlSignerService::signXml(
    $xml,
    '/var/certs/empresa.pem',
    ''    // empty password for unprotected PEM
);

Verifying the certificate works before production

use eseperio\verifactu\services\CertificateManagerService;
use eseperio\verifactu\services\XmlSignerService;

// Check validity first
$valid = CertificateManagerService::isValid('/var/certs/empresa.pfx', 's3cr3t');

if (!$valid) {
    throw new \RuntimeException('Certificate is expired or invalid.');
}

// Then sign
$signedXml = XmlSignerService::signXml($xml, '/var/certs/empresa.pfx', 's3cr3t');

Troubleshooting

xmlseclibs library is required for XML signing

Install the dependency:
composer require robrichards/xmlseclibs "^3.0"

Invalid XML provided for signing

The $xmlString argument could not be parsed as XML. Ensure the string is well-formed and UTF-8 encoded before passing it to signXml().

AEAT rejects with error code 100 (La firma de la petición SOAP no es válida)

This means AEAT could not verify the digital signature. Common causes:
  • The certificate is not approved for VERI*FACTU (must be an electronic seal or qualified certificate registered with AEAT).
  • The certificate is expired — check with CertificateManagerService::isValid().
  • The XML was modified after signing (e.g. whitespace normalisation by an intermediate HTTP layer).
  • The certificate is on AEAT’s block list (error code 106).

Build docs developers (and LLMs) love