RSA加密算法是目前最有影响力的公钥加密算法,它能够抵抗到目前为止已知的绝大多数密码攻击。

那关于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文件。

点击(此处)折叠或打开

  1. package com.fr.privilege;

  2. import java.io.ByteArrayOutputStream;
  3. import java.io.FileInputStream;
  4. import java.io.FileOutputStream;
  5. import java.io.ObjectInputStream;
  6. import java.io.ObjectOutputStream;
  7. import java.math.BigInteger;
  8. import java.security.KeyFactory;
  9. import java.security.KeyPair;
  10. import java.security.KeyPairGenerator;
  11. import java.security.NoSuchAlgorithmException;
  12. import java.security.PrivateKey;
  13. import java.security.PublicKey;
  14. import java.security.SecureRandom;
  15. import java.security.interfaces.RSAPrivateKey;
  16. import java.security.interfaces.RSAPublicKey;
  17. import java.security.spec.InvalidKeySpecException;
  18. import java.security.spec.RSAPrivateKeySpec;
  19. import java.security.spec.RSAPublicKeySpec;

  20. import javax.crypto.Cipher;

  21. /**
  22.  * RSA 工具类。提供加密,解密,生成密钥对等方法。
  23.  * 需要到http://www.bouncycastle.org下载bcprov-jdk14-123.jar。
  24.  *
  25.  */
  26. public class RSAUtil {
  27.     /**
  28.      * * 生成密钥对 *
  29.      *
  30.      * @return KeyPair *
  31.      * @throws EncryptException
  32.      */
  33.     public static KeyPair generateKeyPair() throws Exception {
  34.         try {
  35.             KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA",
  36.                     new org.bouncycastle.jce.provider.BouncyCastleProvider());
  37.             final int KEY_SIZE = 1024;// 没什么好说的了,这个值关系到块加密的大小,可以更改,但是不要太大,否则效率会低
  38.             keyPairGen.initialize(KEY_SIZE, new SecureRandom());
  39.             KeyPair keyPair = keyPairGen.generateKeyPair();
  40.             saveKeyPair(keyPair);
  41.             return keyPair;
  42.         } catch (Exception e) {
  43.             throw new Exception(e.getMessage());
  44.         }
  45.     }

  46.     public static KeyPair getKeyPair() throws Exception {
  47.         FileInputStream fis = new FileInputStream("C:/RSAKey.txt");
  48.         ObjectInputStream oos = new ObjectInputStream(fis);
  49.         KeyPair kp = (KeyPair) oos.readObject();
  50.         oos.close();
  51.         fis.close();
  52.         return kp;
  53.     }

  54.     public static void saveKeyPair(KeyPair kp) throws Exception {

  55.         FileOutputStream fos = new FileOutputStream("C:/RSAKey.txt");
  56.         ObjectOutputStream oos = new ObjectOutputStream(fos);
  57.         // 生成密钥
  58.         oos.writeObject(kp);
  59.         oos.close();
  60.         fos.close();
  61.     }

  62.     /**
  63.      * * 生成公钥 *
  64.      *
  65.      * @param modulus *
  66.      * @param publicExponent *
  67.      * @return RSAPublicKey *
  68.      * @throws Exception
  69.      */
  70.     public static RSAPublicKey generateRSAPublicKey(byte[] modulus,
  71.             byte[] publicExponent) throws Exception {
  72.         KeyFactory keyFac = null;
  73.         try {
  74.             keyFac = KeyFactory.getInstance("RSA",
  75.                     new org.bouncycastle.jce.provider.BouncyCastleProvider());
  76.         } catch (NoSuchAlgorithmException ex) {
  77.             throw new Exception(ex.getMessage());
  78.         }

  79.         RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(new BigInteger(
  80.                 modulus), new BigInteger(publicExponent));
  81.         try {
  82.             return (RSAPublicKey) keyFac.generatePublic(pubKeySpec);
  83.         } catch (InvalidKeySpecException ex) {
  84.             throw new Exception(ex.getMessage());
  85.         }
  86.     }

  87.     /**
  88.      * * 生成私钥 *
  89.      *
  90.      * @param modulus *
  91.      * @param privateExponent *
  92.      * @return RSAPrivateKey *
  93.      * @throws Exception
  94.      */
  95.     public static RSAPrivateKey generateRSAPrivateKey(byte[] modulus,
  96.             byte[] privateExponent) throws Exception {
  97.         KeyFactory keyFac = null;
  98.         try {
  99.             keyFac = KeyFactory.getInstance("RSA",
  100.                     new org.bouncycastle.jce.provider.BouncyCastleProvider());
  101.         } catch (NoSuchAlgorithmException ex) {
  102.             throw new Exception(ex.getMessage());
  103.         }

  104.         RSAPrivateKeySpec priKeySpec = new RSAPrivateKeySpec(new BigInteger(
  105.                 modulus), new BigInteger(privateExponent));
  106.         try {
  107.             return (RSAPrivateKey) keyFac.generatePrivate(priKeySpec);
  108.         } catch (InvalidKeySpecException ex) {
  109.             throw new Exception(ex.getMessage());
  110.         }
  111.     }

  112.     /**
  113.      * * 加密 *
  114.      *
  115.      * @param key
  116.      * 加密的密钥 *
  117.      * @param data
  118.      * 待加密的明文数据 *
  119.      * @return 加密后的数据 *
  120.      * @throws Exception
  121.      */
  122.     public static byte[] encrypt(PublicKey pk, byte[] data) throws Exception {
  123.         try {
  124.             Cipher cipher = Cipher.getInstance("RSA",
  125.                     new org.bouncycastle.jce.provider.BouncyCastleProvider());
  126.             cipher.init(Cipher.ENCRYPT_MODE, pk);
  127.             int blockSize = cipher.getBlockSize();// 获得加密块大小,如:加密前数据为128个byte,而key_size=1024
  128.             // 加密块大小为127
  129.             // byte,加密后为128个byte;因此共有2个加密块,第一个127
  130.             // byte第二个为1个byte
  131.             int outputSize = cipher.getOutputSize(data.length);// 获得加密块加密后块大小
  132.             int leavedSize = data.length % blockSize;
  133.             int blocksSize = leavedSize != 0 ? data.length / blockSize + 1
  134.                     : data.length / blockSize;
  135.             byte[] raw = new byte[outputSize * blocksSize];
  136.             int i = 0;
  137.             while (data.length - i * blockSize > 0) {
  138.                 if (data.length - i * blockSize > blockSize)
  139.                     cipher.doFinal(data, i * blockSize, blockSize, raw, i
  140.                             * outputSize);
  141.                 else
  142.                     cipher.doFinal(data, i * blockSize, data.length - i
  143.                             * blockSize, raw, i * outputSize);
  144.                 // 这里面doUpdate方法不可用,查看源代码后发现每次doUpdate后并没有什么实际动作除了把byte[]放到
  145.                 // ByteArrayOutputStream中,而最后doFinal的时候才将所有的byte[]进行加密,可是到了此时加密块大小很可能已经超出了
  146.                 // OutputSize所以只好用dofinal方法。

  147.                 i++;
  148.             }
  149.             return raw;
  150.         } catch (Exception e) {
  151.             throw new Exception(e.getMessage());
  152.         }
  153.     }

  154.     /**
  155.      * * 解密 *
  156.      *
  157.      * @param key
  158.      * 解密的密钥 *
  159.      * @param raw
  160.      * 已经加密的数据 *
  161.      * @return 解密后的明文 *
  162.      * @throws Exception
  163.      */
  164.     public static byte[] decrypt(PrivateKey pk, byte[] raw) throws Exception {
  165.         try {
  166.             Cipher cipher = Cipher.getInstance("RSA",
  167.                     new org.bouncycastle.jce.provider.BouncyCastleProvider());
  168.             cipher.init(cipher.DECRYPT_MODE, pk);
  169.             int blockSize = cipher.getBlockSize();
  170.             ByteArrayOutputStream bout = new ByteArrayOutputStream(64);
  171.             int j = 0;

  172.             while (raw.length - j * blockSize > 0) {
  173.                 bout.write(cipher.doFinal(raw, j * blockSize, blockSize));
  174.                 j++;
  175.             }
  176.             return bout.toByteArray();
  177.         } catch (Exception e) {
  178.             throw new Exception(e.getMessage());
  179.         }
  180.     }

  181.     /**
  182.      * * *
  183.      *
  184.      * @param args *
  185.      * @throws Exception
  186.      */
  187.     public static void main(String[] args) throws Exception {
  188.         RSAPublicKey rsap = (RSAPublicKey) RSAUtil.generateKeyPair()
  189.                 .getPublic();
  190.         String test = "hello world";
  191.         byte[] en_test = encrypt(getKeyPair().getPublic(), test.getBytes());
  192.         System.out.println("123:" + new String(en_test));
  193.         byte[] de_test = decrypt(getKeyPair().getPrivate(), en_test);
  194.         System.out.println(new String(de_test));
  195.     }
  196. }


4、定义密码验证类

定义TestPasswordValidatorRSA.java密码验证类

定义一个类,命名为TestPasswordValidatorRSA.java,扩展于AbstractPasswordValidator,重写其中密码验证方法encodePassword,先把输入的密码进行翻转,然后再进行加密,返回密码进行验证,具体代码如下:

点击(此处)折叠或打开

  1. package com.fr.privilege;

  2. import com.fr.privilege.providers.dao.AbstractPasswordValidator;
  3. public class TestPasswordValidatorRSA extends AbstractPasswordValidator{
  4.     //@Override
  5.     public String encodePassword( String clinetPassword) {
  6.         try {
  7.             //对密码进行翻转如输入ab翻转后为ba
  8.             StringBuffer sb = new StringBuffer();
  9.      sb.append(new String(clinetPassword));
  10.      String bb = sb.reverse().toString();
  11.             //进行加密
  12.             byte[] en_test = RSAUtil.encrypt(RSAUtil.getKeyPair().getPublic(),bb.getBytes());         
  13.             //进行解密,如果数据库里面保存的是加密码,则此处不需要进行解密
  14.             byte[] de_test = RSAUtil.decrypt(RSAUtil.getKeyPair().getPrivate(),en_test);
  15.             //返回加密密码
  16.             clinetPassword=new String(de_test);        
  17.         } catch (Exception e) {
  18.             // TODO Auto-generated catch block
  19.             e.printStackTrace();
  20.         }
  21.         return clinetPassword; //即获取加密密码再与数据库密码匹配。
  22.     }

  23.     @Override
  24.     public boolean validatePassword(String arg0, String arg1) {
  25.         // TODO Auto-generated method stub
  26.         return false;
  27.     }


  28. }

5、编译类文件

首先编译RSAUtil.java类文件在服务器的D盘生成RSAKey.txt文件,再编译TestPasswordValidatorRSA.java类,把编译后的class文件放到项目工程web-inf/classes/com/fr/privilege文件夹中。

6、登陆Login.jsp页面设置

客户端请求到登录页面,随机生成一字符串,此随机字符串作为密钥加密密码,如下代码:

点击(此处)折叠或打开

  1. <%@page contentType="text/html" pageEncoding="UTF-8"%>
  2. <%@page import="com.fr.privilege.providers.dao.RSAUtil"%>
  3. <%!public String Testmo() {
  4.         String module = "";
  5.         try {
  6.             java.security.interfaces.RSAPublicKey rsap = (java.security.interfaces.RSAPublicKey) RSAUtil
  7.                     .getKeyPair().getPublic();
  8.             module = rsap.getModulus().toString(16);
  9.         } catch (Exception e) {
  10.             // TODO Auto-generated catch block
  11.             e.printStackTrace();
  12.         }
  13.         return module;
  14.     }%>
  15. <%!public String Testem() {
  16.         String empoent = "";
  17.         try {
  18.             java.security.interfaces.RSAPublicKey rsap = (java.security.interfaces.RSAPublicKey) RSAUtil
  19.                     .getKeyPair().getPublic();
  20.             empoent = rsap.getPublicExponent().toString(16);
  21.         } catch (Exception e) {
  22.             // TODO Auto-generated catch block
  23.             e.printStackTrace();
  24.         }
  25.         return empoent;
  26.     }%>
  27. <html>
  28.     <head>
  29.         <script type="text/javascript"
  30.             src="ReportServer?op=emb&resource=finereport.js"></script>
  31.         <script type="text/javascript" src="js/RSA.js"></script>
  32.         <script type="text/javascript" src="js/BigInt.js"></script>
  33.         <script type="text/javascript" src="js/Barrett.js"></script>
  34.         <script type="text/javascript">
  35.     function bodyRSA()
  36.     {
  37.     setMaxDigits(130);
  38.     var a = "";
  39.     var b = "";
  40.     key = new RSAKeyPair(b,"",a);
  41.     }
  42. function doSubmit() {
  43.     bodyRSA();
  44.     var username = FR.cjkEncode(document.getElementById("username").value); //获取输入的用户名
  45.     var password = FR.cjkEncode(document.getElementById("password").value); //获取输入的参数
  46.     $.ajax({
  47.         url : "ReportServer?op=auth_login&fr_username=" + username + "&fr_password=" + password, //将用户名和密码发送到报表认证地址op=auth_login
  48.         data : {__redirect__ : 'false'},
  49.         complete : function(res) {
  50.             var jo = FR.jsonDecode(res.responseText);
  51.             if(jo.url) {
  52.                window.location=jo.url+ "&_=" + new Date().getTime(); //认证成功跳转页面,因为ajax不支持重定向所有需要跳转的设置
  53.             }
  54.             else{
  55.                alert("用户名密码错误!") //认证失败
  56.             }
  57.         }
  58.     })
  59. }
  60. </script>
  61.     </head>
  62.     <body>
  63.         <p>
  64.             请登录
  65.         </p>
  66.         <form name="login" method="POST">
  67.             <p>
  68.                 用户名:
  69.                 <input id="username" type="text" />
  70.             </p>
  71.             <p>
  72.                 密 码:
  73.                 <input id="password" type="password" />
  74.             </p>
  75.             <input type="button" value="登录" onclick="doSubmit()" />
  76.         </form>
  77.     </body>
  78. </html>

10-09 11:59