Search code examples
.netweb-serviceswcf

WCF Webservice request : Add a Reference tag instead of KeyIdentifier tag in SecurityTokenReference


I need to consume a Java webservice with .NET. Requests must be signed, and the certificate needs to be included in the request.

When using the 'GetClient' method below, the request is generatec correct except for one field. In the SecurityTokenReference element, a 'KeyIdentifier' is added. This SecurityTokenReference field should contain a 'Reference' field to the third refence in the 'SignedInfo' element:

</o:Security>
  </s:Header>
    <o:BinarySecurityToken>...</o:BinarySecurityToken>
    <Signature>
      <SignedInfo>
        <CanonicalizationMethod></CanonicalizationMethod>
        <SignatureMethod></SignatureMethod>
        <Reference URI="#_1">...</Reference>
        <Reference URI="#uuid-001">...</Reference>
        <Reference URI="#uuid-002">...</Reference>
      </SignedInfo>
      <SignatureValue>...</SignatureValue>
      <KeyInfo>
        <o:SecurityTokenReference>
          <o:KeyIdentifier >...</o:KeyIdentifier>
        </o:SecurityTokenReference>
      </KeyInfo>
    </Signature>
  </o:Security>
</s:Header>

So

    <o:SecurityTokenReference>
      <o:KeyIdentifier >...</o:KeyIdentifier>
    </o:SecurityTokenReference>

Should be

    <o:SecurityTokenReference>
        <o:Reference ValueType="..." URI="uuid-002" />
    </o:SecurityTokenReference>

However i cannot get this working.

When using

 InitiatorTokenParameters InclusionMode = SecurityTokenInclusionMode.Once

the correct 'Reference' tag is added, but then a double 'BinarySecurityToken' tag is generated, causing the webservice to be rejected. Is there any solution for this?

public wsClient GetClient()  
{  

        CustomBinding b = new CustomBinding();
        HttpsTransportBindingElement transport = new HttpsTransportBindingElement();

        AsymmetricSecurityBindingElement asec = (AsymmetricSecurityBindingElement)SecurityBindingElement.CreateMutualCertificateBindingElement
            (MessageSecurityVersion.WSSecurity10WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10);
        asec.SetKeyDerivation(false);
        asec.AllowInsecureTransport = true;
        asec.DefaultAlgorithmSuite = SecurityAlgorithmSuite.Basic256;
        asec.IncludeTimestamp = true;
        asec.ProtectTokens = true;
        asec.SecurityHeaderLayout = SecurityHeaderLayout.Lax;
        asec.InitiatorTokenParameters = new X509SecurityTokenParameters
        {
            InclusionMode = SecurityTokenInclusionMode.Never, //.Once, ----> Once creates correct SecurityTokenReference, but double BinarySecurityToken
            ReferenceStyle = SecurityTokenReferenceStyle.Internal
        };

        asec.EndpointSupportingTokenParameters.Signed.Add(new X509SecurityTokenParameters());
        TextMessageEncodingBindingElement textMessageEncoding = new TextMessageEncodingBindingElement(MessageVersion.Soap11, Encoding.UTF8);

        b.Elements.Add(asec);
        b.Elements.Add(textMessageEncoding);
        b.Elements.Add(transport);

        string url = "https://service";

        var c = new wsClient(b, new EndpointAddress(new Uri(url), 
            new DnsEndpointIdentity(kgParams.DnsEndpointIdentity), new AddressHeaderCollection()));


        X509Certificate cert = GetCertificate();
        c.ClientCredentials.ClientCertificate.Certificate = new X509Certificate2(cert);
        c.ClientCredentials.ServiceCertificate.DefaultCertificate = c.ClientCredentials.ClientCertificate.Certificate;
        c.Endpoint.Contract.ProtectionLevel = System.Net.Security.ProtectionLevel.Sign;

        return c;
}

Solution

  • After struggling for days with this issue, and not a single remark or answer on SO despite a bounty of 250 points, I can only come to the conclusion that this is an incompatibility between Java and .NET WCF.

    In the end I simply abondoned WCF in frustration, and recreated the whole ***** request from scratch. This means:

    • Creating an XmlDocument containing the needed data
    • Add the certificate as Base64 to the header
    • Manually timestamp the request
    • Manually add the correct KeyInfo tag
    • Manually add all references using appropriate GUIDs
    • Sign all the elements manually using SignedXml and the certificate
    • Manually post the request

    The server accepted the request without any complaint.