I have a service that I am configurating in this way:
options.Listen(miAddress, 5001, l =>
{
l.Protocols = HttpProtocols.Http2;
System.Security.Cryptography.X509Certificates.X509Certificate2 miCertificado = new System.Security.Cryptography.X509Certificates.X509Certificate2(#"certificados\service.crt");
l.UseHttps(miCertificado);
});
But if I realized that I can configure the client to avoid the authentication, with this code:
var httpClientHandler = new System.Net.Http.HttpClientHandler();
httpClientHandler.ServerCertificateCustomValidationCallback =
System.Net.Http.HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;
var httpClient = new System.Net.Http.HttpClient(httpClientHandler);
var channel = GrpcChannel.ForAddress(_serviceAddress,
new GrpcChannelOptions { HttpClient = httpClient });
var client = new Gestor.GestorClient(channel);
In this case, the authentications is ignored and I can use call to the service.
I know that this ignore the authentication because if I try to use the client to use a certificate, I get an error that tells that the connection couldn't be stablish because of the SSL.
So my doubt is, there is some way to set the service to don't allow this kind of connections? If not, anyone could create a client that igonres this authentication and the security has no sense.
Thanks.
Using HTTPS is not the same as using authentication. All you're doing is encrypting the traffic between client and server, so that eavesdroppers can't read your plaintext traffic.
If you configure your client to accept any server certificate, whether that certificate is valid up till its root or not, does not "ignore authentication" - there was no authentication to begin with.
Related
I'm having difficulties trying to connect to a 3rd-party SOAP server that requires two-way SSL. On the client side I have our certificate and private key, and I also have the self-signed certificate chain provided by the service provider. What I'm essentially trying to do is the C# equivalent of this Python code:
r = requests.post(
url,
verify=(ca_path),
cert=(client_public_path, client_private_path),
headers=headers,
data=body)
Back in C# land, I've subscribed to the service and made it as far as this:
var endpoint = new EndpointAddress("https://server.endpoint.com/");
var binding = new BasicHttpBinding();
binding.Security.Mode = BasicHttpSecurityMode.Transport;
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Certificate;
using (var service = new MyServiceReference.ServerClient(binding, endpoint))
{
X509Certificate certificate = LoadCertificate(client_public_path, client_private_path);
service.ClientCredentials.ClientCertificate.Certificate = certificate;
// how to set server CA Bundle?
var response = service.SomeMethod(new Request{...});
At a bit of a loss though trying to figure out how to set the server-side chain? I have the two certs that are in the chain file, and I can also separate them out and load them into a X509Certificate2Collection if need be, just can't seem to figure out what to do with it after that?
The only other thing I've tried is assigning a custom validator on the server certificate:
service.ClientCredentials.ServiceCertificate.Authentication.CertificateValidationMode = X509CertificateValidationMode.Custom;
service.ClientCredentials.ServiceCertificate.Authentication.CustomCertificateValidator = new MyValidator();
The custom validator's Validate method never gets called thoough, which if I'm not mistaken would suggest it's a either a problem with the client cert or the binding config?
We are writing a client to a WCF service that uses both a CSR certificate and basic authentication.
Our C# client is generated via Visual Studio and we can programmatically set the certificate and the username/password. However, we have to manually send the Basic Auth header otherwise we receive the error:
'The HTTP request is unauthorized with client authentication scheme 'Anonymous'. The authentication header received from the server was 'Basic realm="HttpBasicAuthentication"'.'
Our code is:
var myBinding = new WSHttpBinding();
myBinding.Security.Mode = SecurityMode.Transport;
myBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Certificate;
myBinding.Security.Message.ClientCredentialType = MessageCredentialType.UserName;
var ea = new EndpointAddress("https://example.org/myservice");
var client = new MandateWebServiceClient(myBinding, ea);
client.ClientCredentials.UserName.UserName = "wally";
client.ClientCredentials.UserName.Password = "walliesWorld";
client.ClientCredentials.ClientCertificate.Certificate = new X509Certificate2("C:\\some\\path\\to\\csr.pfx", "password");
using (var scope = new OperationContextScope(client.InnerChannel))
{
var httpRequestProperty = new HttpRequestMessageProperty();
httpRequestProperty.Headers[HttpRequestHeader.Authorization] =
"Basic " + Convert.ToBase64String(Encoding.ASCII.GetBytes(client.ClientCredentials.UserName.UserName + ":" + client.ClientCredentials.UserName.Password));
OperationContext.Current.OutgoingMessageProperties[HttpRequestMessageProperty.Name] = httpRequestProperty;
client.create();
}
With the above code, we can successfully talk to the service. If we remove the lines in the using block, the authentication scheme changes to Anonymous, and we get the error above.
The above arrangement seems a little hackey. We have tried all the SecurityMode settings possible and SecurityMode.Transport with HttpClientCredentialType.Certificate is the only combination that allows the certificate to be accepted. Setting or not setting MessageCredentialType.UserName appears to have no effect on the system.
Is there any .Net Framework way of providing both the certificate and the basic authentication header rather than manually adding the header?
How does the server use both Certificate authentication and Basic authentication? This seems superfluous. Because it is secure to authenticate the client with a certificate (issue the certificate and establish the relationship between the server and client), why do we need to authenticate the client with Basic Authentication? Thereby, are you sure that the client needs to provide a certificate? In my opinion, the server may have used Transport Security mode, and set up a Basic authentication, so the client may need not to provide a certificate.
Here is the server side configuration I thought.
Server.
Uri uri = new Uri("https://localhost:9900");
WSHttpBinding binding = new WSHttpBinding();
binding.Security.Mode = SecurityMode.Transport;
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic;
Client (invocation by adding service reference, the client proxy class/binding type is auto-generated via the service MEX endpoint, https://localhost:9900/mex)
ServiceReference1.ServiceClient client = new ServiceReference1.ServiceClient();
client.ClientCredentials.UserName.UserName = "administrator";
client.ClientCredentials.UserName.Password = "abcd1234!";
Based on this, I have a question, what is the auto-generated binding type on the client side when calling the service by adding service reference?
Look forward to your reply.
I have a non-exportable client certificate in the "client" computer's Local Computer / Personal certificates store, and I want to use this certificate for client authentication when making web requests using System.Net.Http.HttpClient.
I have tried using HttpClientHandler thus:
var subject = /* Distinctive subject name of the certificate known to exist in the store */;
var store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadOnly);
var certificates = store.Certificates.Find(X509FindType.FindBySubjectName, subject, true);
// Known/verifiable at this point that certificates[0] is the correct client cert
var clientHandler = new HttpClientHandler();
clientHandler.ClientCertificates.Add(certificates[0]);
using (var client = new HttpClient(clientHandler))
{
var response = await client.GetAsync(URL.Text);
var result = await response.Content.ReadAsStringAsync();
// ... etc...
}
... but the GetAsync call throws an exception with the message:
The request was aborted: Could not create SSL/TLS secure channel.
I have been able to verify that this is a client-side issue and that the correct certificate is being returned from the store.
I have tested this code against known working endpoints (which require a certificate); other clients work.
I know the certificate is valid because it is successfully used for client authentication by other client code (sources I don't currently have at my disposal)
As I understand it, I am only attaching the public portion (because it is non-exportable), so the authentication is failing. There must surely be a way to use a private, non-exportable key for such a purpose, correct? How do I do it?
I'm using Fiddler Core in my project and have had success routing requests through a SOCKS proxy that does not require authentication.
oS["x-OverrideGateway"] = string.Format("socks={0}:{1}", ProxyHost, ProxyPort);
Now I'm trying to connect to a SOCKS proxy that requires authentication. I've tried adding the below, per this non-SOCKS guide: Authenticate With CBT.
oS["X-AutoAuth"] = Auth;
Where Auth is a string containing the credentials in the form username:password. But the connection always fails with a Bad Gateway exception. I've been successful connecting to the same SOCKS proxy using curl and following it's guidelines for an authenticated SOCKS proxy.
After X-AutoAuth didn't work I reflected some of the code and found that this authentication mechanism is not used for SOCKS.
Rather unfortunately, Fiddler (and FiddlerCore) does not support SOCKS5 as of yet.
You could, however, request this at https://fiddler.ideas.aha.io/.
We faced a similar issue and created a NuGet library which helps you "transform" SOCKS v4 traffic to SOCKS v5, and we added authentication support as well.
This small example shows how can you hook up FiddlerCore and a SOCKS5 proxy with username and password:
var localEndpoint = new IPEndPoint(IPAddress.Loopback, 4321);
var remoteEndpoint = new IPEndPoint(IPAddress.Parse("remote proxy IP"), 8080);
ISocksRelayServer relay = new SocksRelayServer.SocksRelayServer(localEndpoint, remoteEndpoint)
{
Username = "...",
Password = "..."
};
// Debug to console
relay.OnLogMessage += (sender, s) => Console.WriteLine($"OnLogMessage: {s}");
relay.OnLocalConnect += (sender, endpoint) => Console.WriteLine($"OnLocalConnect: {endpoint}");
relay.OnRemoteConnect += (sender, endpoint) => Console.WriteLine($"OnRemoteConnect: {endpoint}");
// Start relay server
relay.Start();
// Start FiddlerCore
FiddlerApplication.Startup(...);
// Set upstream gateway before requests
FiddlerApplication.BeforeRequest += session =>
{
session["x-OverrideGateway"] = relay.LocalEndPoint.ToString();
}
I've written a Windows Application to test a connection to a clients SAP web services. The web service call requires X509 certificate security.
After reading various articles on the internet I've come up with three ways to attach the X509 certificate to the web service call. Unfortunately all of these attempts return a '401 Unauthorised Access'. However, I can connect to the web service via the URL in IE.
Does anybody have any sugestions as to what I may be doing wrong? I am using WSE 3.0 and the three methods I am using to attach the certificate are as follows:-
Certificate
X509Certificate2 oCert = GetSecurityCertificate(oCertificate);
svc.ClientCertificates.Add(oCert);
Token
X509SecurityToken oToken = GetSecurityToken(oCertificate);
svc.RequestSoapContext.Security.Tokens.Add(oToken);
Policy
SAPX509Assertion sapX509Assertion = new SAPX509Assertion(oCertificate, oStoreLocation, oStoreName, oFindType);
svc.SetPolicy(sapX509Assertion.Policy());
GetSecurityToken() and GetSecuirtyCertificate both search the certificate store. The SAPX509Assertion does this:-
public SAPX509Assertion(String certSubject, StoreLocation oStoreLocation, StoreName oStoreName, X509FindType oFindType)
{
ClientX509TokenProvider = new X509TokenProvider(oStoreLocation,
oStoreName, certSubject, oFindType);
ServiceX509TokenProvider = new X509TokenProvider(oStoreLocation,
oStoreName, certSubject, oFindType);
Protection.Request.EncryptBody = false;
Protection.Response.EncryptBody = false;
}
Update
OK, I have a WCF call now in place. I couldn't use the BasicHttpBinding method shown by Eugarps as it complained that I was connecting to a https address and expected http...which made sense. The code I now have is:-
var binding = new WSHttpBinding();
binding.MaxReceivedMessageSize = int.MaxValue;
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Windows;
binding.Security.Mode = SecurityMode.Transport;
WCFConnection.CreateAbsenceWSlow.ZWSDHTM_GB_AMS_CREATEABS_lowClient client;
CreateAbsenceWSlow.ZfhhrGbbapiZgeeamsCreateabsResponse response;
CreateAbsenceWSlow.ZfhhrGbbapiZgeeamsCreateabs data;
//Assign address
var address = new EndpointAddress(sUrl);
//Create service client
client = new CreateAbsenceWSlow.ZWSDHTM_GB_AMS_CREATEABS_lowClient(binding, address);
//Assign credentials
client.ClientCredentials.UserName.UserName = sUserName;
client.ClientCredentials.UserName.Password = sPassword;
response = new CreateAbsenceWSlow.ZfhhrGbbapiZgeeamsCreateabsResponse();
data = new WCFConnection.CreateAbsenceWSlow.ZfhhrGbbapiZgeeamsCreateabs();
response = client.ZfhhrGbbapiZgeeamsCreateabs(data);
It's still failing to connect to the SAP web service. The error I am receiving is "The HTTP request is unauthorized with client authentication scheme 'Negotiate'". I've also tried using
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic;
which returned a similar error.
Does anybody have any further suggestions or ideas of where I am going wrong?
Now, this is all coming from my own experience so some of it may be wrong, but here's how I understand the process (I received no documentation and my company had no experience in calling SAP before I began doing it).
SAP WS calls are only supported by WCF BasicHttpBinding, and as far as I can tell, only using plain-text credentials. This means you will want to use IPSec or HTTPS if you need to make your communication private (outside intranet, or sensitive data within intranet). Our SAP server does not have HTTPS configured, but we use VPN with IPSec for external communication. Important to note is that, by default, SAP GUI also does not make communication private. In this situation, you are being no less secure by using the method detailed below than the business user down the hall who is looking up sensitive data in GUI 7.1. Here's how I connect to our SAP server internally:
//Create binding
//Note, this is not secure but it's not up to us to decide. This should only ever be run within
//the VPN or Intranet where IPSec is active. If SAP is ever directly from outside the network,
//credentials and messages will not be private.
var binding = new BasicHttpBinding();
binding.MaxReceivedMessageSize = int.MaxValue;
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic;
binding.Security.Mode = BasicHttpSecurityMode.TransportCredentialOnly;
//Assign address
var address = new EndpointAddress(Host);
//Create service client
var client = new SAP_RFC_READ_TABLE.RFC_READ_TABLEPortTypeClient(binding, address);
//Assign credentials
client.ClientCredentials.UserName.UserName = User;
client.ClientCredentials.UserName.Password = Password;
As far as I have been able to determine, message-level security is not supported, and bindings other than basicHttpBinding (SOAP 1.1) are not supported.
As I said, this is all from experience and not from training, so if anybody can add something through comments, please do so.
I've faced the same problem and it seems I've found the sollution here: http://ddkonline.blogspot.com/2009/08/calling-sap-pi-web-service-using-wcf.html.
CustomBinding binding = new CustomBinding();
binding.Elements.Add(new TextMessageEncodingBindingElement(MessageVersion.Soap11, Encoding.UTF8));
HttpsTransportBindingElement transport = new HttpsTransportBindingElement();
transport.AuthenticationScheme = AuthenticationSchemes.Basic;
//transport.ProxyAuthenticationScheme = AuthenticationSchemes.Basic;
transport.Realm = "XISOAPApps";
binding.Elements.Add(transport);
var address = new EndpointAddress("https://foooo");
........ create client proxy class
service.ClientCredentials.UserName.UserName = "<login>";
service.ClientCredentials.UserName.Password = "<password>";
Unfortunatelly I'm not able to use WCF in my application, I have to stick with .NET 2.0 and WSE 3.0, and I wounder if anybody was able to find sollution to that?
After all this time, the client has finally obtained someone to deal with the issue from their SAP end of things. Turns out the WSDL files we were supplied were incorrect and the certification had been done wrong. I reran my code with the new WSDL files and it worked first time.
Does your certificate happen to be mapped to a valid user in your user store?