基于TLS1.1协议学习笔记

来源:互联网 发布:c语言swap 编辑:程序博客网 时间:2024/06/07 01:08

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(握手得到的服务器随机数)+额外信息+握手得到的服务器公钥=组成clientKeyExchange3.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

 
原创粉丝点击