天天看點

Web應用中保證密碼傳輸安全

  解決這一問題最佳方案當然是使用非對稱加密。簡單的說,非對稱加密算法需要兩個密鑰,分别稱為公鑰和私鑰,其中公鑰會被公布出來,而私鑰由個了保管(就像保管自己的密碼一樣)。使用公鑰加密的資料是不能用公鑰解密的,隻能由私鑰來解密。如果将私鑰儲存在伺服器,把公鑰發送給浏覽器對密碼原文進行加密,那麼加密後的資料在傳輸過程中是安全的,因為私鑰始終不會出現在傳輸過程,這個加密資料就不能輕松的解開。關于非對稱加密的知識,學霸們請去各種百科上搜尋,這裡就不多說了。

  下面就以C#和JavaScript為例說明一下加密傳輸密碼和背景解密的過程。

  當然第一步是要産生公鑰和私鑰,自己用C#寫個小小的控制台程式或者Windows程式就能解決這個問題,順便熟悉一下C#的RSA。順便說一下,維基百科提到,要保證安全至少得使用1024位的Key,.NET的RSA支援384到2048位的Key,這裡就以1024位為例吧,下面的程式會在執行目錄輸出一個key.xml檔案,儲存了産生的公鑰和私鑰

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

<code>[STAThread]</code>

<code>static</code> <code>void</code> <code>Main(</code><code>string</code><code>[] args)</code>

<code>{</code>

<code>    </code><code>string</code> <code>keyFileName</code>

<code>        </code><code>= Path.Combine(AppDomain.CurrentDomain.BaseDirectory,</code>

<code>            </code><code>"key.xml"</code><code>;</code>

<code>    </code><code>const</code> <code>int</code> <code>keySize = 1024;</code>

<code>    </code><code>// 這個類在System.Security.Cryptography命名空間中</code>

<code>    </code><code>RSACryptoServiceProvider sp = </code><code>new</code> <code>RSACryptoServiceProvider(keySize);</code>

<code>    </code><code>// 參數true表示XML中包含私鑰。如果給false表示隻生成公鑰的XML</code>

<code>    </code><code>string</code> <code>str = sp.ToXmlString(</code><code>true</code><code>);</code>

<code>    </code><code>using</code> <code>(TextWriter writer = </code><code>new</code> <code>StreamWriter(keyFileName)) {</code>

<code>        </code><code>writer.Write(str);</code>

<code>    </code><code>}</code>

<code>}</code>

  打開生成的key.xml可以看到,這個Key值包含了如下幾個部分:Modulus、Exponent、P、Q、DP、DQ、InverseQ、D。這幾個部分的值都是以Base64編碼儲存的。其中,Modulus和Exponent就是組成公鑰的部分,也就是需要傳遞給浏覽器,簡稱M和E,用于加密的兩個數值。

  生成key.xml之後,有兩種方式使用,一種是在使用時從XML檔案導入;另一種方式是把這幾個值提取出來寫在某個類中。導入XML需要先将XML文本讀取到一個string中,再用RSACryptoServiceProvider的執行個體方法FromXmlString(string)導入。因為需要向浏覽器提供M和E,是以還要把這兩個值提取出來

<code>RSACryptoServiceProvider rsa = </code><code>new</code> <code>RSACryptoServiceProvider();</code>

<code>rsa.FromXmlString(xmlString);</code>

<code>RSAParameters rsap = rsa.ExportParameters(</code><code>false</code><code>);</code>

<code>//下面就是需要的兩個值</code>

<code>//rsap.Exponent</code>

<code>//rsap.Modulus</code>

  如果嫌每次載入費事,可以這樣(其中所有字元串常量都是從key.xml中拷貝過來的)

<code>private</code> <code>static</code> <code>readonly</code> <code>RSAParameters rsap = </code><code>new</code> <code>RSAParameters</code>

<code>    </code><code>Modulus = Convert.FromBase64String(</code><code>@"uQlRZvfH6MMdhNRgiAlKMY88dqsU2suKNIWbHY/FiTsvDgH5DLmNmGMp85qtQwSPhBQ+/E7DQkvk1OxIN7EBL+21NRPJIaDKuJciWC940ZFVU0d5oUujKy5uCrF/rfZce8MXjoiErtc+QRjCKI8wfGdIKuclooEPiJwb1rydMuE="</code><code>),</code>

<code>    </code><code>Exponent = Convert.FromBase64String(</code><code>@"AQAB"</code><code>),</code>

<code>    </code><code>P = Convert.FromBase64String(</code><code>@"wSwI9i+aM6h7hayvFD01iINAeZ9JK5qExBJAWDzjOQwWRE9x1dCX52jb+HrutwblfqQuOk6hazOmGTluxITXQw=="</code><code>),</code>

<code>    </code><code>Q = Convert.FromBase64String(</code><code>@"9TfkbPTexGpQ9ZHNjYnmRJLcG8wG6yzzJ/RrWIjq1IKQYMhYDq08bNbUVuXlntKW9GgmEYnuhP8smrH5y+mRCw=="</code><code>),</code>

<code>    </code><code>DP = Convert.FromBase64String(</code><code>@"s2Xx7LDIxLD0BnEZJ/KwhNdgSZNkoNof8vgASfJCE/jltQsS7T+L053OrDV+/PuqprJTPFNKFgUhfMuZ02iLgQ=="</code><code>),</code>

<code>    </code><code>DQ = Convert.FromBase64String(</code><code>@"5IfLXXO0LI78lm/khlUPAbdwZIN3qzMABat3Y1Jur9BiZ6Au2LbASprH15h3r9WJE4wAdnX6kX4SfrUBHPW20w=="</code><code>),</code>

<code>    </code><code>InverseQ = Convert.FromBase64String(</code><code>@"FhlNb2WkipUaXvuwDxEWPeE754+qM2F5otEUP9clG91yaerdsBpBmU0G6S2AqUNjr/qgfpQyl1EW2dl10rmTpw=="</code><code>),</code>

<code>    </code><code>D = Convert.FromBase64String(</code><code>@"WjhPXv/Qks7T7UiqGppA+UIoToojPH1C0VIVrEfGHp/jVRakKs6sWhF7yoHwGf22xkUi4t26efBMTn84xSLCexjQwj5AQtYk+3Qr2QjRDdn2ooIV1gWKW/C0O0+80Y6PEeszItuBVfjKC6mNEcZ1g44/wOdvIG7Olsl0F7vmQrM="</code><code>)</code>

<code>};</code>

  将E和M傳遞給浏覽器的方式,最直接就是寫在HTML裡,我個人比較喜歡把一些小資料寫在&lt;HEAD&gt;标簽的屬性中,就像這樣:&lt;HEAD M="..." E="..."&gt;。C#代碼也很簡單:

<code>Header.Attributes[</code><code>"M"</code><code>] = rsap.Modulus.HexEncode();</code>

<code>Header.Attributes[</code><code>"E"</code><code>] = rsap.Exponent.HexEncode();</code>

  這裡用到了一個byte[]的擴充方法HexEncode,即将byte[]轉換為16進制字元串的方法,它最簡單(但不一定是最快)的實作方法是

<code>public</code> <code>static</code> <code>string</code> <code>HexEncode(</code><code>this</code> <code>byte</code><code>[] me)</code>

<code>    </code><code>return</code> <code>BitConverter.ToString(me).Replace(</code><code>"-"</code><code>, </code><code>string</code><code>.Empty);</code>

  現在是浏覽器端的加密過程,引入需要的JS檔案先:

<code>&lt;</code><code>script</code> <code>type</code><code>=</code><code>"text/javascript"</code> <code>src</code><code>=</code><code>"js/BigInt.js"</code><code>&gt;&lt;/</code><code>script</code><code>&gt;</code>

<code>&lt;</code><code>script</code> <code>type</code><code>=</code><code>"text/javascript"</code> <code>src</code><code>=</code><code>"js/Barrett.js"</code><code>&gt;&lt;/</code><code>script</code><code>&gt;</code>

<code>&lt;</code><code>script</code> <code>type</code><code>=</code><code>"text/javascript"</code> <code>src</code><code>=</code><code>"js/RSA.js"</code><code>&gt;&lt;/</code><code>script</code><code>&gt;</code>

<code>&lt;</code><code>script</code> <code>type</code><code>=</code><code>"text/javascript"</code> <code>src</code><code>=</code><code>"js/jquery-2.1.0.js"</code><code>&gt;&lt;/</code><code>script</code><code>&gt;</code>

  然後在加密密碼之前當然要先得到RSAKeyPair對象,這個對象的構造函數定義在RSA.js中。不過在new RSAKeyPair之前,必須先調用setMaxDigits()函數,原因在BigInt.js中有說明,setMaxDigits()的參數值根據選用的RSA的Key大小不同,如果計算我不太清楚,不過按ohdave.com的示例(下載下傳頁面的源碼就是示例),1024位的Key,應該設定setMaxDigits(130);如果是2048位的則應該設定為260。是以産生RSAKeyPair對象的代碼應該是這樣:

<code>$(</code><code>function</code><code>() {</code>

<code>    </code><code>var</code> <code>key = (</code><code>function</code><code>() {</code>

<code>        </code><code>var</code> <code>m = $(</code><code>"head"</code><code>).attr(</code><code>"M"</code><code>);</code>

<code>        </code><code>var</code> <code>e = $(</code><code>"head"</code><code>).attr(</code><code>"E"</code><code>);</code>

<code>        </code><code>setMaxDigits(130);</code>

<code>        </code><code>// 第一個參數是加密因子,第二個參數是解密因子</code>

<code>        </code><code>// 因為浏覽器端不需要解密,是以第二個參數傳入空字元串</code>

<code>        </code><code>return</code> <code>new</code> <code>RSAKeyPair(e, </code><code>""</code><code>, m);</code>

<code>    </code><code>})();</code>

<code>})</code>

  在送出資料之前對密碼進行加密

<code>var</code> <code>encryptedPass = encryptedString(key, $(</code><code>"#password"</code><code>).val());</code>

  送出到背景之後,C#解密的過程

<code>string</code> <code>hex = Request[</code><code>"encrypted_pass"</code><code>]</code>

<code>byte</code><code>[] data = hex.HexDecode();</code>

<code>// 前面定義的private static readonly RSAParameter rsap = ...</code>

<code>rsa.ImportParameters(rsap);</code>

<code>byte</code><code>[] source = rsa.Decrypt(data, </code><code>false</code><code>);</code>

<code>string</code> <code>password = Encodnig.ASCII.GetString(source);</code>

  密碼原文得到,剩下的事情就好說了,當然是儲存密碼,記得先使用HMAC算法加密哦。後面有示例下載下傳,不是很明顯,仔細找找。

本文轉自邊城__ 51CTO部落格,原文連結:http://blog.51cto.com/jamesfancy/1361925,如需轉載請自行聯系原作者

繼續閱讀