I'm investigating the idea of using Blazor WASM to build a retail application that would run on an office Intranet. The application would be installed on a given machine, to be accessed via browser from any of several machines on the LAN.
The biggest stumbling block I'm running into is the question of how to go about securing the channel.
The app itself would run as a Windows Service, listening on port 443 on one of the workstations, e.g. https://reception/. But how do we tell Blazor to use a self-signed TLS cert for that hostname?
If there's a better way to go about this, I'm all ears. I can't use Let's Encrypt certs, because neither the application nor its hostname will be exposed to the public Internet.
There is a glut of information on working with Blazor to build such an app, but most if not all demos run on localhost. That works fine for dev, but not for production (in a self-hosting scenario, anyway). There doesn't seem to be much discussion at all of this aspect of things.
How can we use a custom certificate for browser requests from the client to a Blazor WASM app?
Any ideas?
I was able to get this working using some slightly modified sample code from the official documentation:
builder.WebHost.ConfigureKestrel(serverOptions =>
{
serverOptions.ListenAnyIP(443, listenOptions =>
{
listenOptions.UseHttps(httpsOptions =>
{
var testCert = CertificateLoader.LoadFromStoreCert(
"test", "My", StoreLocation.CurrentUser,
allowInvalid: true);
var certs = new Dictionary<string, X509Certificate2>(
StringComparer.OrdinalIgnoreCase)
{
["test"] = testCert
};
httpsOptions.ServerCertificateSelector = (connectionContext, name) =>
{
if (name is not null && certs.TryGetValue(name, out var cert))
{
return cert;
}
return testCert;
};
});
});
});
The easiest way to handle SSL is to use IIS that will act as a proxy for your Blazor app.
IIS will give you easy access to well documented SSL settings.
https://learn.microsoft.com/en-us/aspnet/core/blazor/host-and-deploy/webassembly?view=aspnetcore-6.0#standalone-deployment
Related
This may well be a duplicate question, but no answer from an existing question has solved my problem.
I have a WebAPI end point running on my dev machine. I've configured it to run on
.UseUrls("http://localhost:57971", "http://10.0.2.2:57971", "http://192.168.44.1:57971", "http://192.168.1.48:57971", "http://*:57971")
where:
192.168.44.1 is Desktop Adapter #2 on the emulator Networks settings tab
10.0.2.2 is the special address for the Android emulator, as set out in Google's doco (possibly not relevant to Xamarin) and
192.168.1.48 is my local IP address for my dev machine.
I have created a firewall rule permitting connections on TCP port 57971.
I researched this pretty heavily and heeded instructions such as those set out here http://briannoyesblog.azurewebsites.net/2016/03/06/calling-localhost-web-apis-from-visual-studio-android-emulator/
I'm kinda out of ideas. The annoying thing is, it fails silently. There is no exception and the output just basically shows the different threads exiting with code 0. And the application keeps running i.e. the debugging session is not returning the IDE to a "code entry" state. This may suggest that something else its at play here.
The code looks pretty innocuous to me:
protected async Task<T> GetAsync<T>(string url)
where T : new()
{
HttpClient httpClient = CreateHttpClient();
T result;
try
{
var response = await httpClient.GetStringAsync(url);
result = await Task.Run(() => JsonConvert.DeserializeObject<T>(response));
}
catch
{
result = new T();
}
return result;
}
I'm using Visual Studio 2015.
I'm using the Visual Studio emulator https://www.visualstudio.com/vs/msft-android-emulator/
Any idea how I can get wheels on the ground on this thing?
Is there a way to Ping my machine from the emulator?
Thanks
I got this working by using 169.254.80.80 i.e. I added it to the list of urls which the API serves and called that ip address from the Xamarin app.
So, in Program.cs became simple:
.UseUrls("http://localhost:57971", "http://169.254.80.80:57971")
I also had to add it to the bindings element ApplicationConfig file in the hidden .vs folder of the ASP.NET API solution. Not sure why it had to be 169.254.80.80, as that was Desktop Adapter #4.
That got it working.
I've read through this question and answer: "
Is it Possible to Dynamically Return an SSL Certificate in NodeJS?"... but it uses .key and .crt files for the domains and the server.
On a Windows 2008 R2 machine, I can't find the domain1.key, server.key and server.crt files. Instead I've created a domain1.pfx file by exporting the SSL certficate from IIS.
I am able to successfully run an https node.js server using this one PFX file with one domain like this:
var fs = require('fs');
var https = require('https');
var crypto = require('crypto');
function getSecureContext(domain) {
return crypto.createCredentials({
pfx: fs.readFileSync('/path/to/' + domain + '.pfx'),
passphrase: 'passphrase'
}).context
}
var secureContext = {
'domain1': getSecureContext('domain1')
}
var options = {
SNICallback: function (domain) {
return (secureContext.hasOwnProperty(domain) ? secureContext[domain] : {});
},
pfx: fs.readFileSync('/path/to/domain1.pfx'); // for the server certificate
};
var server = https.createServer(
options,
requestListener).listen(443);
However what if I have a multiple domain certificate plus another certificate for a single domain, how would the SNICallback and the getSecureContext functions be configured to have each domain name use the correct certificate?
I think the server certificate should be the same for both PFX files since they are on the same server so I'm using only the first PFX file (for domain1) as the server certificate.
I've tried changing the secureContext object like this:
var secureContext = {
'domain1': getSecureContext('domain1'),
'domain2': getSecureContext('domain2'),
.
.
}
This gives me the error "listen EACCES'.
In my specific situation I have two SSL certificates. One is an extended validation certificate for one domain name, and the second is a multiple domain certificate supporting five domain names.
I've found it very difficult to debug the EACCES error. There doesn't seem to be more detail as to what is causing the EACCES. Is my configuration wrong, is there a problem with the certificates? I do know that these certificates work correctly when I use them in IIS running an IIS server (instead of a node.js server) on the same Windows 2008 R2 server.
I would like to stay with a pure windows and node.js configuration. (Not nginx, iisnode or any other libraries if possible).
Solved it. The EACCES error was due to my not listing all the sites that need to use the two certificates. Since I was testing, I only was working with two site names, but the multi-domain certificate includes some other sites. Each site needs to be listed as below. Otherwise one or more of the sites will not have a certificate associated with it causing the EACCES error.
var secureContext = {
'domain1': getSecureContext('domain1'),
'domain2': getSecureContext('domain2'),
'domain3': getSecureContext('domain2'),
'domain4': getSecureCOntext('domain2')
}
We want to implement a MITM proxy.
It should receive https requests from client, decrypt them and
return pre-recorded responses.
It means that the proxy is not connected to remote server directly.
I know that FiddlerCore supports MITM, but how can I possibly use it in my scenario?
Thanks
https://groups.google.com/forum/#!topic/httpfiddler/E0JZrRRGhVg
This is a pretty straightforward task. If you look at the demo project included in FiddlerCore, you can get most of the way there.
Fiddler.FiddlerApplication.BeforeRequest += delegate(Fiddler.Session oS)
{
if (oSession.HTTPMethodIs("CONNECT")) { oSession.oFlags["X-ReplyWithTunnel"] = "Fake for HTTPS Tunnel"; return; }
if (oS.uriContains("replaceme.txt"))
{
oS.utilCreateResponseAndBypassServer();
oS.responseBodyBytes = SessionIWantToReturn.responseBodyBytes;
oS.oResponse.headers = (HTTPResponseHeaders) SessionIWantToReturn.oResponse.headers.Clone();
}
};
I am writing a bdd test for a component that will startup phantomjs and hit a specific route on my site and do processing on that. Because the component is fundamentally about automating a phantom instance there is no way to easily stub out the http requests.
So I want to stub out a self-hosted endpoint that will stub out the data I'm after. Because this is a unit test I think its really important for it to run in isolation so I do something like this:
async Task can_render_html_for_slide_async() {
var config = new HttpSelfHostConfiguration("http://localhost:54331");
config.Routes.MapHttpRoute("Controller", "{controller}", new {});
using (var server = new HttpSelfHostServer(config)) {
server.OpenAsync().Wait();
var client = new HttpClient();
var resp = await client.GetStringAsync(config.BaseAddress+"/Stub");
Console.WriteLine(resp);
}
}
public class StubController : ApiController
{
public string Get() {
return "Booyah";
}
}
Which gets me
AddressAccessDeniedException : HTTP could not register URL http://+:54331/
I understand that netsh or Admin mode is required for this but I don't understand why. Nodejs for example runs perfectly fine on windows but has no such requirement.
Also using OWIN directly needs no netsh-ing. So....what's going on?
I wrote an article about it on codeproject, it was done to make it possible for multiple application to share the same port.
You can have both, IIS and Apache (or OWIN in your case) listenening port 80. The routing to the right application is done thanks to the path of the url.
IIS and Apache both would use this driver (http.sys). But you need permission to "reserve" a path.
Administrators are always authorized. For other users, use netsh or my GUI tool HttpSysManager to set ACL.
Any method that requires giving permission via netsh uses a Windows kernel driver to provide http access.
If a library opens a socket itself and handles the http communication that way, no netsh use is needed.
So to answer your question, some methods are using the kernel driver and some are handling the protocol themselves.
Ajax HTTPS requests from my PhoneGap/Cordova app on Android inexplicably fail with status=0. It appears only when signing the app with the release key (i.e., exporting from ADT), but doesn't appear when signing with debug key (running directly in emulator or phone).
request = new XMLHttpRequest()
request.open "GET", "https://some.domain/", true
request.onreadystatechange = ->
console.log "** state = " + request.readyState
if request.readyState is 4
console.log "** status = " + request.status
request.send()
always outputs
** state = 4
** status = 0
It doesn't matter if i install the app from Play Store or with adb utility. I presume it could be connected with the certificate, since not all HTTPS domains fail this way.
I was having the same problem but my solution was a little different.
In only the Android App build of my Cordova app, AJAX calls to my server via HTTPS were being blocked. Not in iOS, not in desktop browsers. Most confusingly, in the actual Android Browser the HTTPS AJAX calls would work no problem.
I verified that I could make HTTPS AJAX calls to well known and trusted URLs such as https://google.com as well as regular HTTP calls to any URL I cared to try.
This led me to believe that my SSL cert was either NOT installed 100% correctly OR the cheap (~$10 usd) cert from PositveSSL was not universally trusted OR both.
My cert was installed on my AWS Load Balancer so I looked around about how I may have messed this up and also how PositiveSSL was not the best cert to be using in terms of trustworthiness. Lucky me found an article covering AWS ELB installation of certs AND they happened to be using a PositiveSSL cert! Contained within was this little gem:
"...Don’t be fooled by the AWS dialog, the certificate chain isn’t really optional when your ELB is talking directly to a browser..."
http://www.nczonline.net/blog/2012/08/15/setting-up-ssl-on-an-amazon-elastic-load-balancer/
Drumroll....
I reinstalled the cert with the "optional" Certificate Chain info and voilà!, the HTTPS AJAX calls to my server started working.
So it appears that the Android Webview is more conservative than the Android Browser in terms of cert trust. This is not totally intuitive since they are supposed to be basically the same tech.
It happens when the requested URL responds with an erroneous or self-signed certificate. While testing or distributing the app to friends, setting <application android:debuggable="true"...> in AndroidManifest.xml is enough — it automatically bypasses certificate errors.
But Google Play Store will not accept an APK with android:debuggable="true". First of all, the certificates, of course, need to be fixed. But while that happens, here is a workaround for PhoneGap/Cordova 3:
In your app package create a subclass for CordovaWebViewClient:
public class SSLAcceptingCordovaWebViewClient extends CordovaWebViewClient {
public SSLAcceptingCordovaWebViewClient(CordovaInterface cordova, CordovaWebView view) {
super(cordova, view);
}
#Override
public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
handler.proceed();
}
}
Same for IceCreamCordovaWebViewClient:
public class SSLAcceptingIceCreamCordovaWebViewClient extends IceCreamCordovaWebViewClient {
public SSLAcceptingIceCreamCordovaWebViewClient(CordovaInterface cordova, CordovaWebView view) {
super(cordova, view);
}
#Override
public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
handler.proceed();
}
}
in <Your App Name>.java add an override for makeWebViewClient:
#Override
protected CordovaWebViewClient makeWebViewClient(CordovaWebView webView) {
if(android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.HONEYCOMB) {
return new SSLAcceptingCordovaWebViewClient(this, webView);
} else {
return new SSLAcceptingIceCreamCordovaWebViewClient(this, webView);
}
}
Et voilà! SSL errors will be disregarded. However, never use erroneous certificates. Try to fix them first and use this dirty workaround only when you run out of other solutions.
The other option that works as well is to recompile the underlying cordova.jar file so that the test is removed completely thus no reason to worry about your cert being valid or not. I ran in the issue due to the fact that Android would not recognize the GoDaddy cert that was on the server. The cert shows valid on iOS but even when browsing from Android complained about the cert. This is from the 2.9.x branch as this is what I was working with.
cordova-android / framework / src / org / apache / cordova / CordovaWebViewClient.java
#TargetApi(8)
#Override
public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
final String packageName = this.cordova.getActivity().getPackageName();
final PackageManager pm = this.cordova.getActivity().getPackageManager();
ApplicationInfo appInfo;
try {
appInfo = pm.getApplicationInfo(packageName, PackageManager.GET_META_DATA);
handler.proceed();
return;
/* REMOVED TO BY PASS INVALID CERT CHAIN ****
if ((appInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
// debug = true
handler.proceed();
return;
} else {
// debug = false
super.onReceivedSslError(view, handler, error);
}*/
} catch (NameNotFoundException e) {
// When it doubt, lock it out!
super.onReceivedSslError(view, handler, error);
}
}
NOTE: I understand this is not safe but when all else fails this solved the issue that has been on going for over 2 months including reinstalling the cert following the cert chain install guide and beside it is a site that is our own not 3rd party so no matter if valid or not it is only connecting to this server.
In my case it has been a missing intermediate certificate, which I had to install on my webserver. You have to keep it in mind especially when you use cheap certificates.
You can check it easily online if your certificate chain is proper, you will find a lot on google, e.g. https://www.sslshopper.com/ssl-checker.html
At the Apache2 it's part of the VirtualHost 443 directive, you have three rules in your directive, it looks like that:
SSLCertificateFile /etc/apache2/ssl/mycert.crt
SSLCertificateKeyFile /etc/apache2/ssl/mykey.key
SSLCertificateChainFile /etc/apache2/ssl/certification_auth_intermediate.crt
You can't use relese-ready (phonegap) apks with self-signed certificates. Look at this answer to get further information.
lg
fastrde