? 版权声明:本文为博主原创文章,转载请注明出处
[TOC]
1. RSA算法
RSA是目前最有影响力和最常用的公钥加密算法,它能够抵抗到目前为止已知的绝大多数密码攻击,已被ISO推荐为公钥数据加密标准。今天只有短的RSA钥匙才可能被强力方式破解。但在分布式计算和量子计算机理论日趋成熟的今天,RSA加密安全性收到了挑战和质疑。RSA算法基于一个十分简单的数论事实:将两个大质数相乘十分容易,但是想要对其乘积进行因式分解缺及其困难,因此可以将乘积公开作为加密密钥。
2. HTTPS
2.1 HTTPS优点
1. 使用HTTPS协议可认证用户和服务器,确保数据发送到正确的客户机和服务器。2. HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,要比HTTP协议安全,可防止数据在传输过程中不被窃取、改变,确保数据的完整性。3. HTTPS是现行框架下最安全的解决方案,虽然不是觉得安全,但它增加了中间人攻击的成本。
2.2 HTTPS缺点
1. SSL的专业证书需要购买,功能越强大的证书费用越高2. 相同的网络环境下,HTTPS协议会使页面的加载时间延长50%,增加10%-20%的耗电。此外,HTTPS协议还会影响缓存,增加数据开销和功耗。3. HTTPS协议的安全性是有范围的,在黑客攻击、拒绝服务攻击、服务器劫持等方面几乎起不到什么作用。4. 最关键的是,SSL证书的信用链体系并不安全。特别是在某些国家可以控制CA根证书的情况下,中间人攻击一样可行。
3. RSA传输加密实现
综上所述(其实主要是因为HTTPS购买SSL证书需要花钱),可在某些关键数据传输过程中进行RSA加密。比如:登录时对登录密码进行加密。
3.1 所需插件
3.1.1 JS插件
BigInt.js - 用于生成一个大整数(这是RSA算法的需要)
Barrett.js - RSA算法所需要用到的一个支持文件
RSA_Stripped.js - RSA的主要算法
下载密码:bhiq
3.1.2 所需JAR
bcprov-jdk15on
<dependency> ???<groupId>org.bouncycastle</groupId> ???<artifactId>bcprov-jdk15on</artifactId> ???<version>1.58</version></dependency>
3.1.3 代码
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ?xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> ???<modelVersion>4.0.0</modelVersion> ???<groupId>com.study</groupId> ???<artifactId>webrsa</artifactId> ???<version>0.0.1-SNAPSHOT</version> ???<packaging>war</packaging> ???<properties> ???????<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> ???</properties> ???<dependencies> ???????<dependency> ???????????<groupId>junit</groupId> ???????????<artifactId>junit</artifactId> ???????????<version>4.12</version> ???????????<scope>test</scope> ???????</dependency> ???????<!-- bcprov-jdk15on --> ???????<dependency> ???????????<groupId>org.bouncycastle</groupId> ???????????<artifactId>bcprov-jdk15on</artifactId> ???????????<version>1.58</version> ???????</dependency> ???</dependencies> ???????<build> ???????<plugins> ???????????<plugin> ???????????????<groupId>org.apache.maven.plugins</groupId> ???????????????<artifactId>maven-compiler-plugin</artifactId> ???????????????<version>3.6.1</version> ???????????????<configuration> ???????????????????<target>1.7</target> ???????????????????<source>1.7</source> ???????????????????<encoding>UTF-8</encoding> ???????????????</configuration> ???????????</plugin> ???????</plugins> ???</build> ???</project>
RSAUtils.java
package com.study.webrsa.utils;import java.io.ByteArrayOutputStream;import java.security.KeyPair;import java.security.KeyPairGenerator;import java.security.interfaces.RSAPrivateKey;import java.security.interfaces.RSAPublicKey;import java.util.HashMap;import java.util.Map;import javax.crypto.Cipher;import org.bouncycastle.jce.provider.BouncyCastleProvider;/** * RSA加解密工具类 * */public class RSAUtils { ???????public static final String SECURITY = "RSA"; // 加密方式 ???public static final String ALGORITHM = "MD5withRSA"; // 加密算法 ???public static final String PUBLIC_KEY = "RSAPublicKey"; // 公钥 ???public static final String PRIVATE_KEY = "RSAPrivateKey"; // 私钥 ???????/** ????* 获取密钥 ????*/ ???public static Map<String, Object> getKey() { ???????Map<String, Object> map = null; ???????try { ???????????// 生成实现指定算法的KeyPairGenerator对象,用于生成密钥对 ???????????KeyPairGenerator keyPairGenerator = ????????????????????KeyPairGenerator.getInstance(SECURITY, new BouncyCastleProvider()); ???????????keyPairGenerator.initialize(1024); // 初始化密钥长度 ???????????KeyPair keyPair = keyPairGenerator.generateKeyPair(); // 生成密钥对 ???????????RSAPublicKey rsaPublicKey = (RSAPublicKey) keyPair.getPublic(); // 获取公钥 ???????????RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) keyPair.getPrivate(); // 获取私钥 ???????????????????????// 保存到map中 ???????????map = new HashMap<String, Object>(); ???????????map.put(PUBLIC_KEY, rsaPublicKey); ???????????map.put(PRIVATE_KEY, rsaPrivateKey); ???????} catch (Exception e) { ???????????e.printStackTrace(); ???????} ???????return map; ???????????} ???????/** ????* 利用私钥进行解密 ????* ?????* @param privateKey ????* ?????????????????????私钥 ????* @param str ????* ?????????????????????密文 ????* @return ????*/ ???public static String decrypt(RSAPrivateKey privateKey, String str) { ???????????????try { ???????????System.out.println("密文为:" + str); ???????????// 获取实现指定转换的Cipher对象 ???????????Cipher cipher = Cipher.getInstance("RSA/NONE/NoPadding", new BouncyCastleProvider()); ???????????cipher.init(Cipher.DECRYPT_MODE, privateKey); // 用密钥初始化此Cipher对象 ???????????????????????int blockSize = cipher.getBlockSize(); // 返回块的大小 ???????????byte[] bytes = hexStringToBytes(str); // 将十六进制转换为二进制 ???????????int j = 0; ???????????ByteArrayOutputStream baos = new ByteArrayOutputStream(); ???????????while (bytes.length - j * blockSize > 0) { // 将二进制数据分块写入ByteArrayOutputStream中 ???????????????baos.write(cipher.doFinal(bytes, j * blockSize, blockSize)); ???????????????j++; ???????????} ???????????????????????// 将二进制数据转换为字符串 ???????????byte[] bs = baos.toByteArray(); ???????????StringBuilder sb = new StringBuilder(); ???????????sb.append(new String(bs)); ???????????String pwd = sb.reverse().toString(); ???????????System.out.println("明文为:" + pwd); ???????????return pwd; ???????} catch (Exception e) { ???????????e.printStackTrace(); ???????} ????????return null; ???????????} ???????/** ????* 将十六进制字符串转换为二进制数组 ????* ?????* @param hexString ????* ?????????????????十六进制字符串 ????* @return ????*/ ???private static byte[] hexStringToBytes(String hexString) { ???????????????if (hexString == null || "".equals(hexString)) { ???????????return null; ???????} ???????????????hexString = hexString.toUpperCase(); // 全部转换为大写字符 ???????int length = hexString.length() / 2; // 获取十六进制数据个数 ???????char[] hexChars = hexString.toCharArray(); // 将十六进制字符串转换为字符数组 ???????byte[] d = new byte[length]; ???????for (int i = 0; i < length; i++) { ???????????int pos = i * 2; // 开始位置 ???????????d[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1])); ????????} ???????return d; ???} ???????private static byte charToByte(char ch) { ???????????????return (byte) "0123456789ABCDEF".indexOf(ch); ???????????} ???}
login.jsp
<%@page import="java.util.Map"%><%@page import="java.security.interfaces.RSAPrivateKey"%><%@page import="java.security.interfaces.RSAPublicKey"%><%@page import="com.study.webrsa.utils.RSAUtils"%><%@ page language="java" contentType="text/html; charset=UTF-8" ???pageEncoding="UTF-8"%><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><html><head> ???<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> ???<title>用户登录</title> ???????<!-- 最新版本的 Bootstrap 核心 CSS 文件 --> ???<link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous"></head><% ???// 获取密钥对 ???Map<String, Object> map = RSAUtils.getKey(); ???// 获取公钥 ???RSAPublicKey rsaPublicKey = (RSAPublicKey) map.get(RSAUtils.PUBLIC_KEY); ???// 获取私钥 ???RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) map.get(RSAUtils.PRIVATE_KEY); ???// 保存私钥到session中,便于后台进行解密 ???session.setAttribute("rsaKey", rsaPrivateKey); ???// 保存公钥到request中,便于页面加密 ???String publicExponent = rsaPublicKey.getPublicExponent().toString(16); ???String publicModulus = rsaPublicKey.getModulus().toString(16); ???request.setAttribute("publicExponent", publicExponent); ???request.setAttribute("publicModulus", publicModulus);%><body> ???<div class="container-fluid"> ???????<form action="login" method="post" class="col-md-6 col-md-offset-3" ????????????onsubmit="return cmdEncrypt();"> ???????????<div class="form-group"> ???????????????<label for="loginName">登录名</label> ???????????????<input type="text" id="loginName" name="loginName" class="form-control" ????????????????????placeholder="请输入用户名..."> ???????????</div> ???????????<div class="form-group"> ???????????????<label for="loginPwd">登录密码</label> ???????????????<input type="password" id="loginPwd" name="loginPwd" class="form-control" ????????????????????placeholder="请输入登录密码..."> ???????????</div> ???????????<button type="submit" class="btn btn-primary">登录</button> ???????</form> ???</div></body> ???<script src="https://cdn.bootcss.com/jquery/3.2.1/jquery.min.js"></script> ???<!-- 最新的 Bootstrap 核心 JavaScript 文件 --> ???<script src="https://cdn.bootcss.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script> ???<script type="text/javascript" src="resources/rsa/BigInt.js"></script> ???<script type="text/javascript" src="resources/rsa/Barrett.js"></script> ???<script type="text/javascript" src="resources/rsa/RSA_Stripped.js"></script> ???<script type="text/javascript"> ???????????????// 提交前对密码进行加密 ???????function cmdEncrypt() { ???????????????????????setMaxDigits(131); ???????????var pwd = $("#loginPwd").val(); // 获取原始密码 ???????????var key = new RSAKeyPair("${publicExponent}", "", "${publicModulus}"); ???????????pwd = encryptedString(key, pwd); // 对密码进行加密 ???????????$("#loginPwd").val(pwd); ???????????return true; ???????????????????} ???????</script></html>
LoginServlet.java
package com.study.webrsa.servlet;import java.io.IOException;import java.security.interfaces.RSAPrivateKey;import javax.servlet.ServletException;import javax.servlet.annotation.WebServlet;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import com.study.webrsa.utils.RSAUtils;@WebServlet("/login")public class LoginServlet extends HttpServlet { ???private static final long serialVersionUID = 1L; ???@Override ???protected void doGet(HttpServletRequest req, HttpServletResponse resp) ????????????throws ServletException, IOException { ???????????????doPost(req, resp); ???????????} ???@Override ???protected void doPost(HttpServletRequest req, HttpServletResponse resp) ????????????throws ServletException, IOException { ???????????????// 设置编码格式 ???????req.setCharacterEncoding("UTF-8"); ???????resp.setCharacterEncoding("UTF-8"); ???????????????// 获取前台参数 ???????String loginName = req.getParameter("loginName"); ???????String loginPwd = req.getParameter("loginPwd"); ???????????????// 获取私钥 ???????RSAPrivateKey privateKey = (RSAPrivateKey) req.getSession().getAttribute("rsaKey"); ???????????????// 对密码进行解密 ???????loginPwd = RSAUtils.decrypt(privateKey, loginPwd); ???????????????// 校验 ???????if (true) { ???????????req.setAttribute("username", loginName); ???????????System.out.println("用户[" + loginName + "]用密码[" + loginPwd + "]登录本系统"); ???????????req.getRequestDispatcher("/success.jsp").forward(req, resp); ???????} ???????????} ???????}
success.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" ???pageEncoding="UTF-8"%><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><title>登录成功</title></head><body> ???<center> ???????<h1>欢迎您,${username }</h1> ???</center></body></html>
web.xml
<?xml version="1.0" encoding="UTF-8"?><web-app xmlns="http://java.sun.com/xml/ns/javaee" ???xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ???xsi:schemaLocation="http://java.sun.com/xml/ns/javaee ???????????????????????http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" ???version="3.0"> ???????<welcome-file-list> ???????<welcome-file>login.jsp</welcome-file> ???</welcome-file-list> ???</web-app>
4. 注意事项
4.1 setMaxDigits()
setMaxDigits(),到底应该传值多少?在JS文件中给出公式为:n * 2 / 16。其中n为密钥长度。 ???如果n为1024,则值应为 1024 * 2 / 16 = 128。经过测试,传128后台解密会报错;正确的值应该大于128。个人喜好的公式是:n * 2 / 16 + 3即 ?密钥长度若为1024,其值为 131 ???密钥长度若为2048,其值为 259
4.2 解密方式
在网上百度的代码,解密方式一般如下所示:
// 获取实现指定转换的Cipher对象Cipher cipher = Cipher.getInstance("RSA/NONE/NoPadding", new BouncyCastleProvider());cipher.init(Cipher.DECRYPT_MODE, privateKey); // 用密钥初始化此Cipher对象 ???????????int blockSize = cipher.getBlockSize(); // 返回块的大小byte[] bytes = new BigInteger(str, 16).toByteArray();int j = 0;ByteArrayOutputStream baos = new ByteArrayOutputStream();while (bytes.length - j * blockSize > 0) { // 将二进制数据分块写入ByteArrayOutputStream中 ???baos.write(cipher.doFinal(bytes, j * blockSize, blockSize)); ???j++;}
用上述方式,偶尔会报错如下所示:
java.lang.IllegalArgumentException: Bad arguments ???at javax.crypto.Cipher.doFinal(Cipher.java:2185) ???at com.study.webrsa.utils.RSAUtils.decrypt(RSAUtils.java:76) ???at com.study.webrsa.servlet.LoginServlet.doPost(LoginServlet.java:43) ???at javax.servlet.http.HttpServlet.service(HttpServlet.java:650) ???at javax.servlet.http.HttpServlet.service(HttpServlet.java:731) ???at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:303) ???at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208) ???at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52) ???at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241) ???at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208) ???at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:218) ???at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:110) ???at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:506) ???at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:169) ???at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:103) ???at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:962) ???at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116) ???at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:445) ???at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1087) ???at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:637) ???at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:318) ???at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) ???at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615) ???at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) ???at java.lang.Thread.run(Thread.java:745)
后发现问题就出现在toByteArray()上面,因为在用上面的三个JS进行加密时,偶尔得出的密文会比正确的密文多出一个byte,里面是o。因此可使用如下方式:
// 获取实现指定转换的Cipher对象Cipher cipher = Cipher.getInstance("RSA/NONE/NoPadding", new BouncyCastleProvider());cipher.init(Cipher.DECRYPT_MODE, privateKey); // 用密钥初始化此Cipher对象int blockSize = cipher.getBlockSize(); // 返回块的大小byte[] bytes = hexStringToBytes(str); // 将十六进制转换为二进制int j = 0;ByteArrayOutputStream baos = new ByteArrayOutputStream();while (bytes.length - j * blockSize > 0) { // 将二进制数据分块写入ByteArrayOutputStream中 ???baos.write(cipher.doFinal(bytes, j * blockSize, blockSize)); ???j++;} ???????????/** * 将十六进制字符串转换为二进制数组 * ?* @param hexString * ?????????????????十六进制字符串 * @return */private static byte[] hexStringToBytes(String hexString) { ???????if (hexString == null || "".equals(hexString)) { ???????return null; ???} ???????hexString = hexString.toUpperCase(); // 全部转换为大写字符 ???int length = hexString.length() / 2; // 获取十六进制数据个数 ???char[] hexChars = hexString.toCharArray(); // 将十六进制字符串转换为字符数组 ???byte[] d = new byte[length]; ???for (int i = 0; i < length; i++) { ???????int pos = i * 2; // 开始位置 ???????d[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1])); ????} ???return d;}private static byte charToByte(char ch) { ???????return (byte) "0123456789ABCDEF".indexOf(ch); ???}
参考:
JS加密Java解密报rsa bad argument
HTTPS优缺点、原理解析:我们的网站该不该做HTTPS?
更好的markdown体验:https://www.zybuluo.com/chy282/note/975080
使用rsa进行http传输加密
原文地址:http://www.cnblogs.com/jinjiyese153/p/7987735.html