Cannot implement wsHttpBinding with security

Topics: Usage Scenarios, Technical Questions
Jun 4, 2008 at 9:41 PM
I have a NetTcp service with TransportWithMessageCredential security hosted in a Windows service.  I've been able to expose this with the BasicHttp (Soap11) binding in the June CTP, but this does not allow me to pass credentials.  So, I created the following binding:

<bindings xmlns="">
  <wsHttpBinding>
    <binding name="Dev_WSHttpBinding" bypassProxyOnLocal="false" transactionFlow="false" hostNameComparisonMode="StrongWildcard" maxBufferPoolSize="524288" maxReceivedMessageSize="999999999" messageEncoding="Text" textEncoding="utf-8" useDefaultWebProxy="true" receiveTimeout="00:15:00">
      <readerQuotas maxDepth="2147483647" maxStringContentLength="2147483647" maxArrayLength="2147483647" maxBytesPerRead="2147483647" maxNameTableCharCount="2147483647">
      </readerQuotas>
      <reliableSession ordered="true" inactivityTimeout="00:15:00" enabled="false">
      </reliableSession>
      <security mode="TransportWithMessageCredential">
        <message clientCredentialType="UserName">
        </message>
      </security>
    </binding>
  </wsHttpBinding>
</bindings>

Now when I try to access the service I get the following errors in the event log:

Event Type:    Error
Event Source:    MSE Runtime
Event Category:    None
Event ID:    0
Date:        6/4/2008
Time:        8:37:25 PM
User:        N/A
Computer:    APPDEV
Description:
Error in InitializeRuntime: The HttpGetEnabled property of ServiceMetadataBehavior is set to true and the HttpGetUrl property is a relative address, but there is no http base address.  Either supply an http base address or set HttpGetUrl to an absolute address.

Event Type:    Error
Event Source:    MSE Runtime
Event Category:    None
Event ID:    0
Date:        6/4/2008
Time:        8:37:25 PM
User:        N/A
Computer:    APPDEV
Description:
Failed to create listener for [https://appdev:8001/TitleProductService] with reason [Cannot open ChannelDispatcher because it is does not have a MessageVersion set.]


Can anyone tell me why these errors are being thrown?


Developer
Jun 4, 2008 at 10:08 PM
It seems as if RuntimeServer is uanble to create the listener. Did you setup https certificate on the machine?
Jun 5, 2008 at 3:12 PM
Edited Jun 5, 2008 at 3:19 PM
somehow my post got posted twice.
Jun 5, 2008 at 3:18 PM
My colleague is the one who posted this original post, but to answer your question, we did setup a certificate that IIS is able to see. That said, do we need to import the certificate for the Runtime Server, by importing the certificate via the MMC console using the certificate snap-in? Or do we have to create a custom binding and modify the service behavior in order for this work? If we do have to go the latter route, do you have an example that can help us?
Oct 28, 2008 at 10:38 AM
I know this is an old post but a response to the last question would help me greatly also.

I've convinced my client to go with MSE as a service framework standard but at this stage I'm struggling with the lack of documentation around implementing secure virtual services.

Regards
Kenny
Oct 28, 2008 at 1:34 PM
Kenny,

With help from botto, we were able to setup security with a wsHttpBinding with Message security using UserName credentials.  Hope this helps.

We first had to create a MembershipProvider (called MseMembershipProvider in this example).  Then we created a custom behavior to find and apply a certificate and to apply the MembershipProvider:

class

SecurityBehavior : IServiceBehavior  

 

{

private StoreLocation storeLocation;

private StoreName storeName;

private X509FindType x509FindType;

private string certName;

public SecurityBehavior(StoreLocation certificateStoreLocation, StoreName certificateStoreName,

X509FindType certificateFindType, string certificateName)

{

storeLocation = certificateStoreLocation;

storeName = certificateStoreName;

x509FindType = certificateFindType;

certName = certificateName;

}

#region

IServiceBehavior Members

public void AddBindingParameters(ServiceDescription serviceDescription,

System.ServiceModel.ServiceHostBase serviceHostBase,

System.Collections.ObjectModel.Collection<ServiceEndpoint> endpoints,

System.ServiceModel.Channels.BindingParameterCollection bindingParameters)

{

try

 

 

{

bindingParameters.Remove<ServiceCredentials>();

ServiceCredentials cr = new ServiceCredentials();

bindingParameters.Add(cr);

cr.ServiceCertificate.SetCertificate(storeLocation, storeName, x509FindType, certName);

cr.UserNameAuthentication.UserNamePasswordValidationMode = UserNamePasswordValidationMode.MembershipProvider;

cr.UserNameAuthentication.MembershipProvider = new MseMembershipProvider();

//add channel protection requirements for messages with any action

 

ChannelProtectionRequirements cpr = bindingParameters.Find<ChannelProtectionRequirements>();

if (cpr == null)

{

cpr = new ChannelProtectionRequirements();

bindingParameters.Add(cpr);

}

ChannelProtectionRequirements protection = new ChannelProtectionRequirements();

MessagePartSpecification body = new MessagePartSpecification(true);

body.MakeReadOnly();

protection.OutgoingEncryptionParts.AddParts(body, "*");

protection.OutgoingSignatureParts.AddParts(body, "*");

protection.IncomingEncryptionParts.AddParts(body, "*");

protection.IncomingSignatureParts.AddParts(body, "*");

protection.MakeReadOnly();

cpr.Add(protection);

}

catch (Exception ex)

{

throw new Exception("Error in ServiceCredentialsBehavior.AddBindingParameters: " + ex.Message, ex);

}

}

public void ApplyDispatchBehavior(ServiceDescription serviceDescription, System.ServiceModel.ServiceHostBase serviceHostBase)

{

//throw new NotImplementedException();

 

 

}

public void Validate(ServiceDescription serviceDescription, System.ServiceModel.ServiceHostBase serviceHostBase)

{

//throw new NotImplementedException();

 

 

}

#endregion

}


Next, we created a BehaviorExtensionElement to implement the new behavior:

[ContentProperty("Settings")]

public class SecurityExtensionElement :BehaviorExtensionElement

{

private CredentialSettings settings;

public CredentialSettings Settings

{

get { return settings; }

set { settings = value; }

}

public override Type BehaviorType

{

get { return typeof(SecurityBehavior); }

}

protected override object CreateBehavior()

{

StoreLocation location = (StoreLocation)Enum.Parse(typeof(StoreLocation), settings.CertificateStoreLocation, true);

StoreName store = (StoreName)Enum.Parse(typeof(StoreName), settings.CertificateStoreName, true);

X509FindType findType = (X509FindType)Enum.Parse(typeof(X509FindType), settings.CertificateFindType, true);

return new SecurityBehavior(location, store, findType, settings.CertificateName);

}

}

public class CredentialSettings

 

 

 

 

{

private string certStoreLocation;

private string certStoreName;

private string certFindType;

private string certName;

public string CertificateStoreLocation

{

get { return certStoreLocation; }

set { certStoreLocation = value; }

}

public string CertificateStoreName

{

get { return certStoreName; }

set { certStoreName = value; }

}

public string CertificateFindType

{

get { return certFindType; }

set { certFindType = value; }

}

public string CertificateName

{

get { return certName; }

set { certName = value; }

}

}

Once that was created we defined a new policy in the MSE console and then applied that to our endpoint:

<PolicyModel xmlns="http://microsoft.com/mse/2007/runtime/policyModel" xmlns:mse="http://services.microsoft.com/MSE" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:wcf="clr-namespace:System.ServiceModel.Configuration;assembly=System.ServiceModel" xmlns:tf="clr-namespace:MseSecurityPolicy;assembly=MseSecurityPolicy">

<tf:SecurityExtensionElement>

<tf:CredentialSettings>

<tf:CredentialSettings.CertificateStoreLocation>LocalMachine</tf:CredentialSettings.CertificateStoreLocation>

<tf:CredentialSettings.CertificateStoreName>My</tf:CredentialSettings.CertificateStoreName>

<tf:CredentialSettings.CertificateFindType>FindBySubjectName</tf:CredentialSettings.CertificateFindType>

<tf:CredentialSettings.CertificateName>kstockdal</tf:CredentialSettings.CertificateName>

</tf:CredentialSettings>

</tf:SecurityExtensionElement>

</PolicyModel>

Oct 28, 2008 at 4:24 PM

Thanks kdstock, much appreciated.

I'm looking to implement a mse service over https with transport security and this technical insight provides the best clues I've seen so far.

I'm learning MSE at the same time as learning WCF which probably isn't a good idea but I'm getting there....

I have an https service with TransportWithMessageCredential security hosted in iis6 and have imported this in to MSE using the import functionality. I currently have the same MSE error messages you had in your original post.

Regards
Kenny

Developer
Oct 29, 2008 at 12:25 PM
Hi Kenny-

When using transport security with the current version of the MSE, you must supply an endpoint policy or you will continue to get the error that was reported.
The default behavior of the MSE tries to add the ServiceMetadataBehavior to endpoints that do not have a policy defined.  This default behavior tries to use a relative path for the httpGetUrl which is not supported in WCF when transport security is used.  You can fix this issue by either applying the built in policy that ships with the MSE called "Turn off metadata publishing" or defining your own policy to enable the metadata behavior appropriately.  As soon as any policy is applied to the endpont, the MSE does not try to add the ServiceMetadata Behavior allowing you compete control over behaviors.

I'll file this as a bug since this default behavior should be more clear and shouldn't cause the endpoint to fail to start.

Developer
Oct 29, 2008 at 2:17 PM
To complete the circle, I've logged this as an issue:
http://www.codeplex.com/servicesengine/WorkItem/View.aspx?WorkItemId=6515

Also, note the description of the issue as it is more accurate around the default MSE behavior.  Essentially, the MSE will always try to add the ServiceMetadataBehavior to an endpoint unless a policy explicitly adds the behavior and disables the mex endpoint.  When an endpoint doesn't have a policy explicitly defined you will receive this error with transport security.  When an endpoint does have a policy defined (and doesn't explicitly add the ServiceMetadataBehavior) the MSE will add it and properly configure it.

Hope that clears it up.
Oct 30, 2008 at 3:04 PM
Thanks Botto - yes that's another piece in the jigsaw, cheers.