A Client-Side JavaScript RSA Encrypter

Peter Rowntree

October 9, 2011

The Problem

Lots of signing in on the web is still done by sending unencrypted passwords over the wire (or worse: over the air). It's not that all those web developers are just too lazy or incompetent to use TLS, it's the history of SSL/TLS and the current state of things. The inventors of SSL can perhaps be forgiven for assuming one-domain=one-IP, for not anticipating mass name-based virtual hosting. A little less forgivable is not including the server name as part of the clientHello in the original spec for the TLS handshake in 1999. As a result of this history we find ourselves at least temporarily with unreliable support for SNI. Even Apache only started supporting it in version 2.2.12 (2009?), and some browsers (particularly on Windows XP) still don't. What this means is that anyone on a name-based virtual-host server can be diligent about setting up a certificate, but when a user attempts to connect to their website using "https://" chances are, because of a lack of implementation in the user's browser or a lack of implementation in the web server, the user will see an ugly warning, and if that connection is made with AJAX, the connection will just silently fail. This de-facto requirement that a secure connection have a dedicated IP address has been an enormous contributor to IPv4 address exhaustion.

The Solution (at least partially, and for now)

A client-side JavaScript RSA encrypter.

For years I used a subset of Leemon Baird's Big Integer library and my own RSA implementation to do JavaScript RSA in low-rent districts where pretty-address-bar TLS was impractical, and even though we don't need to go too nuts because the encrypted password will still be visible to a man in the middle, and because the usual result—an insecure session cookie—will be clear on the wire anyway, I always wanted a simple drop-in solution that was a little tighter, more secure, less intrusive, and more efficient.

So here's a Google-closure-compiler-advanced-optimized slightly-altered adaptation of Tom Wu's RSA encrypter, wrapped in an anonymous self-invoking function to avoid the almost inevitable name collisions that multiple compiled scripts might entail, and to make closure-compiler's 211 "dangerous use of the global this object" warnings moot. It introduces into global scope only RSAKey and two RSAKey methods: RSAKey.setPublic(n,e), which sets the RSAKey public key, and RSAKey.encrypt(str), which produces the PKCS#1 encryption of an input string as an even-length hex string representing the encrypted byte array. It's minimal intrusion into the global namespace means that it can almost always just be dropped into an existing JavaScript setup. Here it is, a single file less than 9KB (gzipped it's less than 4). I hope you find it useful and that it helps completely eliminate plaintext passwords.