Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Xmldsig Laravel Package

fr3d/xmldsig

fr3d/xmldsig brings XML Digital Signature (XMLDSig) support to Laravel and PHP applications. Sign, verify, and validate XML documents with a simple API for integrating secure, standards-based XML signatures into your workflow.

View on GitHub
Deep Wiki
Context7

Getting Started

Installation

composer require fr3d/xmldsig

Treat this as a low-level XML utility, not a framework package: there is no service provider, no config file, and no facade. You wire it yourself.

First places to look

  1. The GitHub repo (Maks3w/xmldsig) README and the src/Adapter/ directory — the API is adapter-based, with XmlseclibsAdapter as the default implementation backed by robrichards/xmlseclibs.
  2. composer.json for the PHP and extension constraints (ext-dom, ext-openssl, libxml) — check these against your Laravel runtime before committing.
  3. The package's own tests/ fixtures — they are the fastest way to see a correctly signed document and a valid verification call.

First use case: sign an outgoing XML payload

The canonical entry point is a DOMDocument plus a PEM private key and certificate. A minimal Laravel-side wrapper looks like this:

use Fr3d\XmlDSig\Adapter\XmlseclibsAdapter;

$dom = new DOMDocument();
$dom->loadXML($payloadXml);          // or build via SimpleXML/DOM manually

$adapter = new XmlseclibsAdapter();
$adapter->setPrivateKey(
    Storage::disk('certs')->get('app-private.pem'),
    config('xmlsig.passphrase')
);
$adapter->setPublicKey(
    Storage::disk('certs')->get('app-cert.pem')
);

$adapter->sign($dom);

$signedXml = $dom->saveXML();

Verification is the mirror image:

$adapter = new XmlseclibsAdapter();
$adapter->setPublicKey($trustedCertPem);

if (! $adapter->verify($incomingDom)) {
    throw new SignatureVerificationException('Invalid XML signature');
}

Exact method signatures have varied across releases; confirm against the adapter interface in the version you install before building abstraction on top of it.

Implementation Patterns

Bind the adapter in a service provider

Keep instantiation out of controllers so you can swap adapters or mock signing in tests:

$this->app->bind(AdapterInterface::class, function () {
    return new XmlseclibsAdapter();
});

Wrap it in a small XmlSigner / XmlVerifier pair in app/Support/XmlDsig/ that loads keys from Storage, applies your algorithm config, and returns the signed string. That boundary is where you will also want to inject logging and error translation.

Sign-last pipeline

The most common integration pattern: build the document, serialize, then sign. Any serialization after signing (pretty-printing, re-indenting, prefix rewriting) will break the digest unless the canonicalization form happens to match byte-for-byte. In practice:

$xml = $builder->toXml();          // deterministic, no pretty print
$signed = $signer->sign($xml);     // returns final bytes
return response($signed, 200, ['Content-Type' => 'application/xml']);

Verification as middleware / request rule

For inbound signed XML (SAML assertions, bank callbacks, e-invoice receipts), verify early and before parsing domain fields:

class VerifyXmlSignature
{
    public function handle($request, Closure $next)
    {
        $dom = new DOMDocument();
        $dom->loadXML($request->getContent(), LIBXML_NONET);

        if (! app(XmlVerifier::class)->verify($dom)) {
            abort(400, 'Bad signature');
        }

        $request->attributes->set('signed_dom', $dom);
        return $next($request);
    }
}

Job / queue pattern

DOMDocument is not serializable, so pass the raw XML string into the job and build the document inside handle(). Keep private-key reads inside the job too — do not stuff PEMs into serialized payloads.

Config file

Add config/xmlsig.php with key paths, passphrase (via env), digest method, and canonicalization method. This is where you centralize the SHA-256 upgrade and any per-integration overrides. Multiple integrations often need different keys and different signature profiles (enveloped vs. enveloping, different reference URIs), so model it as a named profile map:

return [
    'profiles' => [
        'billing' => [
            'private_key' => 'certs/billing.pem',
            'certificate' => 'certs/billing.crt',
            'passphrase'  => env('BILLING_KEY_PASSPHRASE'),
        ],
    ],
];

Testing pattern

Store known-good signed fixtures and assert verify() returns true; store a tampered variant and assert it returns false. Do not round-trip sign→verify only — that passes even when canonicalization is subtly wrong and the remote party will still reject.

Gotchas and Tips

Maintenance status is the biggest risk. Last release was October 2018, dependents are effectively zero, and the license is reported as NOASSERTION — resolve that before shipping. Audit it against your PHP version; on modern PHP the underlying xmlseclibs will very likely need a manual bump (robrichards/xmlseclibs ^3.x) and you should fork if you must patch. If signature verification against Java/.NET counterparts is business-critical, budget time to validate interop rather than trusting the library alone.

Canonicalization is where signatures die. Default C14N behavior differs between exclusive and inclusive canonicalization, and between implementations. If the receiving side (bank, government portal, SOAP gateway) expects http://www.w3.org/2001/10/xml-exc-c14n#, set it explicitly and test with their sample documents. Mismatched canonicalization produces a signature that verifies locally and fails remotely.

Signature placement and reference URIs. Enveloped signatures require the referenced element to carry an Id (or ID/id) attribute declared as an XML ID; libraries differ in what they accept. Namespaced documents with a default namespace and no prefix frequently break #id reference resolution. If you control the sender, keep the signed element unprefixed or ensure the Id is unambiguous.

Digest defaults to SHA-1 on older paths. SHA-1 XMLDSig is deprecated and rejected by many counterparties. Explicitly set SHA-256 digest and signature methods rather than relying on defaults.

Whitespace and encoding. Any newline added between <ds:Signature> and its siblings, or any re-encoding from ISO-8859-1 to UTF-8 after signing, invalidates the reference digest. Sign the exact bytes you transmit. Do not run the signed output through a formatter or a Blade template.

Key formats. The adapter expects PEM. PKCS#12 (.pfx/.p12) must be converted first (e.g. openssl pkcs12 -in cert.p12 -nodes -out all.pem). Strip any BOM from PEM files read from disk; a leading BOM causes an opaque "unable to load key" failure.

Hardening DOM loading. Always pass LIBXML_NONET, and disable external entity loading (libxml_disable_entity_loader historically, or LIBXML_NOENT never set). You are parsing attacker-supplied XML during verification — treat it as an XXE surface.

Debuggability is poor. Failures surface as boolean false or a generic exception from xmlseclibs with no pointer to which reference failed. Practical debugging: log $dom->C14N() output for the signed node and the expected digest, compute SHA-256 base64 yourself, and compare. When a remote party rejects a signature you accept, dump your canonicalized bytes and ask them to diff — nine times out of ten the difference is a trailing newline or namespace axis.

Extension point. AdapterInterface is the seam: implement it to swap xmlseclibs for a maintained alternative, or to add logging/timing around sign and verify without touching call sites. Because the package is small, wrapping it behind your own interface from day one (rather than calling XmlseclibsAdapter directly in controllers) is the cheapest insurance against having to replace it later.

Do not use it for JWT or JSON signing. Despite the "digital signature" name, this is strictly XMLDSig for XML documents; use a dedicated JWT/JWS library for tokens.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle