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 > </code><code>0</code><code>) {</code>
<code> </code><code>if</code> <code>(data.length - i * blockSize > 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 > </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><%@page contentType=</code><code>"text/html"</code> <code>pageEncoding=</code><code>"UTF-8"</code><code>%></code>
<code><%@page import=</code><code>"com.fr.privilege.providers.dao.RSAUtil"</code><code>%></code>
<code><%!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>}%></code>
<code><%!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><html></code>
<code> </code><code><head></code>
<code> </code><code><script type=</code><code>"text/javascript"</code>
<code> </code><code>src=</code><code>"ReportServer?op=emb&resource=finereport.js"</code><code>></script></code>
<code> </code><code><script type=</code><code>"text/javascript"</code> <code>src=</code><code>"js/RSA.js"</code><code>></script></code>
<code> </code><code><script type=</code><code>"text/javascript"</code> <code>src=</code><code>"js/BigInt.js"</code><code>></script></code>
<code> </code><code><script type=</code><code>"text/javascript"</code> <code>src=</code><code>"js/Barrett.js"</code><code>></script></code>
<code> </code><code><script type=</code><code>"text/javascript"</code><code>> </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>"<%=Testmo()%>"</code><code>;</code>
<code> </code><code>var</code> <code>b = </code><code>"<%=Testem()%>"</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&fr_username="</code> <code>+ username + </code><code>"&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>"&_="</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></script></code>
<code> </code><code></head></code>
<code> </code><code><body></code>
<code> </code><code><p></code>
<code> </code><code>請登入</code>
<code> </code><code></p></code>
<code> </code><code><form name=</code><code>"login"</code> <code>method=</code><code>"POST"</code><code>></code>
<code> </code><code><p></code>
<code> </code><code>使用者名:</code>
<code> </code><code><input id=</code><code>"username"</code> <code>type=</code><code>"text"</code> <code>/></code>
<code> </code><code></p></code>
<code> </code><code>密 碼:</code>
<code> </code><code><input id=</code><code>"password"</code> <code>type=</code><code>"password"</code> <code>/></code>
<code> </code><code><input type=</code><code>"button"</code> <code>value=</code><code>"登入"</code> <code>onclick=</code><code>"doSubmit()"</code> <code>/></code>
<code> </code><code></form></code>
<code> </code><code></body></code>
<code></html></code>
本文轉自 雄霸天下啦 51CTO部落格,原文連結:http://blog.51cto.com/10549520/1839220,如需轉載請自行聯系原作者