尝试在 PHP 中使用 x.509 证书对 SOAP 调用进行数字签名

Posted

技术标签:

【中文标题】尝试在 PHP 中使用 x.509 证书对 SOAP 调用进行数字签名【英文标题】:Trying to digitally sign SOAP call with x.509 certificate in PHP 【发布时间】:2016-02-15 06:01:08 【问题描述】:

我正在与客户合作,尝试使用 php 发送和接收肥皂电话。他们设置了 ws-security,并使用 x.509 证书进行身份验证。我已经能够使用 SoapUI 让它工作,但我无法在 PHP 中使用它。

我遇到的问题是,他们不使用标准的二进制安全令牌或用户名/密码组合。他们改为在安全令牌参考中签署 XML 文件。

我一直在尝试使用 Rob Richards 的库来生成哈希,并且它似乎包含其中的代码来完成我想要做的事情,但我在实现它方面一直没有成功。 (https://github.com/robrichards/wse-php)

这是我们应该得到的:

<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>

但我能得到的最好的结果是:

<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>

还有使这一切正常工作的 PHP 类:

<?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();
    

必须有一种方法可以轻松做到这一点,但我似乎错过了它。以前有人做过吗?

【问题讨论】:

你有没有考虑过因为内容不同而导致签名不同?另外,我相信对于您的具体情况,二进制令牌需要添加到 $objKey 而不是 $objWSSE XMLSecurityKey - 你不应该使用这个类的 RobRichards 版本吗? 【参考方案1】:

据我所知,您使用了错误的密钥类型:它应该是XMLSecurityKey::DSA_SHA1,但您使用的是XMLSecurityKey::RSA_SHA1。顺便说一句,lib不支持第一个。但是还是可以解决的。您可以在下面找到我用来测试的代码。

    生成密钥 (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
    修补库:

vendor/robrichards/xmlseclibs/src/XMLSecurityKey.php 第 216 行添加以下 case 块:

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;
    运行签名功能:

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();
    以下是我通过上面的 sn-p 设法获得的 XML 代码:

<?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>

【讨论】:

抱歉格式化 - 所以会以某种方式破坏它。我还创建了一个 git 来提供帮助:gist.github.com/t1gor/af58fdc4c7bf91dd4ad50528638b92bc。希望这有助于解决您的问题。

以上是关于尝试在 PHP 中使用 x.509 证书对 SOAP 调用进行数字签名的主要内容,如果未能解决你的问题,请参考以下文章

如何建立对中介颁发的 X.509 证书的信任?

X509 证书生成

WCF 上的 X.509 证书?

X.509证书生成

WCF 错误:“X.509 证书 CN=localhost 链构建失败......”

X.509证书验证过程