I have a WCF Cloud Service running in azure which I connect to from a .NET client. This is all working nicely and I have implemented security by enabling SSL (using a self signed certificate) and using active directory authorisation.
However, I have a number of scheduled jobs using the azure scheduler and these jobs call methods in the Cloud service but I am unable to setup the scheduled jobs as HTTPS jobs. They work fine as HTTP jobs but as soon as I change it to HTTPS I get the following error:
“Http Action - Request to host '.cloudapp.net' failed: TrustFailure The underlying connection was closed: Could not establish trust relationship for the SSL/TLS secure channel.”
I tried just accepting all certificates by adding the following code to the WCF web role’s OnStart method:
ServicePointManager.ServerCertificateValidationCallback
= delegate(object s, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
return true;
};
But the callback never gets invoked
So I assume I have to somehow add client certificate authentication to the scheduler job? But I cannot work out how.
I am creating the job through the Microsoft.WindowsAzure.Management.Scheduler Api e.g:
var action = new JobAction();
action.Type = JobActionType.Https;
action.Request = new JobHttpRequest();
action.Request.Method = "POST";
action.Request.Uri = new Uri(serviceURI);
action.Request.Body = soap;
action.RetryPolicy = new RetryPolicy()
{
RetryType = RetryType.None,
RetryCount = null
};
action.Request.Headers = new Dictionary<string, string>()
{
{ "Content-Type", "text/xml" },
{ "SOAPAction", "\"http://tempuri.org/" + serviceInterfaceName + "/" + methodName + "\"" }
};
var result = schedulerClient.Jobs.CreateOrUpdate(jobName, new JobCreateOrUpdateParameters()
{
action = action,
StartTime = startTime,
Recurrence = new JobRecurrence()
{
Frequency = frequency,
Interval = interval
}
});
and I see that in the JobAction class there is a property of the Request object called Authentication, I thought perhaps I might need to use that but I can find no documentation on how to use it?
Alternatively, I could create the schedule job through powershell or the azure portal interface if anyone can tell me how to successfully create an HTTPS schedule job through either of those methods?
Many thanks,
kelly
Scheduler jobs fail because it can't trust this endpoint. You need to use a trusted certificate for HTTPS calls from Scheduler.
Related
I have following code which is calling an API using basic authentication and SSL client certificate but its throwing exception and giving me following error.
"The request was aborted: Could not create SSL/TLS secure channel."
I tried to find a solution on Google but failed to find any solution. Can anyone help me out on this. Thanks.
// Variables
string basicAuthenticationUserName = "username";
string basicAuthenticationPassword = "password";
string clientCertificateFilePath = "Path-To-Certificate-File";
string clientCertificatePassword = "certificate-password";
string url = "https://" + basicAuthenticationUserName + ":" + basicAuthenticationPassword + "#apiserverurl/apimethod";
// Creating RestSharp Request Object
var request = new RestRequest(Method.POST)
{
RequestFormat = DataFormat.Json,
OnBeforeDeserialization = resp =>
{
resp.ContentType = "application/json";
}
};
// Adding Headers
request.AddHeader("Content-Length", "0");
request.AddHeader("Accept", "application/x-null-message");
// Importing Certificates
var certificates = new X509Certificate();
certificates.Import(clientCertificateFilePath, clientCertificatePassword, X509KeyStorageFlags.PersistKeySet);
// Creating RestSharp Client Object
var client = new RestClient
{
BaseUrl = new Uri(url),
ClientCertificates = new X509CertificateCollection { certificates },
Authenticator = new HttpBasicAuthenticator(managingLou, basicAuthenticationPassword)
};
// Executing Request
var response = client.Execute<T>(request);
I have faced the similar issue. Let me mention the steps here for your help.
After the installation of windows service, I went through the following steps to fix the issue:
Go To Start > Run and type Services.msc
Select your service > Right click and choose Properties
Select the 2nd tab "Log On"
Select the radio button "This account"
Enter the username and password of currently log in user. (Make sure Its the same user who has installed the service)
Apply the changes
Start the service
First off, I'm new to Spring-Boot and SSL in general, but I've spent several days researching and am basically trying to get a simple Spring-Boot application configured with Client Authentication.
I've set up a connector like so:
private Connector createSslConnector() {
Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol");
Http11NioProtocol protocol = (Http11NioProtocol) connector.getProtocolHandler();
try {
File keystore = getKeyStoreFile();
File truststore = keystore;
connector.setScheme("https");
connector.setSecure(true);
connector.setPort(sslPort);
protocol.setSSLEnabled(true);
protocol.setKeystoreFile(keystore.getAbsolutePath());
protocol.setKeystorePass("changeit");
protocol.setTruststoreFile(truststore.getAbsolutePath());
protocol.setTruststorePass("changeit");
protocol.setKeyAlias("apitester");
protocol.setClientAuth("need");
return connector;
}
catch (IOException ex) {
throw new IllegalStateException("cant access keystore: [" + "keystore"
+ "] or truststore: [" + "keystore" + "]", ex);
}
}
And a controller that looks like so:
#RequestMapping("/test/{identifier}")
#ResponseBody
ResponseEntity<String> test(HttpServletRequest request, #PathVariable String identifier) {
return new ResponseEntity<String>("hello: " + identifier, HttpStatus.OK)
}
However, once I launch my application I can use a browser to navigate to localhost:sslport/hello/test/xxxx and get a response without any type of client certificate loaded. I was expecting to be prompted for a client certificate.
Spring boot uses tomcat (embedded) web container by default.
As it is called out tomcat doc, we have to set it to true to enforce the propagation of valid certificate chain from the client before accepting a connection. Setting want will allow the client to provide a certificate but not absolutely required.
I doubt if "need" makes any meaning for the container.
protocol.setClientAuth("need");
We have an issue where our web app calls to CRM via Microsoft.Xrm.Sdk OriganizationServiceProxy are failing to authenticate. The issue appears to be environment specific i.e. the calls work on our DEV web server but fail when the app is promoted to our System Test environment. The code that fails is as follows:
using (var serviceProxy = this.serviceFactory.Impersonate(userProvider.PrincipalUserName).ServiceProxy)
{
var countResult = serviceProxy.RetrieveMultiple(new FetchExpression(query));
int? count = 0;
var entity = countResult.Entities.FirstOrDefault();
if (entity != null)
{
count = (int?)((AliasedValue)entity["activity_count"]).Value;
}
return count.Value;
}
The error that appears in our logs is:
System.ServiceModel.Security.SecurityNegotiationException: The caller was not authenticated by the service. ---> System.ServiceModel.FaultException: The request for security token could not be satisfied because authentication failed.
at System.ServiceModel.Security.SecurityUtils.ThrowIfNegotiationFault(Message message, EndpointAddress target)
at System.ServiceModel.Security.SspiNegotiationTokenProvider.GetNextOutgoingMessageBody(Message incomingMessage, SspiNegotiationTokenProviderState sspiState)
--- End of inner exception stack trace ---
I have double checked the apppool identity of the IIS site and CRM settings. Is there anything obvious here that we may have missed?
I found the connection to CRM Online was taking the longest time so I create one instance to pass round of the OrganizationServiceProxy with explicit credentials that I can easily switch between environments.
IServiceManagement<IOrganizationService> management = ServiceConfigurationFactory.CreateManagement<IOrganizationService>(new Uri(CrmUrl));
ClientCredentials credentials = new ClientCredentials();
credentials.UserName.UserName = CrmUserName;
credentials.UserName.Password = CrmPassword;
AuthenticationCredentials authCredentials = management.Authenticate(new AuthenticationCredentials { ClientCredentials = credentials });
SecurityTokenResponse securityTokenResponse = authCredentials.SecurityTokenResponse;
OrganizationServiceProxy orgProxy = new OrganizationServiceProxy(management, securityTokenResponse);
orgProxy.EnableProxyTypes();
_xrmService = new XrmServiceContext(orgProxy)
I am attempting to host a service that serves up basic web content (HTML, javascript, json) using a WebHttpBinding with minimal administrator involvement.
Thus far I have been successful, the only admin priviledges necessary are at install time (register the http reservation for the service account and to create the service itself). However, now I am running into issues with SSL. Ideally I would like to support a certificate outside the windows certificate store. I found this article - http://www.codeproject.com/KB/WCF/wcfcertificates.aspx - which seems to indicate you can specify the certificate on the service host, however at runtime navigating a browser to https://localhost/Dev/MyService results in a 404.
[ServiceContract]
public interface IWhoAmIService
{
[OperationContract]
[WebInvoke(
Method = "GET",
UriTemplate = "/")]
Stream WhoAmI();
}
public class WhoAmIService : IWhoAmIService
{
public Stream WhoAmI()
{
string html = "<html><head><title>Hello, world!</title></head><body><p>Hello from {0}</p></body></html>";
html = string.Format(html, WindowsIdentity.GetCurrent().Name);
WebOperationContext.Current.OutgoingResponse.ContentType = "text/html";
return new MemoryStream(Encoding.UTF8.GetBytes(html));
}
}
static void Main(string[] args)
{
ServiceHost host = new ServiceHost(typeof(WhoAmIService), new Uri("https://localhost:443/Dev/WhoAmI"));
host.Credentials.ServiceCertificate.Certificate = new X509Certificate2(#"D:\dev\Server.pfx", "private");
WebHttpBehavior behvior = new WebHttpBehavior();
behvior.DefaultBodyStyle = WebMessageBodyStyle.Bare;
behvior.DefaultOutgoingResponseFormat = WebMessageFormat.Json;
behvior.AutomaticFormatSelectionEnabled = false;
WebHttpBinding secureBinding = new WebHttpBinding();
secureBinding.Security.Mode = WebHttpSecurityMode.Transport;
secureBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;
ServiceEndpoint secureEndpoint = host.AddServiceEndpoint(typeof(IWhoAmIService), secureBinding, "");
secureEndpoint.Behaviors.Add(behvior);
host.Open();
Console.WriteLine("Press enter to exit...");
Console.ReadLine();
host.Close();
}
If I change my binding security to none and the base uri to start with http, it serves up okay. This post seems to indicate that an additional command needs to be executed to register a certificate with a port with netsh (http://social.msdn.microsoft.com/Forums/en-US/wcf/thread/6907d765-7d4c-48e8-9e29-3ac5b4b9c405/). When I try this, it fails with some obscure error (1312).
C:\Windows\system32>netsh http add sslcert ipport=0.0.0.0:443 certhash=0b740a29f
29f2cc795bf4f8730b83f303f26a6d5 appid={00112233-4455-6677-8899-AABBCCDDEEFF}
SSL Certificate add failed, Error: 1312
A specified logon session does not exist. It may already have been terminated.
How can I host this service using HTTPS without the Windows Certificate Store?
It is not possible. HTTPS is provided on OS level (http.sys kernel driver) - it is the same as providing HTTP reservation and OS level demands certificate in certificate store. You must use netsh to assign the certificate to selected port and allow accessing the private key.
The article uses certificates from files because it doesn't use HTTPS. It uses message security and message security is not possible (unless you develop your own non-interoperable) with REST services and webHttpBinding.
The only way to make this work with HTTPS is not using built-in HTTP processing dependent on http.sys = you will either have to implement whole HTTP yourselves and prepare new HTTP channel for WCF or you will have to find such implementation.
I have a specific problem that I can't solve. Let me explain in detail. I'm new to this technology so I might be using some wrong terms. Please correct and explain or ask for explanation if you don't understand.
I am creating a self hosted WCF REST server, hosted in WPF application. It uses https, SLL with WebHttpSecurityMode.Transport. I am using my own generated certificate.
I would like to create a WinForms client that would use this service. The format of the response form the server is JSON.
I would like to validate the certificate on the client with my custom validator inherited from X509CertificateValidator.
This is my server side code. I'm using a custom username validator that works fine. I have configured the certificate in the IIS Manager on my machine for the Default Website > Bindings, where I have generated the certificate (Windows 7).
WebServiceHost sh = new WebServiceHost(typeof(ReachService));
string uri = "https://localhost:9000/Service";
WebHttpBinding wb = new WebHttpBinding();
wb.Security.Mode = WebHttpSecurityMode.Transport;
wb.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic;
sh.AddServiceEndpoint(typeof(IReachService), wb, uri);
sh.Credentials.UserNameAuthentication.CustomUserNamePasswordValidator = new CustomUserNameValidator();
sh.Credentials.UserNameAuthentication.UserNamePasswordValidationMode = UserNamePasswordValidationMode.Custom;
sh.Open();
and this is my client code
Uri uri = new Uri("https://localhost:9000/Service");
WebChannelFactory<ReachService> cf = new WebChannelFactory<IReachService>(uri);
WebHttpBinding wb = cf.Endpoint.Binding as WebHttpBinding;
wb.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic;
wb.Security.Mode = WebHttpSecurityMode.Transport;
cf.Credentials.ServiceCertificate.Authentication.CertificateValidationMode = X509CertificateValidationMode.Custom;
cf.Credentials.ServiceCertificate.Authentication.CustomCertificateValidator = new CustomCertificateValidator("PL2"); // this is the name that issued the certificate
cf.Credentials.UserName.UserName = "user1";
cf.Credentials.UserName.Password = "user1";
IReachService service = cf.CreateChannel();
try
{
CustomersList auth = service.GetCustomers();
}
catch (Exception ex)
{
throw new Exception(ex.Message);
}
on calling service.GetCustomers() I get:
Could not establish trust relationship for the SSL/TLS secure channel with authority
'localhost:9000'.
InnerException Message:
The underlying connection was closed: Could not establish trust relationship for the SSL/TLS secure channel.
InnerException Message:
The remote certificate is invalid according to the validation procedure.
The server is working fine when I test in the browser.
But the client code is wrong cause it doesn't go to the custom cert validator class. And this class is the same as in the MSDN example on http://msdn.microsoft.com/en-us/library/system.identitymodel.selectors.x509certificatevalidator.aspx.
Can anyone please tell me where am I going wrong with this approach?
If you need more info please ask.
Thank you
It looks like the issue occurs because certificate was issued for some other hostname. You can check this (and customize if necessary) by providing custom ServicePointManager.ServerCertificateValidationCallback.
//don't use HttpWebRequest --you lose all of the strongly-typed method and data contracts!
//the code to create the channel and call a method:
SetCertPolicy();
var cf1 = new WebChannelFactory<TService>(new Uri(remoteServiceAddressSecure));
var service = cf1.CreateChannel();
sevice.DoMethod();
protected static void SetCertPolicy()
{
ServicePointManager.ServerCertificateValidationCallback += RemoteCertValidate;
}
private static bool RemoteCertValidate(object sender, X509Certificate cert, X509Chain chain,
SslPolicyErrors error)
{
// trust any cert!!!
return true;
}
If you want to use WCF on the client, then don't use WebHttpBinding, stick with the SOAP stuff it will work much better.
However, if you want to use a standard HTTP client like, WebClient or HttpWebRequest or HttpClient V.prototype or HttpClient V.Next then stick with the webHttpBinding.
Sorry for not addressing your direct question but you are likely to run into more problems because you are using a binding that was intended to make WCF services accessible to non-WCF platforms but then using WCF to try and access it.