Introduction
Today, security on our applications is a big issue. Companies have dedicated personnel whose sole job is to critique your code and make sure that the best of the best hackers can�t break into your site or application. On a recent project, my Information Security Officer (ISO), days from implementation, sprung on me that a password could be seen being sent across the network using Microsoft�s Network Monitor (or NetMon, as it is more commonly known). Because my work was not limited to this single application, I needed a way to securely pass plain text values from a client to a server in both EXE applications and web application. Oh, and there wasn�t any money to buy an SSL certificate� that would have been an easy way out.
Two-way encryption for executables has been increasingly easier to do with the Cryptography classes of the .NET framework. However, when I found no easy way to use these cryptography classes on a web client. You may be thinking why not just use the .NET cryptography classes then? Well, believe me, I tried. But when we need to execute code through a web browser on a client�s PC, you can understand the challenge. I would have had to come up with some way of guaranteeing that the client PC had the .NET framework installed, and even if they did, how do I execute the code? Because of that simple reason, I had to come up with my own version of the RSA algorithm (with much help from Paul Johnston).
In a nutshell, here is what I came up with� a complied .NET DLL (with COM Interop) that can encrypt data on a web client with JavaScript using a public key of a derivative of the known RSA encryption algorithm. In addition, the dll can be used by .NET executable application, COM executable applications and even COM web applications!
Now that you know why I had to re-write the RSA algorithm, let�s take a look at it...
Code
To begin, any algorithms I know of that have to do with encryption handle incredibly large numbers, even higher than a 64 bit long integer. To handle these long numbers, I used a version of the BigInteger
class (slightly altered to handle a larger radix), found here on The Code Project. You can see how that works at the link above but I wanted to give credit where credit is due, plus, now you know what BigInteger
is in the code below.
If you check out Paul Johnston�s site, you�ll understand that there�s 3 parts to an RSA public/private key pair. N and E are known as your public Key, E and D are known as your private key. You can encrypt with N and E, but can only decrypt if you have the part D. Below is one of the overloads in the dll to create this key pair:
public Encryption(int bitSize)
{if (!((bitSize == 8) | (bitSize == 16) | (bitSize == 32) | (bitSize == 64) |
(bitSize == 128) | (bitSize == 256) | (bitSize == 512)))
{
throw new ArgumentException("Encryption supports only 8, 16, 32, " +
"64, 128, 256, 512 bit encryption");
};
BigInteger q;
BigInteger p;
BigInteger m;
p = BigInteger.genPseudoPrime(bitSize,10,new System.Random());
do
{
q = BigInteger.genPseudoPrime(bitSize,10, new System.Random());
}
while(p == q);
_key.N = (p*q);
m = (p-1)*(q-1);
_key.E = new BigInteger("10001", 16);
_key.D = _key.E.modInverse(m);
}
Now, that I have a key pair, I can encrypt and decrypt virtually anything. For a web application, you can store all 3 parts in the session variable, and use N and E to encrypt, where the server has access to D to do the decryption!
So how do we encrypt? Here�s where it gets tricky? First I�ll show you how to it�s done mathematically in the dll. Then I�ll show you how this can be done on the client.
public BigInteger[] Encrypt(string message )
{
if ((_key.E == 0) || (_key.N == 0)) {
throw new ApplicationException("Invalid Key");};
int i ;
byte[] temp = new byte[1] ;
byte[] digits = System.Text.ASCIIEncoding.ASCII.GetBytes(message);
BigInteger[] bigdigits = new BigInteger[digits.Length] ;
for( i = 0 ; i < bigdigits.Length ; i++ )
{
temp[0] = digits[i] ;
bigdigits[i] = new BigInteger( temp ) ;
}
BigInteger[] encrypted = new BigInteger ;
for( i = 0 ; i < bigdigits.Length ; i++ )
encrypted[i] = bigdigits[i].modPow( _key.E,_key.N) ;
return( encrypted ) ;
}
So how do we do this on the client? Good old JavaScript. You may be thinking: hey, I can view JavaScript source code in a web browser. How is that secure? You�re right you can see the JavaScript code through a web browser, but all we are going to do on the client is encryption. To encrypt we only need the public key: E and N. The missing part D is hidden from the user at all times. The code behind, or ASP, or any other server code has access to this D variable through the session state to do all the decryption. At this point you may ask: Why use the session state? Why not create a key pair, and use that all the time to make it more efficient? Well, that was my initial thought too until a colleague pointed out that he could easily capture the encrypted values from the form as it is posted back to the server, and use those same values at a later time to post back to the server. That would have worked fine because the hacker could have just made a simple script to POST that encrypted data to the web login page, and it would have been decrypted properly because the private key never changed. By using the session variables, any time a new session starts, that client has it�s own unique public and private key pair. Now even if you captured the encrypted values, they would be worthless because the private key would be different when a new session is opened. 99% of the code is how JavaScript implements the
BigInteger
class. This code came from
http://www.leemon.com/. I won�t publish that here, because you can get it from the source files. To encrypt though, here�s what we do. We�ve opened our session to the web site, new keys are made for us. My server code page calls the
GetJavaScriptClientCode
from the DLL which can be used to write out to the client page. This includes all the
BigInteger
code and the small piece for encryption:
function Encrypt (str, n, e) {
var n=str2bigInt(n,16,0);
var x=str2bigInt(str,95,n.length);
var y=str2bigInt(e,16,0);
powMod(x,y,n,0);
x = bigInt2str(x,16);
return x;
}
We can now add a password box for the password the user enters, and a hidden field that will have the encrypted data:
<INPUT type="submit" value="Login" OnClick="var s = txtPassword.value;
password.value=Encrypt(s,'<%=Session("n")%>','<%=Session("e")%>');
var strStar = ''; for (i = 0; i < s.length; i++) {strStar = strStar + '*';}
txtPassword.value = strStar">
<input id="password" type="hidden" name="password">
This code calls the JavaScript encrypt function, replaces the value of the original password with a bunch of *�s, and assigns another hidden variable the actual encrypted password. I did the * thing to make the encryption invisible to the user. If I replaced the password that they typed with the encrypted values, the lengths would be obviously different. The little function above simply replaces their password in the textbox they typed it in with * characters the same length as the password they entered. Then end result is 2 parameters being posted to the form: password which will be equal to the actual encrypted password, and
txtPassword
, which is merely a bunch of * characters. The server code will care about the password variable.
Decrypting is a little more complex than then encryption, but follows the general RSA algorithm rules. Here�s the code for the decrypt function, which resides in the DLL:
public string Decrypt( BigInteger[] encrypted )
{
if (_key.D == 0) {throw new ApplicationException("Invalid key");};
int i ;
BigInteger[] decrypted = new BigInteger[encrypted.Length] ;
for( i = 0 ; i < decrypted.Length ; i++ )
decrypted[i] = encrypted[i].modPow((_key.D),(_key.N)) ;
char[] charArray = new char[decrypted.Length] ;
for( i = 0 ; i < charArray.Length ; i++ )
charArray[i] = (char) ( (byte)decrypted[i].IntValue() ) ;
return( new string( charArray ) ) ;
}
Conclusion
Security can be a difficult task to tackle in many applications. I hope that this article and my code help you in your applications, and whatever requirements they have.
Updates
- 1/16/04 - Added sample source code for web implementation in source download file
- 1/16/04 - Added a feature to the forms control to load machine name if no domains are available.
- 1/16/04 - Fixed an issue with namespace inconsistencies (see thread below).
- 8/19/05 - Latest update (version 1.2.2.0) has the ability to query user properties and change passwords within ActiveDirectory or LDAP.
Extras!
Also included in the DLL code, is a class called Authentication. The methods in this class talk to directory services or LDAP (LDAP wasn�t tested), to authenticate a user in a specified domain, as well as get whatever properties for the user. It also allows you to get SID�s of users, and other cool stuff. Also, there is a windows control (see image) that can be used on any .NET exe to implement a universal logon screen for your applications. That control obviously is integrated with the Authentication class. If you want an article on any of these please ask by posting to this article.