依据HTTPS中TLS/SSL加密原理衍生的数据加密实现

安全是互联网服务特别重要的一个方面, 这里主要解决B2C模式下, 客户端浏览器访问服务器时, 避免通讯期间的数据传输时被网络监听者劫持窃取修改等问题。
HTTPS本身已经实现了在客户端(浏览器)与服务端(网站)之间的数据传输加密, 中间涉及到了对称加密算法(加密业务数据)和非对称加密算法(加密对称加密算法的密钥);


1 . HTTPS在传输数据之前需要客户端(浏览器)与服务端(网站)之间进行一次握手,在握手过程中将确立双方加密传输数据的密码信息。TLS/SSL协议不仅仅是一套加密传输的协议,更是一件经过艺术家精心设计的艺术品,TLS/SSL中使用了非对称加密,对称加密以及HASH算法。握手过程的简单描述如下:
1) 浏览器将自己支持的一套加密规则发送给网站。
2) 网站从中选出一组加密算法与HASH算法,并将自己的身份信息以证书的形式发回给浏览器。证书里面包含了网站地址,加密公钥,以及证书的颁发机构等信息。
3) 获得网站证书之后浏览器要做以下处理:
a) 验证证书的合法性(颁发证书的机构是否合法,证书中包含的网站地址是否与正在访问的地址一致等),如果证书受信任,则浏览器栏里面会显示一个小锁头,否则会给出证书不受信的提示。
b) 如果证书受信任,或者是用户接受了不受信的证书,浏览器会生成一串随机数的密码,并用证书中提供的公钥加密。
c) 使用约定好的HASH计算握手消息,并使用生成的随机数对消息进行加密,最后将之前生成的所有信息发送给网站。
4)网站接收浏览器发来的数据之后要做以下的操作:
a) 使用自己的私钥将信息解密取出密码,使用密码解密浏览器发来的握手消息,并验证HASH是否与浏览器发来的一致。
b) 使用密码加密一段握手消息,发送给浏览器。
5) 浏览器解密并计算握手消息的HASH,如果与服务端发来的HASH一致,此时握手过程结束,之后所有的通信数据将由之前浏览器生成的随机密码并利用对称加密算法进行加密。
这里浏览器与网站互相发送加密的握手消息并验证,目的是为了保证双方都获得了一致的密码,并且可以正常的加密解密数据,为后续真正数据的传输做一次测试。

2 . 参考上面HTTPS加密实现原理, 实现自己的加密处理: 基于Filter, Servlet规范里面过滤器(这里是和springmvc中的interceptor 拦截器 做了比较的说法)的定义, 它刚刚好是一个客户端请求进入服务后抵达应用处理逻辑的前后(filter链的执行过程恰如栈一样的, 所有要把加解密这个filter放置的尽量靠前), 因此以这个地方为切入点是恰好满足我们的问题所需.
1) SpringMVC提供了一个名为OncePerRequestFilter的过滤器(它在request放置了一个参数保证每次请求只被dispatch一次), 这里继承这个类, 然后在doFilterInternal方法里实现自己的加密逻辑;

public class EncryptFilter extends OncePerRequestFilter {
    ......
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        //获取当前请求的URL
        String currentURI = URLUtil.getURIWithoutSuffix(request.getRequestURI());
        //判断是否是获取公钥的请求
        if (isPublicKeyRequest(currentURI)) {
            processGetPublicKey(request, response);
            return;
        }
        //判断是否是传递私钥的请求
        if (isExchangeSecretKeyRequest(currentURI)) {
            processExchangeSecretKey(request, response);
            return;
        }
        //这里可以处理放过一下自定义配置的不进行加解密操作的URLs
        if (!shouldFilter(currentURI)) {
            filterChain.doFilter(request, response);
            return;
        }
        //根据客户端传过来的标志位参数判断请求是否进行了加密, 然后才会进行对应解密操作
        boolean parameterDecrypt = getParameterDecrypt(request);
        String secretKey = null;

        if (parameterDecrypt) {
            secretKey = getSecretKey(request);
            if (secretKey == null) {
                processNoSecretKey(request, response);
                return;
            }
        }

        RequestWrapper requestWrapper = new RequestWrapper(request, request.getParameterMap(), parameterDecrypt, secretKey);
        ResponseWrapper responseWrapper = new ResponseWrapper(response);
        WebContext.registry(requestWrapper, responseWrapper);
        //执行正常的过滤器链,servlet服务也就包含在其中
        filterChain.doFilter(requestWrapper, responseWrapper);
        boolean encryptFlag = encrypt && getResponseEncrypt(request);
        String responseContent = responseWrapper.getContent();
        String responseStr = null;

        if (encryptFlag) {
            //判断需要加密, 随即对将要返回给客户端的报文进行加密
            responseStr = handleResponse(responseContent, secretKey, requestWrapper, responseWrapper);
        } else {
            responseStr = responseContent;
        }

        PrintWriter writer = null;

        try {
            writer = response.getWriter();
        } catch (Exception e) {
        }

        if (writer != null) {
            writer.write(responseStr);
            writer.flush();
        }
    }

    // 对HttpServletRequestWrapper对象的一个自定义包装, 加入加密所需的密钥
    public class RequestWrapper extends HttpServletRequestWrapper {

        private Map<String, String[]> params;

        private boolean decrypt;

        private String secretKey;

        public RequestWrapper(HttpServletRequest request, Map<String, String[]> parameterMap, boolean decrypt, String secretKey) {
            super(request);
            this.decrypt = decrypt;
            this.secretKey = secretKey;
            handleRequest(request, parameterMap);
        }

        @Override
        public Map<String, String[]> getParameterMap() {
            return params;
        }

        @Override
        public Enumeration<String> getParameterNames() {
            Vector<String> l = new Vector<String>(params.keySet());
            return l.elements();
        }

        @Override
        public String[] getParameterValues(String name) {
            Object v = params.get(name);

            if (v == null) {
                return null;
            } else if (v instanceof String[]) {
                return (String[]) v;
            } else if (v instanceof String) {
                return new String[] { (String) v };
            } else {
                return new String[] { v.toString() };
            }
        }

        @Override
        public String getParameter(String name) {
            Object v = params.get(name);

            if (v == null) {
                return null;
            } else if (v instanceof String[]) {
                String[] strArr = (String[]) v;
                if (strArr.length > 0) {
                    return strArr[0];
                } else {
                    return null;
                }
            } else if (v instanceof String) {
                return (String) v;
            } else {
                return v.toString();
            }
        }

        private void handleRequest(HttpServletRequest request, Map<String, String[]> parameterMap) {
            params = new HashMap<String, String[]>(parameterMap);
            Iterator<Entry<String, String[]>> iterator = params.entrySet().iterator();

            while (iterator.hasNext()) {
                Entry<String, String[]> entry = iterator.next();
                String key = entry.getKey();
                String[] value = entry.getValue();

                if (value == null) {
                    params.put(key, value);
                } else {
                    String[] valueArray = (String[]) value;

                    for (int i = 0; i < valueArray.length; i++) {
                        String valueStr = valueArray[i];

                        if (valueStr != null) {
                            String newValue = (String) valueStr;

                            if (decrypt) {
                                newValue = AESUtil.decryptStr(newValue, secretKey);
                            }
                        }
                    }

                    params.put(key, valueArray);
                }
            }
        }
    }

        public class ResponseWrapper extends HttpServletResponseWrapper {

        private PrintWriter cachedWriter;

        private CharArrayWriter bufferedWriter;

        public ResponseWrapper(HttpServletResponse response) {
            super(response);
            bufferedWriter = new CharArrayWriter();
            cachedWriter = new PrintWriter(bufferedWriter);
        }

        @Override
        public PrintWriter getWriter() {
            return cachedWriter;
        }

        /**
         * 获取输出内容
         * 
         * @return
         */
        public String getContent() {
            return bufferedWriter.toString();
        }
    }


    private String handleResponse(String responseContent, String secretKey, RequestWrapper request, ResponseWrapper response) {
        if (secretKey == null) {
            secretKey = getSecretKey(request);
        }

        if (secretKey == null) {
            secretKey = this.secretKey != null ? this.secretKey : DEFAULT_SECRET_KEY;
            response.setStatus(NO_SECRET_KEY);
        }

        response.setHeader(ENCRPY_HEADER, "true");
        return AESUtil.encryptStr(responseContent, secretKey);
    }

    //...
}

2) 在进行握手成功后, 把客户端的私钥放进了session, 原因一次握手的有效性与session生命有效性刚刚符合, 当客户端关闭连接或者session过期时, 便需要进行重新握手建立一次新的加密会话;

附录 : HTTPS一般使用的加密与HASH算法如下:
非对称加密算法:RSA,DSA/DSS
对称加密算法:AES,RC4,3DES
HASH算法:MD5,SHA1,SHA256

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值