RSA在前端加密后端解密的应用

来源:互联网 发布:js 导航栏点击后变色 编辑:程序博客网 时间:2024/05/22 02:05

问题来源

本人要搭出一个系统的原型,需要账户登录功能,那最实用的密码加密算法肯定是RSA了,鉴于密钥使用的特殊性,所以在前端加密后端解密是最好的选择了。本文有两个可行的思路,主要详细写第一个(以JFinal框架为例)。

思路

为了避免密钥对传到前端被截获,所以密钥要在后端生成,并存于session里,然后把公钥传给前端供前端JS加密,前端ajax只传回账号和公钥加密密码,后端得到公钥加密密码后取得session中的私钥进行解密(一般来说密码最好再用对称加密处理一下再存进数据库里更妥,例如MD5)
  • 在一个会话期间只生成一次密钥对:这就需要使用HttpSessionListener来对会话的创建和销毁进行密钥对生成(记得在web.xml里注册监听器)
/** * 会话监听器 * @author Wilson 2016/04/02 * */public class SessionListener implements  HttpSessionListener{private static final Logger logger = Logger.getLogger(SessionListener.class);        @Override    public void sessionCreated(HttpSessionEvent hse) {        try {            Map<String,Object> keyPair = CryptoKit.initKey();            hse.getSession().setAttribute("rsa_public_key", CryptoKit.getPublicKey(keyPair));            hse.getSession().setAttribute("rsa_private_key", CryptoKit.getPrivateKey(keyPair));        } catch (Exception e) {            logger.error(e);        }    }    @Override    public void sessionDestroyed(HttpSessionEvent hse) {            }}
  • 在功能里创建密钥对,每次调用页面都重新生成一对,同样放在session里(记得判断和清除上一次的密钥对),可能比上一个方法更为保密,但确实不方便,见仁见智把
/** * 登陆控制器 * @author Wilson 2017/04/03 * */public class LoginController extends Controller{public void index() throws Exception {HttpSession session = getSession();session.removeAttribute("rsa_public_key");session.removeAttribute("rsa_private_key");Map<String,Object> keyPair = CryptoKit.initKey();String pubKey = CryptoKit.getPublicKey(keyPair);        session.setAttribute("rsa_public_key", pubKey);        session.setAttribute("rsa_private_key", CryptoKit.getPrivateKey(keyPair));setAttr("rsa_public_key",pubKey);renderFreeMarker("Login.html");}}

步骤

  • 先设计好加密工具类(这里借用了网上的代码,并改用common.codec来处理base64)
/** * 密文工具 * @author Wilson 2017/04/01 * */public class CryptoKit {public static final String KEY_ALGORITHM = "RSA";      public static final String SIGNATURE_ALGORITHM = "MD5withRSA";      private static final String PUBLIC_KEY = "RSAPublicKey";      private static final String PRIVATE_KEY = "RSAPrivateKey";      private static final int MAX_ENCRYPT_BLOCK = 117; //RSA最大加密明文大小    private static final int MAX_DECRYPT_BLOCK = 128; //RSA最大解密密文大小        public static final String KEY_SHA = "SHA";      public static final String KEY_MD5 = "MD5";private CryptoKit(){}    public static void main(String[] args) throws Exception{    Map<String, Object> keyPair =  initKey();    String privateKey = getPrivateKey(keyPair);    String publicKey = getPublicKey(keyPair);    System.err.println("公钥加密——私钥解密"); String source = "这是一行没有任何意义的文字,你看完了等于没看,不是吗?"; System.out.println("\r加密前文字:\r\n" + source); byte[] data = source.getBytes(); byte[] encodedData = encryptByPublicKey(data, publicKey);byte[] encodedData2 = encryptByPublicKey(data, publicKey);System.out.println("加密后文字:\r\n" + new String(encodedData));System.out.println("加密后文字:\r\n" + new String(encodedData2));byte[] decodedData = decryptByPrivateKey(encodedData, privateKey); String target = new String(decodedData); System.out.println("解密后文字: \r\n" + target);System.err.println("私钥加密——公钥解密"); byte[] encodedDa = encryptByPrivateKey(data, privateKey); System.out.println("加密后:\r\n" + new String(encodedDa)); byte[] decodedDa = decryptByPublicKey(encodedDa, publicKey); String tar = new String(decodedDa); System.out.println("解密后: \r\n" + tar); System.err.println("私钥签名——公钥验证签名"); String sign = sign(encodedData, privateKey); System.err.println("签名:\r" + sign); boolean status = verify(encodedData, publicKey, sign); System.err.println("验证结果:\r" + status);      }        /**      * BASE64解密      *       * @param key      * @return      * @throws Exception      */      public static byte[] decryptBASE64(String key) {          return Base64.decodeBase64(key);    }         /**      * BASE64加密      *       * @param key      * @return      * @throws Exception      */      public static String encryptBASE64(byte[] key) {          return Base64.encodeBase64String(key);     }         /**      * MD5加密 (信息摘要不可逆)     *       * @param data      * @return      * @throws Exception      */      public static byte[] encryptMD5(byte[] data) throws Exception {          MessageDigest md5 = MessageDigest.getInstance(KEY_MD5);          md5.update(data);          return md5.digest();      }         /**      * SHA加密     *       * @param data      * @return      * @throws Exception      */      public static byte[] encryptSHA(byte[] data) throws Exception {          MessageDigest sha = MessageDigest.getInstance(KEY_SHA);          sha.update(data);             return sha.digest();    }          /**      * 用私钥对信息生成数字签名      *       * @param data 加密数据      * @param privateKey 私钥      * @return      * @throws Exception      */      public static String sign(byte[] data, String privateKey) throws Exception {          // 解密由base64编码的私钥          byte[] keyBytes = decryptBASE64(privateKey);             // 构造PKCS8EncodedKeySpec对象          PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);             // KEY_ALGORITHM 指定的加密算法          KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);             // 取私钥匙对象          PrivateKey priKey = keyFactory.generatePrivate(pkcs8KeySpec);             // 用私钥对信息生成数字签名          Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);          signature.initSign(priKey);          signature.update(data);             return encryptBASE64(signature.sign());      }         /**      * 校验数字签名      *       * @param data 加密数据      * @param publicKey 公钥      * @param sign 数字签名     * @return 校验成功返回true 失败返回false      * @throws Exception      */      public static boolean verify(byte[] data, String publicKey, String sign) throws Exception {             // 解密由base64编码的公钥          byte[] keyBytes = decryptBASE64(publicKey);             // 构造X509EncodedKeySpec对象          X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);             // KEY_ALGORITHM 指定的加密算法          KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);             // 取公钥匙对象          PublicKey pubKey = keyFactory.generatePublic(keySpec);             Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);          signature.initVerify(pubKey);          signature.update(data);             // 验证签名是否正常          return signature.verify(decryptBASE64(sign));      }         /**      * 用私钥解密      * @param data      * @param key (BASE64)      * @return      * @throws Exception      */      public static byte[] decryptByPrivateKey(byte[] data, String key) throws Exception {              // 取得私钥      byte[] keyBytes = decryptBASE64(key);        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);          KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);          Key privateKey = keyFactory.generatePrivate(pkcs8KeySpec);             // 对数据解密          Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());          cipher.init(Cipher.DECRYPT_MODE, privateKey);                  int inputLen = data.length;        ByteArrayOutputStream out = new ByteArrayOutputStream();        int offSet = 0;        byte[] cache;        int i = 0;        // 对数据分段解密        while (inputLen - offSet > 0) {            if (inputLen - offSet > MAX_DECRYPT_BLOCK) cache = cipher.doFinal(data, offSet, MAX_DECRYPT_BLOCK);            else cache = cipher.doFinal(data, offSet, inputLen - offSet);            out.write(cache, 0, cache.length);            i++;            offSet = i * MAX_DECRYPT_BLOCK;        }        byte[] decryptedData = out.toByteArray();        out.close();        return decryptedData;    }         /**      * 用公钥解密      * @param data      * @param key (BASE64)     * @return      * @throws Exception      */      public static byte[] decryptByPublicKey(byte[] data, String key) throws Exception {          // 取得公钥      byte[] keyBytes = decryptBASE64(key);        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);          KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);          Key publicKey = keyFactory.generatePublic(x509KeySpec);             // 对数据解密          Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());          cipher.init(Cipher.DECRYPT_MODE, publicKey);                  int inputLen = data.length;        ByteArrayOutputStream out = new ByteArrayOutputStream();        int offSet = 0;        byte[] cache;        int i = 0;        // 对数据分段解密        while (inputLen - offSet > 0) {            if (inputLen - offSet > MAX_DECRYPT_BLOCK) {                cache = cipher.doFinal(data, offSet, MAX_DECRYPT_BLOCK);            } else {                cache = cipher.doFinal(data, offSet, inputLen - offSet);            }            out.write(cache, 0, cache.length);            i++;            offSet = i * MAX_DECRYPT_BLOCK;        }        byte[] decryptedData = out.toByteArray();        out.close();        return decryptedData;    }         /**      * 用公钥加密      *       * @param data      * @param key (BASE64)     * @return      * @throws Exception      */      public static byte[] encryptByPublicKey(byte[] data, String key) throws Exception {          // 对公钥解密          byte[] keyBytes = decryptBASE64(key);             // 取得公钥          X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);          KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);          Key publicKey = keyFactory.generatePublic(x509KeySpec);             // 对数据加密          Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());          cipher.init(Cipher.ENCRYPT_MODE, publicKey);        int inputLen = data.length;        ByteArrayOutputStream out = new ByteArrayOutputStream();        int offSet = 0;        byte[] cache;        int i = 0;        // 对数据分段加密        while (inputLen - offSet > 0) {            if (inputLen - offSet > MAX_ENCRYPT_BLOCK) cache = cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK);            else cache = cipher.doFinal(data, offSet, inputLen - offSet);            out.write(cache, 0, cache.length);            i++;            offSet = i * MAX_ENCRYPT_BLOCK;        }        byte[] encryptedData = out.toByteArray();        out.close();        return encryptedData;    }         /**      * 用私钥加密      *       * @param data      * @param key (BASE64)     * @return      * @throws Exception      */      public static byte[] encryptByPrivateKey(byte[] data, String key) throws Exception {          // 对密钥解密          byte[] keyBytes = decryptBASE64(key);             // 取得私钥          PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);          KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);          Key privateKey = keyFactory.generatePrivate(pkcs8KeySpec);             // 对数据加密          Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());          cipher.init(Cipher.ENCRYPT_MODE, privateKey);          return cipher.doFinal(data);      }         /**      * 取得私钥      *       * @param keyMap      * @return      * @throws Exception      */      public static String getPrivateKey(Map<String, Object> keyMap) throws Exception {      RSAPrivateKey key = (RSAPrivateKey) keyMap.get(PRIVATE_KEY);        return encryptBASE64(key.getEncoded());      }         /**      * 取得公钥      *       * @param keyMap      * @return      * @throws Exception      */      public static String getPublicKey(Map<String, Object> keyMap) throws Exception {      RSAPublicKey key = (RSAPublicKey) keyMap.get(PUBLIC_KEY);          return encryptBASE64(key.getEncoded());      }         /**      * 初始化密钥对      *       * @return      * @throws Exception      */      public static Map<String, Object> initKey() throws Exception {          KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM);          keyPairGen.initialize(1024);             KeyPair keyPair = keyPairGen.generateKeyPair();             // 公钥          RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();             // 私钥          RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();             Map<String, Object> keyMap = new HashMap<String, Object>(2);             keyMap.put(PUBLIC_KEY, publicKey);          keyMap.put(PRIVATE_KEY, privateKey);          return keyMap;      } }
  • 前端JS,利用了jsencrypt.js来进行加密,通过后端的公钥便可以进行密码的加密(这里是requireJS的写法)
<script>require([         'jquery',         '${base}/res/plugin/cryptico/jsencrypt.min.js'         ], function($,jsencrypt) {var t = '${rsa_public_key}';var encrypt = new jsencrypt.JSEncrypt();    encrypt.setPublicKey(t);    var encrypted = encrypt.encrypt("123456");$.ajax({url: '${base}/index/login',data: {user:'admin',password:encrypted},success: function(data){}});});</script>
  • 后端java
/** * 登陆控制器 * @author Wilson 2017/04/03 * */public class LoginController extends Controller{public void index() {String pubKey = getSessionAttr("rsa_public_key");setAttr("rsa_public_key",pubKey);renderFreeMarker("Login.html");}/** * 登陆 * @throws Exception  */public void login() throws Exception {String user = getPara("user");String password = getPara("password");String priKey = getSessionAttr("rsa_private_key");String text2 = new String(CryptoKit.decryptByPrivateKey(CryptoKit.decryptBASE64(password), priKey));//TO DO
renderNull();}}







0 0