I have several remote servers that communicate with a central SOAP Service, where they can download the latest X509Certificate2 which can then be used to call a third-party API that requires this certificate to authenticate the requests.
Some of these remote servers are hosted by some of our clients on their own Windows Servers, which may be VMs or physical boxes, and others are hosted by us on Azure VMs.
We have had no previous issue with this functionality until recently when we moved our APIs from being hosted on a physical box to now being hosted in an Azure App Service (with an App Gateway handling requests).
What now happens is that all of the non-Azure servers, download the certificate successfully, but the certificate fails when used with the third-party rejecting the certificate with the error:
The request was aborted: Could not create SSL/TLS secure channel.
I have confirmed that if I manually copy the certificate to the servers, it works fine, but for some reason somewhere in the process of downloading it from the Azure SOAP API it now fails.
The code that we have to export the certificate on the API side so that it can be downloaded is something like:
[WebMethod]
public ClientCertificateMessage GetCertificate(LoginMessage login, string customer)
{
ClientCertificateMessage returnValue = new ClientCertificateMessage();
if (Authentication.VerifyLogin(login))
{
try
{
string filePathNameCertificate = ConfigurationManager.AppSettings["PathToLocalCert"].ToString() + customer + ".p12";
string filePathNamePassword = ConfigurationManager.AppSettings["PathToLocalCert"].ToString() + customer + ".txt";
string password = File.ReadAllText(filePathNamePassword);
X509Certificate2 x509Certificate2 = new X509Certificate2( filePathNameCertificate, password, X509KeyStorageFlags.Exportable);
returnValue.Certificate = x509Certificate2.Export(X509ContentType.Pkcs12, password);
}
catch (Exception ex)
{
// Log Error stuff here is removed
}
}
else
{
// error stuff here is removed
}
return returnValue;
}
The service that retrieves this certificate and uses it looks like:
public static X509Certificate2 GetCertificate(int certificateID, string password, string customer)
{
X509Certificate2 x509Certificate2 = null;
try
{
SoapClient client = new SoapClient();
Login login = CreateLogin();
ClientCertificateMessage clientCertificateMessage = null;
try
{
clientCertificateMessage = client.GetCertificate(login, customer);
}
catch (Exception ex)
{
// error logging removed
}
if ((clientCertificateMessage != null) && (clientCertificateMessage.Certificate != null))
{
using (CertificateData cd = new CertificateData())
{
dynamic revisedCertificate = new ExpandoObject();
revisedCertificate.Certificate = clientCertificateMessage.Certificate;
cd.Update(revisedCertificate, certificateID); // Save certificate data in database for later use
}
x509Certificate2 = new X509Certificate2(clientCertificateMessage.Certificate, password);
}
else
{
// handle logic got no certificate remove
}
}
catch (Exception ex)
{
// error logging removed
}
return x509Certificate2;
}
I cannot see anything that would explain why VMs on Azure continue to work, but all other VMs do not. We know those other servers can communicate and download the certificate, and when recreating the certificate from the byte array saved in the database the thumbprint and everything else matches.
I have seen other articles regarding Certificates on App Services where you need add Settings for WEBSITE_LOAD_CERTIFICATES or WEBSITE_LOAD_USER_PROFILE, however, we have not done this as the certificates do not fail to be generated.
Is there anything that I am missing where perhaps some odd Azure configuration or even some obvious technical reason for why a certificate downloaded fails, but the same certificate manually copied to the server works?
Thanks in advance for helping out.
Related
I've done work with WCF before - but since it was for in-house use only, not accessable from the internet at all, i just used net.tcp and not cared much about security.
However, i am now in pre-production for a project that will be made availlable over the internet to customers, so security must be planed for.
I've been doing some research on the matter, and from what i gathered (correct me if I am wrong here), HTTPS is my best bet, as HTTP isn't secured at all (by default) and net.tcp could find problems with some firewalls.
Howerer, I don't want to force customers to have to install IIS in their servers if they don't want to, so the plan is to use a self hosted Windows Service. However, i can't seem to find any information on how to setup the server to use HTTPS without na IIS.
I found information about using makecert and httpcfg set ssl to add a new certificate to the store and then set it to a port - that's ok for testing but i'm not seeing this feaseable in the customer's server - not to mention this means i'll be using aself signed certificate - again ok for testing, not so much in production
I also found information (ServiceCredentials MSDN page) about using something like
sh.Credentials.ServiceCertificate.SetCertificate(
StoreLocation.LocalMachine, StoreName.My,
X509FindType.FindByThumbprint,
"af1f50b20cd413ed9cd00c315bbb6dc1c08da5e6");
to set a certificate that is already in the server's certificate store - that would almost be ok - it still require the customer to know how to manage certificates in the store, not perfect but ok. However i couldn't get it to work - i don't get any error starting the servisse, but if i try to go to the service address in a browser i get na error regarding TLS beeing out of date - Q1: Any idea what could be the problem here?
Q2: Is it possible to have a configuration somewhere where the customer could input the has or at least location for the cert and key files one gets when buying a certificate and use that to secure the service?
Q1: As mentioned in the errors, there may be a problem in your certificate. be sure that the certificate is valid(self signed certificate can not expire).
Q2: As far as I know, we could save the certificate as a file(pfx, cert) or install the certificate in the certificate store(certlm.msc, certmgr.msc) in order to manage.
Do you want to host the WCF service over https in windows service project? I have made a demo, wish it is useful to you.
Service1.cs
public partial class Service1 : ServiceBase
{
public Service1()
{
InitializeComponent();
}
Uri uri = new Uri("https://localhost:1017");
ServiceHost sh = null;
protected override void OnStart(string[] args)
{
BasicHttpBinding binding = new BasicHttpBinding();
binding.Security.Mode = BasicHttpSecurityMode.Transport;
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;
try
{
ServiceHost sh = new ServiceHost(typeof(MyService), uri);
sh.AddServiceEndpoint(typeof(IService), binding, "");
ServiceMetadataBehavior smb;
smb = sh.Description.Behaviors.Find<ServiceMetadataBehavior>();
if (smb == null)
{
smb = new ServiceMetadataBehavior()
{
HttpsGetEnabled=true,
};
sh.Description.Behaviors.Add(smb);
}
Binding mexbinding = MetadataExchangeBindings.CreateMexHttpsBinding();
sh.AddServiceEndpoint(typeof(IMetadataExchange), mexbinding, "mex");
sh.Open();
WriteLog($"Service is ready at {DateTime.Now.ToString("hh-mm-ss")}");
}
catch (Exception e)
{
WriteLog(e.ToString());
throw;
}
}
protected override void OnStop()
{
if (sh!=null&&sh.State==CommunicationState.Opened)
{
sh.Close();
WriteLog($"Service is closed at {DateTime.Now.ToString("hh-mm-ss")}");
}
}
public static void WriteLog(string text)
{
using (StreamWriter sw = File.AppendText(#"C:\Mylog.txt"))
{
sw.WriteLine(text);
sw.Flush();
}
}
}
[ServiceContract(Namespace = "mydomain")]
public interface IService
{
[OperationContract]
string SayHello();
}
public class MyService : IService
{
public string SayHello()
{
Service1.WriteLog(string.Format("Wow, I have been called at {0}", DateTime.Now.ToString("hh-mm-ss")));
return "Hello stranger";
}
}
ProjectInstaller.cs
Install the windows service(administrator privilege CMD)
Bind the certificate to the application port.
https://learn.microsoft.com/en-us/windows/desktop/http/add-sslcert
https://learn.microsoft.com/en-us/dotnet/framework/wcf/feature-details/how-to-configure-a-port-with-an-ssl-certificate
Certhash parameter specifies the thumbprint of the certificate. The appid parameter is a GUID that can be used to identify the owning application(open the project.csproj file)
<ProjectGuid>{56FDE5B9-3821-49DB-82D3-9DCE376D950A}</ProjectGuid>
Start the windows service.
Test(Server Ip is 10.157.13.70):
Client invocation(there is a step that validates the server certificate by default)
static void Main(string[] args)
{
ServicePointManager.ServerCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true;
ServiceReference1.ServiceClient client = new ServiceReference1.ServiceClient();
try
{
var result = client.SayHello();
Console.WriteLine(result);
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
Result
Feel free to let me know if there is anything I can help with.
I'm developing a authentication system with a user certificates based on C# and MVC and I’m having some problems.
I use a SSL security with a CA certificate and I want to get back some certificates on a client side. When I run my application on the local side, I get the personal certificate, and my authentication system works correctly.
However, when I run the same application on the server side, I don't get back any personal certificate if it's installed in the navigator...
Here, you have the C# code that im developing.
private X509Certificate2 GetClientCertificate()
{
X509Store userCaStore = new X509Store(StoreName.My,StoreLocation.CurrentUser);
try
{
userCaStore.Open(OpenFlags.OpenExistingOnly);
X509Certificate2Collection certificatesInStore = userCaStore.Certificates
.Find(X509FindType.FindByTimeValid, DateTime.Now, true);
X509Certificate2 clientCertificate = null;
if (certificatesInStore.Count > 0)
{
clientCertificate = certificatesInStore[0];
}
else
{
return null;
}
return clientCertificate;
}
catch
{
throw;
}
finally
{
userCaStore.Close();
}
}
Currently I am working in POC with CRUD operations using AmazonS3 Sdk for .net 3.5 version 3. I am trying to retrieve the Region Endpoint(Location) of the specific bucket name using secret key and Access Key and bucket name( has Location: EU (Frankfurt) (eu-central-1)). in order to establish connection
with AmazonS3 and perform CRUD operations
So I get the A WebException with status TrustFailure was thrown when I tried to get the Region Endpoint from share point(web page I create my own page using the master page of SharePoint) in order to create AmazonS3Client instance with Region Retrieve.
with the following code:
private string defaultAmazonHttpsHost = "https://s3.amazonaws.com";
private string defaultAmazonHttpHost = "http://s3.amazonaws.com";
private Amazon.RegionEndpoint GetRegionEndpoint(string bucket, BasicAWSCredentials amazonCredentials, bool useSSL)
{
Amazon.RegionEndpoint regiongEndpoint = null;
AmazonS3Config configurationClient = new AmazonS3Config();
configurationClient.UseHttp = !useSSL;
configurationClient.ServiceURL = useSSL ? defaultAmazonHttpsHost : defaultAmazonHttpHost;
try
{
using (AmazonS3Client clientConnection = new AmazonS3Client(amazonCredentials, configurationClient))
{
GetBucketLocationRequest locationRequest = new GetBucketLocationRequest();
locationRequest.BucketName = bucket;
string locationName = clientConnection.GetBucketLocation(locationRequest).Location.Value;
if (locationName.Equals("EU", StringComparison.InvariantCultureIgnoreCase))
{
regiongEndpoint = Amazon.RegionEndpoint.EUWest1;
}
else if (string.IsNullOrEmpty(locationName))
{
regiongEndpoint = Amazon.RegionEndpoint.USEast1;
}
else
{
regiongEndpoint = Amazon.RegionEndpoint.GetBySystemName(locationName);
}
}
}
catch (AmazonS3Exception amazonS3Exception)
{
throw amazonS3Exception;
}
catch (Exception unExpectedException)
{
throw unExpectedException;
}
return regiongEndpoint;
}
BasicAWSCredentials credentials = new BasicAWSCredentials("my access Key", "my secret key");
AmazonS3Config configurationAmazon = new AmazonS3Config();
configurationAmazon.RegionEndpoint = GetRegionEndpoint("bucketName", credentials, false);
AmazonS3Client _s3 = new AmazonS3Client(credentials, configurationAmazon );
My task Perform CRUD operations + test connection with AmazonS3 Sdk .net 3.5 version 3 , with the source information :
-secret key
- access key
- bucket Name
the strange is if this part code run(execute) since another Project (without share point interaction for example: Console Project) I do not get this exception) Do you know what is the problem?
I used the following before execute any request to amazonS3 and now it works as expected I think the problem was with the certificates that sharepoint is using .
ServicePointManager.ServerCertificateValidationCallback +=
delegate(
object sender,
X509Certificate certificate,
X509Chain chain,
SslPolicyErrors sslPolicyErrors)
{
return true;
};
the post provide a explanation about it
The key point here is "TrustFailure". There's something wrong with the certificate. In my case, this error was caused because my company uses Websense, a web filter/security suite that intercepts and reissues https certificates for web traffic so it can spy on you. Even if you don't use anything like that, the bottom line is that your computer doesn't trust the issuer of the certificate being used by the remote computer. The server on which I was receiving this error did not have the correct certificate in its Trusted Root Certification Authorities. After importing the correct trusted root cert (Add trusted root certificate authority to local computer), I no longer received the error.
If you don't think this is the case, you can get more details on what the exception is by either writing the details to console or putting a breakpoint on the Console.WriteLine here and actually inspect the certificate and ssl errors:
using System.Security.Cryptography.X509Certificates;
using System.Net.Security;
.....
.....
//before you make the request
System.Net.ServicePointManager.ServerCertificateValidationCallback +=
delegate (
object sender,
X509Certificate certificate,
X509Chain chain,
SslPolicyErrors sslPolicyErrors)
{
Console.WriteLine("Subject: " + certificate.Subject + ", Issuer: " + certificate.Issuer + ". SSL Errors: " + sslPolicyErrors.ToString());
return false;
};
The key point here is that you need to find the certificate issue and resolve it instead of leaving yourself vulnerable by ignoring all ssl errors.
TrustFailure can also be caused by the date being incorrect on the machine.
EDIT:
What should I use to catch the error?
I have this where it fails to connect for an error message and defaults to a generic message.
public string ResponseError {
get {
string retVal = "";
try {
retVal = xmlElements.GetElementValue( FullResponse, "/VancoWS/Response/Errors/Error/ErrorDescription" );
} catch {
retVal = "There has been a problem processing your request. Please try again!";
}
return retVal;
}
}
A payment gateway said they made a change to their SSL Cert and since then our web application has not made a successful connection. The code was written in VS.NET 2008 .NET 3.5 I believe in C#. From what I can tell this looks like the code making the connection:
/* Method to perform web post */
private void SendBuffer(string strXml, out string fullResponse, out bool success)
{
String BaseAddress = Url + "?xml=";
try
{
System.Net.WebClient objRequest = new System.Net.WebClient();
objRequest.Encoding = System.Text.Encoding.ASCII;
byte[] buffer = System.Text.Encoding.ASCII.GetBytes(strXml);
byte[] responseBuffer = objRequest.UploadData(BaseAddress, "POST", buffer);
fullResponse = System.Text.Encoding.ASCII.GetString(responseBuffer);
success = true;
}
catch (Exception ex)
{
fullResponse = ex.ToString();
success = false;
}
}
The payment gateway says they are not getting any connections from our server to theirs. I contacted out webhost and they said they have not changed anything to block connections.
The host says they could access the URL fine with no invalid certificate.
URL in question: https://www.vancoservices.com/cgi-bin/ws2.vps
They also are still running SSLv3 so it's not a problem with protocol change or being forced to TLS 1.x
Any ideas as to what would break this?
Looks like they have set up a certificate which is either not signed by a valid authority or your web server doesn't have the authority that signed this certificate in its trusted CA.
To understand whether it is a problem with the certificate you may try connecting to the webserver that is executing this code, opening a webbrowser and attempting to connect to the address. The browser will tell you whether the certificate is valid or not (probably you will get a warning if it is not).
If you want to disable certificate validations in your code you may try the following:
System.Net.ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
Obviously this is not something you wanna be doing with a Production system, but you could just experiment with it to know whether it is a problem with the SSL certificate they set up and if it is self signed.
The "try" clause around the code is hiding any errors that are happening. One thing you could try is commenting out the try statement allowing the code to break and fall over with an error:
// try
{
...
}
or add a catch clause:
try
{
...
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
just add the following to your config and change the log file path. this will give a sense if handshake/validation for cert is happening
http://msdn.microsoft.com/en-us/library/ty48b824(v=vs.110).aspx
In my WCF self-hosting WebService using mutual certificate to validate the client, i set the CertificateValidationMode = PeerTrust but its seems ignored, since i can still execute the methods with some client wich i have deleted the corresponding certificate of the TrustedPeople server store.
Heres the host example:
static void Main()
{
var httpsUri = new Uri("https://192.168.0.57:xxx/HelloServer");
var binding = new WSHttpBinding
{
Security =
{
Mode = SecurityMode.Transport,
Transport = {ClientCredentialType = HttpClientCredentialType.Certificate}
};
var host = new ServiceHost(typeof(HelloWorld), httpsUri);
//This line is not working
host.Credentials.ClientCertificate.Authentication.CertificateValidationMode =X509CertificateValidationMode.PeerTrust;
host.AddServiceEndpoint(typeof(IHelloWorld), binding, string.Empty, httpsUri);
host.Credentials.ServiceCertificate.SetCertificate(
StoreLocation.LocalMachine,
StoreName.My,
X509FindType.FindBySubjectName,
"server.com");
// Open the service.
host.Open();
Console.WriteLine("Listening on {0}...", httpsUri);
Console.ReadLine();
// Close the service.
host.Close();
}
The client app:
static void Main(string[] args)
{
try
{
var c = new HelloWorld.HelloWorldClient();
ServicePointManager.ServerCertificateValidationCallback = (sender, cert, chain, error) => true;
c.ClientCredentials.ClientCertificate.SetCertificate(
StoreLocation.LocalMachine,
StoreName.My,
X509FindType.FindBySubjectName,
"client.com");
Console.WriteLine(c.GetIp());
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
Console.ReadKey();
}
I generate the server.com and the client.com with a RootCA certificate. This RootCA certificate is instaled on the trusted root store of the client and server.
The question is, i should not execute the GetIp() method if my client.com certificate is not in the TrustedPeople store of the server, right? But im executing it without any problems.
The question is, how to, in this scenario, validate the client certificate put its public key on TrustedPeople of server?
ps: In this MSDN article of Transport security with client certificate, theres a quote saying The server’s certificate must be trusted by the client and the client’s certificate must be trusted by the server. But i can execute the webmethods from client even if the client certificate isnt in the server TrustedPeople store.
My suggestion would be to use custom validation. This way you can set some breakpoints and watch the validation take place as well as see what other validation options you could come up with based on the data available throughout the validation process.
First make sure you have your binding requiring Certificate for Message Client Credentials. If you only use Certificate for Transport, the Client in my tests did not validate. This alone may fix your issue.
binding.Security.Mode = SecurityMode.TransportWithMessageCredential;
binding.Security.Message.ClientCredentialType =
MessageCredentialType.Certificate;
To setup a custom validator follow the rest.
Replace:
host.Credentials.ClientCertificate.Authentication.CertificateValidationMode
=X509CertificateValidationMode.PeerTrust;
With:
host.Credentials.ClientCertificate.Authentication.CertificateValidationMode
=X509CertificateValidationMode.Custom;
host.Credentials.ClientCertificate.Authentication.CustomCertificateValidator =
new IssuerNameCertValidator("CN=client.com");
Then add this to create the custom validator and tweak as needed (this one validates based on Issuer):
public class IssuerNameCertValidator : X509CertificateValidator
{
string allowedIssuerName;
public IssuerNameCertValidator(string allowedIssuerName)
{
if (allowedIssuerName == null)
{
throw new ArgumentNullException("allowedIssuerName");
}
this.allowedIssuerName = allowedIssuerName;
}
public override void Validate(X509Certificate2 certificate)
{
// Check that there is a certificate.
if (certificate == null)
{
throw new ArgumentNullException("certificate");
}
// Check that the certificate issuer matches the configured issuer.
if (allowedIssuerName != certificate.IssuerName.Name)
{
throw new SecurityTokenValidationException
("Certificate was not issued by a trusted issuer");
}
}
}