Custom Development

Privileged Client .NET Code in a Browser - Part 1

Bob Snyder

Calling Privileged Client .NET Code from a Web Application - The Why and What

The Problem

Code that runs in a web browser hosted application is intentionally isolated from the host system (sandboxed). This is a good thing, for very good security reasons. In spite of this, there are times we wish to escape the bonds of these restrictions. While breaking through these restrictions would typically be irresponsible in general public-facing websites or web applications, there are settings where it is desirable.

Web applications are becoming prevalent in line of business applications, and web-based internal portal applications have been proliferating for a long time. This is due to several factors:

  • minimal installation footprint and even more importantly, minimal deployment management overhead
  • single point of upgrade
  • single point of integration to other systems
  • scalability of server-side business logic
  • hosted, or even cloud deployments of applications that used to have thick client implementations or UI components
 

There is often an issue with perceived loss of functionality in these applications when implemented in a browser, and I don’t just mean the ever narrowing gap in UI richness. The most common issue is the loss of direct interaction with client peripheral devices, most commonly printers and scanners. There may also be a need to support integrations with other client-hosted software, or software with a client-based API.

A Solution

In these cases it is nice to know that there is a supported, standard way to access privileged code on the client in a browser application. That said, discovering the correct project configurations for the entire stack was difficult and time-consuming. I hope that this series will smooth that process out for any of you needing to accomplish this.

To keep the application side of things simple, I will discuss this solution from the point of view of managing printing and printers on a Windows client. By the way, I am not claiming that this is the only solution – if you have other methods that work well for you, let me know.

The simplest stack for doing this, and the universal starting point, is a Java Applet. Java plugins are available in all modern browsers, and the behavior is consistent enough that it can be relied on. If the JVM offers the access to the underlying system that you need, you may not need to go any farther.

However, it may very well be that you do need to take it further, to invoke existing software, or to develop new software using technologies familiar to your development team. Given the predominance of Windows clients in the business world, Windows and .NET make a logical choice. Extending the stack to allow the Applet to invoke .NET DLLs is actually fairly simple, with the only performance overhead incurred being the marshaling of any data passed or returned. The caveat, and it is a significant one (read “hair-pulling”), is getting the various project configurations set correctly. Once set up correctly however, this approach is a robust one.

 

General Structure of the Solution

Here I am describing the technique and settings to use with Visual Studio 2010 or later, and .NET 3 or later. Earlier .NET versions had a different mixed mode implementation requiring a different set of hoops to jump through.

The stack required to invoke a .NET assembly has more components than you might expect, and consists of:

  • The web page (html, with the Applet in an <object> tag, and javascript to invoke the Applet methods)
  • The signed Java Applet, which can both contain directly implemented functionality, and act as a pass-through.
  • A signed mixed mode (C++/C#) JNI DLL to act as a pass-through from the Applet to a .netmodule. Such a mixed mode JNI DLL cannot invoke a .NET assembly directly.
  • A signed .netmodule generated from a mixed mode (C++/C#) DLL to act as a pass-through from the JNI DLL to the .NET assembly. The .netmodule cannot also be a JNI DLL.
  • A signed .NET assembly containing the desired functional code.

Each layer must of course reference the binaries for the layer below.

 

Security – Allowing the Execution of Privileged Code

In order to execute privileged code in and from an Applet, the first requirement is that the jar file must be signed (http://docs.oracle.com/javase/7/docs/technotes/tools/windows/jarsigner.html), and permissions must be requested using the API for Privileged Blocks (http://docs.oracle.com/javase/7/docs/technotes/guides/security/doprivileged.html). When privileged code is needed, the standard recommendation is that the Applet be marked as Trusted-Only, but in that configuration javascript is blocked from interacting with the Applet. Therefore, in this case, the Applet must be marked as Trusted-Library (http://docs.oracle.com/javase/7/docs/technotes/guides/jweb/mixed_code.html). If the Applet has mixed code – some code restricted to the security sandbox, and some that is privileged – the potential exists for an attack accessing privileged code via the sandboxed code. Therefore, with a self-certification, you will get a warning dialog when the Applet loads. Fortunately for development headaches, there is a checkbox to tell the JVM to stop nagging you about that particular module. The dialog will not be shown if you sign with a valid certificate from a trusted Certificate Authority.

Using the Trusted-Library setting to invoke privileged operations via .NET assemblies, all of the components invoked by the Applet must also be signed. Since existing DLLs can be signed, you can use existing DLLs even if you don’t want to modify them. However, to quote MSDN (http://msdn.microsoft.com/en-us/library/xc31ft41.aspx): A strong-named assembly can only use types from other strong-named assemblies. Otherwise, the security of the strong-named assembly would be compromised. This could get complicated if you have a deep stack of existing un-signed DLLs you would like to invoke.

To .EXE or not to .EXE

I would like to recommend against the option (short-cut) of invoking an executable program (.EXE) in this setting instead of interfacing with DLLs. Launching an application can easily be done, and it can be done directly from the Applet, so it is very simple to implement. However the web application loses control of the flow at that point. Your web application doesn’t know whether the program is still running or not, and it is vulnerable to executing the wrong program. In addition, while there are several techniques for creating Windows applications that only allows one instance to run, depending on the application’s implementation technology, there is risk that there are conditions where you could still get more than one instance running, or fail to pop the existing instance to the front of the UI. Still, to whatever extent invoking an executable resembles “I shot an arrow into the air – where it lands…”, it might be what you need to do.

 

Deployment

To deploy the solution, the Applet’s jar file is deployed with the web application, referenced appropriately by one or more web pages. A client installer must be created to deploy the other components to the client. The JNI DLL and the .netmodule must be installed somewhere on the client path. The .NET assembly must be installed into the Global Assembly Cache (GAC). The installer can be automatically downloaded and run from the web application. The Applet or any of the other layers can include code to check for whether the correct versions of the client software are installed.

Versioning and Signing (Strong Names)

The strong name of a signed / strongly named assembly includes the version number. Therefore, any reference to a signed assembly is functionally a version-specific reference regardless of the value of the Specific Version setting of the reference in the consuming project. Therefore, assuming you will need to version your implementation assembly, you will need to rebuild your .netmodule and JNI DLL so they are linked to the right versions of their referenced modules. If you want an installer to be able to update an existing installation, it will likely do so based on version numbers. Therefore, you will have to increment the versions of each of the binaries. If all your code is new, the simplest thing in this case is to keep the version numbers identical across your JNI DLL, .netmodule, and .NET assembly.

 

Next Up... All the Myriad Technical Details

I will continue with a detailed description of the project settings and some example code for the entire stack. I will break this into two sections:

  • Signing the ultimately executed .NET assembly, the Applet, a test HTML page, and creating the .h file for the JNI DLL
  • Signing and settings for the JNI DLL, and the .netmodule, and putting it all together

 

Bob Snyder
ABOUT THE AUTHOR
Bob Snyder, Summa

Senior Technical Consultant