WARNING: This is a pet project done a long time ago just for fun. In general, don’t do cryptography in javascript, see Javascript Cryptography Considered Harmful.

There are a number of useful applications for Javascript based RSA encryption, one of which is to encrypt password before transmitting it through unsecured channel. HTTPS is HTTP over a secured channel, which is both encrypted and identity verifiable based on trust. However, not everybody can afford HTTPS, due to the fact that HTTPS requires unique IP address (not possible on a cheap shared host) and expensive SSL certificate (usually costs around $100/year). Sometimes, HTTPS is too slow for web applications, for example, Meebo does not use HTTPS at all to provide a better online chat experience. But transmitting passwords in clear text through regular HTTP is potentially dangerous. Lucky for all of us, there are many ways to overcome it.

While millions of forums and blogs on the Internet today transmit your password in clear text when you login, the most common way to protect your password is to simply hash the password in your browser before transmitting it over to the server. We often use MD5, SHA1, SHA256 or SHA512 to accomplish that. Hashing is an one way trip, meaning once the password is hashed, in theory it cannot be undone. So the server would never know what your password really is, but it can compare the hash with the copy stored in a database to verify your login, because same text would result in the same hash. But this method has some vulnerabilities, your username or login handle cannot be hashed (otherwise, we don’t know who you are), hackers can capture the hashed password and replay your login at a later time, meaning they still know who you are and how to pretend to be you. They just won’t know your sensitive password which you might be using in other important places where you really should not, like the bank. And in some cases, the servers need to store your password in a revertible way due to some special reasons, then they cannot use hash algorithms to do that.

So the better way is to use RSA public key encryption. The idea is to encrypt your username or login handle with the password on the client side and decrypted later on the server side. The safe way to do such encryption is to use RSA Public/Private Key Pair system. This system allows anyone with the Public Key to encrypt data, but never be able to decrypt encrypted data. Only the person with the corresponding Private Key can ever decrypt the data. The Private Key is kept safe and secure on the server, the Public Key is published. So your browser would get the Public Key and encrypt your username + password with it, send the encrypted message to the server. The server will decrypt the message using the Private Key that only it knows, and extract the username and password. If someone intercepted the encrypted message in the way, he would not be able to decrypted even if he knows the well-known Public Key, therefore, your username and password remains hidden.

However, no secure system is perfect. The biggest vulnerability of this method is Man-in-the-middle attack. Someone could pose as the server and publish his own Public Key to all the clients and fool them to believe that they should use his Public Key to encrypt data instead of the real Public Key. The clients would encrypted the data using the fake key and send it to the attacker. The attacker would then be able to decrypt that message, and re-encrypt it with the real Public Key and send it to the real server. Neither the server or the clients would notice the man in the middle. The problem relies in the lack of a trust system, meaning there is no system in place to verify a Public Key, not like the SSL certificates system.

Anyway, to implement RSA in Javascript, I got some help from Dave’s RSA In Javascript. Dave’s implementation has one defect though, he is padding the message using 0’s, which not very secure, meaning same message will be encrypted into the same ciphertext every time. So I found this article written by blackinkbottle that explains how to add PKCS#1 v1.5 padding to Dave’s algorithm. Dave’s algorithm also requires to know the modulus and encryption exponent. I am using openssl in PHP on the server side, so I am publishing the Public Key in PEM form which uses ASN.1 standard to encode the modulus and encryption exponent in a base64 format. So I found this opensslkey.cs written in C# that parses such PEM file. However, it only works on certain key length, the parser is doing the job in a hacky and lazy way. So I modified a real Javascript ASN.1 parser. And it all comes together.

Right now, I am still using Dave’s BigInt implementation to support large integer arithmetic in Javascript. But this implementation has its limitation and hits performance issues while doing 2048-bit encryption on Safari or on an iPhone. So, my plan is to look into a better implementation, and eventually work out a nice object-oriented Javascript library for doing Javascript RSA.

Update: Source code: https://github.com/ziyan/javascript-rsa