基於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

發佈了42 篇原創文章 · 獲贊 9 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章