11

how do make a the following section of Service Settings of app.config in C# programmatically:

    <client>
  <endpoint address="https://someServiceUrl"
      binding="basicHttpBinding" bindingConfiguration="Contact"
      contract="ServiceReference.PostingWebService" name="PostingWebServicePort">
    <headers>
      <wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
        <wsse:UsernameToken>
          <wsse:Username>someusername</wsse:Username>
          <wsse:Password Type='http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText'>somepassword</wsse:Password>
        </wsse:UsernameToken>
      </wsse:Security>
    </headers>
  </endpoint>
</client>

I have managed to generate binding section (not included above) and endpoint section from C#. I am unable to create the headers section.

The error that comes up is: (this is because I don't have headers section when I generate everything from C#)

This service requires <wsse:Security>, which is missing.

the headers section is important, as if I exclude it from the config and run the code using config it also gives the above error.

I don't want to use web.config/app.config. I have to run every thing from C#. (the above app.config works fine, but I want to do that same through C#)

NOTE: THE UPDATES BELOW ARE BASED ON THE SOLUTION PROVIDED BELOW PLEASE GO THROUGH THE COMMENTS ON THE SOLUTION BELOW, FOR BETTER UNDERSTANDING

UPDATE 1: (programmatically using BasicHttpBinding first)

BasicHttpBinding binding = new BasicHttpBinding();
        binding.Name = "Contact";
        binding.CloseTimeout = TimeSpan.FromMinutes(1);
        binding.OpenTimeout = TimeSpan.FromMinutes(1);
        binding.ReceiveTimeout = TimeSpan.FromMinutes(10);
        binding.SendTimeout = TimeSpan.FromMinutes(1);
        binding.AllowCookies = false;
        binding.BypassProxyOnLocal = false;
        binding.HostNameComparisonMode = HostNameComparisonMode.StrongWildcard;
        binding.MaxBufferSize = 524288;
        binding.MaxBufferPoolSize = 524288;
        binding.MaxReceivedMessageSize = 524288;
        binding.MessageEncoding = WSMessageEncoding.Text;
        binding.TextEncoding = System.Text.Encoding.UTF8;
        binding.TransferMode = TransferMode.Buffered;
        binding.UseDefaultWebProxy = true;

        binding.ReaderQuotas.MaxDepth = 32;
        binding.ReaderQuotas.MaxStringContentLength = 65536;
        binding.ReaderQuotas.MaxArrayLength = 131072;
        binding.ReaderQuotas.MaxBytesPerRead = 32768;
        binding.ReaderQuotas.MaxNameTableCharCount = 131072;

        binding.Security.Mode = BasicHttpSecurityMode.Transport;
        binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;
        binding.Security.Transport.ProxyCredentialType = HttpProxyCredentialType.None;
        binding.Security.Transport.Realm = "";
        binding.Security.Message.ClientCredentialType = BasicHttpMessageCredentialType.UserName;
        binding.Security.Message.AlgorithmSuite = System.ServiceModel.Security.SecurityAlgorithmSuite.Default;

        CustomBinding customBinding = new CustomBinding(binding);
        SecurityBindingElement element = customBinding.Elements.Find<SecurityBindingElement>();
        // Remove security timestamp because it is not used by your original binding
        //element.IncludeTimestamp = false; (element is NULL in my case)

EndpointAddress endpoint = new EndpointAddress("https://myserviceaddress");

        PostingWebServiceClient client = new PostingWebServiceClient(customBinding, endpoint);

        client.ClientCredentials.UserName.UserName = "myusername";
        client.ClientCredentials.UserName.Password = "mypassword";

        client.getActiveChannels(new getActiveChannels());

Using Custom Bindgin Directly:

SecurityBindingElement securityElement = SecurityBindingElement.CreateUserNameOverTransportBindingElement();
        securityElement.IncludeTimestamp = false;
        TextMessageEncodingBindingElement encodingElement = new TextMessageEncodingBindingElement(MessageVersion.Soap11, Encoding.UTF8);
        HttpsTransportBindingElement transportElement = new HttpsTransportBindingElement();

        CustomBinding customBinding = new CustomBinding(securityElement, encodingElement, transportElement);


EndpointAddress endpoint = new EndpointAddress("https://myserviceaddress");

        PostingWebServiceClient client = new PostingWebServiceClient(customBinding, endpoint);

        client.ClientCredentials.UserName.UserName = "myusername";
        client.ClientCredentials.UserName.Password = "mypassword";

        client.getActiveChannels(new getActiveChannels());
Cœur
  • 37,241
  • 25
  • 195
  • 267
user402186
  • 489
  • 2
  • 7
  • 17
  • First example is wrong. You are using incorrect mode of security. You must use `TransportWithMessage` credentials not only `Transport`. In your second example the error is clearly in authentication. The service found the username and password but it returned that it is invalid. – Ladislav Mrnka Sep 01 '11 at 08:55
  • Thank you very much for summarizing the answers, this is the cleanest way I've found to add the security header. – Jim109 Jul 03 '15 at 13:06

2 Answers2

6

You don't have to configure header directly in this case because your scenario should be supported by BasicHttpBinding or CustomBinding directly.

If you need to configure it from C# you must create binding in code:

// Helper binding to have transport security with user name token
BasicHttpBinding binding = new BasicHttpBinding(BasicHttpSecurityMode.TransportWithMessageCredential);
binding.Security.Message.ClientCredentialType = BasicHttpMessageCredentialType.UserName;
// Rest of your binding configuration comes here

// Custom binding to have access to more configuration details of basic binding
CustomBinding customBinding = new CustomBinding(binding);
SecurityBindingElement element = customBinding.Elements.Find<SecurityBindingElement>();
// Remove security timestamp because it is not used by your original binding
element.IncludeTimestamp = false;

EndpointAddress address = new EndpointAddress("https://...");

ProxyWebServiceClient client = new ProxyWebServiceClient(customBinding, address);
client.ClientCredentials.UserName.UserName = "...";
client.ClientCredentials.UserName.Password = "...";

Other solution is building custom binding directly instead of starting with basic binding:

SecurityBindingElemetn securityElement = SecurityBindingElement.CreateUserNameOverTransportBindingElement();
securityElement.IncludeTimestamp = false; 
TextMessageEncodingBindingElement encodingElement = new TextMessageEncodingBindingElement(MessageVersion.Soap11, Encoding.UTF8);
HttpsTransportBindingElement tranportElement = new HttpsTransportBindingElement();

// Other configurations of basic binding are divided into properties of 
// encoding and transport elements

CustomBinding customBinding = new CustomBinding(securityElement, encodingElement, transportElement);

EndpointAddress address = new EndpointAddress("https://...");

ProxyWebServiceClient client = new ProxyWebServiceClient(customBinding, address);
client.ClientCredentials.UserName.UserName = "...";
client.ClientCredentials.UserName.Password = "...";
Ladislav Mrnka
  • 360,892
  • 59
  • 660
  • 670
  • Both of the above didn't work. In the first case, (where i create basic binding first), i get null at SecurityBindingElement element = customBinding.Elements.Find(); so cant set element.IncludeTimestamp = false; as element is null. commenting element.IncludeTimestamp = false; out again gives me the same error (i.e.: This service requires , which is missing.) – user402186 Sep 01 '11 at 08:40
  • in the second senario, where i directly create custom binding, the error is (Authentication failure: Invalid username: myusername) but am sure my credentials are correct – user402186 Sep 01 '11 at 08:41
  • i am updating both senario codes in my question, please have a look – user402186 Sep 01 '11 at 08:42
  • I added reason why the first scenario doesn't work for you in comment under your question. The second scenario works. Now you have problem with passed credentials and you must discuss with service provider why it doesn't work. – Ladislav Mrnka Sep 01 '11 at 08:56
  • i tried TransportWithMessageCredential, the error now is: An unsecured or incorrectly secured fault was received from the other party. See the inner FaultException for the fault code and detail. Inner Exception: An internal WS-Security error occurred. See log for details – user402186 Sep 01 '11 at 09:01
  • my credentials are good for sure. as they are still workign if i connect to the service through app.config settigns i mentioned in my question – user402186 Sep 01 '11 at 09:02
  • How was the service created? What language / API does it use? – Ladislav Mrnka Sep 01 '11 at 09:04
  • It looks like the service is expecting the header exactly as is - perhaps also with exact prefixes (which is completely wrong because prefix is under control of the client). Turn on [WCF message logging](http://msdn.microsoft.com/en-us/library/ms730064.aspx) and try to use the header (with prefixes) from the logged message in your working example (logging will hide the password so you will have to add it again). – Ladislav Mrnka Sep 01 '11 at 09:10
  • k, let me try ... (i'll be using logging/tracing for the first time) so not really sure what u suggected, but i'll get back to you in few minutes time ... (Thanks alot by the way) – user402186 Sep 01 '11 at 09:27
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/3059/discussion-between-user402186-and-ladislav-mrnka) – user402186 Sep 01 '11 at 11:39
  • Yes it makes but you should be able to open the file in [SvcTraceViewer.exe](http://msdn.microsoft.com/en-us/library/ms732023.aspx) – Ladislav Mrnka Sep 01 '11 at 11:40
  • +1 I used your other example to set the header statically in the config, but I didn't want to have to bring that to each new config. I also was hoping to set the username password dynamically, and this did the trick. Thanks! – kwelch Feb 16 '12 at 14:32
2

Look at the accepted answer to this StackOverflow question. It shows how to programatically add client credentials to the proxy. It also shows adding the headers in the client endpoint configuration XML which I hadn't seen before.

Community
  • 1
  • 1
Sixto Saez
  • 12,610
  • 5
  • 43
  • 51