基于TLS1.1协议学习笔记

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值