Consuming a web service with WCF with just a public key - wcf

I'm consuming a third-party's web service with WCF. I've got a PFX certificate file that I'm attaching via the ClientCredentials.ClientCertificate.SetCertificate method. I'm using the "Message Security Version" WSSecurity10WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10.
Everything works fine. Now the third-party's certificate is expiring so they've issued a new one. However, this time it's a P7B file with just the public key.
When I try to use this certificate, I get a NotSupportedException with the message "The private key is not present in the X.509 certificate."
No part of my code is supplying the private key password, so I'm assuming this means that the private key is not being used. If this is the case, how can I consume this web service using only the public key? Or have I misunderstood something? (very likely)
EDIT
Ok, here's some code. The service client class I'm using was generated by svcutil and has been modified via a partial class to implement IDisposable. These are the relevant fragments:
private ServiceResponse CallService(ServiceParameters serviceParameters)
{
...
using (var client = new ThirdPartyServiceClient())
{
SetClientCredentials(client);
client.RemoteCall(serviceParameters);
}
...
}
private void SetClientCredentials(ThirdPartyServiceClient client)
{
if (client.ClientCredentials == null)
{
throw new InvalidOperationException("ClientCredentials was null and certificate could not be set");
}
client.ClientCredentials.ClientCertificate.SetCertificate(
StoreLocation.LocalMachine,
StoreName.My,
X509FindType.FindBySubjectName,
_configuration.CertificateSubject);
}
And this is my WCF config:
<system.serviceModel>
<bindings>
<customBinding>
<binding name="ThirdPartyServiceBinding">
<security includeTimestamp="true" enableUnsecuredResponse="true" authenticationMode="CertificateOverTransport" messageSecurityVersion="WSSecurity10WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10" />
<textMessageEncoding messageVersion="Soap11WSAddressing10" />
<httpsTransport requireClientCertificate="true" />
</binding>
</customBinding>
</bindings>
<client>
<endpoint address="https://third-party.com/service" binding="customBinding" bindingConfiguration="ThirdPartyServiceBinding" contract="Namespace.To.ProxyClasses" name="ThirdPartyService" />
</client>
</system.serviceModel>
The exception is thrown by the client.RemoteCall(serviceParameters); call, and the stack trace is
Server stack trace:
at System.IdentityModel.Tokens.X509AsymmetricSecurityKey.GetSignatureFormatter(String algorithm)
at System.IdentityModel.SignedXml.ComputeSignature(SecurityKey signingKey)
at System.ServiceModel.Security.WSSecurityOneDotZeroSendSecurityHeader.CompletePrimarySignatureCore(SendSecurityHeaderElement[] signatureConfirmations, SecurityToken[] signedEndorsingTokens, SecurityToken[] signedTokens, SendSecurityHeaderElement[] basicTokens, Boolean isPrimarySignature)
at System.ServiceModel.Security.WSSecurityOneDotZeroSendSecurityHeader.CreateSupportingSignature(SecurityToken token, SecurityKeyIdentifier identifier)
at System.ServiceModel.Security.SendSecurityHeader.SignWithSupportingToken(SecurityToken token, SecurityKeyIdentifierClause identifierClause)
at System.ServiceModel.Security.SendSecurityHeader.SignWithSupportingTokens()
at System.ServiceModel.Security.SendSecurityHeader.CompleteSecurityApplication()
at System.ServiceModel.Security.SecurityAppliedMessage.OnWriteMessage(XmlDictionaryWriter writer)
at System.ServiceModel.Channels.BufferedMessageWriter.WriteMessage(Message message, BufferManager bufferManager, Int32 initialOffset, Int32 maxSizeQuota)
at System.ServiceModel.Channels.TextMessageEncoderFactory.TextMessageEncoder.WriteMessage(Message message, Int32 maxMessageSize, BufferManager bufferManager, Int32 messageOffset)
at System.ServiceModel.Channels.HttpOutput.SerializeBufferedMessage(Message message, Boolean shouldRecycleBuffer)
at System.ServiceModel.Channels.HttpOutput.Send(TimeSpan timeout)
at System.ServiceModel.Channels.HttpChannelFactory`1.HttpRequestChannel.HttpChannelRequest.SendRequest(Message message, TimeSpan timeout)
at System.ServiceModel.Channels.RequestChannel.Request(Message message, TimeSpan timeout)
at System.ServiceModel.Channels.SecurityChannelFactory`1.SecurityRequestChannel.Request(Message message, TimeSpan timeout)
at System.ServiceModel.Dispatcher.RequestChannelBinder.Request(Message message, TimeSpan timeout)
at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan timeout)
at System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage methodCall, ProxyOperationRuntime operation)
at System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message)
Exception rethrown at [0]:
at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)
at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)
at Namespace.To.ProxyClasses.ThirdPartyService.RemoteCall(ServiceParameters request)
[back up the normal call hierarchy of my code]

With message security, you sign a document with your private key, and you encrypt a document with the other parties public key. They can decrypt it with their private key, and they can verify your signature with your public key. It sounds like they replaced their key so they provided you their new public key. If their public key doesn't have a publicly verifiable chain of trust, then you need to install their public key in your local certificate store as a trusted key. If you don't do this and they aren't publicly verifiable, you will get an exception about being unable to verify the chain trust. If it is your key which is expiring, then they need a public key to identify you, and you need the private half which they shouldn't have.

Related

how to access EF navigation properties via WCF SOAP service?

I am struggling with this WCF error for some time now without any luck. Basically I am tying to fetch an Entity Poco with Navigation Properties and connected objects via WCF Services. My EF v6 code successfully get the poco with all the related entities from the DB
Poco debug view
but as i try to access this Entity via WCF service, i see the following error -
An error occurred while receiving the HTTP response to http://localhost:8734/Design_Time_Addresses/BusinessLogicServicesLayer/userServices/. This could be due to the service endpoint binding not using the HTTP protocol. This could also be due to an HTTP request context being aborted by the server (possibly due to the service shutting down). See server logs for more details.
Server stack trace:
at System.ServiceModel.Channels.HttpChannelUtilities.ProcessGetResponseWebException(WebException webException, HttpWebRequest request, HttpAbortReason abortReason)
at System.ServiceModel.Channels.HttpChannelFactory1.HttpRequestChannel.HttpChannelRequest.WaitForReply(TimeSpan timeout)
at System.ServiceModel.Channels.RequestChannel.Request(Message message, TimeSpan timeout)
at System.ServiceModel.Dispatcher.RequestChannelBinder.Request(Message message, TimeSpan timeout)
at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan timeout)
at System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage methodCall, ProxyOperationRuntime operation)
at System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message)
Exception rethrown at [0]:
at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)
at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)
at IuserServicesQuery.getCompleteUserSnapshot(String emailAddress)
at IuserServicesQueryClient.getCompleteUserSnapshot(String emailAddress)
Inner Exception:
The underlying connection was closed: An unexpected error occurred on a receive.
at System.Net.HttpWebRequest.GetResponse()
at System.ServiceModel.Channels.HttpChannelFactory1.HttpRequestChannel.HttpChannelRequest.WaitForReply(TimeSpan timeout)
Inner Exception:
Unable to read data from the transport connection: An existing connection was forcibly closed by the remote host.
at System.Net.Sockets.NetworkStream.Read(Byte[] buffer, Int32 offset, Int32 size)
at System.Net.PooledStream.Read(Byte[] buffer, Int32 offset, Int32 size)
at System.Net.Connection.SyncRead(HttpWebRequest request, Boolean userRetrievedStream, Boolean probeRead)
Inner Exception:
An existing connection was forcibly closed by the remote host
at System.Net.Sockets.Socket.Receive(Byte[] buffer, Int32 offset, Int32 size, SocketFlags socketFlags)
at System.Net.Sockets.NetworkStream.Read(Byte[] buffer, Int32 offset, Int32 size)
my AppConfig file looks like this -
<endpoint address="" behaviorConfiguration="MyBehavior" binding="basicHttpBinding"
bindingConfiguration="IncreasedTimeout" name="BasicHttpEndpoint"
contract="BusinessLogicServicesLayer.IuserServicesQuery" listenUriMode="Explicit">
<identity>
<dns value="localhost" />
</identity>
</endpoint>
&&
<bindings>
<basicHttpBinding>
<binding name="IncreasedTimeout"
openTimeout="12:00:00"
receiveTimeout="12:00:00" closeTimeout="12:00:00"
sendTimeout="12:00:00">
</binding>
</basicHttpBinding>
</bindings>
.
.
<behaviors>
<endpointBehaviors>
<behavior name="MyBehavior">
<dataContractSerializer maxItemsInObjectGraph="2147483646" />
</behavior>
</endpointBehaviors>
<serviceBehaviors>
can someone please help out or point me to a correct direction
This is because when data is returned, serialization fails, causing the WCF service to stop automatically.
Solution:
We can serialize the proxy class into the entities we need before returning the data.
Here is a demo,The student class contains the navigation properties of other entities:
public Student Getstu()
{
CodeFirstDBContext codeFirstDBContext = new CodeFirstDBContext();
Student student =codeFirstDBContext.Student.Find(1);
var serializer = new DataContractSerializer(typeof(Student), new DataContractSerializerSettings()
{
DataContractResolver = new ProxyDataContractResolver()
});
using (var stream = new MemoryStream())
{
serializer.WriteObject(stream, student);
stream.Seek(0, SeekOrigin.Begin);
var stu = (Student)serializer.ReadObject(stream);
return stu;
}
}
This is the method that the client will call.
ServiceReference1.ServiceClient serviceClient = new ServiceReference1.ServiceClient();
var stu = serviceClient.Getstu();
Client-side will call successfully.
UPDATE Disabling Loading loading also solves this problem:
public class CodeFirstDBContext : DbContext
{
public CodeFirstDBContext() : base("name=DBConn")
{
this.Configuration.ProxyCreationEnabled = false;
this.Configuration.LazyLoadingEnabled = false;
Database.SetInitializer(new CreateDatabaseIfNotExists<CodeFirstDBContext>());
}
}

Public Key Error WCF Client

I am working on a WCF service. It is hosted in IIS and secured by SSL. I am using custom biding with Soap11. I have a private key for this service and it is referenced in the web.config file. I have exported the public key from the private key and plan to give this public key to a vendor who will call this service.
When I try to test the service using a client. I am getting the below error. In my client I have referenced the public key. My client is sending the request to the service which is secured by the private key. I have checked the private key to make sure I have "you have a private key that correspond to this certificate. also, i have the IIS app pool running under my user name. I am the administrator in the computer.
However, when i try to send a request, I get the error "The private key is not present in the X.509 certificate." in the client. What could be causing this error?
Stack Trace:
System.NotSupportedException was caught
HResult=-2146233067
Message=The private key is not present in the X.509 certificate.
Source=mscorlib
StackTrace:
Server stack trace:
at System.IdentityModel.Tokens.X509AsymmetricSecurityKey.GetSignatureFormatter(String algorithm)
at System.IdentityModel.SignedXml.ComputeSignature(SecurityKey signingKey)
at System.ServiceModel.Security.WSSecurityOneDotZeroSendSecurityHeader.CompletePrimarySignatureCore(SendSecurityHeaderElement[] signatureConfirmations, SecurityToken[] signedEndorsingTokens, SecurityToken[] signedTokens, SendSecurityHeaderElement[] basicTokens, Boolean isPrimarySignature)
at System.ServiceModel.Security.SendSecurityHeader.CompleteSignature()
at System.ServiceModel.Security.SendSecurityHeader.CompleteSecurityApplication()
at System.ServiceModel.Security.SecurityAppliedMessage.OnWriteMessage(XmlDictionaryWriter writer)
at System.ServiceModel.Channels.BufferedMessageWriter.WriteMessage(Message message, BufferManager bufferManager, Int32 initialOffset, Int32 maxSizeQuota)
at System.ServiceModel.Channels.TextMessageEncoderFactory.TextMessageEncoder.WriteMessage(Message message, Int32 maxMessageSize, BufferManager bufferManager, Int32 messageOffset)
at System.ServiceModel.Channels.HttpOutput.SerializeBufferedMessage(Message message)
at System.ServiceModel.Channels.HttpOutput.Send(TimeSpan timeout)
at System.ServiceModel.Channels.HttpChannelFactory`1.HttpRequestChannel.HttpChannelRequest.SendRequest(Message message, TimeSpan timeout)
at System.ServiceModel.Channels.RequestChannel.Request(Message message, TimeSpan timeout)
at System.ServiceModel.Channels.SecurityChannelFactory`1.SecurityRequestChannel.Request(Message message, TimeSpan timeout)
at System.ServiceModel.Dispatcher.RequestChannelBinder.Request(Message message, TimeSpan timeout)
at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan timeout)
at System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage methodCall, ProxyOperationRuntime operation)
at System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message)
Exception rethrown at [0]:
at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)
at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)
at PaymentGatewayTestClient.Customer.ICustomerDetails.CustomerDetails(CustomerDetailRequest request)
at PaymentGatewayTestClient.Customer.CustomerDetailsClient.CustomerDetails(CustomerDetailRequest request) in c:\temp\Test\PaymentGatewayTestClient\PaymentGatewayTestClient\Service References\Customer\Reference.cs:line 937
at PaymentGatewayTestClient.Program.Main(String[] args) in c:\temp\Test\PaymentGatewayTestClient\PaymentGatewayTestClient\Program.cs:line 38
InnerException:
It looks like the server can't access the private part of the key. You have to grant permission to the private key for the user that runs your app pool.
Also see this:
PrivateKey trust permissions for local machine "Trusted roots" certificates

Working with Named Pipes in WCF, PipeException thrown?

Despite many searches and read articles such as this: Exploring the WCF Named Pipe Binding - Part 1(part 2 and 3 inclusively), I haven't been able to make my service work properly.
Here's my config:
<system.serviceModel>
<client>
<endpoint address="net.pipe://localhost/GlobalPositioningService"
binding="netNamedPipeBinding"
contract="GI.Services.GlobalPositioning.Contracts.IGlobalPositioning" />
</client>
<services>
<service name="GI.Services.GlobalPositioning.Services.GlobalPositioningService">
<endpoint address=""
binding="wsHttpBinding"
contract="GI.Services.GlobalPositioning.Contracts.IGlobalPositioning">
<identity>
<dns value="localhost" />
</identity>
</endpoint>
<endpoint address="net.pipe://localhost/GlobalPositioningService"
binding="netNamedPipeBinding"
contract="GI.Services.GlobalPositioning.Contracts.IGlobalPositioning" />
<host>
<baseAddresses>
<add baseAddress="http://localhost:8080/GlobalPositioningService/"/>
</baseAddresses>
</host>
</service>
</services>
Then, I try to test my service through Named Pipes:
[TestFixture]
public class GlobalPositioningServiceTests {
[TestFixtureSetUp]
public void SetUpHost() {
var channelFactory = new ChannelFactory<IGlobalPositioning>(binding, new EndpointAddress(address));
channelFactory.Open();
service = channelFactory.CreateChannel();
}
private const string address = "net.pipe://localhost/GlobalPositioningService";
private static readonly Binding binding = new NetNamedPipeBinding();
private static IGlobalPositioning service;
}
And I have also tried another way using a ServiceHost instance:
[TestFixtureSetUp]
public void SetUpHost() {
host = new ServiceHost(typeof(GlobalPositioningService));
host.AddServiceEndpoint(typeof(IGlobalPositioning), binding, address);
host.Open();
service = new GlobalPositioningService();
}
And I always obtain this error with stack trace:
Error 2 Test 'GI.Services.GlobalPositioning.Services.Tests.GlobalPositioningServiceTests.GetGlobalPositionWorksWithDiacriticsInMunicipalityName("143, rue Marcotte, Sainte-Anne-de-la-P\x00E9rade",46.5736528d,-72.2021346d)' failed:
System.ServiceModel.EndpointNotFoundException : There was no endpoint listening at net.pipe://localhost/GlobalPositioningService that could accept the message. This is often caused by an incorrect address or SOAP action. See InnerException, if present, for more details.
----> System.IO.PipeException : The pipe endpoint 'net.pipe://localhost/GlobalPositioningService' could not be found on your local machine.
Server stack trace:
at System.ServiceModel.Channels.PipeConnectionInitiator.GetPipeName(Uri uri)
at System.ServiceModel.Channels.NamedPipeConnectionPoolRegistry.NamedPipeConnectionPool.GetPoolKey(EndpointAddress address, Uri via)
at System.ServiceModel.Channels.CommunicationPool`2.TakeConnection(EndpointAddress address, Uri via, TimeSpan timeout, TKey& key)
at System.ServiceModel.Channels.ConnectionPoolHelper.EstablishConnection(TimeSpan timeout)
at System.ServiceModel.Channels.ClientFramingDuplexSessionChannel.OnOpen(TimeSpan timeout)
at System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout)
at System.ServiceModel.Channels.ServiceChannel.OnOpen(TimeSpan timeout)
at System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout)
at System.ServiceModel.Channels.ServiceChannel.CallOpenOnce.System.ServiceModel.Channels.ServiceChannel.ICallOnce.Call(ServiceChannel channel, TimeSpan timeout)
at System.ServiceModel.Channels.ServiceChannel.CallOnceManager.CallOnce(TimeSpan timeout, CallOnceManager cascade)
at System.ServiceModel.Channels.ServiceChannel.EnsureOpened(TimeSpan timeout)
at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan timeout)
at System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage methodCall, ProxyOperationRuntime operation)
at System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message)
Exception rethrown at [0]:
at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)
at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)
at GI.Services.GlobalPositioning.Contracts.IGlobalPositioning.GetGlobalPosition(String mailingAddress)
at GI.Services.GlobalPositioning.Services.Tests.GlobalPositioningServiceTests.GetGlobalPositionWorksWithDiacriticsInMunicipalityName(String address, Double latitude, Double longitude) in C:\Open\Projects\Framework\Src\GI.Services\GI.Services.GlobalPositioning.Services.Tests\GlobalPositioningServiceTests.cs:line 27
--PipeException C:\Open\Projects\Framework\Src\GI.Services\GI.Services.GlobalPositioning.Services.Tests\GlobalPositioningServiceTests.cs 27
For your information, I'm using:
Visual Studio 2010
Windows 7
NUnit
And my service is contained within a WCF Service Library.
It seems that you are attempting to do integration testing with a running instance of your service using the netNamedPipesBinding. To do this, you need to have both a service host providing an instance of your service and a service proxy instance to use for making calls to the service. You could try combining both the code in both of your sample TestFixtureSetup methods so that you are instantiating both the service host and the service proxy (the result of the CreateChannel method). For an example of how to do this, look at this blog post.

Basic HTTP Authentication over HTTPS with WCF

I’m calling a web service via WCF (w/ .NET 4.0) that requires basic HTTP authentication (with username and password.) over HTTPS. While, I think I’ve got everything setup correctly, I’m getting a 401 error whenever I make the call to the service. I monitored the HTTP traffic and noticed that WCF seems to be ignoring that I told it to use an authorization header with the username and password, as none is sent in the request. Here’s my configuration below. Any ideas? Thanks!
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="BasicAuthSecured">
<security mode="Transport">
<transport clientCredentialType="Basic" />
</security>
</binding>
</basicHttpBinding>
</bindings>
<client>
<endpoint address="https://REMOVED FOR CONFIDENTIALITY"
binding="basicHttpBinding" bindingConfiguration="BasicAuthSecured"
contract="Five9.WsAdmin" name="WsAdminPort" />
</client>
<behaviors>
<serviceBehaviors>
<behavior name="">
<serviceMetadata httpsGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="true"/>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
And here’s my code:
var five_9_client = new WsAdminClient();
five_9_client.ClientCredentials.UserName.UserName = “REMOVED FOR CONFIDENTIALITY";
five_9_client.ClientCredentials.UserName.Password = "REMOVED FOR CONFIDENTIALITY";
var call_log_response = five_9_client.getCallLogReport(call_log); //Bombing out here
I’m getting this exception:
{"The HTTP request is unauthorized with client authentication scheme 'Basic'. The authentication header received from the server was ''."}
With inner exception:
{"The remote server returned an error: (401) Unauthorized."}
With stack trace:
at System.ServiceModel.Channels.HttpChannelUtilities.ValidateAuthentication(HttpWebRequest request, HttpWebResponse response, WebException responseException, HttpChannelFactory factory)
at System.ServiceModel.Channels.HttpChannelUtilities.ValidateRequestReplyResponse(HttpWebRequest request, HttpWebResponse response, HttpChannelFactory factory, WebException responseException, ChannelBinding channelBinding)
at System.ServiceModel.Channels.HttpChannelFactory.HttpRequestChannel.HttpChannelRequest.WaitForReply(TimeSpan timeout)
at System.ServiceModel.Channels.RequestChannel.Request(Message message, TimeSpan timeout)
at System.ServiceModel.Dispatcher.RequestChannelBinder.Request(Message message, TimeSpan timeout)
at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan timeout)
at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs)
at System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage methodCall, ProxyOperationRuntime operation)
at System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message)
Found the solution: basically, the problem was that the header had to be attached to the current request, like so:
var base_64_encoded_credentials =
BasicHTTPAuthenticationEncoder.base_64_encode_credentials(
client.ClientCredentials.UserName.UserName, client.ClientCredentials.UserName.Password);
HttpRequestMessageProperty request = new HttpRequestMessageProperty();
request.Headers[System.Net.HttpRequestHeader.Authorization] = "Basic " + base_64_encoded_credentials;
OperationContextScope clientScope = new OperationContextScope(five_9_client.InnerChannel);
OperationContext.Current.OutgoingMessageProperties.Add(HttpRequestMessageProperty.Name, request);
It may be that the problem is that you have not set the client credential type, see http://msdn.microsoft.com/en-us/library/ms731925.aspx
Either it is not being sent at all, or the default client credential type does not send the credentials in the header.

BizTalk SendPort WCF Calling .asmx web service using WS-Security

Everything I've found so far says I should be able to use WCF to call a .asmx web service that uses WS-Security. The question is how to configure the WCF-Port. I'm using WCF-BasicHttp. First of all, is that okay? Second, how to enter the user/pass properly. On the security tab, which "Security Mode" should I pick?
The only one that seems to let me enter credentials is TransportWithMessageCredential, then I can click the "Edit" button by username credentials and enter a user/pass.
But when I did, I got this:
<soap:Fault xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<faultcode xmlns:q0="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">q0:Security</faultcode>
<faultstring>Microsoft.Web.Services3.Security.SecurityFault: Security requirements are not satisfied because the security header is not present in the incoming message.
at Microsoft.Web.Services3.Design.UsernameOverTransportAssertion.ServiceInputFilter.ValidateMessageSecurity(SoapEnvelope envelope, Security security)
at MSB.RCTExpress.Presentation.Web.UsernameOverTransportAssertion.ServiceInputFilter.ValidateMessageSecurity(SoapEnvelope envelope, Security security)
in C:\projects\la1safe1\RCT Express\MSB.RCTExpress\3.10\Presentation.Web\UsernameOverTransportNoSendNone.cs:line 27
at Microsoft.Web.Services3.Security.ReceiveSecurityFilter.ProcessMessage(SoapEnvelope envelope)
at Microsoft.Web.Services3.Pipeline.ProcessInputMessage(SoapEnvelope envelope)
at Microsoft.Web.Services3.WseProtocol.FilterRequest(SoapEnvelope requestEnvelope)
at Microsoft.Web.Services3.WseProtocol.RouteRequest(SoapServerMessage message)
at System.Web.Services.Protocols.SoapServerProtocol.Initialize()
at System.Web.Services.Protocols.ServerProtocol.SetContext(Type type, HttpContext context, HttpRequest request, HttpResponse response)
at System.Web.Services.Protocols.ServerProtocolFactory.Create(Type type, HttpContext context, HttpRequest request, HttpResponse response, Boolean& abortProcessing)</faultstring>
<faultactor>http://rct3.msbexpress.net/demo/ExpressLync/ValuationService.asmx</faultactor>
</soap:Fault>
Any ideas?
Thanks,
Neal Walters
Follow-up to TomasR's post - using WS-HTTP binding:
1) BizTalk "Consume WCF Wizard" builds a custom binding file and a WS-BasicHTTP Binding file, so I changed SendPort, and manually copied over all the configurations.
Set as follows:
Security Mode: Message
Message Client Credential Type: UseName
Algorithm Suite: Basic256 [I had no idea what to put here]
I also checked two other boxes:
a) Negotiate service credential [if I don't check this, it wants a "thumbprint"]
b) Establish security context [also tried not checking this one]
2) Ran and got this error:
Description:
The adapter failed to transmit message going to send port "WcfSendPort_ValuationServicePort_ValuationServicePortSoap" with URL "http://rct3.msbexpress.net/demo/ExpressLync/ValuationService.asmx". It will be retransmitted after the retry interval specified for this Send Port.
Details:"System.NullReferenceException: Object reference not set to an instance of an object.
Server stack trace:
at System.ServiceModel.Security.IssuanceTokenProviderBase`1.DoNegotiation(TimeSpan timeout)
at System.ServiceModel.Security.SspiNegotiationTokenProvider.OnOpen(TimeSpan timeout)
at System.ServiceModel.Security.TlsnegoTokenProvider.OnOpen(TimeSpan timeout)
at System.ServiceModel.Security.WrapperSecurityCommunicationObject.OnOpen(TimeSpan timeout)
at System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout)
at System.ServiceModel.Security.CommunicationObjectSecurityTokenProvider.Open(TimeSpan timeout)
at System.ServiceModel.Security.SecurityUtils.OpenTokenProviderIfRequired(SecurityTokenProvider tokenProvider, TimeSpan timeout)
at System.ServiceModel.Security.SymmetricSecurityProtocol.OnOpen(TimeSpan timeout)
at System.ServiceModel.Security.WrapperSecurityCommunicationObject.OnOpen(TimeSpan timeout)
at System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout)
at System.ServiceModel.Channels.SecurityChannelFactory`1.ClientSecurityChannel`1.OnOpen(TimeSpan timeout)
at System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout)
at System.ServiceModel.Security.SecuritySessionSecurityTokenProvider.DoOperation(SecuritySessionOperation operation,
EndpointAddress target, Uri via, SecurityToken currentToken, TimeSpan timeout)
at System.ServiceModel.Security.SecuritySessionSecurityTokenProvider.GetTokenCore(TimeSpan timeout)
at System.IdentityModel.Selectors.SecurityTokenProvider.GetToken(TimeSpan timeout)
at System.ServiceModel.Security.SecuritySessionClientSettings`1.ClientSecuritySessionChannel.OnOpen(TimeSpan timeout)
at System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout)
at System.ServiceModel.Channels.ServiceChannel.OnOpen(TimeSpan timeout)
at System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout)
at System.ServiceModel.Channels.CommunicationObject.Open()
Exception rethrown at [0]:
at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)
at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)
at System.ServiceModel.ICommunicationObject.Open()
at Microsoft.BizTalk.Adapter.Wcf.Runtime.WcfClient`2.GetChannel[TChannel](IBaseMessage bizTalkMessage,
ChannelFactory`1& cachedFactory)
at Microsoft.BizTalk.Adapter.Wcf.Runtime.WcfClient`2.SendMessage(IBaseMessage bizTalkMessage)".
Now tried custom binding, added user/pass and get this error:
<soap:Fault xmlns:soap="http://www.w3.org/2003/05/soap-envelope">
<soap:Code>
<soap:Value xmlns:q0="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">q0:Security</soap:Value>
</soap:Code>
<soap:Reason>
<soap:Text xml:lang="en">Microsoft.Web.Services3.Security.SecurityFault:
Security requirements are not satisfied because the security header is not present in the incoming message.
at Microsoft.Web.Services3.Design.UsernameOverTransportAssertion.ServiceInputFilter.ValidateMessageSecurity(SoapEnvelope envelope,
Security security)
at MSB.RCTExpress.Presentation.Web.UsernameOverTransportAssertion.ServiceInputFilter.ValidateMessageSecurity
(SoapEnvelope envelope, Security security) in
C:\projects\la1safe1\RCT Express\MSB.RCTExpress\3.10\Presentation.Web\UsernameOverTransportNoSendNone.cs:line 27
at Microsoft.Web.Services3.Security.ReceiveSecurityFilter.ProcessMessage(SoapEnvelope envelope)
at Microsoft.Web.Services3.Pipeline.ProcessInputMessage(SoapEnvelope envelope)
at Microsoft.Web.Services3.WseProtocol.FilterRequest(SoapEnvelope requestEnvelope)
at Microsoft.Web.Services3.WseProtocol.RouteRequest(SoapServerMessage message)
at System.Web.Services.Protocols.SoapServerProtocol.Initialize()
at System.Web.Services.Protocols.ServerProtocol.SetContext(Type type, HttpContext context, HttpRequest request, HttpResponse response)
at System.Web.Services.Protocols.ServerProtocolFactory.Create(Type type, HttpContext context, HttpRequest request, HttpResponse response, Boolean& abortProcessing)</soap:Text>
</soap:Reason>
<soap:Node>http://rct3.msbexpress.net/demo/ExpressLync/ValuationService.asmx</soap:Node>
</soap:Fault>
My next attempt, went back to WS-HTTP, but tried to put the User/Pass in a message assignment rather than in the SendPort:
msgRCTGetRequest(SOAP.Username) = "myuser";
msgRCTGetRequest(SOAP.Password) = "mypass";
//msgRCTGetRequest(SOAP.UseSoap12) = true;
Resulted in this error:
<soap:Fault xmlns:soap="http://www.w3.org/2003/05/soap-envelope">
<soap:Code>
<soap:Value>soap:Sender</soap:Value>
</soap:Code><soap:Reason>
<soap:Text xml:lang="en">System.Web.Services.Protocols.SoapHeaderException: WSE012: The input was not a valid SOAP message because the following information is missing: action.
at Microsoft.Web.Services3.Utilities.AspNetHelper.SetDefaultAddressingProperties(SoapContext context, HttpContext httpContext)
at Microsoft.Web.Services3.WseProtocol.CreateRequestSoapContext(SoapEnvelope requestEnvelope)
at Microsoft.Web.Services3.WseProtocol.FilterRequest(SoapEnvelope requestEnvelope)
at Microsoft.Web.Services3.WseProtocol.RouteRequest(SoapServerMessage message)
at System.Web.Services.Protocols.SoapServerProtocol.Initialize()
at System.Web.Services.Protocols.ServerProtocol.SetContext(Type type, HttpContext context, HttpRequest request, HttpResponse response)
at System.Web.Services.Protocols.ServerProtocolFactory.Create(Type type, HttpContext context, HttpRequest request, HttpResponse response, Boolean& abortProcessing)</soap:Text>
</soap:Reason>
</soap:Fault>
Fifth attempt, about to give up and open a Microsoft ticket:
msgRCTGetRequest(WCF.UserName) = "myuser";
msgRCTGetRequest(WCF.Password) = "mypass";
msgRCTGetRequest(WCF.Action) = "GetPropertyInfoSourceRecordPolicyNum";
msgRCTGetRequest(SOAP.MethodName) = "GetPropertyInfoSourceRecordPolicyNum";
msgRCTGetRequest(SOAP.Username) = "myuser";
msgRCTGetRequest(SOAP.Password) = "mypass";
same error as fourth attempt.
According to the doc of the vendor providing the web service, I should put the user in W-Security UsernameToken element, the password in WS-Security password, and set the element's attribute to "PasswordDigest". It also says "This token should be added to the SOAP request for the Web method." I'm not sure how this translates from the old WSE3 days to the new WCF days.
Neal, for WS-Security, you need to use the WCF-WsHttp binding/Adapter. WCF-BasicHttp is only for the simpler scenarios where the WS-* protocols are not needed.
.NET 4.0 and .NET 3.5 SP1 with hotfix 971831 allow WS-Security over http transport. Try using this sample binding:
<customBinding>
<binding name="httpAndWSSecurity">
<security authenticationMode="UserNameOverTransport"
allowInsecureTransport="true"/>
<textMessageEncoding messageVersion="Soap11WSAddressingAugust2004" />
<httpTransport/>
</binding>
Also see this MSDN article on SecurityBindingElement.AllowInsecureTransport
Use custom binding, and from the BizTalk send port, click configure, then go to the right-most tab which says "Import/Export". Paste the following XML into a file (sample.config) and then import it into the configuration port. This basically saves the time of manually typing a lot of stuff on the binding tab.
<configuration>
<system.serviceModel>
<client>
<endpoint
address="http://rct3.msbexpress.net/demo/ExpressLync/ValuationService.asmx"
binding="customBinding"
bindingConfiguration="ValuationServicePortSoap12"
contract="BizTalk"
name="WcfSendPort_ValuationServicePort_ValuationServicePortSoap12"/>
</client>
<bindings>
<customBinding>
<binding name="ValuationServicePortSoap12">
<security authenticationMode="UserNameOverTransport"
messageProtectionOrder="SignBeforeEncrypt"
includeTimestamp="true"
messageSecurityVersion="WSSecurity11WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10"
requireDerivedKeys="false"
requireSignatureConfirmation="false"/>
<textMessageEncoding messageVersion="Soap11WSAddressingAugust2004"/>
<httpsTransport />
</binding>
</customBinding>
</bindings>
</system.serviceModel>
</configuration>
The above is not very intuitive (I'm still waiting for a link from Microsoft which describes this is more detail).
Then you still specify the user/pass on the credentials tab.
However, this caused a problem for us, in that the vendor's .asmx web service we were calling did not have IIS set to "requires SSL". Apparently, that is a requirement for this to work with WCF. In other words, it works fine with WSE3 calling .NET to .NET, but when trying to call WCF to .asmx, WCF has a slightly more stringent requirement.
Neal Walters