Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Trying to digitally sign SOAP call with x.509 certificate in PHP

I'm working with a client, trying to send and receive soap calls using PHP. They have ws-security set up, and use an x.509 certificate for authentication. I have been able to get this to work using SoapUI, but I haven't been able to work with it in PHP.

The problem I'm having is, they don't use a standard binary security token or a username/password combo. They sign the XML file in the Security Token Reference instead.

I've been trying to use Rob Richards' library to generate the hash, and it appears to have the code included in it to do what I'm trying to do, but I've been unsuccessful in implementing it. (https://github.com/robrichards/wse-php)

Here's what we're supposed to get:

<soapenv:Envelope xmlns:ord="http://order.pine.cypresscare.com" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Header>
    <wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
        <ds:Signature Id="SIG-17020931F46DA4F12E144355764463230" xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
            <ds:SignedInfo>
                <ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#">
                    <ec:InclusiveNamespaces PrefixList="ord soapenv" xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#"/>
                </ds:CanonicalizationMethod>
                <ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#dsa-sha1"/>
                <ds:Reference URI="#id-17020931F46DA4F12E144355764463229">
                    <ds:Transforms>
                        <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#">
                            <ec:InclusiveNamespaces PrefixList="ord" xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#"/>
                        </ds:Transform>
                    </ds:Transforms>
                    <ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
                    <ds:DigestValue>BZc+DagseonF6kbBdtONG73wjcE=</ds:DigestValue>
                </ds:Reference>
            </ds:SignedInfo>
            <ds:SignatureValue>eIICrWiZerxelcSNUack5OKgvdSKYS3p5KdblFLVztYksExNoZ9wLQ==</ds:SignatureValue>
            <ds:KeyInfo Id="KI-17020931F46DA4F12E144355764463227">
                <wsse:SecurityTokenReference wsu:Id="STR-17020931F46DA4F12E144355764463228">
                    <wsse:KeyIdentifier EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary" ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3">
                         MIID... (Hash goes here)
                    </wsse:KeyIdentifier>
                </wsse:SecurityTokenReference>
            </ds:KeyInfo>
        </ds:Signature>
    </wsse:Security>
</soapenv:Header>

But the best I've been able to get is this:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ord="http://order.pine.cypresscare.com">
<soapenv:Header>
    <wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" soapenv:mustUnderstand="1">
        <wsse:BinarySecurityToken xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary" wsu:Id="pfx7b827e06-1662-e6e4-78fd-6b4bb95aeb96" ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3">
            MIIC... (Hash goes here)
        </wsse:BinarySecurityToken>
        <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
            <ds:SignedInfo>
                <ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
                <ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
                <ds:Reference URI="#pfx0b88133b-03ed-8bbc-8c8a-4998ef427a3a">
                    <ds:Transforms>
                        <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
                    </ds:Transforms>
                    <ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
                    <ds:DigestValue>fPmwf05DIdXW4K9muNYR6LMXjnI=</ds:DigestValue>
                </ds:Reference>
            </ds:SignedInfo>
            <ds:SignatureValue>q36Dr2TIl1OE0/6bBMPb0dQRVCimwpOx7KeYyUCfxMZVIMvDBXxH+lCiB5xEgEH/aceUsn19b0GTU1LqISOk4/rhVBHGw2Wpq/jBcRZWOO54xZYdpGkqzepagazJWOWVVdDCAD7WpQV34KRu1rT4S4ZCjaOeApVIlI2nhPWRXVQ=</ds:SignatureValue>
            <ds:KeyInfo>
                <wsse:SecurityTokenReference>
                    <wsse:Reference ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3" URI="#pfx7b827e06-1662-e6e4-78fd-6b4bb95aeb96"/>
                </wsse:SecurityTokenReference>
            </ds:KeyInfo>
        </ds:Signature>
    </wsse:Security>
</soapenv:Header>

And the PHP class to make this all work:

<?php
class MySoap extends SoapClient {
    public function __doRequest($request, $location, $saction, $version) {
        $doc = new DOMDocument('1.0');
        $doc->loadXML($request);

        $objWSSE = new WSSESoap($doc);

        /* add Timestamp with no expiration timestamp */
        $objWSSE->addTimestamp();

        /* create new XMLSec Key using AES256_CBC and type is private key */
        $objKey = new XMLSecurityKey(XMLSecurityKey::RSA_SHA1, array('type' => 'public'));

        /* load the private key from file - last arg is bool if key in file (true) or is string (false) */
        $objKey->loadKey("c:\\xampp\htdocs\\XMLSoapApp\\cert1.pem", $isFile=true, true);

        /* Sign the message - also signs appropiate WS-Security items */
        $options = array("insertBefore" => true);

        $objWSSE->signSoapDoc($objKey, $options);

        /* Add certificate (BinarySecurityToken) to the message */ 
        $token = $objWSSE->addBinaryToken(file_get_contents(combine_key));

        /* Attach pointer to Signature */
        $objWSSE->attachTokentoSig($token);
        return $doc->saveXML();
    }
}

There has to be a way to do this without too much difficulty, but I seem to be missing it. Has anyone done this before?

like image 443
MattA Avatar asked Nov 13 '15 15:11

MattA


1 Answers

From what I can see, you're using a wrong key type: it should be XMLSecurityKey::DSA_SHA1, but you're using XMLSecurityKey::RSA_SHA1. The first one is not supported by the lib, BTW. But it can still be solved. Below you can find the code I used to test this.

  1. Generate keys (great hint):
openssl dsa -in dsakey.private
openssl req -x509 -new -days 3650 -key dsakey.private -out dsakey.cert
openssl dsa -in dsakey.private -pubout -out dsakey.pub
  1. Patch the lib:

in vendor/robrichards/xmlseclibs/src/XMLSecurityKey.php line 216 add the following case block:

case (self::DSA_SHA1):
  $this->cryptParams['library'] = 'openssl';
  $this->cryptParams['method']  = 'http://www.w3.org/2000/09/xmldsig#dsa-sha1';
  $this->cryptParams['padding'] = OPENSSL_PKCS1_PADDING;
  $this->cryptParams['digest']  = OPENSSL_ALGO_SHA1;
  if (is_array($params) && ! empty($params['type'])) {
    if ($params['type'] == 'public' || $params['type'] == 'private') {
      $this->cryptParams['type'] = $params['type'];
      break;
    }
  }
  throw new Exception('Certificate "type" (private/public) must be passed via parameters');
break;
  1. Run the signing functioniality:

use RobRichards\WsePhp\WSSESoap;
use RobRichards\XMLSecLibs\XMLSecurityKey;

$doc = new DOMDocument('1.0');
$doc->loadXML(file_get_contents('/request.xml'));

$objWSSE = new WSSESoap($doc);
$objWSSE->addTimestamp();

$objKey = new XMLSecurityKey(XMLSecurityKey::DSA_SHA1, ['type' => 'private']);

$objKey->loadKey('/dsakey.private', true);
$options = ['insertBefore' => true];
$objWSSE->signSoapDoc($objKey, $options);

$token = $objWSSE->addBinaryToken(file_get_contents('/dsakey.cret'));
$objWSSE->attachTokentoSig($token);

echo $doc->saveXML();
  1. Below is the XML code that I've managed to get with the snippet above:

<?xml version="1.0" encoding="UTF-8"?>
    <soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope/" soap:encodingStyle="http://www.w3.org/2003/05/soap-encoding">
       <soap:Header>
          <wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" soap:mustUnderstand="1">
             <wsse:BinarySecurityToken xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary" wsu:Id="pfxc329505d-b83f-6587-529d-912489ee428d" ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3">MIICmTCCAlUCCQCe8BUic962uDALBglghkgBZQMEAwIwLzELMAkGA1UEBhMCUlUxDzANBgNVBAgMBk1vc2NvdzEPMA0GA1UEBwwGTW9zY293MB4XDTE4MTIyMTA3MDYwMVoXDTI4MTIxODA3MDYwMVowLzELMAkGA1UEBhMCUlUxDzANBgNVBAgMBk1vc2NvdzEPMA0GA1UEBwwGTW9zY293MIIBtzCCASwGByqGSM44BAEwggEfAoGBAJWpUD+1sbgTDnFcettpFJ1WitrZDisZHolCRsM6kRBot1NKvSvdF4ib6lAVV0zHc4pQ4UnaKV6/Dao+4XA52h6zwjFLGItYLbLeZgVslCiTTwuO8FbNpNaNvXb44Mw2JOnHtawXdaQZQ7CxTqNbmU2Lucw2xfU3qfi6c+9AOeZxAhUA82+xkInDY2H6tsooX+Oy1MUxyUcCgYEAi9AGHj0ZGNU32Ob64CKyOfWvZlqa6DviVS7uhZSaz+EX1pzbKTtYGUQHTvVHJAuUB8kD1ZKtDN7oT9yRHAA05CVqZ/75Lck4E5K4Tf1dKyLmPWKz37pZ2rnu0RahPy544G2ltRKerKtfMGd05D6qFACbX0vCN/utQkiKtxRP4vkDgYQAAoGAefPHsusLAYDgxSdRmXb8lf9/2mBzzFH4aei1osOGwst+Sczm01WqC0wrg4VTZIUecx3n7fotWsR6JPVkjt9z27qdbNw5Fw+tGXXKjUU/NeG9zU3gAO1dw97mpz4Dm4ahi2eF75w9rMvSYZDpPSuc2VlDn6DofNHZm4nofg0WbrkwCwYJYIZIAWUDBAMCAzEAMC4CFQDNtN7oK33NGn4PzH37ypQCR5NrPgIVALAPzeGOG4HiPCfLSnUnodR+jRXQ</wsse:BinarySecurityToken>
             <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
                <ds:SignedInfo>
                   <ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
                   <ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#dsa-sha1" />
                   <ds:Reference URI="#pfxcd7e3884-68fd-dd2c-ce2e-a0f2db73d6b0">
                      <ds:Transforms>
                         <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
                      </ds:Transforms>
                      <ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
                      <ds:DigestValue>RndT24MzjzRczZbfuqMp68c5fxI=</ds:DigestValue>
                   </ds:Reference>
                </ds:SignedInfo>
                <ds:SignatureValue>MC0CFQCjMCTZUSQSPSBtvp5Kq3rzXvf+YQIURYBRUczJ/3ZMpQzqdQ3k6s/D1Qk=</ds:SignatureValue>
                <ds:KeyInfo>
                   <wsse:SecurityTokenReference>
                      <wsse:Reference ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3" URI="#pfxc329505d-b83f-6587-529d-912489ee428d" />
                   </wsse:SecurityTokenReference>
                </ds:KeyInfo>
             </ds:Signature>
             <wsu:Timestamp xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="pfxcd7e3884-68fd-dd2c-ce2e-a0f2db73d6b0">
                <wsu:Created>2018-12-21T07:11:57Z</wsu:Created>
                <wsu:Expires>2018-12-21T08:11:57Z</wsu:Expires>
             </wsu:Timestamp>
          </wsse:Security>
       </soap:Header>
    </soap:Envelope>
like image 64
t1gor Avatar answered Nov 06 '22 08:11

t1gor