WCF SSL certificate using an enterprise CA - c#

For an application, I need to have a SSL certificate for a WCF service,
So we installed it. If I go with an internet browser with a web browser trough https, I've got no problem, no warning, nothing, so I suppose that this certificate is considered as valid for windows.
The problem is that when I'm trying to connect to my WCF server, I got this error:
The X.509 certificate CN=myHostName, OU=tom, O=myDomainName,
L=MyLocation, S=SO, C=CH chain building failed. The certificate that
was used has a trust chain that cannot be verified. Replace the
certificate or change the certificateValidationMode. The revocation
function was unable to check revocation because the revocation server
was offline.
What can be wrong? How can I know which part of the chain is unvalid? Is there any way to know what is the missing part?
Here is my code
The server:
ServiceHost myHost = new ServiceHost(typeof(MyService));
WSHttpBinding binding = new WSHttpBinding
{
ReaderQuotas = { MaxStringContentLength = int.MaxValue, MaxArrayLength = int.MaxValue, MaxDepth = int.MaxValue, MaxBytesPerRead = int.MaxValue, MaxNameTableCharCount = int.MaxValue },
MaxReceivedMessageSize = int.MaxValue
};
TimeSpan timeoutSpan = TimeSpan.FromMilliseconds(timeout);
binding.CloseTimeout = timeoutSpan;
binding.OpenTimeout = timeoutSpan;
binding.ReceiveTimeout = timeoutSpan;
binding.SendTimeout = timeoutSpan;
binding.ReliableSession.InactivityTimeout = timeoutSpan;
binding.MaxBufferPoolSize = int.MaxValue;
//we set the security type
binding.Security.Mode = SecurityMode.Message;
binding.Security.Message.ClientCredentialType = MessageCredentialType.UserName;
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;
//we set the server's certificate
myHost.Credentials.ServiceCertificate.SetCertificate(StoreLocation.LocalMachine, StoreName.My, X509FindType.FindBySubjectName, ConfigurationManager.AppSettings["Hostname"]);
myHost.Credentials.ClientCertificate.Authentication.CertificateValidationMode = X509CertificateValidationMode.None;
//we add the endPoint(and we indicate which methods are exposed through the interface
myHost.AddServiceEndpoint(services[port], binding, String.Format("http://localhost:{0}", port));
//Some services doesn't need an authentication
if (!servicesWithoutAuth.Contains(services[port]))
{
//We set the authentifier:
myHost.Credentials.UserNameAuthentication.UserNamePasswordValidationMode = UserNamePasswordValidationMode.Custom;
myHost.Credentials.UserNameAuthentication.CustomUserNamePasswordValidator = new CustomUserNameValidator();
myHost.Authorization.PrincipalPermissionMode = PrincipalPermissionMode.Custom;
//we set the AuthorizationPolicy
List<IAuthorizationPolicy> policies = new List<IAuthorizationPolicy> { new CustomAuthorizationPolicy() };
myHost.Authorization.ExternalAuthorizationPolicies = policies.AsReadOnly();
}
else
{
//We set the authentifier:
myHost.Credentials.UserNameAuthentication.UserNamePasswordValidationMode = UserNamePasswordValidationMode.Custom;
myHost.Credentials.UserNameAuthentication.CustomUserNamePasswordValidator = new NoUserNamePasswordValidator();
}
//We bypass the certificate verification(our certificate is only self signed)
//HACK Only to desactivate the SSL check:
ServicePointManager.ServerCertificateValidationCallback += ValidateCertificate;
//HACK: Remove when debug finished
private static bool ValidateCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslpolicyerrors)
{
return true;
}
My client side:
// the remote adress is of the form "net.tcp://localhost:8000"
string remoteAddress = String.Format("{0}://{1}:{2}", Tools.GetDescription(accessInfo.ServiceHost.Protocol), accessInfo.ServiceHost.HostName, accessInfo.PortNumber);
// HACK: binding depends on protocol -> switch over accessInfo.ServiceHost.Protocol
// avoid seralization/deserialization problems with large XML's
WSHttpBinding binding = new WSHttpBinding();
binding.ReaderQuotas.MaxStringContentLength = int.MaxValue;
binding.ReaderQuotas.MaxArrayLength = int.MaxValue;
binding.MaxReceivedMessageSize = int.MaxValue;
binding.ReaderQuotas.MaxStringContentLength = int.MaxValue;
binding.ReaderQuotas.MaxArrayLength = int.MaxValue;
binding.ReaderQuotas.MaxDepth = int.MaxValue;
binding.ReaderQuotas.MaxBytesPerRead = int.MaxValue;
binding.ReaderQuotas.MaxNameTableCharCount = int.MaxValue;
TimeSpan timeoutSpan = DateTime.Now.AddMinutes(30) - DateTime.Now;
binding.CloseTimeout = timeoutSpan;
binding.OpenTimeout = timeoutSpan;
binding.ReceiveTimeout = timeoutSpan;
binding.SendTimeout = timeoutSpan;
binding.ReliableSession.InactivityTimeout = timeoutSpan;
//++
binding.MaxBufferPoolSize = int.MaxValue;
//we set the security type
binding.Security.Mode = SecurityMode.Message;
binding.Security.Message.ClientCredentialType = MessageCredentialType.UserName;
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;
ChannelFactory<TService> channelFactory = new ChannelFactory<TService>(binding, remoteAddress);
channelFactory.Credentials.UserName.UserName = ((UsernamePasswordAuthentication)authInfos).Username;
channelFactory.Credentials.UserName.Password = ((UsernamePasswordAuthentication)authInfos).Password;
//We set the maxItemsInObjectGraph
foreach (OperationDescription op in channelFactory.Endpoint.Contract.Operations)
{
DataContractSerializerOperationBehavior dataContractBehavior = op.Behaviors.Find<DataContractSerializerOperationBehavior>();
if (dataContractBehavior != null)
{
dataContractBehavior.MaxItemsInObjectGraph = int.MaxValue;
}
}
SamlSecurityTokenAuthenticator authenticator = new SamlSecurityTokenAuthenticator(new List<SecurityTokenAuthenticator>(new SecurityTokenAuthenticator[] { new RsaSecurityTokenAuthenticator(), new X509SecurityTokenAuthenticator(X509CertificateValidator.None) }), TimeSpan.FromDays(5));
_service = channelFactory.CreateChannel();

How can I know which part of the chain is unvalid? Is there any way to
know what is the missing part?
Well, from my experience, if you open your keystore and view it, you should see that your certificate clearly forms a chain. I don't know what tool you use to view your keystore (or if your using the windows keystore), but when you view your key, you should see a chain of some kind. If a chain is formed correctly, it will appear correctly, and doesn't have any missing parts.
My guess is that when you imported your certificate reply, it didn't form the chain for some reason. In other words, your certificate is in your keystore as a "unchained" public key.

Related

How do I implement WS-security in WCF client service (timestamp, usernametoken, signature)

I need to implement a WCF request with WS-Security. The header must to have this tags (Signature, UsernameToken and Timestamp) as shown below:
<soapenv:Header>
<wsse:Security>
<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">...
<wsse:UsernameToken wsu:Id="UsernameToken-DCF9C511">...
<wsu:Timestamp wsu:Id="TS-DCF9C5119CC59E9AE2159888852210410">...
</wsse:Security>
</soapenv:Header>
I've tried with this code, and I get "Signature" and "TimeStamp" tags in header but the "UsernameToken" tag is not present:
System.Net.ServicePointManager.ServerCertificateValidationCallback = ((sender, certificate, chain, sslPolicyErrors) => true);
Servicio.RecaudoWSPortClient client = new Servicio.RecaudoWSPortClient();
//Configuration certificate
X509Certificate2 cert = new X509Certificate2();
cert.Import(#"C:\Users\jdduitama\Desktop\SCRIPTS\bis\Certificado\PKCS C#\PRUEBA.pfx", "PRUEBA", X509KeyStorageFlags.DefaultKeySet);
X509Certificate2 cert2 = new X509Certificate2();
cert2.Import(#"C:\Users\jdduitama\Desktop\SCRIPTS\bis\Certificado\Certificado.cer", "", X509KeyStorageFlags.DefaultKeySet);
//Configuration Custom Binding
TextMessageEncodingBindingElement textEncoding = new TextMessageEncodingBindingElement { MessageVersion = MessageVersion.Soap11 };
HttpsTransportBindingElement httpsTransport = new HttpsTransportBindingElement { RequireClientCertificate = true };
TransportSecurityBindingElement sec = SecurityBindingElement.CreateCertificateOverTransportBindingElement();
sec.EnableUnsecuredResponse = true;
CustomBinding customBinding = new CustomBinding(sec, textEncoding, httpsTransport);
client.Endpoint.Binding = myBinding;
client.ClientCredentials.ServiceCertificate.Authentication.CertificateValidationMode = System.ServiceModel.Security.X509CertificateValidationMode.PeerOrChainTrust;
client.ClientCredentials.ServiceCertificate.Authentication.RevocationMode = X509RevocationMode.Offline;
client.ClientCredentials.ServiceCertificate.DefaultCertificate = cert2;
client.ClientCredentials.ClientCertificate.Certificate = cert;
client.Endpoint.Address = new System.ServiceModel.EndpointAddress("https://myservice.com/service");
client.Endpoint.Binding.SendTimeout = new TimeSpan(0, 0, 30);
client.ClientCredentials.UserName.UserName = "USERNAME";
client.ClientCredentials.UserName.Password = "PASSWORD";
responseConsulta = client.ConsultaPorValidacion(requestConsulta);
I think the solution should be in the binding security configuration, Because if I use security mode "TransportWithMessageCredential" in config i get the usernameToken in the Header but I lose "Signature" and "TimeStamp"
<binding name="RecaudoWSPortSoap11">
<security mode="TransportWithMessageCredential" />
</binding>
If the security mode is set to TransportWithMessageCredential, it will override the security mode in the custom binding, so I think this is not a solution.
WCF provides 18 authentication modes for custom binding, maybe you can try UserNameOverTransport:
TransportSecurityBindingElement sec = SecurityBindingElement.CreateUserNameOverTransportBindingElement();
You can also try other authentication schemes. For more information about other authentication schemes, you can refer to this linkļ¼š
https://learn.microsoft.com/en-us/dotnet/framework/wcf/feature-details/securitybindingelement-authentication-modes

Is this why a WCF SSL Secure Channel is faulting?

I'm supporting a project where we recently needed to apply a series of upgrades to a newer version of the .Net Framework. This has largely succeeded but for one final component that's been around for a very long time.
Our client uses InfoPath templates to populate information for other users to consume. Everything the templates need comes from a WCF web service we host. We set the web service call up with the following code.
private WSHttpBinding CreateBinding()
{
var wsHttpBinding = new WSHttpBinding();
wsHttpBinding.CloseTimeout = TimeSpan.FromMinutes(10);
wsHttpBinding.OpenTimeout = TimeSpan.FromMinutes(10);
wsHttpBinding.ReceiveTimeout = TimeSpan.FromMinutes(10);
wsHttpBinding.SendTimeout = TimeSpan.FromMinutes(10);
wsHttpBinding.BypassProxyOnLocal = false;
wsHttpBinding.TransactionFlow = false;
wsHttpBinding.HostNameComparisonMode = HostNameComparisonMode.StrongWildcard;
wsHttpBinding.MaxBufferPoolSize = 524288;
wsHttpBinding.MaxReceivedMessageSize = 2147483647;
wsHttpBinding.MessageEncoding = WSMessageEncoding.Text;
wsHttpBinding.TextEncoding = Encoding.UTF8;
wsHttpBinding.UseDefaultWebProxy = true;
wsHttpBinding.AllowCookies = false;
wsHttpBinding.ReaderQuotas.MaxDepth = 32;
wsHttpBinding.ReaderQuotas.MaxStringContentLength = 2147483647;
wsHttpBinding.ReaderQuotas.MaxArrayLength = 16384;
wsHttpBinding.ReaderQuotas.MaxBytesPerRead = 4096;
wsHttpBinding.ReaderQuotas.MaxNameTableCharCount = 16384;
wsHttpBinding.ReliableSession.Ordered = true;
wsHttpBinding.ReliableSession.InactivityTimeout = TimeSpan.FromMinutes(10);
wsHttpBinding.ReliableSession.Enabled = false;
wsHttpBinding.Security.Mode = SecurityMode.TransportWithMessageCredential;
wsHttpBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;
wsHttpBinding.Security.Transport.ProxyCredentialType = HttpProxyCredentialType.None;
wsHttpBinding.Security.Transport.Realm = string.Empty;
wsHttpBinding.Security.Message.ClientCredentialType = MessageCredentialType.UserName;
wsHttpBinding.Security.Message.NegotiateServiceCredential = false;
wsHttpBinding.Security.Message.AlgorithmSuite = SecurityAlgorithmSuite.Basic256;
return wsHttpBinding;
}
private EndpointAddress CreateEndPoint()
{
X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadOnly);
X509Certificate2 certificate = store.Certificates.Find(X509FindType.FindBySubjectName, "*.wildcard.address.foo", false)[0];
store.Close();
EndpointIdentity identity = EndpointIdentity.CreateX509CertificateIdentity(certificate);
string address = getWcfServiceUrl();
AddressHeader header = AddressHeader.CreateAddressHeader(address);
List<AddressHeader> headerList = new List<AddressHeader> { header };
Uri uri = new Uri(address);
var endpointAddress = new EndpointAddress(uri, identity, headerList.ToArray());
return endpointAddress;
}
}
This works fine and if we're testing it out, calls can be made successfully for all other intents and purposes. Except for one.
In one case we need to get information from a 3rd party resource. In that situation, our web service makes a separate call out to this 3rd party at an HTTPS address (passed in to the url parameter here:
private string requestURL(string url)
{
string toReturn = null;
Stream stream = null;
try
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.Method = httpMethod;
stream = ((HttpWebResponse)request.GetResponse()).GetResponseStream();
StreamReader reader = new StreamReader(stream);
toReturn = reader.ReadToEnd();
}
catch(Exception e)
{
throw new Exception("Error with that service please try again: " + e.Message, e);
}
finally
{
if(stream != null)
{
stream.Close();
}
}
return toReturn;
}
In this case, the following error is returned:
The request was aborted: Could not create SSL/TLS secure channel.
My suspicion is that we're setting up a very specific set of constraints around the SSL connection between our local client (i.e. InfoPath) and the web service but the call from that web service to the 3rd party is not set up with any constraints beyond simply calling over HTTPS.
What should I be looking out for in trying to fix this issue?
WCF IMHO is particular about configuration at both ends and asks for things like transport credential specifically in the back and forth. I suspect you have no control of how the security is managed at the third party and can't change it, but your generic method to call all web services won't work because the configuration doesn't match.

WCF Client Sign Soap Message with Smart Card

I have forms application with service reference by using CustomBinding for signing soap request (Only need soap body will be signed). If I try to sign request with private key included pfx wcf client succesfully sign Basic256Sha256Rsa15.
Succesfull case below:
private CustomBinding GetCustomHttpBinding()
{
CustomBinding binding = new CustomBinding();
// Open and Close = 20s
binding.OpenTimeout = new TimeSpan(0, 0, 20);
binding.CloseTimeout = new TimeSpan(0, 0, 20);
// Send and Receive = 300s
binding.SendTimeout = new TimeSpan(0, 5, 0);
binding.ReceiveTimeout = new TimeSpan(0, 5, 0);
// ++ Setting security binding ++
var param = new X509SecurityTokenParameters();
param.X509ReferenceStyle = X509KeyIdentifierClauseType.IssuerSerial;
param.ReferenceStyle = SecurityTokenReferenceStyle.Internal;
param.InclusionMode = SecurityTokenInclusionMode.AlwaysToRecipient;
param.RequireDerivedKeys = false;
var userNameToken = new UserNameSecurityTokenParameters();
userNameToken.InclusionMode = SecurityTokenInclusionMode.AlwaysToRecipient;
var securityElement = new AsymmetricSecurityBindingElement();
securityElement.EnableUnsecuredResponse = true;
securityElement.IncludeTimestamp = true;
securityElement.RecipientTokenParameters = new X509SecurityTokenParameters(X509KeyIdentifierClauseType.IssuerSerial, SecurityTokenInclusionMode.Never);
securityElement.InitiatorTokenParameters = new X509SecurityTokenParameters(X509KeyIdentifierClauseType.IssuerSerial, SecurityTokenInclusionMode.AlwaysToRecipient);
securityElement.DefaultAlgorithmSuite = SecurityAlgorithmSuite.Basic256Sha256Rsa15;
securityElement.SecurityHeaderLayout = SecurityHeaderLayout.Strict;
securityElement.SetKeyDerivation(false);
securityElement.EndpointSupportingTokenParameters.Signed.Add(param);
//securityElement.MessageProtectionOrder = MessageProtectionOrder.SignBeforeEncrypt;
securityElement.MessageSecurityVersion = MessageSecurityVersion.WSSecurity11WSTrust13WSSecureConversation13WSSecurityPolicy12BasicSecurityProfile10;
binding.Elements.Add(securityElement);
// ++ Setting message encoding binding ++
var encodingElement = new TextMessageEncodingBindingElement();
encodingElement.MessageVersion = MessageVersion.Soap12;
encodingElement.WriteEncoding = Encoding.UTF8;
//encodingElement.MaxReadPoolSize = 50000000;
//encodingElement.MaxWritePoolSize = 50000000;
encodingElement.ReaderQuotas.MaxArrayLength = 50000000;
encodingElement.ReaderQuotas.MaxStringContentLength = 50000000;
binding.Elements.Add(encodingElement);
// ++ Setting https transport binding ++
var httpsElement = new HttpsTransportBindingElement();
// Messagge buffer size
httpsElement.MaxBufferSize = 50000000;
httpsElement.MaxReceivedMessageSize = 50000000;
httpsElement.MaxBufferPoolSize = 50000000;
httpsElement.RequireClientCertificate = true;
// Others
httpsElement.UseDefaultWebProxy = true;
binding.Elements.Add(httpsElement);
return binding;
}
And Service Sertificate set as
client.ClientCredentials.ClientCertificate.Certificate=cert;// From pfx file
client.ClientCredentials.ServiceCertificate.DefaultCertificate =serverCert;//from server certificate
I try to sign with my company smart card which is ACS38 smart card. If I use DefaultAlgotihmSuite Basic128 or Basic128Rsa15 then smartCard certificate succesfully sign body elements. I change algorihmsuite Basic256Sha256Rsa15 for requirement then I get KeySet does not exist. In this Smart Card have private key but WCF does not reach that private key.
Is there way to sign to Sign Soap Body with Sha256Rsa on SmartCard ?

The 'Action', 'http://www.w3.org/2005/08/addressing' required message part was not signed

I am accessing an external java-based web service I have no control over from a WCF client, using dual certificates for encryption and signature as well as custom binding. I am getting a successful response from the server but WCF is throwing a MessageSecurityException : The 'Action', 'http://www.w3.org/2005/08/addressing' required message part was not signed.
My custom binding:
private CustomBinding GetCustomBinding()
{
CustomBinding binding = new CustomBinding();
binding.OpenTimeout = new TimeSpan(0, 0, 20);
binding.CloseTimeout = new TimeSpan(0, 0, 20);
binding.SendTimeout = new TimeSpan(0, 5, 0);
binding.ReceiveTimeout = new TimeSpan(0, 5, 0);
var userNameToken = new UserNameSecurityTokenParameters();
userNameToken.InclusionMode = SecurityTokenInclusionMode.AlwaysToRecipient;
var securityElement = new AsymmetricSecurityBindingElement();
securityElement.EnableUnsecuredResponse = true;
securityElement.IncludeTimestamp = true;
securityElement.RecipientTokenParameters = new X509SecurityTokenParameters(X509KeyIdentifierClauseType.IssuerSerial, SecurityTokenInclusionMode.Never);
securityElement.InitiatorTokenParameters = new X509SecurityTokenParameters(X509KeyIdentifierClauseType.IssuerSerial, SecurityTokenInclusionMode.AlwaysToRecipient);
securityElement.DefaultAlgorithmSuite = SecurityAlgorithmSuite.Basic128Rsa15;
securityElement.SecurityHeaderLayout = SecurityHeaderLayout.Strict;
securityElement.SetKeyDerivation(false);
securityElement.EndpointSupportingTokenParameters.Signed.Add(userNameToken);
securityElement.MessageProtectionOrder = System.ServiceModel.Security.MessageProtectionOrder.SignBeforeEncrypt;
securityElement.MessageSecurityVersion = MessageSecurityVersion.WSSecurity11WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10;
binding.Elements.Add(securityElement);
var encodingElement = new TextMessageEncodingBindingElement();
encodingElement.MessageVersion = MessageVersion.Soap11WSAddressing10;
encodingElement.WriteEncoding = Encoding.UTF8;
encodingElement.ReaderQuotas.MaxArrayLength = 50000000;
encodingElement.ReaderQuotas.MaxStringContentLength = 50000000;
binding.Elements.Add(encodingElement);
var httpsElement = new HttpsTransportBindingElement();
httpsElement.MaxBufferSize = 50000000;
httpsElement.MaxReceivedMessageSize = 50000000;
httpsElement.MaxBufferPoolSize = 50000000;
httpsElement.UseDefaultWebProxy = true;
binding.Elements.Add(httpsElement);
return binding;
}
Now I don't care if that Action element is signed or not, or even if it's not there at all, but hacking the response to remove the tag altogether results in a 'No signature message parts were specified for messages with the '' action.' exception.
How can I configure my client to accept the Action and other addressing elements in the response message as they are? Alternatively, what can I change them to so WCF will let them pass?
To override the default checking of the remote Secure Sockets Layer (SSL) certificate used for authentication, specify this on client:
ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(delegate { return true; });
To investigate the certificate errors, check the sslPolicyErrors parameter of the RemoteCertificateValidationCallback delegate (Link to MSDN manual page).

Creating Headers (wsse) Section of WCF Client Programmatically in C#

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());
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 = "...";
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.

Categories