HTTP認證方式與https簡介

HTTP認證與https簡介

HTTP請求報頭: Authorization    [ˌɔ:θəraɪˈzeɪʃn]
HTTP響應報頭: WWW-Authenticate    [ɔ:ˈθentɪkeɪt]
HTTP認證是基於質詢/迴應(challenge/response)的認證模式。javascript

HTTP認證

BASIC認證

BASIC認證概述

當一個客戶端向HTTP服務器進行數據請求時,若是客戶端未被認證,則HTTP服務器將經過基本認證過程對客戶端的用戶名及密碼進行驗證,以決定用戶是否合法。客戶端在接收到HTTP服務器的身份認證要求後,會提示用戶輸入用戶名及密碼,而後將用戶名及密碼以BASE64加密,加密後的密文將附加於請求信息中, 如當用戶名爲anjuta,密碼爲:123456時,客戶端將用戶名和密碼用「:」合併,並將合併後的字符串用BASE64加密爲密文,並於每次請求數據時,將密文附加於請求頭(Request Header)中。HTTP服務器在每次收到請求包後,根據協議取得客戶端附加的用戶信息(BASE64加密的用戶名和密碼),解開請求包,對用戶名及密碼進行驗證,若是用戶名及密碼正確,則根據客戶端請求,返回客戶端所須要的數據;不然,返回錯誤代碼或從新要求客戶端提供用戶名及密碼。html

BASIC認證的過程

基本認證步驟:
一、客戶端訪問一個受http基本認證保護的資源。
二、服務器返回401狀態,要求客戶端提供用戶名和密碼進行認證。(驗證失敗的時候,響應頭會加上WWW-Authenticate: Basic realm="請求域"。)
401 Unauthorized
WWW-Authenticate: Basic realm="WallyWorld"
三、客戶端將輸入的用戶名密碼用Base64進行編碼後,採用非加密的明文方式傳送給服務器。
Authorization: Basic xxxxxxxxxx.
四、服務器將Authorization頭中的用戶名密碼解碼並取出,進行驗證,若是認證成功,則返回相應的資源。若是認證失敗,則仍返回401狀態,要求從新進行認證。java

BASIC認證的JAVA實現代碼

HttpSession session=request.getSession();
     String user=(String)session.getAttribute("user");
     String pass;
     if(user==null){
     try{
        response.setCharacterEncoding("GBK");
        PrintWriter ut=response.getWriter();
        String authorization=request.getHeader("authorization");
        if(authorization==null||authorization.equals("")){
            response.setStatus(401);
            response.setHeader("WWW-authenticate","Basic realm=\"請輸入管理員密碼\"");
            out.print("對不起你沒有權限!!");
            return;
        }
    String userAndPass=new String(new BASE64Decoder().decodeBuffer(authorization.split(" ")[1]));
    if(userAndPass.split(":").length<2){
        response.setStatus(401);
        response.setHeader("WWW-authenticate","Basic realm=\"請輸入管理員密碼\"");
        out.print("對不起你沒有權限!!");
        return;
    }
    user=userAndPass.split(":")[0];
    pass=userAndPass.split(":")[1];
    if(user.equals("111")&&pass.equals("111")){
        session.setAttribute("user",user);
        RequestDispatcher dispatcher=request.getRequestDispatcher("index.jsp");
        dispatcher.forward(request,response);
    }else{
        response.setStatus(401);
        response.setHeader("WWW-authenticate","Basic realm=\"請輸入管理員密碼\"");
        out.print("對不起你沒有權限!!");
        return;
    }
     }catch(Exception ex){
        ex.printStackTrace();
     }
     }else{
        RequestDispatcher dispatcher=request.getRequestDispatcher("index.jsp");
        dispatcher.forward(request,response);
    }

特記事項:

一、Http是無狀態的,同一個客戶端對同一個realm內資源的每個訪問會被要求進行認證。
二、客戶端一般會緩存用戶名和密碼,並和authentication realm一塊兒保存,因此,通常不須要你從新輸入用戶名和密碼。
三、以非加密的明文方式傳輸,雖然轉換成了不易被人直接識別的字符串,可是沒法防止用戶名密碼被惡意盜用。雖然用肉眼看不出來,但用程序很容易解密。git

優勢:

基本認證的一個優勢是基本上全部流行的網頁瀏覽器都支持基本認證。基本認證不多在可公開訪問的互聯網網站上使用,有時候會在小的私有系統中使用(如路由器
網頁管理接口)。後來的機制HTTP摘要認證是爲替代基本認證而開發的,容許密鑰以相對安全的方式在不安全的通道上傳輸。github

缺點:

  • 雖然基本認證很是容易實現,但該方案創建在如下的假設的基礎上,即:客戶端和服務器主機之間的鏈接是安全可信的。特別是,若是沒有使用SSL/TLS這樣的傳輸
    層安全的協議,那麼以明文傳輸的密鑰和口令很容易被攔截。該方案也一樣沒有對服務器返回的信息提供保護。web

  • 現存的瀏覽器保存認證信息直到標籤頁或瀏覽器被關閉,或者用戶清除歷史記錄。HTTP沒有爲服務器提供一種方法指示客戶端丟棄這些被緩存的密鑰。這意味着服務
    器端在用戶不關閉瀏覽器的狀況下,並無一種有效的方法來讓用戶登出。redis

HTTP OAuth認證

OAuth對於Http來講,就是放在Authorization header中的不是用戶名密碼, 而是一個token。微軟的Skydrive就是使用這樣的方式。
參考:http://www.tuicool.com/articles/qqeuE3算法

摘要認證

digest authentication(HTTP1.1提出的基本認證的替代方法)
這個認證能夠看作是基本認證的加強版本,不包含密碼的明文傳遞。數據庫

引入了一系列安全加強的選項;「保護質量」(qop)、隨機數計數器由客戶端增長、以及客戶生成的隨機數。
跨域

在HTTP摘要認證中使用 MD5 加密是爲了達成"不可逆的",也就是說,當輸出已知的時候,肯定原始的輸入應該是至關困難的。若是密碼自己太過簡單,也許能夠
經過嘗試全部可能的輸入來找到對應的輸出(窮舉攻擊),甚至能夠經過字典或者適當的查找表加快查找速度。

示例及說明
下面的例子僅僅涵蓋了「auth」保護質量的代碼,由於在撰寫期間,所知道的只有Opera和Konqueror網頁瀏覽器支持「auth-int」(帶完整性保護的認證)。

典型的認證過程包括以下步驟:

客戶端請求一個須要認證的頁面,可是不提供用戶名和密碼。一般這是因爲用戶簡單的輸入了一個地址或者在頁面中點擊了某個超連接。
服務器返回401 "Unauthorized" 響應代碼,並提供認證域(realm),以及一個隨機生成的、只使用一次的數值,稱爲密碼隨機數 nonce。
此時,瀏覽器會向用戶提示認證域(realm)(一般是所訪問的計算機或系統的描述),而且提示用戶名和密碼。用戶此時能夠選擇取消。
一旦提供了用戶名和密碼,客戶端會從新發送一樣的請求,可是添加了一個認證頭包括了響應代碼。

注意:客戶端可能已經擁有了用戶名和密碼,所以不須要提示用戶,好比之前存儲在瀏覽器裏的。

客戶端請求 (無認證):

GET /dir/index.html HTTP/1.0
    Host: localhost
    (跟隨一個新行,形式爲一個回車再跟一個換行)

服務器響應:

HTTP/1.0 401 Unauthorized
    Server: HTTPd/0.9
    Date: Sun, 10 Apr 2005 20:26:47 GMT
    WWW-Authenticate: Digest realm="testrealm@host.com",   //認證域
    qop="auth,auth-int",   //保護質量
    nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",  //服務器密碼隨機數
    opaque="5ccc069c403ebaf9f0171e9517f40e41"
    Content-Type: text/html
    Content-Length: 311
    
    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
     "http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd">
    <HTML>
      <HEAD>
    <TITLE>Error</TITLE>
    <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=ISO-8859-1">
      </HEAD>
      <BODY><H1>401 Unauthorized.</H1></BODY>
    </HTML>

客戶端請求 (用戶名 "Mufasa", 密碼 "Circle Of Life"):

GET /dir/index.html HTTP/1.0
    Host: localhost
    Authorization: Digest username="Mufasa",
     realm="testrealm@host.com",
     nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",
     uri="/dir/index.html",
     qop=auth,
     nc=00000001,//請求計數
     cnonce="0a4f113b",   //客戶端密碼隨機數
     response="6629fae49393a05397450978507c4ef1",
     opaque="5ccc069c403ebaf9f0171e9517f40e41"
    (跟隨一個新行,形式如前所述)。

服務器響應:

HTTP/1.0 200 OK
    Server: HTTPd/0.9
    Date: Sun, 10 Apr 2005 20:27:03 GMT
    Content-Type: text/html
    Content-Length: 7984
    (隨後是一個空行,而後是所請求受限制的HTML頁面)

response 值由三步計算而成。當多個數值合併的時候,使用冒號做爲分割符:

一、對用戶名、認證域(realm)以及密碼的合併值計算 MD5 哈希值,結果稱爲 HA1。
二、對HTTP方法以及URI的摘要的合併值計算 MD5 哈希值,例如,"GET" 和 "/dir/index.html",結果稱爲 HA2。
三、對HA一、服務器密碼隨機數(nonce)、請求計數(nc)、客戶端密碼隨機數(cnonce)、保護質量(qop)以及 HA2 的合併值計算 MD5 哈希值。結果即爲客戶端提供的
response 值。

由於服務器擁有與客戶端一樣的信息,所以服務器能夠進行一樣的計算,以驗證客戶端提交的 response 值的正確性。在上面給出的例子中,結果是以下計算的。
(MD5()表示用於計算MD5哈希值的函數;「」表示接下一行;引號並不參與計算)

HA1 = MD5( "Mufasa:testrealm@host.com:Circle Of Life" )
       = 939e7578ed9e3c518a452acee763bce9

HA2 = MD5( "GET:/dir/index.html" )
       = 39aff3a2bab6126f332b942af96d3366

Response = MD5( "939e7578ed9e3c518a452acee763bce9:\
                         dcd98b7102dd2f0e8b11d0f600bfb0c093:\
00000001:0a4f113b:auth:\
39aff3a2bab6126f332b942af96d3366" )
= 6629fae49393a05397450978507c4ef1

此時客戶端能夠提交一個新的請求,重複使用服務器密碼隨機數(nonce)(服務器僅在每次「401」響應後發行新的nonce),可是提供新的客戶端密碼隨機數(cnonce)。在後續的請求中,十六進制請求計數器(nc)必須比前一次使用的時候要大,不然攻擊者能夠簡單的使用一樣的認證信息重放老的請求。由服務器來確保在每一個發出的密碼隨機數nonce時,計數器是在增長的,並拒絕掉任何錯誤的請求。顯然,改變HTTP方法和/或計數器數值都會致使不一樣的 response值。

服務器應當記住最近所生成的服務器密碼隨機數nonce的值。也能夠在發行每個密碼隨機數nonce後,記住過一段時間讓它們過時。若是客戶端使用了一個過時的值,服務器應該響應「401」狀態號,而且在認證頭中添加stale=TRUE,代表客戶端應當使用新提供的服務器密碼隨機數nonce重發請求,而沒必要提示用戶其它用戶名和口令。

Cookie認證機制:用戶輸入用戶名和密碼發起請求,服務器認證後給每一個Session分配一個惟一的JSESSIONID,並經過Cookie發送給客戶端。
當客戶端發起新的請求的時候,將在Cookie頭中攜帶這個JSESSIONID。這樣服務器可以找到這個客戶端對應的Session。默認的,當咱們關閉瀏覽器的時候,客戶端cookie會被刪除,能夠經過修改cookie 的expire time使cookie在必定時間內有效。可是服務器端的session不會被銷燬,除非經過invalidate或超時。

Token Auth

經常使用的Token Auth(和Cookie Auth區別不大):

  1. 首次登錄,用戶名和密碼驗證過以後,將sessionId保存在token中,或者將一個key保存在token中,key的值能夠設置爲用戶惟一性的信息(帳號/密碼/身份認證機制(電話號/身份證號/支付寶帳號/銀行卡信息)...);固然咱們在程序中的實現是保存UUID做爲ticket。
  2. 設置token的有效期,並保存在服務器數據庫中;
  3. 服務器將這個token值返回給客戶端,客戶端拿到 token 值以後,將 token 保存在 cookie 中,之後客戶端再次發送網絡請求(通常不是登陸請求)的時候,就會將這個 token 值附帶到參數中發送給服務器。服務器接收到客戶端的請求以後,會取出token值與保存在本地(數據庫)中的token值作對比!
    若是兩個 token 值相同 :說明用戶登陸成功過!當前用戶處於登陸狀態!若是沒有這個token或者過時,則設置token爲無效,並讓用戶從新登陸。

這種方式在客戶端變化不大,也要利用cookie,改動的是服務器端
過去:經過sessionId查找Tomcat服務器內存中是否有sessionId對應的session存在,若存在,則表示登錄過,而後從session找出用戶信息;
如今:經過token查找數據庫中是否有相同的token,而且token要處於有效期前,有的話經過token在數據庫中找出用戶信息,不然從新登陸,(其實還包括sessionId的驗證,由於jsp默認建立session)。
若是以爲查詢數據庫比較耗時,能夠用memcache或者redis緩存一下。

首先說明一下session什麼時候會被建立:

  • 一、 請求JSP頁面時自動建立session,利用request.getSession(true);語句
    緣由:
    因爲HTTP是無狀態協議,這意味着每次客戶端檢索網頁時,都要單獨打開一個服務器http鏈接,若是我同一個瀏覽器,不一樣頁面打開你的主頁10次,那就要進行10次鏈接和斷開(TCP3次握手,4次揮手),浪費系統資源,http提供了一種長鏈接,keep-alive,相同會話的不一樣請求能夠用同一鏈接,故jsp默認建立session。而session的建立過程當中會自動將sessionId寫入cookie的JSESSIONID中的,這樣,只要不關閉瀏覽器,你在同一網站的任意網頁跳轉,因爲每次請求都會攜帶同一個sessionId,不會從新建立新的會話,防止建立多個會話浪費系統資源。
    不然:黑客利用幾臺主機,瘋狂的點擊某一個JSP頁面,若是每次點擊都建立一個新的會話,可能使服務器崩潰。

例子:
登陸函數:

// 用戶登陸操做
    public void login(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {
        request.setCharacterEncoding("gb2312");
        String account = request.getParameter("account");
        consumerDao = new ConsumerDao();
        ConsumerForm consumerForm = consumerDao.getConsumerForm(account);
        if (consumerForm == null) {
            request.setAttribute("information", "您輸入的用戶名不存在,請從新輸入!");
        } else if (!consumerForm.getPassword().equals(request.getParameter("password"))) {
            request.setAttribute("information", "您輸入的登陸密碼有誤,請從新輸入!");
        } else {

            request.setAttribute("form", consumerForm);
        }
        RequestDispatcher requestDispatcher = request.getRequestDispatcher("dealwith.jsp");
        requestDispatcher.forward(request, response);
    }

登陸主頁:

<body onselectstart="return false">
    <table width="800" height="496" border="0" align="center" cellpadding="0" cellspacing="0" background="images/login.jpg">
        <tr>
            <td valign="top">
                <table width="658" border="0">
                    <tr>
                        <td colspan="2">&nbsp;</td>
                    </tr>
                    <tr>
                        <td width="92" height="358">&nbsp;</td>
                        <td width="550" valign="bottom">
                            <form name="form1" method="post" action="ConsumerServlet?method=0&sign=0"
                                onSubmit="return userCheck()">
                                <table width="291" border="0" align="center" cellpadding="0" cellspacing="0">
                                    <tr>
                                        <td width="66" height="30">用戶名:</td>
                                        <td width="225"><input name="account" type="text" class="inputinput" id="account" size="30"></td>
                                    </tr>
                                    <tr>
                                        <td height="30">密&nbsp;&nbsp;碼:</td>
                                        <td><input name="password" type="password" class="inputinput" id="password" size="30"></td>
                                    </tr>
                                    <tr>
                                        <td height="30" colspan="2" align="center"><input type="image" class="inputinputinput" src="images/land.gif">
                                            &nbsp;&nbsp; <a href="#" onClick="javascript:document.form1.reset()"><img src="images/reset.gif"></a> &nbsp;&nbsp; <a
                                            href="consumer/accountAdd.jsp"><img src="images/register.gif"></a></td>
                                    </tr>
                                </table>
                            </form>
                        </td>
                    </tr>
                </table>
            </td>
        </tr>
    </table>
</body>

其中並無利用session的語句,可是主界面,當尚未登陸時:

可見有一個JSESSIONID,而且點擊其餘頁面並無開啓新的JSESSIONID。可是換一個瀏覽器就會產生新的JSESSIONID,由於不一樣瀏覽器的會話緩存是不能夠互相用的

登陸之後仍是這個JSESSIONID:

Session的銷燬只有三種方式:
1.調用了session.invalidate()方法
2.session過時(超時)
3.服務器從新啓動

單個JSP頁面禁用session方式

<%@ page session="false">
  • 在servlet中,只要代碼不調用Session相關的API就不會建立session
    request.getSession() 等價於 request.getSession(true)
    這兩個方法的做用是相同的,查找請求中是否有關聯的JSESSIONID,若是有則返回這個號碼所對應的session對象,若是沒有則生成一個新的session對象。因此說,經過此方法是必定能夠得到一個session對象。
    request.getSession(false) 查找請求中是否有關聯的JSESSIONID號,若是有則返回這個號碼所對應的session對象,若是沒有則返回一個null。
    注意在建立session的過程當中,sessionId是自動寫入cookie的JSESSIONID中的,若是禁用cookie,則經過url回傳

參考的頭條項目:

JSESSIONID和token(即ticket)同時存在,儘可能不往session裏放東西,將用戶主鍵,過時時間等都存到和ticket一塊兒的表中,優勢:1.減小內存佔用;2.Tomcat默認30分鐘session銷燬,採用token能夠長時間保持登陸狀態,但可能就不是這一個session了;3.除了JSESSIONID驗證一致性,增長一個token驗證,防止黑客暴力破解JSESSIONID,但黑客能夠得到JSESSIONID,估計得到token也不難,只是會麻煩一點。

牛客網:

淘寶網:

都用了token和session(NOWCODERCLINETID和cookie2,這個只是猜想),只是否是直接保存JSESSIONID,更名字了,若是攻擊者不分析站點,就不能猜到Session名稱,阻擋部分攻擊。

真正的應用:JWT
經過token能夠將用戶的基本信息(非隱私的,好比UserId,過時時間,生成的隨機key等)所有加密簽名後放入token中,從而服務器端不須要保存用戶登陸信息,大大減輕服務器壓力。用戶認證徹底靠token識別,經過簽名來保證token沒有被修改過(只有服務器才知道祕鑰,好比常見的非對稱加密算法),是服務器下發的token。在後續請求中,服務端只須要對用戶請求中包含的token進行解碼,驗證用戶登陸是否過時。

不少大型網站也都在用,好比 Facebook,Twitter,Google+,Github 等等,比起傳統的身份驗證方法,Token 擴展性更強,也更安全點,很是適合用在 Web 應用或者移動應用上。

Token Auth優勢:

  • 減輕服務器壓力:經過token能夠將用戶的基本信息(非隱私的,好比UserId,過時時間,生成的隨機key等)所有加密簽名後放入token中,從而服務器端不須要保存用戶登陸信息,大大減輕服務器壓力。用戶認證徹底靠token識別,經過簽名來保證token沒有被修改過(只有服務器才知道祕鑰,好比常見的非對稱加密算法),是服務器下發的token。
  • 支持跨域訪問:由於服務器並無保存登陸狀態,徹底靠簽名的token識別,那麼另外一個網站只要有對應的私鑰,就能夠對token驗證,前提是傳輸的用戶認證信息經過HTTP頭傳輸;
  • 更適用CDN: 能夠經過內容分發網絡請求你服務端的全部資料(如:javascript,HTML,圖片等),由於不須要同步服務器上的登陸狀態信息;
  • 性能更好: 由於從token中能夠得到userId,不用查詢登陸狀態表;

詳細

可是都不能很好的預防會話劫持

什麼是Realm

Tomcat提供Realm支持。
Tomcat使用Realm使某些特定的用戶組具備訪問某些特定的Web應用的權限,而沒有權限的用戶不能訪問這個應用。
Tomcat提供了三種不一樣Realm對訪問某個Web應用程序的用戶進行相應的驗證。

一、JDBCRealm,這個Realm將用戶信息存在數據庫裏,經過JDBC從數據庫中得到用戶信息並進行驗證。

二、JNDIRealm,將用戶信息存在基於LDAP等目錄服務的服務器裏,經過JNDI技術從LDAP服務器中獲取用戶信息並進行驗證。

三、MemoryRealm,將用戶信息存在一個xml文件中,對用戶進行驗證時,將會從相應的xml文件中提取用戶信息。manager(Tomcat提供的一個web應用程序)應用在進行驗證時即便用此種Realm。Realm相似於Unix裏面的group。在Unix中,一個group對應着系統的必定資源,某個group不能訪問不屬於它的資源。Tomcat用Realm來對不一樣的應用(相似系統資源)賦給不一樣的用戶(相似group)。沒有權限的用戶則不能訪問這個應用。

HTTPS傳輸協議原理

HTTPS(HTTP over SSL,其實是在原有的 HTTP 數據外面加了一層 SSL 的封裝。HTTP 協議原有的 GET、POST 之類的機制,基本上原封不動),是以安全爲目標的HTTP通道,簡單講是HTTP的安全版。即HTTP下加入SSL層,HTTPS的安全基礎是SSL,所以加密的詳細內容請看SSL。

兩種基本的加解密算法類型

  • 對稱加密:密鑰只有一個,加密解密爲同一個密碼,且加解密速度快,典型的對稱加密算法有DES、AES等。
  • 非對稱加密:密鑰成對出現(且根據公鑰沒法推知私鑰,根據私鑰也沒法推知公鑰),加密解密使用不一樣密鑰(公鑰加密須要私鑰解密,私鑰加密須要公鑰解密),相對對稱加密速度較慢,典型的非對稱加密算法有RSA、DSA等。

HTTPS通訊過程

HTTPS通訊的優勢

客戶端產生的密鑰只有客戶端和服務器端能獲得;
加密的數據只有客戶端和服務器端才能獲得明文;
客戶端到服務端的通訊是安全的。

SSL/TLS協議

SSL/TLS協議但願達到:
(1) 全部信息都是加密傳播,第三方沒法竊聽。
(2) 具備校驗機制,一旦被篡改,通訊雙方會馬上發現。
(3) 配備身份證書,防止身份被冒充。

歷史

1994年,NetScape公司設計了SSL協議(Secure Sockets Layer)的1.0版,可是未發佈。
1995年,NetScape公司發佈SSL 2.0版,很快發現有嚴重漏洞。
1996年,SSL 3.0版問世,獲得大規模應用。
1999年,互聯網標準化組織ISOC接替NetScape公司,發佈了SSL的升級版TLS 1.0版。
2006年和2008年,TLS進行了兩次升級,分別爲TLS 1.1版和TLS 1.2版。最新的變更是2011年TLS 1.2的修訂版。
目前,應用最普遍的是TLS 1.0,接下來是SSL 3.0。可是,主流瀏覽器都已經實現了TLS 1.2的支持。
TLS 1.0一般被標示爲SSL 3.1,TLS 1.1爲SSL 3.2,TLS 1.2爲SSL 3.3。

基本的運行過程

SSL/TLS協議的基本思路是採用公鑰加密法,也就是說,客戶端先向服務器端索要公鑰,而後用公鑰加密信息,服務器收到密文後,用本身的私鑰解密。

如何保證公鑰不被篡改?
解決方法:將公鑰放在數字證書中。只要證書是可信的,公鑰就是可信的。

公鑰加密計算量太大,如何減小耗用的時間?
解決方法:每一次對話(session),客戶端和服務器端都生成一個"對話密鑰"(session key),用它來加密信息。因爲"對話密鑰"是對稱加密,因此運算速度很是快,而服務器公鑰只用於加密"對話密鑰"自己,這樣就減小了加密運算的消耗時間。即在客戶端與服務器間傳輸的數據是經過使用對稱算法(如 DES 或 RC4)進行加密的。

所以,SSL/TLS協議的基本過程是這樣的:
(1) 客戶端向服務器端索要並驗證公鑰。
(2) 雙方協商生成"對話密鑰"。
(3) 雙方採用"對話密鑰"進行加密通訊。
上面過程的前兩步,又稱爲"握手階段"(handshake)。

握手階段的詳細過程

"握手階段"涉及四次通訊,咱們一個個來看。

1 客戶端發出請求(ClientHello)
首先,客戶端(一般是瀏覽器)先向服務器發出加密通訊的請求,這被叫作ClientHello請求。
在這一步,客戶端主要向服務器提供如下信息。
(1) 支持的協議版本,好比TLS 1.0版。
(2) 一個客戶端生成的隨機數,稍後用於生成"對話密鑰"。
(3) 支持的加密方法,好比RSA公鑰加密。
(4) 支持的壓縮方法。
這裏須要注意的是,客戶端發送的信息之中不包括服務器的域名。也就是說,理論上服務器只能包含一個網站,不然會分不清應該向客戶端提供哪個網站的數字證書。這就是爲何一般一臺服務器只能有一張數字證書的緣由。
對於虛擬主機的用戶來講,這固然很不方便。2006年,TLS協議加入了一個Server Name Indication擴展,容許客戶端向服務器提供它所請求的域名。

2 服務器迴應(SeverHello)
服務器收到客戶端請求後,向客戶端發出迴應,這叫作SeverHello。服務器的迴應包含如下內容。
(1) 確認使用的加密通訊協議版本,好比TLS 1.0版本。若是瀏覽器與服務器支持的版本不一致,服務器關閉加密通訊。
(2) 一個服務器生成的隨機數,稍後用於生成"對話密鑰"。
(3) 確認使用的加密方法,好比RSA公鑰加密。
(4) 服務器證書。
除了上面這些信息,若是服務器須要確認客戶端的身份,就會再包含一項請求,要求客戶端提供"客戶端證書"。好比,金融機構每每只容許認證客戶連入本身的網絡,就會向正式客戶提供USB密鑰,裏面就包含了一張客戶端證書。

3 客戶端迴應
客戶端收到服務器迴應之後,首先驗證服務器證書。若是證書不是可信機構頒佈、或者證書中的域名與實際域名不一致、或者證書已通過期,就會向訪問者顯示一個警告,由其選擇是否還要繼續通訊。
若是證書沒有問題,客戶端就會從證書中取出服務器的公鑰。而後,向服務器發送下面三項信息。
(1) 一個隨機數。該隨機數用服務器公鑰加密,防止被竊聽。
(2) 編碼改變通知,表示隨後的信息都將用雙方商定的加密方法和密鑰發送。
(3) 客戶端握手結束通知,表示客戶端的握手階段已經結束。這一項同時也是前面發送的全部內容的hash值,用來供服務器校驗。
上面第一項的隨機數,是整個握手階段出現的第三個隨機數,又稱"pre-master key"。有了它之後,客戶端和服務器就同時有了三個隨機數,接着雙方就用事先商定的加密方法,各自生成本次會話所用的同一把"會話密鑰"。

至於爲何必定要用三個隨機數,來生成"會話密鑰":
"無論是客戶端仍是服務器,都須要隨機數,這樣生成的密鑰纔不會每次都同樣。因爲SSL協議中證書是靜態的,所以十分有必要引入一種隨機因素來保證協商出來的密鑰的隨機性。
對於RSA密鑰交換算法來講,pre-master-key自己就是一個隨機數,再加上hello消息中的隨機,三個隨機數經過一個密鑰導出器最終導出一個對稱密鑰。
pre master的存在在於SSL協議不信任每一個主機都能產生徹底隨機的隨機數,若是隨機數不隨機,那麼pre master secret就有可能被猜出來,那麼僅適用pre master secret做爲密鑰就不合適了,所以必須引入新的隨機因素,那麼客戶端和服務器加上pre master secret三個隨機數一同生成的密鑰就不容易被猜出了,一個僞隨機可能徹底不隨機,但是是三個僞隨機就十分接近隨機了,每增長一個自由度,隨機性增長的可不是一。"
此外,若是前一步,服務器要求客戶端證書,客戶端會在這一步發送證書及相關信息。

4 服務器的最後迴應
服務器收到客戶端的第三個隨機數pre-master key以後,計算生成本次會話所用的"會話密鑰"。而後,向客戶端最後發送下面信息。
(1)編碼改變通知,表示隨後的信息都將用雙方商定的加密方法和密鑰發送。
(2)服務器握手結束通知,表示服務器的握手階段已經結束。這一項同時也是前面發送的全部內容的hash值,用來供客戶端校驗。
至此,整個握手階段所有結束。接下來,客戶端與服務器進入加密通訊,就徹底是使用普通的HTTP協議,只不過用"會話密鑰"加密內容。

非對稱加密

  須要一對密鑰,一個是私人密鑰,另外一個則是公開密鑰。這兩個密鑰是數學相關,用某用戶密鑰加密後所得的信息,只能用該用戶的解密密鑰才能解密。若是知道了其中一個,並不能計算出另一個。所以若是公開了一對密鑰中的一個,並不會危害到另一個的祕密性質。稱公開的密鑰爲公鑰;不公開的密鑰爲私鑰。
  若是加密密鑰是公開的,這用於客戶給私鑰全部者上傳加密的數據,這被稱做爲公開密鑰加密(狹義)。例如,網絡銀行的客戶發給銀行網站的操做信息的加密數據,採用公鑰加密。
  若是解密密鑰是公開的,用私鑰加密的信息,能夠用公鑰對其解密,用於客戶驗證持有私鑰一方發佈的數據或文件是完整準確的,接收者由此可知這條信息確實來自於擁有私鑰的某人,這被稱做數字簽名,公鑰的形式就是數字證書
  每一個人都有一對「鑰匙」(數字身份),其中一個只有她/他本人知道(私鑰),另外一個公開的(公鑰)。簽名的時候用私鑰,驗證簽名的時候用公鑰。又由於任何人均可以落款申稱她/他就是使用者本人,所以公鑰必須向接受者信任的人(身份認證機構)來註冊。註冊後身份認證機構給使用者發一數字證書。對文件簽名後,使用者把此數字證書連同文件及簽名一塊兒發給接受者,接受者向身份認證機構求證是否真地是用使用者的密鑰簽發的文件。
  在中國,數字簽名是具法律效力的,正在被廣泛使用。
  使用最普遍的是RSA算法

RSA算法原理

  1977年,三位數學家Rivest、Shamir 和 Adleman 設計了一種算法,能夠實現非對稱加密。這種算法用他們三我的的名字命名,叫作RSA算法。
  這種算法很是可靠,密鑰越長,它就越難破解。根據已經披露的文獻,目前被破解的最長RSA密鑰是768個二進制位。也就是說,長度超過768位的密鑰,還沒法破解(至少沒人公開宣佈)。所以能夠認爲,1024位的RSA密鑰基本安全,2048位的密鑰極其安全。

  "對極大整數作因數分解的難度決定了RSA算法的可靠性。換言之,對一極大整數作因數分解愈困難,RSA算法愈可靠。
  假若有人找到一種快速因數分解的算法,那麼RSA的可靠性就會極度降低。但找到這樣的算法的可能性是很是小的。今天只有短的RSA密鑰纔可能被暴力破解。到2008年爲止,世界上尚未任何可靠的攻擊RSA算法的方式。
  只要密鑰長度足夠長,用RSA加密的信息其實是不能被解破的。"
  RSA性能是很是低的,緣由在於尋找大素數、大數計算、數據分割須要耗費不少的CPU週期,因此通常的HTTPS鏈接只在握手時使用非對稱加密,經過握手交換對稱加密密鑰(其實只有第三次通訊,客戶端向服務器發送隨機數時用公鑰加密),在以後的通訊走對稱加密。

互質關係

  若是兩個正整數,除了1之外,沒有其餘公因子,咱們就稱這兩個數是互質關係(coprime)。好比,15和32沒有公因子,因此它們是互質關係。這說明,不是質數也能夠構成互質關係。
關於互質關係,不可貴到如下結論:
  1. 任意兩個質數構成互質關係,好比13和61。
  2. 一個數是質數,另外一個數只要不是前者的倍數,二者就構成互質關係,好比3和10。
  3. 若是兩個數之中,較大的那個數是質數,則二者構成互質關係,好比97和57。
  4. 1和任意一個天然數是都是互質關係,好比1和99。
  5. p是大於1的整數,則p和p-1構成互質關係,好比57和56。
  6. p是大於1的奇數,則p和p-2構成互質關係,好比17和15。

歐拉函數

任意給定正整數n,請問在小於等於n的正整數之中,有多少個與n構成互質關係?(好比,在1到8之中,有多少個數與8構成互質關係?)
計算這個值的方法就叫作歐拉函數,以φ(n)表示。在1到8之中,與8造成互質關係的是一、三、五、7,因此 φ(n) = 4。

由於任意一個大於1的正整數,均可以寫成一系列質數的積。

歐拉函數的通用計算公式

歐拉定理
模反元素

詳細看:
http://www.ruanyifeng.com/blog/2013/06/rsa_algorithm_part_one.html

密鑰生成的步驟

假設愛麗絲要與鮑勃進行加密通訊,她該怎麼生成公鑰和私鑰呢?

第一步,隨機選擇兩個不相等的質數p和q。
愛麗絲選擇了61和53。(實際應用中,這兩個質數越大,就越難破解。)

第二步,計算p和q的乘積n。
愛麗絲就把61和53相乘。
  n = 61×53 = 3233
n的長度就是密鑰長度。3233寫成二進制是110010100001,一共有12位,因此這個密鑰就是12位。實際應用中,RSA密鑰通常是1024位,重要場合則爲2048位。

第三步,計算n的歐拉函數φ(n)。
根據公式:
  φ(n) = (p-1)(q-1)
愛麗絲算出φ(3233)等於60×52,即3120。

第四步,隨機選擇一個整數e,條件是1< e < φ(n),且e與φ(n) 互質。
愛麗絲就在1到3120之間,隨機選擇了17。(實際應用中,經常選擇65537。)

第五步,計算e對於φ(n)的模反元素d。
所謂"模反元素"就是指有一個整數d,可使得ed被φ(n)除的餘數爲1。
  ed ≡ 1 (mod φ(n))
這個式子等價於
  ed - 1 = kφ(n)
因而,找到模反元素d,實質上就是對下面這個二元一次方程求解。
  ex + φ(n)y = 1
已知 e=17, φ(n)=3120,
  17x + 3120y = 1
這個方程能夠用"擴展歐幾里得算法"求解,此處省略具體過程。總之,愛麗絲算出一組整數解爲 (x,y)=(2753,-15),即 d=2753。
至此全部計算完成。

第六步,將n和e封裝成公鑰,n和d封裝成私鑰。
在愛麗絲的例子中,n=3233,e=17,d=2753,因此公鑰就是 (3233,17),私鑰就是(3233, 2753)。
實際應用中,公鑰和私鑰的數據都採用ASN.1格式表達(實例)。

結論:若是n能夠被因數分解,d就能夠算出,也就意味着私鑰被破解。
大整數的因數分解,是一件很是困難的事情。目前,除了暴力破解,尚未發現別的有效方法。

詳細看:
http://www.ruanyifeng.com/blog/2013/07/rsa_algorithm_part_two.html

誤區

HTTPS沒法緩存

在你們的觀念裏,出於安全考慮,瀏覽器不會在本地保存HTTPS緩存。 實際上, 只要在HTTP頭中使用特定命令,HTTPS是能夠緩存的。
Microsoft 項目經理Eric Lawrence 寫道:
好比,若是頭命令是Cache-Control:max-age=600,那麼HTTPS的網頁就將被IE緩存10分鐘,IE的緩存策略與是否使用HTTPS協議無線。其它瀏覽器也有相似的操做方法。
Firefox默認只在內存中緩存HTTPS。可是,只要頭命令中有Cache-Control:Public,緩存就會被寫到硬盤上。下面的圖片顯示,Firefox的硬盤緩存中有HTTPS內容,頭命令正是Cache-Control:Public。

有了HTTPS,Cookie和查詢字符串就安全了

雖然沒法直接從HTTPS數據中讀取Cookie和查詢字符串,可是你仍然須要使它們的值變得難以預測。 好比,曾經有一家英國銀行,直接使用順序排列的數值表示session id: 黑客能夠先註冊一個帳戶,找到這個cookie,看到這個值的表示方法。而後,改動cookie,從而劫持其餘人的session id。至於查詢字符串,也能夠經過相似方式泄漏。

相關文章
相關標籤/搜索