基于TLS1.1协议学习笔记

原创 2015年11月24日 13:58:36

SSL

SSL(Secure Sockets Layer,安全套接层),及其继任者 TLS(Transport Layer Security,传输层安全)是为网络通信提供安全及数据完整性的一种安全协议。TLS与SSL在传输层对网络连接进行加密。

为Netscape所研发,用以保障在Internet上数据传输之安全,利用数据加密(Encryption)技术,可确保数据在网络上之传输过程中不会被截取及窃听。

SSL协议位于TCP/IP协议与各种应用层协议之间,为数据通讯提供安全支持。SSL协议可分为两层:
SSL记录协议(SSL Record Protocol):它建立在可靠的传输协议(如TCP)之上,为高层协议提供数据封装、压缩、加密等基本功能的支持。
SSL握手协议(SSL Handshake Protocol):它建立在SSL记录协议之上,用于在实际的数据传输开始前,通讯双方进行身份认证、协商加密算法、交换加密密钥等。

提供服务

SSL协议提供的服务主要有:
  1)认证用户和服务器,确保数据发送到正确的客户机和服务器;
  2)加密数据以防止数据中途被窃取;
  3)维护数据的完整性,确保数据在传输过程中不被改变。

工作流程

服务器认证阶段:

1)客户端向服务器发送一个开始信息“Hello”以便开始一个新的会话连接;
2)服务器根据客户的信息确定是否需要生成新的主密钥,如需要则服务器在响应客户的“Hello”信息时将包含生成主密钥所需的信息;
3)客户根据收到的服务器响应信息,产生一个主密钥,并用服务器的公开密钥加密后传给服务器;
4)服务器回复该主密钥,并返回给客户一个用主密钥认证的信息,以此让客户认证服务器。

用户认证阶段:
在此之前,服务器已经通过了客户认证,这一阶段主要完成对客户的认证。经认证的服务器发送一个提问给客户,客户则返回(数字)签名后的提问和其公开密钥,从而向服务器提供认证。

SSL协议提供的安全通道有以下三个特性:

机密性:SSL协议使用密钥加密通信数据。
可靠性:服务器和客户都会被认证,客户的认证是可选的。
完整性:SSL协议会对传送的数据进行完整性检查。

从SSL 协议所提供的服务及其工作流程可以看出,SSL协议运行的基础是商家对消费者信息保密的承诺,这就有利于商家而不利于消费者。在电子商务初级阶段,由于运作电子商务的企业大多是信誉较高的大公司,因此这问题还没有充分暴露出来。但随着电子商务的发展,各中小型公司也参与进来,这样在电子支付过程中的单一认证问题就越来越突出。虽然在SSL3.0中通过数字签名和数字证书可实现浏览器和Web服务器双方的身份验证,但是SSL协议仍存在一些问题,比如,只能提供交易中客户与服务器间的双方认证,在涉及多方的电子交易中,SSL协议并不能协调各方间的安全传输和信任关系。在这种情况下,Visa和 MasterCard两大信用卡公组织制定了SET协议,为网上信用卡支付提供了全球性的标准。

SSL 协议的握手过程

开始加密通信之前,客户端和服务器首先必须建立连接和交换参数,这个过程叫做握手(handshake)。
假定客户端叫做爱丽丝,服务器叫做鲍勃,整个握手过程可以用下图说明(点击看大图)。

这里写图片描述

握手阶段分成五步。

第一步,爱丽丝给出协议版本号、一个客户端生成的随机数(Client random),以及客户端支持的加密方法。

第二步,鲍勃确认双方使用的加密方法,并给出数字证书、以及一个服务器生成的随机数(Server random)。

第三步,爱丽丝确认数字证书有效,然后生成一个新的随机数(Premaster secret),并使用数字证书中的公钥,加密这个随机数,发给鲍勃。

第四步,鲍勃使用自己的私钥,获取爱丽丝发来的随机数(即 Premaster secret)。

第五步,爱丽丝和鲍勃根据约定的加密方法,使用前面的三个随机数,生成"对话密钥"(session key),用来加密接下来的整个对话过程。

上面的五步,画成一张图,就是下面这样。

这里写图片描述

结合产品代码来学习:

描述:

基于TLS v1.1实现信道加密协议。不直接在客户端和服务器间使用TLS建立加密信道的原因是:(1)客户端的本地TLS实现规范不统一,不能保证加密强度和实现的质量,(2)一些地区的移动运营商WAP网关对于建立HTTPS通道支持不稳定。所以,我们希望在HTTP之上建立加密信道,采用修改后的TLS作为信道加密协议。

目标:

   当前加密通道的建立与初始化页面请求在同一个接口getui中实现,这样对接口职责的独立产生了一定的影响,同时不利于扩展。只有如老版的getkey接口才实现了接口职责独立,但实际应用中,getkey又在扩展能力上明显不足,比如当前登陆操作需要重建加密通道的情况下,getui接口无法实现,而getkey又因为实际需要必须传递clienttype,这时又为了避免明文传送,不得不又对这个参数执行了多余的RSA公钥加密,此类设计预留的不足往往导致了后期扩展的情况下,客户端和服务器端不得不引入更多的复杂设计,实现和维护的代价和成本都更高。

   为此,我们希望建立一个独立的接口用于建立客户端、服务器间的加密信道。该接口的设计需要参考TLS v1.1的实现。这个接口应和客户端的用户登陆等界面区分开;这样,虽然效率可能降低,但是我们可以在任意业务流程中随时建立加密通道,而不一定要求同时进行用户的身份认证,从而将信道加密和身份认证完全分离。这样的做法也是HTTP、HTTPS的实现方式。在加密信道之上,我们可以对于某些数据进行进一步加密。处理完全由应用层决定,和这里描述的协议无关。

协议概述:

TLS提供两类基础协议:Record Protocol提供了对于应用数据的封装,Handshaking Protocols提供了客户端、服务器间对于安全参数的协商。其中,Handshaking Protocols包含三个子协议:加密算法修改协议(Change Cipher Protocol),告警协议(Alert Protocol),握手协议(Handshake Protocol);核心的安全参数协商协议为握手协议。

我们要求所有的数据传输都基于HTTP:例如,所有的参数都基于HTTP POST提供。因此,我们不直接使用TLS,而是在TLS基础修改。

/**
     * 建立加密信道
     * 
     * @param context
     * @param sendData 项目中需要向加密信道传递的数据
     * @param isOfflineUpdate 是否在握手时做离线更新
     * 
     * @throws Exception
     */
    public ClientHello(final Context context, String sendData, boolean isOfflineUpdate) 
            throws Exception {
            //是否离线更新
        mIsOfflineUpdate = isOfflineUpdate;
        //获取配置参数
        mEMPConfig = EMPConfig.newInstance();
        //初始化TLS参数
        initTlsData(context);
        //双向验证标记
        readClientTwoWaySign();
        mCurContext = context;
        //服务器地址
        String url = mEMPConfig.getServerUri();
        String version = Utils.getVersionName(context);//客户端版本号

        // get RNS2.服务器随机数
        byte[] rns2 = readServerRandom2(context);

        // get Server Certificate.服务器证书
        byte[] cerByts = readServerCertificate(context);

        // send ClientHello request.走简化流程
        if (rns2 != null && rns2.length > 0 && cerByts != null && cerByts.length > 0) {
            Object certification = RSAAdapter.getCertificate(cerByts);
            mServerPubKey = RSAAdapter.getPublicKey(certification);
            mRNS = rns2;
            mRNS2 = rns2;
            facilityClientHello(context, url, version, sendData);
            //走全部流程
            fullClientHello(context, url, version, sendData);
        }
        OfflinePerfTestManager.printDuration(OfflinePerfTestManager.CLIENTHEOOL);
    }

走全部流程代码:

/**
     * 全流程信道建立
     * 
     * @param context
     * @param url
     * @param version
     * @param sendData
     * 
     * @throws Exception
     */
    final void fullClientHello(final Context context, final String url, final String version, 
            String sendData) throws Exception {
        // get ClientHello body组装客户端信息为body,之后发送网络请求给服务器验证
        final byte[] body = createFullClientHelloBody();

        String isfirst = AndroidPreferenceDB.ANDROIDDB.getString(AndroidPreferenceDB.ISFIRST_DB);
        if (null == isfirst || isfirst.equals(""))
            isfirst = "0";
        //根据客户端信息组装url
        String uri = url.concat(CLIENT_HELLO)
                .concat("&clientinfo=").concat("android-").concat(Utils.getPhoneTarget())
                    .concat("-").concat(version).concat("-").concat(Utils.getClientID())
                .concat("&is_first=").concat(isfirst).concat(sendData);

        // store the Client Hello request body.
        mClientHelloBody = body;
        byte[] byts = null;
        // get server hello.
        //加密body
        String bodyStr = Base64.encode(body);

        try {
        //发送网络请求
            byts = (byte[]) mHttpManager.sendPostRequest(uri, bodyStr, false, null, null, null);
        } catch (HttpResponseException ex) {
            String msg = EMPTips.getTLSHttpConnectFail();
            String errorCode = EMPTips.getErrorCode();
            if (!Utils.isEmpty(errorCode)) {
                msg += errorCode + String.valueOf(mHttpManager.mResponseCode);
            }
            throw new Exception(msg);
        }
        final byte[] temp = byts;

        //处理获取到的服务器信息,保存服务器随机数,利用本地保存的证书,获取公钥,用拿到的客户端公钥再验证从服务器受到证书的有效性。验证通过之后,通过获取的服务器证书拿到服务器公钥并保存到本地。最后,处理服务器是否发送双向验证消息,更新客户端双向验证标记
        handleFullServerHelloResponse(byts, context);

        // send ClientKeyExchange.
        /**
1.获取用客户端公钥生成的服务端的RSA公钥证书(通过客户端的本地保存的双向验证公钥PK.dat设备ID,组装服务器认识的URL,发送POST网络请求,得到用客户端公钥生成的服务器证书,并保存到本地)

2.getClientKeyExchangeBody(),预主密钥mPMS+mRNS(握手得到的服务器随机数)+额外信息+握手得到的服务器公钥=组成clientKeyExchange

3.clientCertificate=getClientCertificateBody()拿到客户端本地保存的双向验证公钥证书

4.certificateVerify证书验证,getCertificateVerifyBody(context, request1, reponse1, Utils.joinBytes(clientKeyExchange, clientCertificate));request1 = 客户端握手时组装的Body,reponse1=服务器返回的握手信息,第四个参数为客户端验证服务器时的body+客户端本地保存的证书,这步中把这三个参数拼接起来,先进行MD5加密,再进行SHA1加密,得到messageData,之后再用客户端保存的双向验证客户端私钥的到签名后的messageData,加上数据长度组装成CertificateVerify body。

5.getChangeCipherSpecBody() 发送ChangeCipherSpec声明切换到加密信道传输。
6.Utils.joinBytes(clientKeyExchange, clientCertificate, certificateVerify, changeCipherSpec);吧前面得到的客户端交换信息,客户端双向验证证书,签名后的证书验证信息(客户端与服务器沟通的各种信息),是否改变加密算法集等组装成request3Body,之后handshakeMsg = getHandshakeMessage(mClientHelloBody, mServerHelloBody, request3Body);request3Body+客户端握手信息,服务器响应的握手信息。之后finish = getFinishBody(handshakeMsg);应用PRF加密算法 加密getVerifyData(handshakeMsg)预主密钥+客户端服务端随机数通过PRF算法生成主密钥,再用PRF算法加密主密钥+(MD5+SHA1生成的handshakeMsg)生成最后的待验证信息。

7.之后// create body
        byte[] bodyByts = Utils.joinBytes(clientKeyExchange, clientCertificate, certificateVerify, changeCipherSpec, finish, offline);
        创建body,String bodyStr = Base64.encode(bodyByts);加密body,reply = (byte[]) mHttpManager.sendPostRequest(url, bodyStr, false, null, HttpManager.MIME_ARC, task)发送网络请求,服务器返回replay。此处为byts
        */
        byts = sendClientKeyExchange(context, url, version, body, temp, null);

        Utils.printLog("fullClientHello", "");
        /**处理上面服务器返回的数据,byts
服务器收到ClientKeyExchange并处理,返回消息,完成信道协商:

a. 使用私钥解密并取出{PMS,ServerHello.Timestamp , ServerHello.Random, extensionField(最大32字节) }。使用PMS、(ClientHello.Timestamp+ClientHello.Random)[RNC]、(ServerHello.Timestamp+ServerHello.Random) [RNS]计算MS,并提取需要的extensionField数值。

b. 生成服务器的预主密钥premaster secret2 [PMS2]。使用PMS2、RNC、RNS生成服务器主密钥master secret2 [MS2],在会话中保存MS2作为传输密钥。

c. 如服务器选择CipherSuite为传输一次一密的特性,则使用MS2与每次请求报文头中的X-EMP-SessionNum执行传输的一次一密。

d. 生成下次使用的服务器缓存随机数(ServerHello.Timestamp+ServerHello.Random) [RNS2]。根据协商好的对称加密算法,使用MS对称加密tuple{ RNS2, PMS2},结果以二进制形式保存在消息ServerKeyExchange。其中该消息后有hmac的摘要签名保障完整性。

e. 如果有ClientCertificate,验证相关信息。验证客户端的Finished信息。如果错误,返回标准失败信息。

f. 生成服务器的Finished,将Finished之前发出的和接收到消息(不包括Finished本身)的二进制数据,按照顺序连接后,使用MS做PRF签名。
g. 回传:(1)ServerKeyExchange,(2)确认加密算法集ChangeCipherSpec,(3)确认传输密钥的安全级别,(4)发送自己的Finished消息。

5. 客户端收到服务器信息后:
a. 验证服务器的Finished消息。如失败,切断当前连接。
b. 根据协商好的对称加密算法,使用本地保存的MS解密ServerKeyExchange,使用MS对该消息后的hmac摘要签名执行验证,取出tuple{ RNS2, PMS2}。使用PMS2、RNC、RNS生成MS2作为信道密钥。在缓存中保存RNS2。
c. 如CipherSuite为传输一次一密的特性,则使用MS2与每次请求或响应报文头中的X-EMP-SessionNum执行传输的一次一密。
        */
        String initContent = handleFullServerKeyExchangeResponse(byts, context);
        setText(initContent);
        mConnectTimes++;
    }

握手时组装的Body:

/**
     * 【ClientHello】
     * 
     * @return 组装好的Body
     * 
     * @throws Exception
     */
    private final byte[] createFullClientHelloBody() throws Exception {
        // ClientVesion
        byte[] protocolVersion = getClientProtocolVersion();//拿到客户端信道版本号

        // ClientRandom
        byte[] clientGmtUnixTime = Utils.getClientGMTUnixTime();//获取客户端时区信息的byte数组
        byte[] clientRandom = getClientRandom(28);//生成一个28位的客户端随机数
        mRNC = Utils.joinBytes(clientGmtUnixTime, clientRandom);//时区信息和28位随机数,共同组成最终的客户端随机数
        //获取组编号
        byte[] groupInfor = getGroupInfor();
        //获取加密算法
        byte[] cipherSuiteInfor = getCipherSuiteInfor();

        // Certificate_SerialNumber
        //证书编号
        byte[] certificateSerialNumberInfor = getCertSerialNumberInfor();

        //组装握手信息传递给服务器的数据
        byte[] clientHelloData = Utils.joinBytes(protocolVersion, mRNC, groupInfor, cipherSuiteInfor, certificateSerialNumberInfor);

        // message type and length
        byte[] messageType = new byte[1];
        messageType[0] = Constant.HandshakeType[Constant.htIndex.client_hello.ordinal()];
        int mLen = clientHelloData.length;
        byte[] messageLength = Utils.intToByteArrayInNBO(mLen);

        // add MessageType
        byte[] clientHelloBody = Utils.joinBytes(messageType, messageLength, clientHelloData);
        //返回加入消息类型的body
        return clientHelloBody;
    }

SSL/TLS协议簇加解密流程 :http://blog.csdn.net/sealyao/article/details/5901510

版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

TLS/SSL 协议详解 (28) TLS 1.0、TLS 1.1、TLS 1.2之间的区别

HTTPS, TLS, SSL
  • mrpre
  • mrpre
  • 2017年09月14日 11:32
  • 822

传输层安全协议(TLS)1.2版

1.介绍 TLS协议的主要目标是在两个通信应用之间提供私密性和数据完整性。这个协议由两层组成:TLS记录协议和TLS握手协议。最低层是基于一些可靠传输协议(如TCP)的TLS记录协议。TLS记录协议...

在Windows服务器上启用TLS 1.2及TLS 1.2基本原理

在Windows服务器上启用TLS 1.2及TLS 1.2基本原理 2015-10-23 17:28 在Windows服务器上启用TLS 1.2及TLS 1.2基本原理   ...

从远程客户端应用程序收到一个 SSL 连接请求,但 客户端应用程序支持的密码套件 iis

 如何启用 Schannel 事件日志记录 你可以使用此注册表设置来启用日志记录由 Schannel 安全支持提供程序生成的客户端证书验证失败。 记录客户端证书验证失败是一...
  • bixing
  • bixing
  • 2017年04月11日 10:58
  • 902

TCP连接、Http连接与Socket连接

1、TCP连接 手机能够使用联网功能是因为手机底层实现了TCP/IP协议,可以使手机终端通过无线网络建立TCP连接。TCP协议可以对上层网络提供接口,使上层网络数据的传输建立在“无差别”的网络之上。 ...
  • wuruixn
  • wuruixn
  • 2013年09月25日 17:28
  • 3094

Delphi7高级应用开发随书源码

  • 2003年04月30日 00:00
  • 676KB
  • 下载

微信小程序、SSL证书、开启服务器TSL1.0、TSL1.1、TSL1.2服务

看到这个标题的你一定很无语,“这什么跟什么嘛。”,其实这是我这几天折腾微信小程序给我们公司网站安装ssl证书遇到的各种问题,好的来我们进入正题: 由于小程序的接口要求https协议,我们需要先申请一个...

关系型数据库横向扩展的三种方法

本文是 Oracle Coherence 3.5一书,第一章: Achieving Performance, Scalability, and Availability Objectives,第二节:...

Android https 自定义 证书 问题

转自 https://my.oschina.net/AliMobileSecurity/blog/755304 摘要: 为了保护用户的信息安全、保护自己的商业利益,减少攻击面,我们需要保...

学习笔记1.1 AAA服务器及协议

AAA是Authentication、Authorization、Accounting(认证、授权、计费)的简称,是网络安全的一种管理机制,提供了认证、授权、计费三种安全功能。 AAA一般采用客户机...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:基于TLS1.1协议学习笔记
举报原因:
原因补充:

(最多只允许输入30个字)