天天看點

RSA加密算法的簡單案例

SA加密算法是目前最有影響力的公鑰加密算法,它能夠抵抗到目前為止已知的絕大多數密碼攻擊。

那關于RSA加密算法有哪些應用呢?以下舉一個資料庫身份驗證的案例。

在使用資料集進行身份認證時,密碼存在資料庫中,認證時使用者輸入的密碼與資料庫中密碼相同則認證通過,若資料庫被破解了則對系統造成威脅,怎樣保證系統安全呢?這裡就可以應用RSA加密算法,對權限加密。

思路:

就是在url中傳使用者名密碼時,先把使用者名進行翻轉,然後再進行加密,如輸入的密碼為12,實際背景進行加密的值為21,再與資料庫進行驗證,這樣就可以避免資料庫被破解檢視到的是21的加密碼,登陸系統時以21是無法登陸成功的。

以報表軟體FineReport為例,這是一個能讀取各類資料庫的報表軟體,分用戶端和前端展示。

實作方案:

1、把RSA加密使用的第三方包,放到工程web-inf/lib檔案夾下即可。

2、調用js檔案

RSA檔案夾為前端js加密時需要調用js檔案,是以需要将Barrett.js、BigInt.js、RSA.js放到工程目錄下如:WebReport/js,建立js檔案夾放入js檔案。

3、定義RSA加密類

定義RSAUtil.java類檔案,先運作類中generateKeyPair()方法,會在伺服器D盤中生成一個随機的RSAKey.txt檔案,儲存公鑰和密鑰,每通路一次這個方法會重新整理一次txt檔案。

<code>package</code> <code>com.fr.privilege;</code>

<code>import</code> <code>java.io.ByteArrayOutputStream;</code>

<code>import</code> <code>java.io.FileInputStream;</code>

<code>import</code> <code>java.io.FileOutputStream;</code>

<code>import</code> <code>java.io.ObjectInputStream;</code>

<code>import</code> <code>java.io.ObjectOutputStream;</code>

<code>import</code> <code>java.math.BigInteger;</code>

<code>import</code> <code>java.security.KeyFactory;</code>

<code>import</code> <code>java.security.KeyPair;</code>

<code>import</code> <code>java.security.KeyPairGenerator;</code>

<code>import</code> <code>java.security.NoSuchAlgorithmException;</code>

<code>import</code> <code>java.security.PrivateKey;</code>

<code>import</code> <code>java.security.PublicKey;</code>

<code>import</code> <code>java.security.SecureRandom;</code>

<code>import</code> <code>java.security.interfaces.RSAPrivateKey;</code>

<code>import</code> <code>java.security.interfaces.RSAPublicKey;</code>

<code>import</code> <code>java.security.spec.InvalidKeySpecException;</code>

<code>import</code> <code>java.security.spec.RSAPrivateKeySpec;</code>

<code>import</code> <code>java.security.spec.RSAPublicKeySpec;</code>

<code>import</code> <code>javax.crypto.Cipher;</code>

<code>/**</code>

<code> </code><code>* RSA 工具類。提供加密,解密,生成密鑰對等方法。</code>

<code> </code><code>* 需要到http://www.bouncycastle.org下載下傳bcprov-jdk14-123.jar。</code>

<code> </code><code>* </code>

<code> </code><code>*/</code>

<code>public</code> <code>class</code> <code>RSAUtil {</code>

<code>    </code><code>/**</code>

<code>     </code><code>* * 生成密鑰對 *</code>

<code>     </code><code>* </code>

<code>     </code><code>* @return KeyPair *</code>

<code>     </code><code>* @throws EncryptException</code>

<code>     </code><code>*/</code>

<code>    </code><code>public</code> <code>static</code> <code>KeyPair generateKeyPair() </code><code>throws</code> <code>Exception {</code>

<code>        </code><code>try</code> <code>{</code>

<code>            </code><code>KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(</code><code>"RSA"</code><code>,</code>

<code>                    </code><code>new</code> <code>org.bouncycastle.jce.provider.BouncyCastleProvider());</code>

<code>            </code><code>final</code> <code>int</code> <code>KEY_SIZE = </code><code>1024</code><code>;</code><code>// 沒什麼好說的了,這個值關系到塊加密的大小,可以更改,但是不要太大,否則效率會低</code>

<code>            </code><code>keyPairGen.initialize(KEY_SIZE, </code><code>new</code> <code>SecureRandom());</code>

<code>            </code><code>KeyPair keyPair = keyPairGen.generateKeyPair();</code>

<code>            </code><code>saveKeyPair(keyPair);</code>

<code>            </code><code>return</code> <code>keyPair;</code>

<code>        </code><code>} </code><code>catch</code> <code>(Exception e) {</code>

<code>            </code><code>throw</code> <code>new</code> <code>Exception(e.getMessage());</code>

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

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

<code>    </code><code>public</code> <code>static</code> <code>KeyPair getKeyPair() </code><code>throws</code> <code>Exception {</code>

<code>        </code><code>FileInputStream fis = </code><code>new</code> <code>FileInputStream(</code><code>"C:/RSAKey.txt"</code><code>);</code>

<code>        </code><code>ObjectInputStream oos = </code><code>new</code> <code>ObjectInputStream(fis);</code>

<code>        </code><code>KeyPair kp = (KeyPair) oos.readObject();</code>

<code>        </code><code>oos.close();</code>

<code>        </code><code>fis.close();</code>

<code>        </code><code>return</code> <code>kp;</code>

<code>    </code><code>public</code> <code>static</code> <code>void</code> <code>saveKeyPair(KeyPair kp) </code><code>throws</code> <code>Exception {</code>

<code>        </code><code>FileOutputStream fos = </code><code>new</code> <code>FileOutputStream(</code><code>"C:/RSAKey.txt"</code><code>);</code>

<code>        </code><code>ObjectOutputStream oos = </code><code>new</code> <code>ObjectOutputStream(fos);</code>

<code>        </code><code>// 生成密鑰</code>

<code>        </code><code>oos.writeObject(kp);</code>

<code>        </code><code>fos.close();</code>

<code>     </code><code>* * 生成公鑰 *</code>

<code>     </code><code>* @param modulus *</code>

<code>     </code><code>* @param publicExponent *</code>

<code>     </code><code>* @return RSAPublicKey *</code>

<code>     </code><code>* @throws Exception</code>

<code>    </code><code>public</code> <code>static</code> <code>RSAPublicKey generateRSAPublicKey(</code><code>byte</code><code>[] modulus,</code>

<code>            </code><code>byte</code><code>[] publicExponent) </code><code>throws</code> <code>Exception {</code>

<code>        </code><code>KeyFactory keyFac = </code><code>null</code><code>;</code>

<code>            </code><code>keyFac = KeyFactory.getInstance(</code><code>"RSA"</code><code>,</code>

<code>        </code><code>} </code><code>catch</code> <code>(NoSuchAlgorithmException ex) {</code>

<code>            </code><code>throw</code> <code>new</code> <code>Exception(ex.getMessage());</code>

<code>        </code><code>RSAPublicKeySpec pubKeySpec = </code><code>new</code> <code>RSAPublicKeySpec(</code><code>new</code> <code>BigInteger(</code>

<code>                </code><code>modulus), </code><code>new</code> <code>BigInteger(publicExponent));</code>

<code>            </code><code>return</code> <code>(RSAPublicKey) keyFac.generatePublic(pubKeySpec);</code>

<code>        </code><code>} </code><code>catch</code> <code>(InvalidKeySpecException ex) {</code>

<code>     </code><code>* * 生成私鑰 *</code>

<code>     </code><code>* @param privateExponent *</code>

<code>     </code><code>* @return RSAPrivateKey *</code>

<code>    </code><code>public</code> <code>static</code> <code>RSAPrivateKey generateRSAPrivateKey(</code><code>byte</code><code>[] modulus,</code>

<code>            </code><code>byte</code><code>[] privateExponent) </code><code>throws</code> <code>Exception {</code>

<code>        </code><code>RSAPrivateKeySpec priKeySpec = </code><code>new</code> <code>RSAPrivateKeySpec(</code><code>new</code> <code>BigInteger(</code>

<code>                </code><code>modulus), </code><code>new</code> <code>BigInteger(privateExponent));</code>

<code>            </code><code>return</code> <code>(RSAPrivateKey) keyFac.generatePrivate(priKeySpec);</code>

<code>     </code><code>* * 加密 *</code>

<code>     </code><code>* @param key</code>

<code>     </code><code>*            加密的密鑰 *</code>

<code>     </code><code>* @param data</code>

<code>     </code><code>*            待加密的明文資料 *</code>

<code>     </code><code>* @return 加密後的資料 *</code>

<code>    </code><code>public</code> <code>static</code> <code>byte</code><code>[] encrypt(PublicKey pk, </code><code>byte</code><code>[] data) </code><code>throws</code> <code>Exception {</code>

<code>            </code><code>Cipher cipher = Cipher.getInstance(</code><code>"RSA"</code><code>,</code>

<code>            </code><code>cipher.init(Cipher.ENCRYPT_MODE, pk);</code>

<code>            </code><code>int</code> <code>blockSize = cipher.getBlockSize();</code><code>// 獲得加密塊大小,如:加密前資料為128個byte,而key_size=1024</code>

<code>            </code><code>// 加密塊大小為127</code>

<code>            </code><code>// byte,加密後為128個byte;是以共有2個加密塊,第一個127</code>

<code>            </code><code>// byte第二個為1個byte</code>

<code>            </code><code>int</code> <code>outputSize = cipher.getOutputSize(data.length);</code><code>// 獲得加密塊加密後塊大小</code>

<code>            </code><code>int</code> <code>leavedSize = data.length % blockSize;</code>

<code>            </code><code>int</code> <code>blocksSize = leavedSize != </code><code>0</code> <code>? data.length / blockSize + </code><code>1</code>

<code>                    </code><code>: data.length / blockSize;</code>

<code>            </code><code>byte</code><code>[] raw = </code><code>new</code> <code>byte</code><code>[outputSize * blocksSize];</code>

<code>            </code><code>int</code> <code>i = </code><code>0</code><code>;</code>

<code>            </code><code>while</code> <code>(data.length - i * blockSize &gt; </code><code>0</code><code>) {</code>

<code>                </code><code>if</code> <code>(data.length - i * blockSize &gt; blockSize)</code>

<code>                    </code><code>cipher.doFinal(data, i * blockSize, blockSize, raw, i</code>

<code>                            </code><code>* outputSize);</code>

<code>                </code><code>else</code>

<code>                    </code><code>cipher.doFinal(data, i * blockSize, data.length - i</code>

<code>                            </code><code>* blockSize, raw, i * outputSize);</code>

<code>                </code><code>// 這裡面doUpdate方法不可用,檢視源代碼後發現每次doUpdate後并沒有什麼實際動作除了把byte[]放到</code>

<code>                </code><code>// ByteArrayOutputStream中,而最後doFinal的時候才将所有的byte[]進行加密,可是到了此時加密塊大小很可能已經超出了</code>

<code>                </code><code>// OutputSize是以隻好用dofinal方法。</code>

<code>                </code><code>i++;</code>

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

<code>            </code><code>return</code> <code>raw;</code>

<code>     </code><code>* * 解密 *</code>

<code>     </code><code>*            解密的密鑰 *</code>

<code>     </code><code>* @param raw</code>

<code>     </code><code>*            已經加密的資料 *</code>

<code>     </code><code>* @return 解密後的明文 *</code>

<code>    </code><code>public</code> <code>static</code> <code>byte</code><code>[] decrypt(PrivateKey pk, </code><code>byte</code><code>[] raw) </code><code>throws</code> <code>Exception {</code>

<code>            </code><code>cipher.init(cipher.DECRYPT_MODE, pk);</code>

<code>            </code><code>int</code> <code>blockSize = cipher.getBlockSize();</code>

<code>            </code><code>ByteArrayOutputStream bout = </code><code>new</code> <code>ByteArrayOutputStream(</code><code>64</code><code>);</code>

<code>            </code><code>int</code> <code>j = </code><code>0</code><code>;</code>

<code>            </code><code>while</code> <code>(raw.length - j * blockSize &gt; </code><code>0</code><code>) {</code>

<code>                </code><code>bout.write(cipher.doFinal(raw, j * blockSize, blockSize));</code>

<code>                </code><code>j++;</code>

<code>            </code><code>return</code> <code>bout.toByteArray();</code>

<code>     </code><code>* * *</code>

<code>     </code><code>* @param args *</code>

<code>    </code><code>public</code> <code>static</code> <code>void</code> <code>main(String[] args) </code><code>throws</code> <code>Exception {</code>

<code>        </code><code>RSAPublicKey rsap = (RSAPublicKey) RSAUtil.generateKeyPair()</code>

<code>                </code><code>.getPublic();</code>

<code>        </code><code>String test = </code><code>"hello world"</code><code>;</code>

<code>        </code><code>byte</code><code>[] en_test = encrypt(getKeyPair().getPublic(), test.getBytes());</code>

<code>        </code><code>System.out.println(</code><code>"123:"</code> <code>+ </code><code>new</code> <code>String(en_test));</code>

<code>        </code><code>byte</code><code>[] de_test = decrypt(getKeyPair().getPrivate(), en_test);</code>

<code>        </code><code>System.out.println(</code><code>new</code> <code>String(de_test));</code>

<code>}</code>

4、定義密碼驗證類

定義TestPasswordValidatorRSA.java密碼驗證類

定義一個類,命名為TestPasswordValidatorRSA.java,擴充于AbstractPasswordValidator,重寫其中密碼驗證方法encodePassword,先把輸入的密碼進行翻轉,然後再進行加密,傳回密碼進行驗證,具體代碼如下:

<code>package</code> <code>com.fr.privilege;  </code>

<code>import</code> <code>com.fr.privilege.providers.dao.AbstractPasswordValidator;  </code>

<code>public</code> <code>class</code> <code>TestPasswordValidatorRSA </code><code>extends</code> <code>AbstractPasswordValidator{  </code>

<code>    </code><code>//@Override</code>

<code>    </code><code>public</code> <code>String encodePassword( String clinetPassword) {</code>

<code>            </code><code>//對密碼進行翻轉如輸入ab翻轉後為ba</code>

<code>            </code><code>StringBuffer sb = </code><code>new</code> <code>StringBuffer();  </code>

<code>            </code><code>sb.append(</code><code>new</code> <code>String(clinetPassword));</code>

<code>            </code><code>String bb = sb.reverse().toString();</code>

<code>            </code><code>//進行加密</code>

<code>            </code><code>byte</code><code>[] en_test = RSAUtil.encrypt(RSAUtil.getKeyPair().getPublic(),bb.getBytes());          </code>

<code>            </code><code>//進行解密,如果資料庫裡面儲存的是加密碼,則此處不需要進行解密</code>

<code>            </code><code>byte</code><code>[] de_test = RSAUtil.decrypt(RSAUtil.getKeyPair().getPrivate(),en_test);  </code>

<code>            </code><code>//傳回加密密碼</code>

<code>            </code><code>clinetPassword=</code><code>new</code> <code>String(de_test);       </code>

<code>            </code><code>// TODO Auto-generated catch block</code>

<code>            </code><code>e.printStackTrace();</code>

<code>        </code><code>return</code> <code>clinetPassword; </code><code>//即擷取加密密碼再與資料庫密碼比對。  </code>

<code>    </code><code>@Override</code>

<code>    </code><code>public</code> <code>boolean</code> <code>validatePassword(String arg0, String arg1) {</code>

<code>        </code><code>// TODO Auto-generated method stub</code>

<code>        </code><code>return</code> <code>false</code><code>;</code>

5、編譯類檔案

首先編譯RSAUtil.java類檔案在伺服器的D盤生成RSAKey.txt檔案,再編譯TestPasswordValidatorRSA.java類,把編譯後的class檔案放到項目工程web-inf/classes/com/fr/privilege檔案夾中。

6、登陸Login.jsp頁面設定

用戶端請求到登入頁面,随機生成一字元串,此随機字元串作為密鑰加密密碼,如下代碼:

<code>&lt;%@page contentType=</code><code>"text/html"</code> <code>pageEncoding=</code><code>"UTF-8"</code><code>%&gt;</code>

<code>&lt;%@page import=</code><code>"com.fr.privilege.providers.dao.RSAUtil"</code><code>%&gt;</code>

<code>&lt;%!public String Testmo() {</code>

<code>        </code><code>String module = </code><code>""</code><code>;</code>

<code>            </code><code>java.security.interfaces.RSAPublicKey rsap = (java.security.interfaces.RSAPublicKey) RSAUtil</code>

<code>                    </code><code>.getKeyPair().getPublic();</code>

<code>            </code><code>module = rsap.getModulus().toString(16);</code>

<code>        </code><code>return</code> <code>module;</code>

<code>    </code><code>}%&gt;</code>

<code>&lt;%!public String Testem() {</code>

<code>        </code><code>String empoent = </code><code>""</code><code>;</code>

<code>            </code><code>empoent = rsap.getPublicExponent().toString(16);</code>

<code>        </code><code>return</code> <code>empoent;</code>

<code>&lt;html&gt;</code>

<code>    </code><code>&lt;head&gt;</code>

<code>        </code><code>&lt;script type=</code><code>"text/javascript"</code>

<code>            </code><code>src=</code><code>"ReportServer?op=emb&amp;resource=finereport.js"</code><code>&gt;&lt;/script&gt;</code>

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

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

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

<code>        </code><code>&lt;script type=</code><code>"text/javascript"</code><code>&gt;    </code>

<code>    </code><code>function</code> <code>bodyRSA()  </code>

<code>    </code><code>{  </code>

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

<code>    </code><code>var</code> <code>a = </code><code>"&lt;%=Testmo()%&gt;"</code><code>;</code>

<code>    </code><code>var</code> <code>b = </code><code>"&lt;%=Testem()%&gt;"</code><code>;</code>

<code>    </code><code>key = </code><code>new</code> <code>RSAKeyPair(b,</code><code>""</code><code>,a);</code>

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

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

<code>    </code><code>bodyRSA(); </code>

<code>    </code><code>var</code> <code>username = FR.cjkEncode(document.getElementById(</code><code>"username"</code><code>).value); </code><code>//擷取輸入的使用者名    </code>

<code>    </code><code>var</code> <code>password = FR.cjkEncode(document.getElementById(</code><code>"password"</code><code>).value);  </code><code>//擷取輸入的參數    </code>

<code>    </code><code>$.ajax({    </code>

<code>        </code><code>url : </code><code>"ReportServer?op=auth_login&amp;fr_username="</code> <code>+ username + </code><code>"&amp;fr_password="</code> <code>+ password,   </code><code>//将使用者名和密碼發送到報表認證位址op=auth_login   </code>

<code>        </code><code>data : {__redirect__ : </code><code>'false'</code><code>},        </code>

<code>        </code><code>complete : </code><code>function</code><code>(res) {    </code>

<code>            </code><code>var</code> <code>jo = FR.jsonDecode(res.responseText);    </code>

<code>            </code><code>if</code><code>(jo.url) {    </code>

<code>               </code><code>window.location=jo.url+ </code><code>"&amp;_="</code> <code>+ </code><code>new</code> <code>Date().getTime();   </code><code>//認證成功跳轉頁面,因為ajax不支援重定向所有需要跳轉的設定  </code>

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

<code>            </code><code>else</code><code>{    </code>

<code>               </code><code>alert(</code><code>"使用者名密碼錯誤!"</code><code>)  </code><code>//認證失敗    </code>

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

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

<code>}    </code>

<code>&lt;/script&gt;</code>

<code>    </code><code>&lt;/head&gt;</code>

<code>    </code><code>&lt;body&gt;</code>

<code>        </code><code>&lt;p&gt;</code>

<code>            </code><code>請登入</code>

<code>        </code><code>&lt;/p&gt;</code>

<code>        </code><code>&lt;form name=</code><code>"login"</code> <code>method=</code><code>"POST"</code><code>&gt;</code>

<code>            </code><code>&lt;p&gt;</code>

<code>                </code><code>使用者名:</code>

<code>                </code><code>&lt;input id=</code><code>"username"</code> <code>type=</code><code>"text"</code> <code>/&gt;</code>

<code>            </code><code>&lt;/p&gt;</code>

<code>                </code><code>密 碼:</code>

<code>                </code><code>&lt;input id=</code><code>"password"</code> <code>type=</code><code>"password"</code> <code>/&gt;</code>

<code>            </code><code>&lt;input type=</code><code>"button"</code> <code>value=</code><code>"登入"</code> <code>onclick=</code><code>"doSubmit()"</code>  <code>/&gt;</code>

<code>        </code><code>&lt;/form&gt;</code>

<code>    </code><code>&lt;/body&gt;</code>

<code>&lt;/html&gt;</code>

本文轉自 雄霸天下啦 51CTO部落格,原文連結:http://blog.51cto.com/10549520/1839220,如需轉載請自行聯系原作者