通常我們做一個Web應用程式的時候都需要登入,登入就要輸入使用者名和登入密碼,并且,使用者名和登入密碼都是明文傳輸的,這樣就有可能在中途被别人攔截,尤其是在網吧等場合。
這裡順帶一個小插曲,我以前有家公司,辦公室裝修時候安排的網口相對較少,不太夠用,于是我和另外一個同僚使用了一個hub來共享一個網口,這就導緻了很有趣的現象:任何他的網絡包我都能抓得到,當然了,我的他也能抓得到。這是不是有很大的安全隐患了?我有可能在不經意間會洩漏自己的密碼。
是以,很多安全要求較高的網站都不會明文傳輸密碼,它們會使用https來確定傳輸過程的安全,https是用證書來實作的,證書來自于證書頒發機構,當然了,你也可以自己造一張證書,但這樣别人通路你的網站的時候還是會遇到麻煩,因為你自己造的證書不在使用者浏覽器的信任範圍之内,你還得在使用者浏覽器上安裝你的證書,來讓使用者浏覽器相信你的網站,很多使用者并不知道如何操作,就算會操作,也能也不樂意幹;另一種選擇是你向權威證書頒發機構申請一張證書,但這樣有一定的門檻,還需要付費,也不是我們樂意幹的事。
是以,我打算自己實作一個密碼加密傳輸方法。
這裡使用了RSA非對稱加密算法,對稱加密也許大家都已經很熟悉,也就是加密和解密用的都是同樣的密鑰,沒有密鑰,就無法解密,這是對稱加密。而非對稱加密算法中,加密所用的密鑰和解密所用的密鑰是不相同的:你使用我的公鑰加密,我使用我的私鑰來解密;如果你不使用我的公鑰加密,那我無法解密;如果我沒有私鑰,我也沒法解密。
我設計的這個登入密碼加密傳輸方法的原理圖如下:

首先,先演練一下非對稱加密:
大家可以清楚看到,密碼被加密成128位元組長度的密文,為什麼是固定128位元組呢?這是因為我們的RSACryptoServiceProvider預設生成的key的長度是1024,即1024位的加密,是以不管你要加密的密碼有多長,它生成的密文的長度肯定是128位元組,也因為這樣,密碼的長度是有限制的,1024位的RSA算法,隻能加密大約100個位元組長度的明文,要提高可加密的明文的長度限制,就得增加key的長度,比如把key改到2048位,這樣能加密的明文的長度限制也就變為大概200出頭這樣……還是太少啊!而且這樣會帶來加密速度的顯著下降,RSA本來就很慢……是的,比同沒有長度限制的對稱加密,這種非對稱加密的限制可真多,即便是200個字元,又能傳輸什麼東西呢?——密碼!這個就夠了,傳輸完密碼之後,我們就使用對稱加密,是以,RSA往往是用來“協商”一個對稱加密的key的。
接下去,真正的難點在于用javascript實作一個和.net的RSA相容的算法。密碼學,對我來說真像天書一般,每次我一看就頭大,這個工作是沒辦法自己做的了,隻能到網上找,那是相當的費力啊,找到許多js的RSA實作,但都和.net的這套東西不相容,最後還是功夫不負有心人,終于找到了一套。不多說,上代碼:
這是用戶端代碼,大家可以看到,基本沒有什麼伺服器端代碼,<%=postbackUserName%>用于回顯輸入的使用者名,<%=LoginResult%>用于顯示登入結果,<%=strPublicKeyExponent%>和<%=strPublicKeyModulus%>則用來告訴用戶端RSA公鑰。需要的javascript檔案說明:
jQuery.md5.js - 用于對密碼進行兩次md5加密;(我通常在資料庫中儲存的使用者密碼是兩次MD5後的結果)
BigInt.js - 用于生成一個大整型;(這是RSA算法的需要)
RSA.js - RSA的主要算法;
Barrett.js - RSA算法所需要用到的一個支援檔案;
對于密碼學,我幾乎一無所知,是以沒辦法跟大家解釋清楚RSA算法的原理,抱歉,我隻知道怎麼用。關于javascript中這行代碼:“setMaxDigits(129);”具體表示什麼我也不清楚,我隻知道,把參數改為小于129的數之後會導緻用戶端的javascript執行進入死循環。伺服器端代碼也很簡單:
使用者名“user1”
密碼“123456”
登入成功!
抓取http封包看看POST的“密碼”:
這樣的“密碼”的破解就成為了理論上的可行了。:)
下面提供完整代碼下載下傳(使用VS2010開發環境):
javascript:void(0)