Ruby X509 证书的 SOAP 响应失败 XML SignatureValue 验证

Ruby SOAP response with X509 certificates failing XML SignatureValue validation

我已经阅读了很多与 XML 签名相关的内容,这是一个非常痛苦的世界,就像今天一样。

我能够向远程 WSDL (https://tbk.orangepeople.cl/WSWebpayTransaction/cxf/WSWebpayService?wsdl) 发出 SOAP 请求,而且我也应该验证他们的响应(当然!),但我似乎无法理解了解如何正确验证 XML SignatureValue.

我也无法使用 openssl 在终端 (MAC OSX 10.11.1) 上验证此签名(OpenSSL 0.9.8zg 7 月 14 日2015),虽然我没有足够的信心我的文件输入和命令是 100% 正确的给出肯定的结果。

向我提供 public 证书的实体向我保证这是正确的,所以运气不好。我还创建了一个使用传出和传入 WSS 正确配置的 SoapUI 项目来支持这一说法。

对于任何好奇的人,我使用 Signer 和 Nokogiri::XML::Builder 为我的请求生成签名的 XML,Savon 自己处理请求,在下面的代码中我使用Akami::WSSE::VerifySignature 单独处理响应。

这是我目前的情况:

XML(响应):

<?xml version="1.0"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap: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" soap:mustUnderstand="1">
      <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#" Id="SIG-1426">
        <ds:SignedInfo>
          <ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#">
            <ec:InclusiveNamespaces xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#" PrefixList="soap"/>
          </ds:CanonicalizationMethod>
          <ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
          <ds:Reference URI="#id-1425">
            <ds:Transforms>
              <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#">
                <ec:InclusiveNamespaces xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#" PrefixList=""/>
              </ds:Transform>
            </ds:Transforms>
            <ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
            <ds:DigestValue>o6RiSp7nGmMWf01mYh3FGNpK80A=</ds:DigestValue>
          </ds:Reference>
        </ds:SignedInfo>
        <ds:SignatureValue>irR3w9BKEf64eN9FJRnCBqHEj5kWZu+Bn5QlNrBTxp/tbGxgU1ViTLFNu6kQJFfxEFB1AoN7Ks5c
QKOK5eBwbA3j6486XQPMoYOvOWxtr73PS09LVFp6P1aZ0y5wHTf7dQ89GJ35Zb0JemvkeSN0XWQt
X3USE7A6z4t04jx95FX+Me6dTAFyf3ealyISfrfYkIsasqU6W/orhRgyKunq6N1aTZ7HmphaSgtu
EEncUiKS6aEdvD0NjwKWXlTr/5NT5BQ7T9cmWS00QYjlRlF2SGww44SAehNojwqFy40SEpuVPVJv
DH9GH4ITsy72DeY/PXkHkaEpDIPM32EUfobE1zRM0zwPLGQysGcELSRfzAWR9QWO1NmPecABymZ8
qMNQRxUK5MkX2S/O29Jpmq/8q/VWQQhnMmj6YdL8NAE1RmjH11wNXdWHRM+3iLndMk5EpiDtFZSo
fDmtNBWhiBzE3g/OZYBbZVM9MvQjMj0x2aK8rZK/qRylbVjhYaJI8hEOiAJZeAHErwuynjP01ONI
bXeyqZik5x54zamdsQs5UlXaGYRAqVInKr1j4+trJTstAqP7C22cEUULyKO2jkBj/wWpRhucjbJH
4XXHuVHQWP2myvnImaOHuAC8TFSCsV6/hOx206G4Yd1cyHSfNM3XEQURVlOaO0an582OlFxFvnU=</ds:SignatureValue>
        <ds:KeyInfo Id="KI-FBCFA2CE61C97ADD4A14467251913442138">
          <wsse:SecurityTokenReference wsu:Id="STR-FBCFA2CE61C97ADD4A14467251913442139">
            <ds:X509Data>
              <ds:X509IssuerSerial>
                <ds:X509IssuerName>CN=10,OU=ExperTI,O=ExperTI,L=Santiago,ST=Santiago,C=CL,1.2.840.113549.1.9.1=#16116a636572646140657870657274692e636c</ds:X509IssuerName>
                <ds:X509SerialNumber>1401281826</ds:X509SerialNumber>
              </ds:X509IssuerSerial>
            </ds:X509Data>
          </wsse:SecurityTokenReference>
        </ds:KeyInfo>
      </ds:Signature>
    </wsse:Security>
  </soap:Header>
  <soap:Body xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="id-1425">
    <ns2:initTransactionResponse xmlns:ns2="http://service.wswebpay.webpay.transbank.com/">
      <return>
        <token>e30965b17f7854b21bf77f313cb9a117214e62fb5d98c6ff2580e859d013ed32</token>
        <url>https://tbk.orangepeople.cl/filtroUnificado/initTransaction</url>
      </return>
    </ns2:initTransactionResponse>
  </soap:Body>
</soap:Envelope>

证书(certificate_server.crt):

-----BEGIN CERTIFICATE-----
MIIFhTCCA20CBFOF3SIwDQYJKoZIhvcNAQEFBQAwgYYxIDAeBgkqhkiG9w0BCQEW
EWpjZXJkYUBleHBlcnRpLmNsMQswCQYDVQQGEwJDTDERMA8GA1UECAwIU2FudGlh
Z28xETAPBgNVBAcMCFNhbnRpYWdvMRAwDgYDVQQKDAdFeHBlclRJMRAwDgYDVQQL
DAdFeHBlclRJMQswCQYDVQQDDAIxMDAeFw0xNDA1MjgxMjU3MDZaFw0xNjA1Mjcx
MjU3MDZaMIGGMSAwHgYJKoZIhvcNAQkBFhFqY2VyZGFAZXhwZXJ0aS5jbDELMAkG
A1UEBhMCQ0wxETAPBgNVBAgMCFNhbnRpYWdvMREwDwYDVQQHDAhTYW50aWFnbzEQ
MA4GA1UECgwHRXhwZXJUSTEQMA4GA1UECwwHRXhwZXJUSTELMAkGA1UEAwwCMTAw
ggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCX1t11QZSgfodg+NPwKdof
lakbpxsFmCvjbY3Vpp6/bTv56XIZe/Z3gBlU4zeGslEaqzs1k4cGAcdZPHxSMydC
oLxmyXpdS2hVFUZTYAeanXHhQzUKmNlgYj3uObprPEEQzD/vEuqpwz2/ZGyaHsWs
exu9aeuLAUUSNne0yqobrzRfEp2FqCp4sJB80cXgGfPA4Cr5jROHqUi8sVWpWRy5
ai5ZaiXRPo3YKF1336twuw9lRS3cRtJh9/AoJElGT7G7BMDpxyTTa08y1iRliDGu
mwWivQMHoKqEs0lCtF9Uz8cFdmiIoRPTt6McpmLoapL9v19xjNnim4lke6DPvtcg
uato7T+frDqA5Cj5GRP/8jbe90Y+YjHuJTkw+fkV6gDTRmJ3wCWDIw/07aY6nZ+H
24Imu6N2YBsMEsa8j9OW04mNgtppRC4dFBh0FIKXC35kJgN38y+6T7MsQThX1XZS
SlK0FygJJADVGelmxtsrRRfnp4yLYRjwdkRGExRjVs/+fkOyKI+fX0o68z6MEDyA
5epVHpgwJ/Yz3Lo7cgXy0hO5a/MfZc0Y0ofb29g8sJMJ8j/SSR85i4pFxudn+HH0
SmkkzE/P10adF/X+pqjscOE+aXwnX09lUUQ9TIlpYaR3hUhONsuefYJ2sHz2z+vt
K5btQwN7u9+QeXLgb20PMwIDAQABMA0GCSqGSIb3DQEBBQUAA4ICAQAqCepOSFi7
8d6lKTfWpMuj5ygpplBa3Bj81AKkNfSGDN7zkNX2sCbqn9aEjniIPtldG6I2fgCs
FYTZE9oEPNiuEuH7PNtAjTExIUi9Jzm7bqjCdSs7Ioek1cPePgst017zJ6NSTkaM
r7pUDaS3855xO92uaJskppAeegwz9Dv3d5wY+wVViqUki4pZyxa95IvBJz4NR1Xr
cO6XtUUT9M0wbd0jAkRp7DPQfkihZj8vLSvlUYTRdlF2swIBE/ME2T3NCa0/kt1c
IA1Aq/zn7t0yKvyaJ/O//LrHA1Lfa/uC61O/9P3t+eXDsYl73CeGQdSYZp2DAZmA
Ek3tzwhFa6HR+POIo8MptWMT3DQ0ISHH+EW1Xp8GHIGsk2ELsXuA6XTNwpfz9yvl
9d7IGsq4cdX88cNUCbXm4tj7F3s6i8pNWeCImaYcXKGCBdsLM+lbmqbuV7o3d1Ei
efbR1TQkCxRBNCMUI0pF7NW8PvY3QER9/jEnN52SX+tuQRVdpgl+PyTdSASr4FhV
+HHmgeOgeOewXDnZ7aA1F6f8+CY8Niv4FGZIAptdxTqdynY4nUy/wFowBouO3LEF
6nIcQ3Jx1pDXoEmcLa03JaL7qQNSHyqSe/YEl8E5fdDr7vApzw9pvpAjj1aslidL
bNd4l1YGlL2vbGsIXZlbdBLiblXRi78AyQ==
-----END CERTIFICATE-----

Ruby源码(稍作修改,使用OpenSSL::Digest::SHA1.new代替digester映射方式来简化):

# load certificate
certificate_server = OpenSSL::X509::Certificate.new(File.read("certificate_server.crt"))

# CERT INFORMATION IS VERIFIED SUCCESSFULLY HERE
# DIGEST IS VERIFIED SUCCESSFULLY HERE

# certificate signature initialization
response_signature = Akami::WSSE::VerifySignature.new(response)
document = response_signature.document
namespaces = response_signature.namespaces

# retrieve Signature data & digest value
data = document.at_xpath('//wse:Security/ds:Signature/ds:SignedInfo', namespaces).canonicalize(Nokogiri::XML::XML_C14N_EXCLUSIVE_1_0)
signature = Base64.decode64(document.at_xpath('//wse:Security/ds:Signature/ds:SignatureValue', namespaces).text)

# check if Signature is valid
return false unless certificate_server.public_key.verify(OpenSSL::Digest::SHA1.new, signature, data)

true

这只在最后一行失败:

certificate_server.public_key.verify(signature_digester, signature, data)

如果有人能发现 error/bug/missing 代码,或者提供一个工作示例,我将不胜感激。

必须通过使用 PHP xmlseclibs 发出新请求来验证这一点,但结果是 signature 字段中缺少 '\n' 并且以下行的 data 字段上的根信封 XML 标记中缺少命名空间:

certificate_server.public_key.verify(signature_digester, signature, data)

特别是对于这种情况,data 中的元素 SignedInfo 需要验证,同时还具有以下名称空间,这不是包含在 'canonicalize' 方法中:

xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"

希望这对以后的人有所帮助!