基于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
- 基于TLS1.1协议学习笔记
- 基于TLS1.1协议学习笔记
- window中的tls1.0、tls1.1、tls1.2启用方法!
- 基于TCP协议的网络编程学习笔记(1)
- TLS/SSL 协议详解 (31)TLS1.1 TLS1.2 在CBC模式下两种不同的加解密方式及优化思考
- TLS/SSL 协议详解 (22)TLS1.3
- 发送基于TLS1.2的HTTPS请求
- 基于libevent的http协议 学习笔记之认识基本函数(1)
- 【Android学习笔记】基于TCP协议的Socket通信
- 基于xmpp协议的开源框架androidpn学习笔记
- HTTP 协议 学习笔记 1
- http协议学习笔记1
- TCP/IP协议详解卷1学习笔记_UDP协议
- TCP/IP协议详解卷1学习笔记_TCP协议
- RTP协议学习笔记(1)-RTP/RTCP/RTSP协议初探
- ZigBee无线协议学习笔记(1)
- ns学习笔记---AODV协议1
- ZigBee无线协议学习笔记(1)
- PROE技巧【显示隐含的对象】
- 系统管理员应该知道的 20 条 Linux 命令
- 《大话数据结构》-数据结构绪论与算法
- 面对对象之多态,接口
- 数据结构(Java)---单链表的转置问题
- 基于TLS1.1协议学习笔记
- 关于网页载入,页面顶部显示页面加载线性进度条效果实现。
- 求1+2!+3!+...+20!的和
- 投资感悟
- Java的方式向HTML形式的字符串中指定位置插入信息
- PAT——1016部分A+B
- js中同步与异步
- 关于系统安全性的几个概念
- 1024 多组最大字段和 循环使用优化