I'm asked to write a WCF client connecting to SAP NetWeaver web services, secured by SAML 1.1 sender-vouches. The WCF client will be integrated to a SharePoint 2013 WebPart. The SharePoint web application is configured with a claims SAML authentication and the STS is an IBM DataPower.
As a SharePoint architect and not being an expert in SAML security standards, I admitt I am encountering troubles to write the client... Could someone help me?
Here are the web service requirements:
SAML Sender Vouches with AS ABAP 7.00 requirements
Scenario
A web services consumer sends a SOAP 1.1 message protected with a X.509 XML signature to the provider. The message contains a timestamp, SAML assertion, SecurityTokenReference, BinarySecurityToken, Signature. The SAML assertion must be signed according to the SecurityTokenReferenceTransform (see section 8.3 from http://docs.oasisopen.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0.pdf). When computing the C14N of the SAML assertion. Correct handling of the default namespace needs to be ensured.
- SOAP Version: 1.1
- Addressing: No (optional)
- Signature Certificate: RSA
- Timestamp: Yes (precision: seconds)
- Signed parts: Timestamp, Body, SAML assertion over STR transform
- Canonicalization: XML-EXC-C14N
- Signature: SHA1 (WS-SP asymmetric key binding)
Example message
<soap-env:Envelope xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/">
<soap-env: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">
<wsu:Timestamp wsu:Id="ts-0018FE864EEE1DDE86C5371CA2C55C43">
<wsu:Created>2009-03-26T17:15:36Z</wsu:Created>
<wsu:Expires>2009-03-26T17:17:06Z</wsu:Expires>
</wsu:Timestamp>
<wsse:BinarySecurityToken wsu:Id="bst-0018FE864EEE1DDE86C5371CA2C5" EncodingType="http://docs.oasisopen.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary" ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wssx509-token-profile-1.0#X509v3">MIIDBDCCAsQCAQAwCQYHKoZIzjgEAzBrMQswCQYDVQQGEwJERTEcMBoGA1UEChMTU0FQIFRydXN0 IENvbW11bml0eTETMBEGA1UECxMKU0FQIFdlYiBBUzEUMBIGA1UECxMLSTAwMjAyMjQ1MjAxEzAR BgNVBAMTCkJYSSBTeXN0ZW0wHhcNMDYwNzEzMDgyNjE3WhcNMzgwMTAxMDAwMDAxWjBrMQswCQYD VQQGEwJERTEcMBoGA1UEChMTU0FQIFRydXN0IENvbW11bml0eTETMBEGA1UECxMKU0FQIFdlYiBB UzEUMBIGA1UECxMLSTAwMjAyMjQ1MjAxEzARBgNVBAMTCkJYSSBTeXN0ZW0wggG4MIIBLAYHKoZI zjgEATCCAR8CgYEA//WnblZ7kAOAmw9A8+mTxtQgKvOjJ01QqxgOZb/cG8Y87vwq38F7PxYeISlx S/amwPu/oa9w1gC3oyoVgR6f6/ItQ9EvBnsGCl3qkFUHNBO48CyK0p8e5npH4JtEOXmHFknwwS6F wls4r+nxK/9B88tf88EvrfSylrVgPdsmIIMCFQDEpZBhQVQDSpJV2N+P8hAZzDs+NQKBgQDKTmmW EsMZ6x3K+00M6odL3h6pr8m7osHMXF2xsnC1M2/hIZgHEIUfywWpjLWYaecY59als7aX9M2UMGbG qaAQ+6DQUQpaosXGXSZEkQP0lPg6St1rg+4/kjhQCKgtAoJj7PNE/1hf4IESYeaSVHYpzYLzChwb RFcDweAPAOnsyQOBhQACgYEAq6yOb8eoXNy16vDfhLG82xxOPrbAwIQhyVhmPEQLTQAK7XvSzIkR PtKNvndxKNwkYluWsc+hnX+0x1BeFyuwIHXk4WxWMBIpqvrJcdV6oS6pJ88AuoQx6kyhSLnp6e6T g5rZE5tnVNX20YcUQuliT4/5f/SV974TisJQJn9rgbswCQYHKoZIzjgEAwMvADAsAhRwwTSAEEpt 2FkKuBhNa27CPdUqNgIUa1xIFJbCxE07SL9XyL7ABSF/zs0=</wsse:BinarySecurityToken>
<ds:Signature Id="sig-0018FE864EEE1DDE86C5371CA2C57C43" 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="#part-Body-9">
<ds:Transforms>
<ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#">
<wsse:TransformationParameters>
<ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
</wsse:TransformationParameters>
</ds:Transform>
</ds:Transforms>
<ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
<ds:DigestValue>HRxp0v+mwsR7S7h0l8blXRTrY14=</ds:DigestValue>
</ds:Reference>
<ds:Reference URI="#ts-0018FE864EEE1DDE86C5371CA2C55C43">
<ds:Transforms>
<ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#">
<wsse:TransformationParameters>
<ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
</wsse:TransformationParameters>
</ds:Transform>
</ds:Transforms>
<ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
<ds:DigestValue>QoFDfx9y5uWYVNkL9HV+Dh7QBro=</ds:DigestValue>
</ds:Reference>
<ds:Reference URI="#str-0018FE864EEE1DDE86C5371CA2C59C43">
<ds:Transforms>
<ds:Transform Algorithm="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security1.0#STR-Transform">
<wsse:TransformationParameters>
<ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
</wsse:TransformationParameters>
</ds:Transform>
</ds:Transforms>
<ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
<ds:DigestValue>bnNHZmU1muXgTIndsCYNORGEAos=</ds:DigestValue>
</ds:Reference>
</ds:SignedInfo>
<ds:SignatureValue>MC0CFQCAZkHCg9u9gVcte3HpxRzOGivkggIUIVomV4eXfOcaoLyggEK1oUoz4Rg=</ds:SignatureValue>
<ds:KeyInfo>
<wsse:SecurityTokenReference>
<wsse:Reference URI="#bst-0018FE864EEE1DDE86C5371CA2C5" ValueType="http://docs.oasisopen.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3"/>
</wsse:SecurityTokenReference>
</ds:KeyInfo>
</ds:Signature>
<wsse:SecurityTokenReference wsu:Id="str-0018FE864EEE1DDE86C5371CA2C59C43">
<wsse:Reference URI="#saml-0018FE864EEE1DDE86C5371CA2C53C43" ValueType="http://docs.oasisopen.org/wss/oasis-wss-saml-token-profile-1.0#SAMLAssertionID"/>
</wsse:SecurityTokenReference>
<saml:Assertion MajorVersion="1" MinorVersion="1" AssertionID="saml-0018FE864EEE1DDE86C5371CA2C53C43" Issuer="BXI/000" IssueInstant="2009-03-26T17:15:36Z" xmlns:saml="urn:oasis:names:tc:SAML:1.0:assertion">
<saml:Conditions NotBefore="2009-03-26T17:15:36Z" NotOnOrAfter="2009-03-26T17:20:36Z"/>
<saml:AuthenticationStatement AuthenticationMethod="urn:oasis:names:tc:SAML:1.0:am:unspecified" AuthenticationInstant="2009-03-26T17:15:36Z">
<saml:Subject>
<saml:NameIdentifier NameQualifier="" Format="urn:oasis:names:tc:SAML:1.1:nameidformat:unspecified">DEBOER</saml:NameIdentifier>
<saml:SubjectConfirmation>
<saml:ConfirmationMethod>urn:oasis:names:tc:SAML:1.0:cm:sender-vouches</saml:ConfirmationMethod>
</saml:SubjectConfirmation>
</saml:Subject>
</saml:AuthenticationStatement>
</saml:Assertion>
</wsse:Security>
</soap-env:Header>
<soap-env:Body wsu:Id="part-Body-9" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<n0:WSSE_ECHO xmlns:n0="urn:sap-com:document:sap:rfc:functions">
<INPUT>some data</INPUT>
</n0:WSSE_ECHO>
</soap-env:Body>
</soap-env:Envelope>
Policy description
<wsp:ExactlyOne xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy" xmlns:sapsp="http://www.sap.com/webas/630/soap/features/security/policy" xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702" xmlns:wsa="http://www.w3.org/2005/08/addressing" xmlns:wst="http://docs.oasis-open.org/ws-sx/ws-trust/200512" xmlns:wsu="http://schemas.xmlsoap.org/ws/2002/07/utility" xmlns:wsx="http://schemas.xmlsoap.org/ws/2004/09/mex">
<wsp:All>
<sp:AsymmetricBinding>
<wsp:Policy>
<sp:InitiatorSignatureToken>
<wsp:Policy>
<sp:X509Token sp:IncludeToken="http://docs.oasis-open.org/ws-sx/wssecuritypolicy/200702/IncludeToken/AlwaysToRecipient">
<wsp:Policy>
<sp:WssX509V3Token10/>
</wsp:Policy>
</sp:X509Token>
</wsp:Policy>
</sp:InitiatorSignatureToken>
<sp:AlgorithmSuite>
<wsp:Policy>
<sp:Basic256/>
</wsp:Policy>
</sp:AlgorithmSuite>
<sp:Layout>
<wsp:Policy>
<sp:Strict/>
</wsp:Policy>
</sp:Layout>
<sp:IncludeTimestamp/>
<sp:OnlySignEntireHeadersAndBody/>
</wsp:Policy>
</sp:AsymmetricBinding>
<sp:Wss10>
<wsp:Policy>
<sp:MustSupportRefKeyIdentifier/>
</wsp:Policy>
</sp:Wss10>
<sp:SignedParts>
<sp:Body/>
<sp:Header Name="Trace" Namespace="http://www.sap.com/webas/630/soap/features/runtime/tracing/"/>
<sp:Header Name="messageId" Namespace="http://www.sap.com/webas/640/soap/features/messageId/"/>
<sp:Header Name="CallerInformation" Namespace="http://www.sap.com/webas/712/soap/features/runtime/metering/"/>
<sp:Header Name="Session" Namespace="http://www.sap.com/webas/630/soap/features/session/"/>
<sp:Header Name="To" Namespace="http://schemas.xmlsoap.org/ws/2004/08/addressing"/>
<sp:Header Name="ReplyTo" Namespace="http://schemas.xmlsoap.org/ws/2004/08/addressing"/>
<sp:Header Name="From" Namespace="http://schemas.xmlsoap.org/ws/2004/08/addressing"/>
<sp:Header Name="Action" Namespace="http://schemas.xmlsoap.org/ws/2004/08/addressing"/>
<sp:Header Name="FaultTo" Namespace="http://schemas.xmlsoap.org/ws/2004/08/addressing"/>
<sp:Header Name="MessageID" Namespace="http://schemas.xmlsoap.org/ws/2004/08/addressing"/>
<sp:Header Name="RelatesTo" Namespace="http://schemas.xmlsoap.org/ws/2004/08/addressing"/>
<sp:Header Name="To" Namespace="http://www.w3.org/2005/08/addressing"/>
<sp:Header Name="ReplyTo" Namespace="http://www.w3.org/2005/08/addressing"/>
<sp:Header Name="From" Namespace="http://www.w3.org/2005/08/addressing"/>
<sp:Header Name="Action" Namespace="http://www.w3.org/2005/08/addressing"/>
<sp:Header Name="FaultTo" Namespace="http://www.w3.org/2005/08/addressing"/>
<sp:Header Name="MessageID" Namespace="http://www.w3.org/2005/08/addressing"/>
<sp:Header Name="RelatesTo" Namespace="http://www.w3.org/2005/08/addressing"/>
<sp:Header Name="ReferenceParameters" Namespace="http://www.w3.org/2005/08/addressing"/>
<sp:Header Name="Sequence" Namespace="http://schemas.xmlsoap.org/ws/2005/02/rm"/>
<sp:Header Name="SequenceAcknowledgement" Namespace="http://schemas.xmlsoap.org/ws/2005/02/rm"/>
<sp:Header Name="AckRequested" Namespace="http://schemas.xmlsoap.org/ws/2005/02/rm"/>
<sp:Header Name="SequenceFault" Namespace="http://schemas.xmlsoap.org/ws/2005/02/rm"/>
<sp:Header Name="Sequence" Namespace="http://docs.oasis-open.org/ws-rx/wsrm/200702"/>
<sp:Header Name="AckRequested" Namespace="http://docs.oasis-open.org/ws-rx/wsrm/200702"/>
<sp:Header Name="SequenceAcknowledgement" Namespace="http://docs.oasis-open.org/ws-rx/wsrm/200702"/>
<sp:Header Name="SequenceFault" Namespace="http://docs.oasis-open.org/ws-rx/wsrm/200702"/>
<sp:Header Name="UsesSequenceSTR" Namespace="http://docs.oasis-open.org/ws-rx/wsrm/200702"/>
<sp:Header Name="UsesSequenceSSL" Namespace="http://docs.oasis-open.org/ws-rx/wsrm/200702"/>
</sp:SignedParts>
<sp:SignedSupportingTokens>
<wsp:Policy>
<sp:SamlToken sp:IncludeToken="http://docs.oasis-open.org/ws-sx/wssecuritypolicy/200702/IncludeToken/AlwaysToRecipient">
<wsp:Policy>
<sp:WssSamlV11Token10/>
</wsp:Policy>
</sp:SamlToken>
</wsp:Policy>
</sp:SignedSupportingTokens>
</wsp:All>
</wsp:ExactlyOne>
My problems
I generated a correct proxy and I am able to communicate with another endpoint of the service configured without SAML. My problem is to make the correct binding to handle the SAML endpoint, but I don't know what it will have to handle, and I'm not sure either what exactly will be the SAML process... From what I understood, my WCF client need to generate a SOAP message with a signed SAML token, issued by the STS.
But what part of the system will sign it? I was not provided any certificate allowing me to sign anything : is it the job of the STS to sign it? By code, is it possible to provide the WCF client a valid already signed token? I suppose the factory.CreateChannelWithIssuedToken(token);
need a unsigned token, am I right?
My attempts
Configuration generation using svcutil
When I try to generate a proxy from the policy, I get:
An exception was thrown in a call to a policy import extension.
Extension: System.ServiceModel.Channels.SecurityBindingElementImporterError:
An unsupported security policy assertion was detected during the security policy import: <sp:AsymmetricBinding ...
My conclusion is that the policy is not supported by svcutil
, so I have to generate the binding myself...
Custom binding
From what I found in equivalent questions and blog posts, I started writing a custom binding for the policy:
AsymmetricSecurityBindingElement asbe = new AsymmetricSecurityBindingElement
{
MessageSecurityVersion = MessageSecurityVersion.WSSecurity10WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10, // Or WSSecurity10WSTrust13WSSecureConversation13WSSecurityPolicy12BasicSecurityProfile10 ?
InitiatorTokenParameters = new X509SecurityTokenParameters { InclusionMode = SecurityTokenInclusionMode.AlwaysToRecipient },
RecipientTokenParameters = new X509SecurityTokenParameters(), // This part is mandatory, but what do I put in here?
MessageProtectionOrder = MessageProtectionOrder.SignBeforeEncrypt,
SecurityHeaderLayout = SecurityHeaderLayout.Strict,
IncludeTimestamp = true,
DefaultAlgorithmSuite = SecurityAlgorithmSuite.Basic256,
};
asbe.SetKeyDerivation(false); // What is it for?
asbe.EndpointSupportingTokenParameters.Signed.Add(new X509SecurityTokenParameters { InclusionMode = SecurityTokenInclusionMode.AlwaysToRecipient });
CustomBinding binding = new CustomBinding();
binding.Elements.Add(asbe);
binding.Elements.Add(new TextMessageEncodingBindingElement(MessageVersion.Soap11, Encoding.UTF8));
binding.Elements.Add(new HttpTransportBindingElement
{
AuthenticationScheme = AuthenticationSchemes.Anonymous,
MaxReceivedMessageSize = 1024 * 1024
});
ChannelFactory<ZELOG> factory = new ChannelFactory<ZELOG>(binding, EndpointUrl);
//factory.Credentials.ClientCertificate.SetCertificate(/*???*/);
//factory.Credentials.ServiceCertificate.SetDefaultCertificate(/*???*/);
//factory.ConfigureCredentials(SPServiceAuthenticationMode.Claims); // SharePoint extension method. Do I need this?
Getting the SAML token
I am also having trouble to decide how to retrieve the SAML token from the STS. My first thought was that as the SharePoint web app was already bound to the STS to authenticate the users, I could somehow easily retrieve the token.
- by directly using the SAML token of the currently logged user. How do I know if it is valid or if I need to request another? And how do I know the token type (bootstrap token, issue token, ... ?)
- or by using the SharePoint API SPSecurityContext.SecurityTokenForContext(new Uri(EndpointUrl));
SecurityToken token = SPSecurityContext.SecurityTokenForContext(new Uri(EndpointUrl));
ZELOG elog = factory.CreateChannelWithIssuedToken(token);
Do I need to write my own custom WSTrust client to get the expected token?
Conclusion
I feel that I am not far from the solution, but I obviously miss something and now, I am asking myself too much questions to achieve this on my own...
Could you give me some directions to follow? Am I in the right way?
Thank you in advance