This is not really a issue, when will BasicHttpMessageSecurity be supported by .net core 2.0?
@LEsperanca please refer to 2.0 release notes:
At this point we don't have a plan to support message security due to lack of dependent functionalities in System.IdentityModel on .NET Core.
How can we help?
I need to have or emulate BasicHttpMessageSecurity in a .net core service and i don't know how... @hugoterelle
@zhenlan my question was for you. I could help improve your library if needed.
@LEsperanca
To solve this problem, I created my own SOAP Header (as a temporary solution). Find an example below using username/password in clear text but the method will remain the same for other security protocols
public class SoapSecurityHeader : MessageHeader
{
private readonly string _username;
private readonly string _password;
public SoapSecurityHeader(string username, string password)
{
_username = username;
_password = password;
}
public override string Name { get; } = "Security";
public override string Namespace { get; } = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd";
private const string WsuNamespace = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd";
protected override void OnWriteHeaderContents(XmlDictionaryWriter writer, MessageVersion messageVersion)
{
writer.WriteStartElement("wsse", "UsernameToken", Namespace);
writer.WriteAttributeString("wsu", "Id", WsuNamespace, Guid.NewGuid().ToString());
writer.WriteStartElement("wsse", "Username", Namespace);
writer.WriteValue(_username);
writer.WriteEndElement();
writer.WriteStartElement("wsse", "Password", Namespace);
writer.WriteAttributeString("Type", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText");
writer.WriteValue(_password);
writer.WriteEndElement();
writer.WriteEndElement();
}
protected override void OnWriteStartHeader(XmlDictionaryWriter writer, MessageVersion messageVersion)
{
writer.WriteStartElement("wsse", Name, Namespace);
writer.WriteXmlnsAttribute("wsse", Namespace);
}
}
public class SoapSecurityHeaderInspector : IClientMessageInspector
{
private readonly string _username;
private readonly string _password;
public SoapSecurityHeaderInspector(string username, string password)
{
_username = username;
_password = password;
}
public void AfterReceiveReply(ref Message reply, object correlationState)
{
}
public object BeforeSendRequest(ref Message request, IClientChannel channel)
{
request.Headers.Add(new SoapSecurityHeader(_username, _password));
return null;
}
}
public class SoapSecurityHeaderBehavior : IEndpointBehavior
{
private readonly string _username;
private readonly string _password;
public SoapSecurityHeaderBehavior(string username, string password)
{
_username = username;
_password = password;
}
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
}
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
clientRuntime.ClientMessageInspectors.Add(new SoapSecurityHeaderInspector(_username, _password));
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
}
public void Validate(ServiceEndpoint endpoint)
{
}
}
And then, I created a channel factory (used by Autofac)
private static ChannelFactory<T> CreateSecuredChannel<T>(string url, string username, string password)
{
var binding = new BasicHttpsBinding
{
TextEncoding = Encoding.UTF8,
UseDefaultWebProxy = true,
BypassProxyOnLocal = false,
Security =
{
Mode = BasicHttpsSecurityMode.Transport,
Transport =
{
ClientCredentialType = HttpClientCredentialType.None,
ProxyCredentialType = HttpProxyCredentialType.None
}
}
};
var channel = new ChannelFactory<T>(binding, new EndpointAddress(url));
channel.Endpoint.EndpointBehaviors.Add(new SoapSecurityHeaderBehavior(username, password));
channel.Credentials.UserName.UserName = username; // not sure if needed
channel.Credentials.UserName.Password = password;
return channel;
}
@hugoterelle I'm not sure how to actually use the ChannelFactory you've created, can you tell me how I inject this into the WCF service so it uses the correct channelfactory?
@nickcoad
I use autofac to register the channel factory:
builder.Register(c => CreateSecuredChannel
builder.Register(c => c.Resolve
.As
.UseWcfSafeRelease();
Then, you just need to inject your IMySoapService to use it.
And for the UseWcfSafeRelease used by Autofac, just refer to the issue I raised on the Autofac github
https://github.com/autofac/Autofac.Wcf/issues/11
Any ETA on this issue? This is important feature to support for a lot of WCF service migration projects for us.
@hugoterelle Thank you for your code. I managed to get Basic Auth Message level security working using your code.
@hugoterelle Your code works for me. Thank you very much! Do you wanna make a nuget package till MS do it properly?
@nickcoad FYI You don't have to use Autofac. You able to use any DI or simply:
client.Endpoint.EndpointBehaviors.Add(new SoapSecurityHeaderBehavior("user", "pass"));
@hugoterelle Thank you very much for your work. It saved my day.
Most helpful comment
@zhenlan my question was for you. I could help improve your library if needed.
@LEsperanca
To solve this problem, I created my own SOAP Header (as a temporary solution). Find an example below using username/password in clear text but the method will remain the same for other security protocols
And then, I created a channel factory (used by Autofac)