Is it possible in WinRT to attach a client certificate to a SOAP client request?
In previous versions you would simply do:
MyServiceSoapClient client = new MyServiceSoapClient()
X509Certificate2 cert = CertificateHelper.GetClientCertificate();
client.ClientCredentials.ClientCertificate.Certificate = cert;
But ClientCertificate property doesn't seem to be available anymore.
How could I achieve this in UWP?
Thank you.
try this:
var client = new ServiceClient();
client.Endpoint.Address = new EndpointAddress(url);
BasicHttpBinding binding = client.Endpoint.Binding as BasicHttpBinding;
binding.Security.Mode = BasicHttpSecurityMode.Transport;
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Certificate;
client.ClientCredentials.ClientCertificate.Certificate = ...
You may edit the cert to fit your requirements :)
Service Client Documentation
https://msdn.microsoft.com/en-ca/library/mt185502.aspx
Refer to:
https://social.msdn.microsoft.com/Forums/sqlserver/en-US/f1b8ef52-8c3e-417c-94b9-ba2a545e9beb/uwp-app-making-soap-call-requiring-clientcredential?forum=wpdevelop
Related
I have a web service feature that I have been using for many years. Today they sent me a new certificate, a .cer file, which I inserted instead of the old one but I get this error:
(Translated with Google, sorry)
MessageSecurityException
Failed to complete identity check for outgoing message.
The expected DNS identity of the remote endpoint was 'pddasl-coll.rmmg.rsr.rupar.puglia.it'
but the remote endpoint provided a DNS claim 'pdd-virtasl.rmmg.rsr.rupar.puglia.it'.
If this is a legitimate remote endpoint, you can fix the problem by specifying
explicitly the DNS identity 'pdd-virtasl.rmmg.rsr.rupar.puglia.it'
as the Identity property of EndpointAddress when creating the channel proxy.
I asked the owner of the web service and they told me that I have to make sure to ignore the error, but I don't know how. I tried to insert in the app.config: enableUnsecuredResponse = "true" but it didn't work.
This is the method for connecting to the web service:
public static CVPClient Connect()
{
CVPClient oConsist = null;
string cEndPoint = "https://pddasl-coll.rmmg.rsr.rupar.puglia.it:8181/aslba/CVPService";
ServicePointManager.ServerCertificateValidationCallback = Leo.CertificateHandler;
datiOperatore DataOp = Leo.OperatorData();//unimportant parameters
datiApplicativo DataApp = Leo.AppData();//unimportant parameters
var b = new CustomBinding();
var sec = new AsymmetricSecurityBindingElement(
new X509SecurityTokenParameters(X509KeyIdentifierClauseType.Any, SecurityTokenInclusionMode.Never),
new X509SecurityTokenParameters(X509KeyIdentifierClauseType.Any, SecurityTokenInclusionMode.AlwaysToRecipient));
sec.MessageSecurityVersion = MessageSecurityVersion.WSSecurity10WSTrust13WSSecureConversation13WSSecurityPolicy12BasicSecurityProfile10;
sec.SecurityHeaderLayout = SecurityHeaderLayout.Strict;
sec.IncludeTimestamp = true;
sec.SetKeyDerivation(false);
sec.KeyEntropyMode = System.ServiceModel.Security.SecurityKeyEntropyMode.ServerEntropy;
sec.EnableUnsecuredResponse = true;
b.Elements.Add(sec);
b.Elements.Add(new TextMessageEncodingBindingElement(MessageVersion.Soap11, Encoding.UTF8));
b.Elements.Add(new HttpsTransportBindingElement());
EndpointAddress ea = new EndpointAddress(cEndPoint);
oConsist = new CVPClient(b, ea);
X509Certificate2 certSigned = Leo.GetSignedCert();//this returns my private certificate, not the one they replaced me
string cPin = "123456";
System.Security.SecureString SecurePIN = new System.Security.SecureString();
foreach (char ch in cPin)
{ SecurePIN.AppendChar(ch); }
var rsa = (RSACryptoServiceProvider)certSigned.PrivateKey;
string ContinerName = rsa.CspKeyContainerInfo.KeyContainerName;
string CspName = rsa.CspKeyContainerInfo.ProviderName;
int CspType = rsa.CspKeyContainerInfo.ProviderType;
CspParameters csp = new CspParameters(CspType, CspName, ContinerName, new System.Security.AccessControl.CryptoKeySecurity(), SecurePIN);
RSACryptoServiceProvider CSP = new RSACryptoServiceProvider(csp);
X509Certificate2 certUnsigned = Leo.GetUnSignedCertificate();//Here I read the new certificate
oConsist.ClientCredentials.ClientCertificate.Certificate = certSigned;
oConsist.ClientCredentials.ServiceCertificate.DefaultCertificate = certUnsigned;
oConsist.Open();
return oConsist;
}
As you can see also here I have inserted sec.EnableUnsecuredResponse = true; The connection is successful but, as I said before, I get the error when I call the web service.
How can I solve the problem?
UPDATE:
Since the error tells me to explicitly assign the DNS identity 'pdd-virtasl.rmmg.rsr.rupar.puglia.it'
as the Identity property of EndpointAddress, I replaced this line:
EndpointAddress ea = new EndpointAddress(cEndPoint);
with this:
DnsEndpointIdentity identity = new DnsEndpointIdentity(cEndPoint);
EndpointAddress ea = new EndpointAddress(new Uri(cEndPoint), identity, new AddressHeaderCollection());
but it doesn't work, I get the same error
The correct solution is this:
DnsEndpointIdentity identity = new DnsEndpointIdentity("pdd-virtasl.rmmg.rsr.rupar.puglia.it");
EndpointAddress ea = new EndpointAddress(new Uri(cEndPoint), identity, new AddressHeaderCollection());
You have to use the web service endpoint but specify the endopint contained in the certificate in the identity.
I have a .NET Core 2.0 application and need to call a WCF client from one of its controllers, and pass the user credentials for authentication.
Within the .net core app I created a reference for the WCF client using the Connected Services (WCF Web Service Reference Provider) and now in a process of configuring the call. Note that I can use the same endpoint form a 4.6 framework application without any problems.
Here's my code:
var binding = new BasicHttpBinding {Security = {Mode = BasicHttpSecurityMode.Transport}};
var address = new EndpointAddress("https://my-endpoint.asmx");
var client = new MyAppSoapClient(binding, address);
var credentials = CredentialCache.DefaultNetworkCredentials;
client.ClientCredentials.Windows.ClientCredential = credentials;
client.ClientCredentials.Windows.AllowedImpersonationLevel = System.Security.Principal.TokenImpersonationLevel.Impersonation;
var response = client.GetStuff("param").Result;
I face a number of problems:
It has to be a https call
I need to pass the currently log in user credentials to the call
The current error I get is as follows:
The HTTP request is unauthorized with client authentication scheme 'Anonymous'. The authentication header received from the server was 'Negotiate, NTLM'
Also the ConnectedService.json (created automativcally by WCF Web Service Reference Provider) has a predefined endpoint Uri.. I don't understand why I need to pass the address to the client manually (the code seems to be forcing me to do so).. ideally I'd like to get this dynamically amended in json depending on environment.
Thanks.
I noticed that you passed the current logged-in user as a Windows credential (which is also necessary for enabling impersonation), but you did not explicitly set the client credentials for the transport layer security.
BasicHttpBinding binding = new BasicHttpBinding();
binding.Security.Mode = BasicHttpSecurityMode.Transport;
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Windows;
Also the ConnectedService.json (created automativcally by WCF Web
Service Reference Provider) has a predefined endpoint Uri.. I don't
understand why I need to pass the address to the client manually (the
code seems to be forcing me to do so)
You can modify the method of automatic generation of proxy client to construct client proxy class (located in the reference.cs)
Modify the binding security
private static System.ServiceModel.Channels.Binding GetBindingForEndpoint(EndpointConfiguration endpointConfiguration)
{
if ((endpointConfiguration == EndpointConfiguration.WebService1Soap))
{
System.ServiceModel.BasicHttpBinding result = new System.ServiceModel.BasicHttpBinding();
result.Security.Mode = System.ServiceModel.BasicHttpSecurityMode.Transport;
result.Security.Transport.ClientCredentialType = System.ServiceModel.HttpClientCredentialType.Windows;
result.MaxBufferSize = int.MaxValue;
result.ReaderQuotas = System.Xml.XmlDictionaryReaderQuotas.Max;
result.MaxReceivedMessageSize = int.MaxValue;
result.AllowCookies = true;
return result;
}
Modify the endpoint.
private static System.ServiceModel.EndpointAddress GetEndpointAddress(EndpointConfiguration endpointConfiguration)
{
if ((endpointConfiguration == EndpointConfiguration.WebService1Soap))
{
return new System.ServiceModel.EndpointAddress("http://10.157.13.69:8001/webservice1.asmx");
Construct the client proxy class.
ServiceReference1.WebService1SoapClient client = new WebService1SoapClient(WebService1SoapClient.EndpointConfiguration.WebService1Soap);
client.ClientCredentials.Windows.AllowedImpersonationLevel = System.Security.Principal.TokenImpersonationLevel.Impersonation;
client.ClientCredentials.Windows.ClientCredential.UserName = "administrator";
client.ClientCredentials.Windows.ClientCredential.Password = "123456";
Feel free to let me know if there is anything I can help with.
My binding was missing the security Ntlm credential type (see below).
Problem solved.
var binding = new BasicHttpBinding {Security = {Mode = BasicHttpSecurityMode.Transport,
Transport = new HttpTransportSecurity(){ClientCredentialType = HttpClientCredentialType.Ntlm } }};
I coded a WCF Service using HttpTransportBindingElement in conjunction with IIS on port 80.
The code works fine as long as no proxy is used. But if a customer has a http-proxy the communication between WCF-Client and Server does not work in this case by occuring following error:
'There was no endpoint listening at ... that could accept the message. This is often caused by an incorrect address or SOAP action.'
It is essential to use settings by code ONLY!
here is my code approach for that issue but i stuck on it:
bool SendClientRequest(Action<ICustomerService> channel)
{
string proxy ="my.proxy.domain:8080";
string user = "user1";
string password="secret";
// maybe i do not need this 3 lines!
WebProxy webproxy = new WebProxy(proxy, true);
webproxy.Credentials = new NetworkCredential(user, password);
WebRequest.DefaultWebProxy = webproxy;
CustomBinding customBinding = new CustomBinding();
customBinding.Elements.Add(new HttpTransportBindingElement()
{
AuthenticationSchemes.None : AuthenticationSchemes.Basic,
ProxyAddress = string.IsNullOrEmpty(proxy) ? null : new Uri(proxy),
UseDefaultWebProxy = false,
BypassProxyOnLocal = true,
TransferMode = TransferMode.Streamed,
MaxReceivedMessageSize = 84087406592,
MaxBufferPoolSize = 0x1000000,
MaxBufferSize = 0x1000000
});
using (ChannelFactory<ICustomerService> factory = new
ChannelFactory<ICustomerService>(customBinding ))
{
IClientChannel contextChannel = null;
string url = "http://my.domain.de/Distribution/eService.svc",
EndpointAddress ep = new EndpointAddress(url);
ICustomerService clientChannel = factory.CreateChannel(ep);
contextChannel = clientChannel as IClientChannel;
contextChannel.OperationTimeout = TimeSpan.FromMinutes(rcvTimeout );
channel(clientChannel); // <- here i get the exception!
return true;
}
}
I tried several solution approaches but nothing seems to be specific like mine.
I think you have a few options, some of which I'll detail below.
First you could set UseDefaultWebProxy to true. This would then mean that proxy information is retrieved automatically from system proxy settings, configurable in Internet Explorer (Internet Options > Connections > LAN settings > Proxy server). This may be appropriate if you don't need to specify credentials for proxy use.
Another approach that's worked for me is to use the ProxyAuthenticationScheme property within your HttpTransportBindingElement() object. This property is only available on the CustomBinding class and allows an authentication scheme to be specified that will be used to authenticate against a proxy. In conjunction with this, the proxy server must be set against property ProxyAddress. Last but not least, the credentials to use against the proxy should be set according to the authentication scheme used, so for example, using AuthenticationSchemes.Ntlm would mean setting the UserName and Password properties on ChannelFactory.ClientCredentials.Windows.ClientCredential or perhaps ChannelFactory.ClientCredentials.HttpDigest.ClientCredential
With the second approach, be sure to note the difference between holding credentials in the ChannelFactory for use with the remote service versus credentials used for the proxy server. I've highlighted these in the code example below for clarity:
// Example service call using a CustomBinding that is configured for client
// authentication based on a user name and password sent as part of the message.
var binding = new CustomBinding();
TransportSecurityBindingElement securityBindingElement = SecurityBindingElement.CreateUserNameOverTransportBindingElement();
var secureTransport = new HttpsTransportBindingElement();
secureTransport.UseDefaultWebProxy = false;
secureTransport.ProxyAddress = new Uri("http://some-proxy");
secureTransport.ProxyAuthenticationScheme = AuthenticationSchemes.Ntlm;
binding.Elements.Add(securityBindingElement);
binding.Elements.Add(secureTransport);
var endpointAddress = new EndpointAddress("https://some-service");
var factory = new ChannelFactory<IService>(binding, endpointAddress);
// Credentials for authentication against the remote service
factory.Credentials.UserName.UserName = "serviceUser";
factory.Credentials.UserName.Password = "abc";
// Credentials for authentication against the proxy server
factory.Credentials.Windows.ClientCredential.UserName = "domain\user";
factory.Credentials.Windows.ClientCredential.Password = "xyz";
var client = factory.CreateChannel();
client.CallMethod();
I am trying to call a web service on my server over https from my mvc3 app. I have web services located at this address:
https:localhost/web_services/web_services.asmx
And in my code i try to connect like this:
var binding = new BasicHttpsBinding();
binding.maxbuffersize = 10000;
binding.maxbufferPoolsize = 10000;
binding.maxreceivedmessageSize= 10000;
binding.Security.Mode = System.ServiceModel.BasicHttpsSecurityMode.Transport;
binding.Security.Transport.ClientCredentialsType = HttpClientCredentialType.Certificate
var endpointAddress = new EndpointAddress("https:/localhost/web_services/web_services.asmx");
new ChannelFactory<ws_name_webreqSoap>(basicHttpsBinding, endpointAddress).CreateChannel();
var webServices = new ws_name_webreqSoapClient(basicHttpsBinding, endpointAddress);
However, when this runs on the server, i get the following message:
"The client certificate is Not provided. Specify a client certificate in client credentials"
My knowledge of HTTPS and certificates is limited. Does anyone know a solution to this?
Thanks,
You can specify the client certificate on the ChannelFactory:
var channelFactory = new ChannelFactory<ws_name_webreqSoap>(basicHttpsBinding, endpointAddress);
channelFactory.Credentials.ClientCertificate.SetCertificate("CN=client.com", StoreLocation.CurrentUser, StoreName.My);
var channel = channelFactory.CreateChannel();
// ...
Is there any example of using a WCF REST service with basic HTTP authentication from a desktop client?
I am using WCF REST Contrib. and authentication works fine when a use a javascript client from the browser, but when I try to use a C# Console app. I get a BasicUnauthorizedException {"You have unsuccessfully attempted to access a secure resource."}. even though I supplied the correct username and password.
WebHttpBinding binding = new WebHttpBinding();
binding.SendTimeout = TimeSpan.FromSeconds(25);
binding.Security.Mode = WebHttpSecurityMode.TransportCredentialOnly;
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic;
Uri address = new Uri("http://localhost:3525/wcfrestdemo/students.svc");
WebChannelFactory<ISudentService> factory =
new WebChannelFactory<ISudentService>(binding, address);
factory.Credentials.UserName.UserName = "jon";
factory.Credentials.UserName.Password = "123";
ISudentService proxy = factory.CreateChannel();
var response = proxy.GetStudents(2010, 4, 2); //throws an error.
Any help will be appreciated.