天天看点

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,如需转载请自行联系原作者