Skip to content

Instantly share code, notes, and snippets.

Created June 1, 2021 14:23
What would you like to do?
Sign SOAP messages with xmlseclibs

Sign SOAP messages with xmlseclibs


  • PHP 7.4+
  • OpenSSL

Generating Public and Private Keys

First we have to create a private key for signature creation, and a public key for verification. This means that it's fine to distribute your public key. However, the private key should remain secret.

Generate the private key with this OpenSSL command (enter a password):

openssl genrsa -out private.pem 2048

The private key is generated and saved in a file named "private.pem", located in the same directory.

The number "2048" in the above command indicates the size of the private key. You can choose one of these sizes: 512, 758, 1024, 2048 or 4096 (these numbers represent bits). The larger sizes offer greater security, but this is offset by a penalty in CPU performance.

Generating the Public Key

Later we need a public key for the token verification. To extract the public key file, type the following:

openssl rsa -in private.pem -outform PEM -pubout -out public.pem

The public key is saved in a file named public.pem located in the same directory.

Generating a Certificate

In order to append a X509 certificate to the SOAP signature you have to generate a cert file using the private key:

openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -keyout private.pem -out certificate.crt


Extract private key from a PFX file:

openssl pkcs12 -in filename.pfx -nocerts -out private.pem

Extract certificate from a PFX file:

openssl pkcs12 -in filename.pfx -clcerts -nokeys -out certificate.crt



composer require robrichards/xmlseclibs



namespace Acme\XmlDSig\Soap\Test;

use DOMDocument;
use DOMXPath;
use PHPUnit\Framework\TestCase;
use RobRichards\XMLSecLibs\XMLSecurityDSig;
use RobRichards\XMLSecLibs\XMLSecurityKey;

 * Test.
class XmlDsigSoapTest extends TestCase
     * Test.
     * @return void
    public function testSignAndVerify()
        $privateKeyPath = __DIR__ . '/private.pem';
        $certificatePath = __DIR__ . '/certificate.crt';

        $dom = $this->createSoapXmlMessage();

        $dsig = new XMLSecurityDSig();
            ['force_uri' => true]

        $dsigKey = new XMLSecurityKey(XMLSecurityKey::RSA_SHA256, ['type' => 'private']);

        // Optional
        //$dsigKey->passphrase = 'secret';

        $dsigKey->loadKey(file_get_contents($privateKeyPath), false, false);

        // Add the associated public key to the signature
        $options = [
            'issuerSerial' => 'serial',
            'subjectName' => 'subject',

        $dsig->add509Cert(file_get_contents($certificatePath), true, false, $options);

        // Append signature to root element

        // Append signature to RegisterTCRRequest element

        // Register all namespace to find the Reference element
        $xpath = new DOMXPath($dom);
        $xpath->registerNamespace('SOAP-ENV', '');
        $xpath->registerNamespace('ds', '');
        $xpath->registerNamespace('xmlns', '');
        $xpath->registerNamespace('ns2', '');

        // Set reference URI
        $elements = $xpath->query('//ds:Signature/ds:SignedInfo/ds:Reference');
        $elements->item(0)->attributes->item(0)->nodeValue = '#Request';

        // Get raw XML
        $xmlRaw = $dom->saveXML();

        $this->assertStringContainsString('<ds:Signature xmlns:ds="">', $xmlRaw);
        $this->assertStringContainsString('ds:DigestValue', $xmlRaw);
        $this->assertStringContainsString('ds:SignatureValue', $xmlRaw);
        $this->assertStringContainsString('ds:X509Certificate', $xmlRaw);
        $this->assertStringContainsString('<ds:Reference URI="#Request">', $xmlRaw);

     * Create soap message.
     * @return DOMDocument The dom
    private function createSoapXmlMessage(): DOMDocument
        $dom = new DOMDocument('1.0', 'UTF-8');
        $dom->formatOutput = true;

        $envelope = $dom->createElementNS('', 'SOAP-ENV:Envelope');

        $soapHeader = $dom->createElement('SOAP-ENV:Header');

        $body = $dom->createElement('SOAP-ENV:Body');

        $request = $dom->createElement('RegisterTCRRequest');

        $request->setAttributeNS('', 'xmlns', '');
        $request->setAttributeNS('', 'xmlns:ns2', '');
        $request->setAttribute('Id', 'Request');
        $request->setAttribute('Version', '1');

        $requestHeader = $dom->createElement('Header');

        $requestHeader->setAttribute('SendDateTime', '2021-05-22T13:21:01+01:00');
        $requestHeader->setAttribute('UUID', '53f0616c-3078-4678-8716-59353e7475ee');

        $tcr = $dom->createElement('TCR');

        $tcr->setAttribute('BusinUnitCode', 'cx614cf575');
        $tcr->setAttribute('IssuerTIN', '03204308');
        $tcr->setAttribute('MaintainerCode', 'ov571vp018');
        $tcr->setAttribute('SoftCode', 'ca181vu261');
        $tcr->setAttribute('TCRIntID', '1');
        $tcr->setAttribute('ValidFrom', '2021-05-21');
        $tcr->setAttribute('Type', 'REGULAR');

        return $dom;


composer test
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment