Click here to Skip to main content
Click here to Skip to main content

Client/Server Encryption plus extras

By , 19 Aug 2005
 

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");
};
 // I only want to allow certain bit lengths. Note that the higher the
 // bit length the longer the computation time

 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[bigdigits.Length] ; 
 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.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

Domenic
Web Developer
United States United States
Member
I've been writing software applications since the early 90's from ASP and VB5 to C# and ASP.NET. I am currently working on law enforcement and criminal justice applications delivered over the web. I've always been astounded by the fact that the only 2 industries that refer to customers as "users" are narcotics and software development. Wink | ;-)

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
QuestionDecryption?memberMember 989676016 Mar '13 - 4:05 
First thanks for your good work! That's exactly what I was searching for.
It seems to be very difficult to find a public/private key encryption/decryption, which can communicate between JavaScript and .Net (C#).
 
The way it encrypts on the client and decrypts on the server is perfect for me, but I would need it in the other direction too, means that I would like to encrypt sensitive data on the server, which the client should decrypt. Does anybody know if there is a patched "decryptedString" version too?
Generaldouble or more encryptionmemberchuxs2 Sep '10 - 1:25 
Hi guys
 
Any ideal how i can encrypt more than one value on click of button?
GeneralRe: double or more encryptionmemberDomenic2 Sep '10 - 2:15 
Just use a javascript method that calls Encrypt for as many fields as you want. Example below:
 
onclick="encryptLots()"
 
function encryptLots(){
var enc1 = Encrypt(document.getElementById('id1').value,'<%=Session("n")%>;','<%=Session("e")%>');
var enc2 = Encrypt(document.getElementById('id2').value,'<%=Session("n")%>;','<%=Session("e")%>');
...
}
GeneralRe: double or more encryptionmemberchuxs2 Sep '10 - 7:38 
Thanks testing will revert as soon as possible
GeneralFile not foundmembergaurav_misra5 Jul '08 - 2:24 
When I try to download the source files for this article, it says - "File requested does not exist, download aborted". Please send a link from where I could download the source files for this article.
GeneralRe: File not foundmemberDomenic7 Jul '08 - 2:20 
Try again. It works fine for me.
GeneralNice article, need some suggestions.memberspinoza15 Jan '07 - 23:50 
Nice article, what I am searching for is an equivalent article to by own
 
http://www.codeproject.com/internet/YourOwnSecureProtocol.asp (*)
 
but for .Net/C#. I do not like to use third party libraries (even the .net crypto API) and not to know what happens "under the hood". Furthermore I would like to know the weakness of the protocol etc, in a more scientific perspective.
 
Some comments:
 
* Your source code does not provide variable keysize (as big as you like to) for RSA encryption/decryption. (see *)
* Did you know that 512 bit RSA is not secure anymore (2007)? (see *)
* Did you know that you need to scramble (using base64 or other to protect against a number of attacks). The data before RSA encryption, RSA is not safe at all if you encrypt normal text as
"Hello How Are you?".
* All of the security you provide relies on the genPseudoPrime function, what statistical properties does this function have (in terms of entropy)? (see *)
 

Any suggestion ?
 

However, keep up the good work. I will look at your souce code more in detail.
 
best
 

 

 
//Spinoza

GeneralRe: Nice article, need some suggestions.memberDomenic16 Jan '07 - 2:28 
RSA requires the use of numbers that are a power of 2. Hence the need for 8, 16, 32... I stopped at 512 as it takes a long time to parse this with JavaScript if your client is an older PC. 1024 would be nice, but takes a long time. "not secure" is a relative term. For my purposes 512 was plenty secure. If someone wanted to take the time to brute force the key, then by all means...
genPseudoPrime is needed because RSA needs prime numbers to create the keys. It is used in my article to create a random key. I agree with base64-ing your text. It's been a while since I've written this, but I believe the sample project I put together creates a new key at each request. So even if someone broke the key, they'd have to do it over and over again to maintain the connection.

Question.NET framework ?memberstixoffire14 Jul '06 - 9:46 
Hello
You said your reason for not using the .NET crypto components was that the client would need .NET -
I thought that .NET converted the code to javascript - when you had the project set for the client target to be Javascript. Therefore it was not needed to have .NET framework installed on client. Of course I am referring to a WebApp . and Not a Windows App.
 
Could you help to understand the reasoning for the article ? - because maybe I am missing something and need to do some more research.

GeneralString lengthmemberitobob130 Jun '06 - 3:43 
I've noticed in the demo login page that
decryption fails if the clear text is longer than ~78 characters.
GeneralDoenst work on a macmemberEntDevGuy25 Apr '06 - 13:26 
Safari seems to have problems from request to request. I keep getting garbage out of the serverside decryption.
Consistantly doesn't work on IE 5.0 on a mac. Good article, but doesn't seem to be cross browser compliant.
General0A0B0C -> Encrypt -> decrypt -> A0B0Cmemberzoltix14 Feb '06 - 21:42 
Thank a lot for your article. It is very useful in my web project. I think that I detected a litle bug. When I have a string which begin by “0...”, the first character(numeric=0) disappears and only when the strings begin by ‘0’.
Example 0A0B0C -> Encrypt -> decrypt -> A0B0C
Could help me to dectect my problem ?
 

 
-=zoltx=-
GeneralRe: 0A0B0C -> Encrypt -> decrypt -> A0B0CmemberDomenic28 Feb '06 - 2:25 
I recently got another report of that problem from someone else. I haven't gotten the chance to dig very deeply into it. If you find a solution before I do, please post it here.
Dom
AnswerRe: 0A0B0C -&gt; Encrypt -&gt; decrypt -&gt; A0B0C [modified]memberDan Randolph22 May '09 - 6:02 
The problem is in BigInteger.cs. When the radix value array is converted back to an encoded string, the ToString function can't tell the difference between a '0' saved as 0 and an empty array element.
Here is a fix for the problem:
 
1) (BigInteger.cs) Create an overloaded method of ToString with the count of characters to output.
    
public string ToString(int radix, int outCount)
{
...
  //change the condition in the while loop that builds the string to this
        while (a.dataLength >= 1 )
        {
          singleByteDivide(a, biRadix, quotient, remainder);
          result = m_charSet[(int)remainder.data[0]] + result;
          if (result.Length == outCount) break;
          a = quotient;
        }
...
 
2) In your decryption code-behind for the Login page, do this:
        BigInteger bi_decrypted = bi_encrypted.modPow(bi_d, bi_n);
      //get the password's star count
        int lenPwd = this.txtPassword.Text.Length; 
        string strDecPassword = bi_decrypted.ToString(95,lenPwd);

 
modified on Friday, May 22, 2009 12:16 PM

GeneralRe: 0A0B0C -> Encrypt -> decrypt -> A0B0CmemberScarfish12 Feb '07 - 23:40 
This is a bug in the BigInteger class. Follow the link in the article to the article about the BigInteger class and scroll down to the messages. Read the messages about the GetBytes method so you'll be able to fix this little bug.
 
Scarfish
GeneralAny benchmark datamemberhanofee3 Feb '06 - 5:51 
To repeat what other said: very nice article...
 
But have you or somebody else do a benchmark on it, let say how long will the jscript encryption take to encrypt a small data (8 or 16 bytes) with 512 or 1024 keys ? Thanks a lot before.
GeneralRe: Any benchmark datamemberDomenic3 Feb '06 - 5:55 
I have not, as it is completely reliant on the client PC (CPU speed, RAM, etc). If anyone want to do such benchmarking, I'd be interested to see the results!
Generalplease help mememberaaku21 Dec '05 - 6:48 
hello
 
i need details and code for client/server encryption.please help me

 
aaku
GeneralRe: please help mememberDomenic21 Dec '05 - 6:52 
The code and article is all you need to implement this. Not sure what you're looking for help in.
GeneralWhy not use the Capicom.dll from MSFTmemberjreddy19 Aug '05 - 9:05 
Hi
 
why not use the capicom.dll (com wrapper for seucrity)
in the client side.
I successfully used capicom both on client and server side..
 
Regards
Jay B
GeneralRe: Why not use the Capicom.dll from MSFTmemberDomenic22 Aug '05 - 3:01 
From what I know about capicom (which is somewhat limited), it requires you to install an ActiveX control on the client. When I wrote this, it was to be used on machines that had no access to install any ActiveX, so javascript was the only option. I also believe that capicom requires certificates. The benefit of my approach is that you don't need certificates from a trusted root. The web server sortof issues them on the fly. The only drawback is that javascript is a bit slow when it does the encryption. If you'd like to post some of your implementation code here, I'd like to see how you did it. Thanks!
GeneralObject reference not set to an instance of an objectmemberwormbyte18 Aug '05 - 2:01 
This seems to be the solution I am looking for except I can not get it to work.
 
I am using version 1.2.1.1 of the DJD.Security.dll
 
When I call the GetJavaScriptClientCode() method, like:
 
Dim str As String = encrpt.GetJavaScriptClientCode()
 
Then I get....
 
-----------------------------------------------------------------------------------
 
Object reference not set to an instance of an object.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
 
Exception Details: System.NullReferenceException: Object reference not set to an instance of an object.
 
Source Error:
 

Line 56:
Line 57: If Not (IsClientScriptBlockRegistered("script")) Then
Line 58: Dim str As String = encrpt.GetJavaScriptClientCode()
Line 59: RegisterClientScriptBlock("script", str)
Line 60:

 
Source File: C:\Inetpub\wwwroot\OAGFlights\login.aspx.vb Line: 58
 
Stack Trace:
 

[NullReferenceException: Object reference not set to an instance of an object.]
DJD.Security.Encryption.GetJavaScriptClientCode() +24
OAGFlights._login.Page_Load(Object sender, EventArgs e) in C:\Inetpub\wwwroot\OAGFlights\login.aspx.vb:58
System.Web.UI.Control.OnLoad(EventArgs e) +67
System.Web.UI.Control.LoadRecursive() +35
System.Web.UI.Page.ProcessRequestMain() +731
 

-------------------------------------------------------------------------------------
 

Now I have seen from some previous thread about the following...
 

 
I solved the problem!!!
 
first I like to takeback my assumption about the 'key.snk' file in my earlier post it probebly has nothing to do with your future encryption keys, i must admit that I really dont know much about assemblies.
 
However as you assumed the 'BigInt.js' is not embeded in the DLL, not as 'DJD.Security.BigInt.js' anyhow (for some reason,I dont know rigth now) if you look at the source code for GetJavaScriptClientCode() function you will find the folowing row of code:
 
System.IO.Stream st = GetEmbeddedFile("DJD.Security.BigInt.js");
 
this is where the fault is located this beacause 'BigInt.js' does not resside in the namespace 'DJD.Security' instead it resides in a namespace called 'TOA.Security' (dont ask me why??) so if you change the row of code above to this:
 
System.IO.Stream st = GetEmbeddedFile("TOA.Security.BigInt.js");
 

 

 
But the source I have of the DLL has this...
 
System.IO.Stream st = GetType().Assembly.GetManifestResourceStream("DJD.Security.BigInt.js");
 

 
Can anyone help?
 

GeneralRe: Object reference not set to an instance of an objectmemberDomenic18 Aug '05 - 7:41 
Hello there. I am unclear as to if you are still having a problem. The TOA.Security is from a long time ago, when I first started feveloping this. It has since been renamed to DJD.Security. If you are still seeing TOA... as the name of the embedded resource let me know. It should not be. Otherwise, it you try to change the namespace of the DLL, you'll need to change evrewhere it is calling the GetManifestResourceStream... Namespaces is a big deal with embedded resources. I recommend you not change it unless you know how to use it. Good luck. FYI - there's a new version of the DLL, that I'll be posting soon that now allows you to change your password (though a web interface), and get the properties of a user. The new version is 1.2.2.0...
GeneralRe: Object reference not set to an instance of an objectsussAnonymous18 Aug '05 - 9:32 
Sorry to cause such confusion. I only mentioned the "TOA.Security" because it was mentioned in another thread relating to what seemed to be the same problem.
 
I downloaded the DLL today and made a reference to it in my ASP.NET project. I add the sections in my login page for the server code and the client code. Tried to run and got the error.
 
I have no wish to modify the code in any way, not change namespaces.
 
Can I not use the dll this way?
 

GeneralRe: Object reference not set to an instance of an objectmemberDomenic18 Aug '05 - 9:46 
Did you create a new object when calling that code? It should look something like this:
 
DJD.Security.Encryption sec = new Encryption(256);
if (!IsClientScriptBlockRegistered("script") )
{
string str = sec.GetJavaScriptClientCode();
RegisterClientScriptBlock("script",str);
}
 
Notice sec is defined as a new Encryption object. See also login.aspx and login.aspx.cs

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web03 | 2.6.130516.1 | Last Updated 19 Aug 2005
Article Copyright 2003 by Domenic
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid