Thursday, September 1. 2016
When the blog was started a three post series was published about digital signature in the web. At that time I recommended the use of applets in order to access local certificates. Nowadays applets are an old fashion technology and, even worse, it is being deprecated or, at least, they are not recommended by all the major browsers. So applets are out of the game and I advise against them. In more recent times the blog dealt with the new Web Cryptography API, a W3C standard which, I thought, was the long awaited technology to cover digital singing in the web. Nevertheless the new API does not integrate this specific use-case due to some reasons analyzed previously in the blog (mainly because the internal browser certificates and keys are not exposed to the API). So, here we are again, signature in the web is a hard and tough subject which (in the year 2016) has no direct solution. There is no clear API or technology that fits to the case. In my humble opinion this is an extremely strange situation produced by the enormous indolence of all the players in the game (I suppose no one is interested in the case).
Today's entry is my little contribution to the subject in the current messy environment. Remember that there is no clear solution and, I think, this situation will not last forever and will improve shortly. But, in the meantime, a temporary solution is needed because applets are very problematic. During the last weeks I have been thinking about the current alternatives and exploring some of them. My final thoughts were summarized in only two ideas:
Implementing a plug-in or extension for the major browsers. You know that firefox 48 is now exposing the WebExtensions, which is the try of showing the same API for extensions than chrome. For that reason I decided that exploring the implementation of a WebExtension was a good idea. The documentation of chrome offers some APIs to access to the certificates (for example platformKeys) but, as I discovered later, all those extensions are only for Chrome OS (it is curious that the signature use-case is interesting for the Google's OS but not for the others). So, again, an extension seemed a closed path (in chrome you can implement a native client but the solution is restricted to this browser and implementing an extension for every browser is a crazy idea).
The other solution is just a workaround for the messy situation. Instead of using the browser directly (the clear solution simply does not work) a local process or daemon will give the access to the certificates and will expose them to the browser using http(s). This way the browser will communicate with the local daemon using a Web Service and the access to the certificates will be centralized through the daemon. In order to make the solution viable some headers are needed in the WS (Access-Control-Allow-*), the headers to permit cross-origin HTTP requests (CORS). The general solution is described in the following diagram.
I know this idea is just a workaround. Besides it is quite laborious too, implementing a full daemon is not an easy task and it needs some thinking to make it secure and light. In this entry a simple PoC will be presented implemented in Java.
So let's start with the PoC. As it is commented above, the idea is running a local HTTP server in the client machine using a daemon (something started by the init system -windows service, rc or systemd script, Solaris SMF or whatever-). The PoC implements it using the internal HTTP server that the Java SE integrates (since version 7) and jettison library (for interchanging messages in JSON). It also re-uses the old Signer implementation used in the applet series to communicate with the real certificate repository. This way the server just handles one repo (the process does not manage two or more different certificate stores). The server is started with a configuration file (a properties file) which contains the properties for the repository (the same props used previously for each Signer implementation for the applet) and some new ones for specifying other server characteristics (port and context for the WS -the http server only listens in localhost obviously-, which domains are allowed to connect -CORS data-, the Signer implementation to use and so on). The server provides the following web services:
isInitialized: A simple method that indicates if the repository is initialized or it is closed. The method responds with some simple data about the characteristics of the certificate store besides the initialization condition.
initialize: Opens the repository with a password (maybe not needed in some repos). It also returns the characteristics of the repo.
listCertificates: Method to list all the certificates in the repository. It responds with a list of certificates with some properties for each one (subject, issuer, serial number, dates,...).
sign: Method to perform the signature. It just receives the data to sign (encoded in base64), the type of signature (JCE style, SHA1withRSA for example), the alias of the certificate to use and the password (if needed). The WS returns the signature in base64.
verify: Verification of a signature. It receives the signature (base64), the plain data to check (base64 too), the type of signature and the alias of the certificate to use. It just returns if the signature is valid or not. This operation can be executed in the browser cos it depends on the public key (WebCrypto API for example), but I included it in the daemon for simplicity.
Finally I developed a little HTML page which performs a signature and then validates it. It calls the WS methods provided to perform the process. The page just uses JQuery to communicate via JSON and the CORS headers let the browser access from the internet page to the local server.
As usual a video shows the whole process. First the daemon is started manually using the command line (configured to use Spanish eID DNIe with OpenSC in port 8000, I wanted to show a complicated environment with hardware tokens). Then the page is accessed and a simple message is prepared to be signed. As soon the Sign process is started, the page performs the initialization and a password is requested to the user. The initialize method opens the PKCS#11 module in the daemon. Then the page calls for the certificates and presents them to choose the one for signing. Once the certificate is chosen by the user the sign WS is called (in OpenDNIe no password is needed for each key). As I am using the OpenSC implementation a warning message is displayed that must be accepted by the user (this can be problematic if a real daemon with no X session was used). The signature is returned and placed in another text-area. Finally a verification is performed.
Today's entry is a simple PoC that shows a possible solution to perform digital signature in the web due to the current messy situation. The idea is counterintuitive because it uses a local process running in your system instead some new and bright web technology. But do not blame me for that, in the year 2016 there is no way to perform a signature just using web technologies (old technologies are deprecated, new ones do not care about it). You have to understand that the solution is a workaround. It solves all the issues with previous technologies and runs in any modern browser. For non pc devices (phones and tablets) the idea would work too but for sure it needs some adaptation (indeed for those devices everything is an app, so why bother about the browser). The workaround presented in the entry will be replaced by a new standard that hopefully will come in the future. You can download the NetBeans project used for the PoC from here. Please do not use the code just like it is. This is just the presentation of the idea, not any final solution. The implementation should be carefully improved in terms of security, usability and agility. Besides, it is a workaround, all the more reason for being specially meticulous in such a sensitive use-case.
Think before act! But act!
But in this point, having accessible browser source code (I think in Chromium project for example) why don't make the effort to build a proper API to work with internal certificate according your needs. Obviously will have to be the same API the major browser, but why not?