My company uses WIF (Windows Identity Foundation) to secure our sevices. Currently we only use WIF over HTTPS. However, we need to secure a TCP endpoint and I'm running into some trouble.
I'm getting following exception:
The '{binding name}'.'http://tempuri.org/' binding for the '{IService}'.
'http://tempuri.org/' contract is configured with an authentication mode that requires
transport level integrity and confidentiality. However the transport cannot provide
integrity and confidentiality.
In order to get WIF into the picture we have to do this inside the service host:
var istp = new IssuedSecurityTokenParameters(_TokenType, _IssuerAddress, _IssuerBinding) // issuer address/binding do not matter for this, but must provide something
{
RequireDerivedKeys = false,
KeyType = System.IdentityModel.Tokens.SecurityKeyType.BearerKey
};
TransportSecurity = new TransportSecurityBindingElement();
TransportSecurity.EndpointSupportingTokenParameters.Signed.Add(istp);
TransportSecurity.MessageSecurityVersion = MessageSecurityVersion.WSSecurity11WSTrust13WSSecureConversation13WSSecurityPolicy12;
I can't take those lines of code out but I don't know what configuration I'm missing to make this work with TCP. Any help at all would be great.
Related
I have created a self-hosted WCF RESTful service with basic http authentication that runs over https with a self-signed SSL certificate. Everything works fine. Users access the service operations via a web browser.
The problem is that my customer now wants the service users to authenticate with BOTH basic authentication (user name + password) AND a certificate. I have not been able to achieve this.
I have seen that it could be possible to have multiple authentication schemes in WCF 4.5. I have looked into this but to no avail.
I have also come across this post (see the last answer), but when I tried it I got this error:
"An exception occurred: HTTPS listener factory was configured to require a client certificate and the authentication scheme 'Basic'. Only one form of authentication can be required at once."
My configuration is done in code, and here is what it looks like (this is the version that works):
Uri baseAdress = new Uri("https://localhost:8446/");
WebServiceHost host = new WebServiceHost(typeof(RestService));
WebHttpBinding wb = new WebHttpBinding();
wb.Security.Mode = WebHttpSecurityMode.Transport;
wb.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic;
host.AddServiceEndpoint(typeof(IRestService), wb, baseAdress);
host.Credentials.UserNameAuthentication.CustomUserNamePasswordValidator = new CustomUserNameValidator();
host.Credentials.UserNameAuthentication.UserNamePasswordValidationMode = UserNamePasswordValidationMode.Custom;
host.Description.Endpoints[0].Behaviors.Add(new WebHttpBehavior { HelpEnabled = true });
host.Open();
Thanks for any tips,
Multi auth on a single endpoint is for web hosted scenarios (not selfhost). You can configure this in web host by saying clientCredentialType='InheritFromHost' and set all the auth schemes that you want to set for that particular endpoint in vdir authentication. Check out this documentation for title "Multiple authentication support".
Multiple Authentication Support
Support has been added to support multiple authentication modes, as supported by IIS, on a single WCF endpoint when using the HTTP transport and transport security. IIS allows you to enable multiple authentication modes on a virtual directory, this feature allows a single WCF endpoint to support the multiple authentication modes enabled for the virtual directory where the WCF service is hosted.
Here on MSDN as well as here states the when dealing with wsHttpBinding, Transport security is handled via SSL.
On the MSDN page about SSL and WCF it states that when a ServiceHost is hosted within IIS, the ServiceHost leaves the SSL to be handled by IIS.
Would this not imply that if binding/securityMode="Transport", that any wsHttpBinding/binding/security/transport/clientCredentialType values would be ignored as none of their options are needed to set up the SSL transport?
It even appears to to say something to this effect here when it states
"When setting the security mode to TransportWithMessageCredential, the
transport determines the actual mechanism that provides the
transport-level security. For example, the HTTP protocol uses Secure
Sockets Layer (SSL) over HTTP (HTTPS). Therefore, setting the
ClientCredentialType property of any transport security object (such
as HttpTransportSecurity) is ignored. In other words, you can only set
the ClientCredentialType of the message security object (for the
WSHttpBinding binding, the NonDualMessageSecurityOverHttp object)."
And yet here for basicHttpBinding and for wsHttpBinding, they both categorically emphasis with examples that if security mode is set to Transport, set the binding/transport/clientCredentialType to something (eg: Windows).
What's the exact difference between Transport and TransportWithMessageCredential?
And do I have the wrong end of the stick, and the SecurityType enum (None|Message|Transport|Mixed) is not just for privacy, but for authentication to the server?
If Transport security is provided by SSL encryption, how did Authentication/Authorization get tangled into this stage?
Thanks immensely for helping me get a better picture of how this all fits together.
As far as I know the TransportWithMessageCredential is kind of "best of both worlds". The channel is secured on the transport layer so there is a secure connection between client and service (which can be very fast, implemented in hardware), plus the message is signed with message credentials so it can survive multiple hops before arriving at the service (validated in WCF).
And of course, you can use message credentials which are not supported on the transport layer, username/password for example.
I'm using netTcpBinding with "Transport" security ("Windows" credentials & "EncryptAndSign" protection).
I already know that Kerberos (after SPNEGO) is used to authenticate client & server (both machines on the same Windows domain).
I'd like to know what is/are the algorythm(s) used then to encrypt and sign the TCP Transport channel since there is no config choice ("Message" security has "algorithmSuite" config parameter but it's not available for "Transport" security)
Thanks
The NetTcpBinding class uses TCP for message transport. Security for the transport mode is provided by implementing Transport Layer Security (TLS) over TCP. The TLS implementation is provided by the operating system.
You can also specify the client's credential type by setting the ClientCredentialType property of the TcpTransportSecurity class to one of the TcpClientCredentialType values, as shown in the following code.
C#
NetTcpBinding b = new NetTcpBinding();
b.Security.Mode = SecurityMode.Transport;
b.Security.Transport.ClientCredentialType =
TcpClientCredentialType.Certificate;
More info can be found here: http://msdn.microsoft.com/en-us/library/ms729700
Hope that helps.
I need to call a WCF service programmatically. The service may be hosted with either NTLM or Kerberos authentication and needs to work under either. That is, if connecting to the service via Kerberos fails, then it should fall back to NTLM.
Here's the code I'm using for Kerberos auth (if relevant, the service is hosted in SharePoint 2010 and is being called from a web part):
public static SiteMembershipSvc.SiteMembershipServiceClient InitialiseSiteMembershipService(string url)
{
var binding = new BasicHttpBinding();
binding.Security.Mode = BasicHttpSecurityMode.TransportCredentialOnly;
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Windows;
url = url.EndsWith("/") ? url + SiteMembershipAddress : url + "/" + SiteMembershipAddress;
var endpoint = new EndpointAddress(url);
var proxy = new SiteMembershipSvc.SiteMembershipServiceClient(binding, endpoint);
proxy.ClientCredentials.Windows.AllowedImpersonationLevel = System.Security.Principal.TokenImpersonationLevel.Impersonation;
return proxy;
}
Calling a method on the proxy when run in an NTLM environment gives the error:
The HTTP request is unauthorized with
client authentication scheme
'Negotiate'. The authentication header
received from the server was 'NTLM'.
Note: The URL may be in another web application on another server. I can't check what authentication the web part's web app runs under and assume it is the same as where the WCF service is hosted.
How can I (automatically or manually) ensure authentication falls back from Kerberos back to NTLM on failure?
Update:
As mentioned, the authentication error occurs when a web method is called. However I don't want to wait that long as there are several web methods in the service called from several places. I'd like to test the authentication at the point where the proxy is configured (in the code snippet above).
I've tried using proxy.Open() but that doesn't seem to cause the failure.
This is a bit off a curveball, but why is it falling back to NTLM. I've had significant difficulty with security in active directory and WCF all related to service principal names (SPNs).
Kerberos will fail if you are running the service as something other than Network Service unless you have an SPN declared in the domain for your service. To set the SPN you need the windows server administrative kit, which has the command setspn.
setspn -A HTTP\machinename domain\service_account
This will then allow Kerberos to share client credentials to your service within the domain.
Please do some reading, as you could break kerberos for any other services running on the same box depending on your setup.
(I recognize the original post is very old.)
Can you use something other than BasicHttpBinding (like WsHttpBinding)? According to this article, BasicHttpBinding is the one exception to the binding objects, in that it does not automatically negotiate. This is why allowNTLM has no effect.
I had the same error msg which I posted about here and solved it by creating a dynamic endpoint like so:
public static SiteMembershipSvc.SiteMembershipServiceClient InitialiseSiteMembershipService(string url)
{
//create endpoint
EndpointAddress ep = new EndpointAddress(new Uri(string), EndpointIdentity.CreateUpnIdentity("MyDomain\WCFRunAsUser"));
//create proxy with new endpoint
SiteMembershipSvc.SiteMembershipServiceClient service = new SiteMembershipSvc.SiteMembershipServiceClient("wsHttp", ep);
//allow client to impersonate user
service.ClientCredentials.Windows.AllowedImpersonationLevel = System.Security.Principal.TokenImpersonationLevel.Impersonation;
//return our shiny new service
return service;
}
I was running the WCF service as a specific Active Directory user rather than the default NETWORK_SERVICE.
Try setting:
proxy.ClientCredentials.Windows.AllowNTLM = true;
According to this, AllowNTLM is now obsolete - i'm not sure what the correct alternative is.
I guess you are using the full dns name of the server as the address of the service. Try using the NETBIOS name or the IP address. That should force it to use NTLM.
If you know what protocol the server is using you can configure your app to use either the full name or the ip.
Hope that works for you.
If your Kerberos fail it will automatically default to NTLM, you don't have to do anything special.
http://www.windowsecurity.com/articles/Troubleshooting-Kerberos-SharePoint-environment-Part1.html
http://www.windowsecurity.com/articles/Troubleshooting-Kerberos-SharePoint-environment-Part2.html
http://www.windowsecurity.com/articles/Troubleshooting-Kerberos-SharePoint-environment-Part3.html
I haven't been able to find a way to do this automatically. Instead I've added UI to the application where the type of authentication must be chosen.
I'm trying to build a minimal client for a WCF service, using the WSHttpBinding with SecurityMode: Message over a direct channel interface.
My current code is very simple:
EndpointIdentity i = EndpointIdentity.CreateX509CertificateIdentity(clientCertificate);
EndpointAddress a = new EndpointAddress(new Uri("http://myServerUrl"), i);
WSHttpBinding b= new WSHttpBinding(SecurityMode.Message);
ChannelFactory<IRequestChannel> channelFactory = new ChannelFactory<IRequestChannel>(b, a);
channelFactory.Open();
IRequestChannel channel = channelFactory.CreateChannel();
channel.Open();
Message response = channel.Request(requestMessage);
The clientCertificate gets loaded properly.
However, afterwards, I'm unsure if I call every function the correct way.
The Fact is: The last line of the code snippet throws a MessageSecurityException with the content
Client cannot determine the Service Principal Name based on the identity in the target address 'http://myServerUrl' for the purpose of SspiNegotiation/Kerberos. The target address identity must be a UPN identity (like acmedomain\alice) or SPN identity (like host/bobs-machine).
What could be the reason for this problem?
The default ClientCredentialType seems to be Windows and that's why you're getting an error related to Sspi/Kerberos. You need to specify "Certificate" as credential type and also set the actual certificate in the client credentials container. Check out the Client section of this link for more details:
http://msdn.microsoft.com/en-us/library/ms733098.aspx