Search code examples
language-agnosticx509ws-securityxml-signature

How does STR-Transform works?


How does the STR-Transform transformation algorithm works for XML Signature, when working with WS Security? I need to sign the SecurityTokenReference used for the signature on a SOAP message, and this is the required transformation for the security token. I am using an x509 certificate to do the signature, so the security token is this certificate. However, in the message I only need the reference to the certificate thumbprint.

Here is the signature structure that I need to replicate, and the only bit that I am missing is the signature reference to the SecurityTokenReference:

<dsig:Signature xmlns:dsig="http://www.w3.org/2000/09/xmldsig#">
  <dsig:SignedInfo>
    <dsig:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
    <dsig:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
    <dsig:Reference URI="#Timestamp_C1Ih1AB1vpPT5uG2">
      <dsig:Transforms>
        <dsig:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
      </dsig:Transforms>
      <dsig:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
      <dsig:DigestValue>fVSyToUO8yS131cV8oT1h6fa69Jvtt+pKFeP4BFf1P4=</dsig:DigestValue>
    </dsig:Reference>
    <!-- Other signature references -->
    <dsig:Reference URI="#str_U1sjQ5j8JtKnObLk">
      <dsig:Transforms>
        <dsig:Transform Algorithm="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#STR-Transform">
          <wsse:TransformationParameters>
            <dsig:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
          </wsse:TransformationParameters>
        </dsig:Transform>
      </dsig:Transforms>
      <dsig:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
      <dsig:DigestValue>gRa3zakGn13XISoKpekB3zl0iDqb/LmNy7+aMDtzKIY=</dsig:DigestValue>
    </dsig:Reference>
  </dsig:SignedInfo>
  <dsig:SignatureValue>ptO...E9Q==</dsig:SignatureValue>
  <dsig:KeyInfo>
    <wsse:SecurityTokenReference
                    xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
                    xmlns:wsse11="http://docs.oasis-open.org/wss/oasis-wss-wssecurity-secext-1.1.xsd"
                    xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
                    wsse11:TokenType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3"
                    wsu:Id="str_U1sjQ5j8JtKnObLk">
      <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/oasis-wss-soap-message-security-1.1#ThumbprintSHA1">h5...ow=</wsse:KeyIdentifier>
    </wsse:SecurityTokenReference>
  </dsig:KeyInfo>
</dsig:Signature>

Can someone explain me how to do the signature for such token? The step-by-step description of the algorithm, or an example using any language/library will be good.

In this document is the description of the transformation, from page 38, but I am unable to understand how to apply it in practice.


Solution

  • OK, after checking Oracle's WebLogic server debug and verbose log file having a working service example and setting the flags -Dweblogic.xml.crypto.dsig.debug=true -Dweblogic.xml.crypto.dsig.verbose=true -Dweblogic.xml.crypto.keyinfo.debug=true -Dweblogic.xml.crypto.keyinfo.verbose=true -Dweblogic.wsee.verbose=* -Dweblogic.wsee.debug=* (more info here, here, here and here), thank God there was a preety good insight on how the security token was de-referenced. Basically, having a SecurityTokenReference for an x509 certificate, with a KeyIdentifier, is dereferenced as a BinarySecurityToken in this way:

    <wsse:BinarySecurityToken xmlns="" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3">CertificateBase64String</wsse:BinarySecurityToken>
    

    Some important things to notice are:

    • The ValueType, and thus the content of the BinarySecurityToken, is defined by the TokenType of the SecurityTokenReference. In this case, the text of the BinarySecurityToken is the x509 certificate that is referenced by the KeyIdentifier element, encoded as a base64 string.
    • According to the specification, the BinarySecurityToken only includes the ValueType attribute. So it should not include the EncodingType attribute, neither the Id attribute that the SecurityTokenReference has.
    • The same namespace prefix of the SecurityTokenReference is used. Also, the namespace for this prefix is included in the tag.
    • The default namespace attribute is set as empty: xmlns=""

    So basically the whole SecurityTokenReference element is replaced by the new BinarySecurityToken, and this is the one to be canonicalized and hashed (to get its digest value). Beware that it is canonicalized and digested as is, so the operation may provide a wrong result if the XML is simplified by removing the empty xmlns namespace or the prefix namespace, or by changing the namespace prefix.

    The example BinarySecurityToken is already canonicalized using the algorithm "http://www.w3.org/2001/10/xml-exc-c14n#", so in .NET, to get the DigestValue using the digest algorithm "http://www.w3.org/2001/04/xmlenc#sha256", is enough to do this:

    System.Security.Cryptography.SHA256 sha = System.Security.Cryptography.SHA256.Create();
    byte[] hash = sha.ComputeHash(Encoding.UTF8.GetBytes("<wsse:BinarySecurityToken xmlns=\"\" xmlns:wsse=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd\" ValueType=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3\">MIIF...2A8=</wsse:BinarySecurityToken>"));
    string digestValue = Convert.ToBase64String(hash);