一文讀懂Https的安全性原理、數字證書、單項認證、雙項認證等

jsjsjjs發表於2018-05-07

本文引用了作者Smily(部落格:blog.csdn.net/qq_20521573)的文章內容,感謝無私分享。

1、前言

目前蘋果公司已經強制iOS應用必須使用HTTPS協議開發(詳見《蘋果即將強制實施 ATS,你的APP準備好切換到HTTPS了嗎?》),雖然Google沒有強制開發者使用HTTPS,但相信不久的將來Android也會跟隨iOS全面轉向HTTPS。因此,HTTPS的學習也是相當重要。本篇文章涉及到的程式碼不多,主要內容是對HTTPS協議的講解,最後將結合Retrofit實現HTTPS的單雙向認證。

下面將通過以下幾節內容來學習HTTPS:

1)HTTPS概述;

2)HTTPS實現原理;

3)數字證照;

4)Https單項認證;

5)Https雙向認證。

學習交流:

– 即時通訊開發交流3群:185926912[推薦]

– 移動端IM開發入門文章:《新手入門一篇就夠:從零開發移動端IM

(本文同步釋出於:http://www.52im.net/thread-1574-1-1.html

2、相關文章

即時通訊安全篇(一):正確地理解和使用Android端加密演算法

即時通訊安全篇(二):探討組合加密演算法在IM中的應用

即時通訊安全篇(三):常用加解密演算法與通訊安全講解

即時通訊安全篇(四):例項分析Android中金鑰硬編碼的風險

即時通訊安全篇(五):對稱加密技術在Android平臺上的應用實踐

即時通訊安全篇(六):非對稱加密技術的原理與應用實踐

傳輸層安全協議SSL/TLS的Java平臺實現簡介和Demo演示

理論聯絡實際:一套典型的IM通訊協議設計詳解(含安全層設計)

微信新一代通訊安全解決方案:基於TLS1.3的MMTLS詳解

來自阿里OpenIM:打造安全可靠即時通訊服務的技術實踐分享

簡述實時音視訊聊天中端到端加密(E2EE)的工作原理

移動端安全通訊的利器——端到端加密(E2EE)技術詳解

Web端即時通訊安全:跨站點WebSocket劫持漏洞詳解(含示例程式碼)

通俗易懂:一篇掌握即時通訊的訊息傳輸安全原理

IM開發基礎知識補課(四):正確理解HTTP短連線中的Cookie、Session和Token

>> 更多同類文章 ……

3、HTTPS概述

那麼什麼是HTTPS? 

我們看維基百科給HTTPS的定義:

HTTPS(Hypertext Transfer Protocol Secure)是一種通過計算機網路進行安全通訊的傳輸協議。HTTPS經由HTTP進行通訊,但利用TLS來加密資料包。HTTPS開發的主要目的,是提供對網站伺服器的身份認證,保護交換資料的隱私與完整性。

原來HTTPS就是在HTTP協議的基礎上加入了TLS協議。目的是保證我們的資料在網路上傳輸的安全性。

什麼是TLS?

TLS是傳輸層加密協議,前身是SSL協議。由網景公司於1995年釋出。後改名為TLS。常用的 TLS 協議版本有:TLS1.2, TLS1.1, TLS1.0 和 SSL3.0。其中 SSL3.0 由於 POODLE 攻擊已經被證明不安全。TLS1.0 也存在部分安全漏洞,比如 RC4 和 BEAST 攻擊。

由於HTTP協議採用明文傳輸,我們可以通過抓包很輕鬆的獲取到HTTP所傳輸的資料。因此,採用HTTP協議是不安全的。這才催生了HTTPS的誕生。

HTTPS相對HTTP提供了更安全的資料傳輸保障,主要體現在三個方面:

1)內容加密:客戶端到伺服器的內容都是以加密形式傳輸,中間者無法直接檢視明文內容;

2)身份認證:通過校驗保證客戶端訪問的是自己的伺服器;

3)資料完整性:防止內容被第三方冒充或者篡改。

4、HTTPS實現原理

在學習HTTPS原理之前我們先了解一下兩種加密方式: 對稱加密和非對稱加密。 

對稱加密:

即加密和解密使用同一個金鑰,雖然對稱加密破解難度很大,但由於對稱加密需要在網路上傳輸金鑰和密文,一旦被黑客擷取很容就能被破解,因此對稱加密並不是一個較好的選擇。 (詳見文章:《即時通訊安全篇(三):常用加解密演算法與通訊安全講解》)

非對稱加密:

即加密和解密使用不同的金鑰,分別稱為公鑰和私鑰。我們可以用公鑰對資料進行加密,但必須要用私鑰才能解密。在網路上只需要傳送公鑰,私鑰儲存在服務端用於解密公鑰加密後的密文。但是非對稱加密消耗的CPU資源非常大,效率很低,嚴重影響HTTPS的效能和速度。因此非對稱加密也不是HTTPS的理想選擇。(詳見文章:《即時通訊安全篇(六):非對稱加密技術的原理與應用實踐》)

那麼HTTPS採用了怎樣的加密方式呢?其實為了提高安全性和效率HTTPS結合了對稱和非對稱兩種加密方式。即客戶端使用對稱加密生成金鑰(key)對傳輸資料進行加密,然後使用非對稱加密的公鑰再對key進行加密。因此網路上傳輸的資料是被key加密的密文和用公鑰加密後的密文key,因此即使被黑客擷取,由於沒有私鑰,無法獲取到明文key,便無法獲取到明文資料。所以HTTPS的加密方式是安全的。

接下來我們以TLS1.2為例來認識HTTPS的握手過程:

1)客戶端傳送 client_hello,包含一個隨機數 random1;

2)服務端回覆 server_hello,包含一個隨機數 random2,攜帶了證照公鑰 P;

3)客戶端接收到 random2 之後就能夠生成 premaster_secrect (對稱加密的金鑰)以及 master_secrect(用premaster_secret加密後的資料);

4)客戶端使用證照公鑰 P 將 premaster_secrect 加密後傳送給伺服器 (用公鑰P對premaster_secret加密);

5)服務端使用私鑰解密得到 premaster_secrect。又由於服務端之前就收到了隨機數 1,所以服務端根據相同的生成演算法,在相同的輸入引數下,求出了相同的 master secrect。

HTTPS的握手過程如下圖: 

5、數字證照

我們上面提到了HTTPS的工作原理,通過對稱加密和非對稱加密實現資料的安全傳輸。我們也知道非對稱加密過程需要用到公鑰進行加密。

那麼公鑰從何而來?其實公鑰就被包含在數字證照中。數字證照通常來說是由受信任的數字證照頒發機構CA,在驗證伺服器身份後頒發,證照中包含了一個金鑰對(公鑰和私鑰)和所有者識別資訊。數字證照被放到服務端,具有伺服器身份驗證和資料傳輸加密功能。

除了CA機構頒發的證照之外,還有非CA機構頒發的證照和自簽名證照:

1)非CA機構即是不受信任的機構頒發的證照,理所當然這樣的證照是不受信任的;

2)自簽名證照,就是自己給自己頒發的證照。當然自簽名證照也是不受信任的。

例如12306網站使用的就是非CA機構頒發的證照(最近發現12306購票頁面已經改為CA證照了),12306的證照是由SRCA頒發,SRCA中文名叫中鐵數字證照認證中心,簡稱中鐵CA。這是個鐵道部自己搞的機構,相當於是自己給自己頒發證照。

因此我們訪問12306時通常會看到如下情景: 

說了這麼多,我們來總結一下數字證照的兩個作用:

1)分發公鑰:每個數字證照都包含了註冊者生成的公鑰。在 TLS握手時會通過 certificate 訊息傳輸給客戶端;

2)身份授權:確保客戶端訪問的網站是經過 CA 驗證的可信任的網站。(在自簽名證照的情況下可以驗證是否是我們自己的伺服器)

最後我們從別處搬來一箇中間人攻擊的例子,來認識證照是如何保證我們的資料安全的。 

對於一個正常的網路請求,其流程通常如下: 

但是,如果這過程中有黑客在通訊過程中攔截了這個請求。即相當於在客戶端和服務端中間有一箇中間人,兩者之間的傳輸對中間人來說是透明的,那麼中間人完全可以獲取兩端之間的任何資料並加以修改,然後轉發給兩端。

這個攔截的流程如下圖: 

此時惡意服務端完全可以發起雙向攻擊:對上可以欺騙服務端,對下可以欺騙客戶端,更嚴重的是客戶端段和服務端完全感知不到已經被攻擊了。這就是所謂的中間人攻擊。

到底什麼是中間人攻擊?

中間人攻擊(MITM攻擊)是指,黑客攔截並篡改網路中的通訊資料。又分為被動MITM和主動MITM,被動MITM只竊取通訊資料而不修改,而主動MITM不但能竊取資料,還會篡改通訊資料。最常見的中間人攻擊常常發生在公共wifi或者公共路由上。

現在可以看看使用證照是怎麼樣提高安全性,避免中間人攻擊的,用一張簡單的流程圖來說明:

6、HTTPS單項認證

所謂單項認證只要服務端配置證照,客戶端在請求服務端時驗證伺服器的證照即可。我們上述講到的內容其實都是說的HTTPS單項認證。通常來說對於安全性要求不高的網站單項認證就可以滿足我們的需求了。因此我們訪問的HTTPS網站大部分都是單項認證。

6.1 關於HTTPS的使用存在的誤區

由於我們對安全性的認識不夠重視,通常對於HTTPS存在一些誤區,這些誤區可能直接給我們帶來一些安全隱患。 

誤區1:對於CA機構頒發的證照客戶端無須內建 

上面提到訪問HTTPS伺服器是需要在客戶端配置伺服器證照的。有些小夥伴可能就納悶了,說我們用的就是HTTPS但是並沒有在客戶端配置證照呢?比如請求百度的網站https://www.baidu.com/,和請求HTTP伺服器沒什麼區別。其實這是因為在Android系統中已經內建了所有CA機構的根證照,也就是隻要是CA機構頒發的證照,Android是直接信任的。對於此種情況,雖然可以正常訪問到伺服器,但是仍然存在安全隱患。假如黑客自家搭建了一個伺服器並申請到了CA證照,由於我們客戶端沒有內建伺服器證照,預設信任所有CA證照(客戶端可以訪問所有持有由CA機構頒發的證照的伺服器),那麼黑客仍然可以發起中間人攻擊劫持我們的請求到黑客的伺服器,實際上就成了我們的客戶端和黑客的伺服器建立起了連線。 

誤區2:對於非CA機構頒發的證照和自簽名證照,可以忽略證照校驗 

另外一種情況,如果我們伺服器的證照是非認證機構頒發的 (例如12306)或者自簽名證照,那麼我們是無法直接訪問到伺服器的,直接訪問通常會丟擲如下異常:

javax.net.ssl.SSLHandshakeException:

    java.security.cert.CertPathValidatorException:

        Trust anchor forcertification path not found.

網上很多解決SSLHandshakeException異常的方案是自定義TrustManager忽略證照校驗,程式碼如下:

publicstaticSSLSocketFactory getSSLSocketFactory() throwsException {

        //建立一個不驗證證照鏈的證照信任管理器。

        finalTrustManager[] trustAllCerts = newTrustManager[]{newX509TrustManager() {

            @Override

            publicvoidcheckClientTrusted(

                    java.security.cert.X509Certificate[] chain,

                    String authType) throwsCertificateException {

            }

            @Override

            publicvoidcheckServerTrusted(

                    java.security.cert.X509Certificate[] chain,

                    String authType) throwsCertificateException {

            }

            @Override

            publicjava.security.cert.X509Certificate[] getAcceptedIssuers() {

                returnnewjava.security.cert.X509Certificate[0];

            }

        }};

        // Install the all-trusting trust manager

        finalSSLContext sslContext = SSLContext.getInstance(“TLS”);

        sslContext.init(null, trustAllCerts,

                newjava.security.SecureRandom());

        // Create an ssl socket factory with our all-trusting manager

        returnsslContext

                .getSocketFactory();

    }

  //使用自定義SSLSocketFactory

  privatevoidonHttps(OkHttpClient.Builder builder) {

       try{

            builder.sslSocketFactory(getSSLSocketFactory()).hostnameVerifier(org.apache.http.conn.ssl.SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);

        } catch(Exception e) {

            e.printStackTrace();

        }

    }

對於這樣的處理方式雖然解決了SSLHandshakeException異常,但是卻存在更大的安全隱患。因為此種做法直接使我們的客戶端信任了所有證照(包括CA機構頒發的證照和非CA機構頒發的證照以及自簽名證照),因此,這樣配置將比第一種情況危害更大。

6.2 Retrofit繫結證照實現HTTPS單項認證

對於上述兩種情況中存在的安全隱患,我們應該如何應對?最簡單的解決方案就是在客戶端內建伺服器的證照,我們在校驗服務端證照的時候只比對和App內建的證照是否完全相同,如果不同則斷開連線。那麼此時再遭遇中間人攻擊劫持我們的請求時由於黑客伺服器沒有相應的證照,此時HTTPS請求校驗不通過,則無法與黑客的伺服器建立起連線。

那麼接下來我們就結合Retrofit以訪問12306為例來實現HTTPS的單項認證。 

首先從12306網站下載簽名證照,並放置到我們專案資源目錄raw下。

然後根據證照構造SSLSocketFactory,程式碼如下:

/**

 * 單項認證

 */

publicstaticSSLSocketFactory getSSLSocketFactoryForOneWay(InputStream… certificates) {

    try{

        CertificateFactory certificateFactory = CertificateFactory.getInstance(CLIENT_TRUST_MANAGER, CLIENT_TRUST_PROVIDER);

        KeyStore keyStore = KeyStore.getInstance(CLIENT_TRUST_KEYSTORE);

        keyStore.load(null);

        intindex = 0;

        for(InputStream certificate : certificates) {

            String certificateAlias = Integer.toString(index++);

            keyStore.setCertificateEntry(certificateAlias, certificateFactory.generateCertificate(certificate));

            try{

                if(certificate != null)

                    certificate.close();

            } catch(IOException e) {

                e.printStackTrace();

            }

        }

        SSLContext sslContext = SSLContext.getInstance(CLIENT_AGREEMENT);

        TrustManagerFactory trustManagerFactory =

                TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());

        trustManagerFactory.init(keyStore);

        sslContext.init(null, trustManagerFactory.getTrustManagers(), newSecureRandom());

        returnsslContext.getSocketFactory();

    } catch(Exception e) {

        e.printStackTrace();

    }

    returnnull;

}

接下來為OKHttpClient設定SslSocketFactory以及hostnameVerifier,程式碼如下:

InputStream certificate12306 = Utils.getContext().getResources().openRawResource(R.raw.srca);

        OkHttpClient okHttpClient = newOkHttpClient.Builder()

                .readTimeout(Constants.DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS)

                .connectTimeout(Constants.DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS)

                .addInterceptor(interceptor)

                .addInterceptor(newHttpHeaderInterceptor())

                .addNetworkInterceptor(newHttpCacheInterceptor())

                .sslSocketFactory(SslContextFactory.getSSLSocketFactoryForOneWay(certificate12306)) 

                .hostnameVerifier(newSafeHostnameVerifier())

                .cache(cache)

                .build();

上述程式碼中hostnameVerifier是對伺服器的校驗,SafeHostnameVerifier程式碼如下:

privateclassSafeHostnameVerifier implementsHostnameVerifier {

       @Override

       publicbooleanverify(String hostname, SSLSession session) {

           if(Constants.IP.equals(hostname)) {//校驗hostname是否正確,如果正確則建立連線

               returntrue;

           }

           returnfalse;

       }

   }

verify方法中對比了請求的IP和伺服器的IP是否一致,一致則返回true表示校驗通過,否則返回false,檢驗不通過,斷開連線。對於網上有些處理是直接返回true,即不對請求的伺服器IP做校驗,我們不推薦這樣使用。而且現在谷歌應用商店已經對此種做法做了限制,禁止在verify方法中直接返回true的App上線。

7、HTTPS雙向認證

對於HTTPS雙向認證,用到的情況不多。但是對於像金融行業等對安全性要求較高的企業,通常都會使用雙向認證。所謂雙向認證就是客戶端校驗伺服器證照,同時伺服器也需要校驗客戶端的證照。因此,雙向認證就另需一張證照放到客戶端待服務端去驗證。

單項認證保證了我們自己的客戶端只能訪問我們自己的伺服器,但並不能保證我們自己的伺服器只能被我們自己的客戶端訪問(第三方客戶端忽略證照校驗即可)。那麼雙向認證則保證了我們的客戶端只能訪問我們自己的伺服器,同時我們的伺服器也只能被我們自己的客戶端訪問。因此雙向認證可以說相比單項認證安全性足足提高一個等級。

7.1 雙向認證流程

接下來我們來了解下雙向認證的流程,以加深對雙向認證的理解:

a. 客戶端傳送一個連線請求給伺服器。

b. 伺服器將自己的證照,以及同證照相關的資訊傳送給客戶端。

c. 客戶端檢查伺服器送過來的證照是否和App內建證照相同。如果是,就繼續執行協議;如果不是則終止此次請求。

d. 接著客戶端比較證照裡的訊息,例如域名和公鑰,與伺服器剛剛傳送的相關訊息是否一致,如果是一致的,客戶端認可這個伺服器的合法身份。

e. 伺服器要求客戶傳送客戶自己的證照。收到後,伺服器驗證客戶端的證照,如果沒有通過驗證,拒絕連線;如果通過驗證,伺服器獲得使用者的公鑰。

f. 客戶端告訴伺服器自己所能夠支援的通訊對稱密碼方案。

g. 伺服器從客戶傳送過來的密碼方案中,選擇一種加密程度最高的密碼方案,用客戶的公鑰加過密後通知客戶端。

h. 客戶端針對這個密碼方案,選擇一個通話金鑰,接著用伺服器的公鑰加過密後傳送給伺服器。

i. 伺服器接收到客戶端送過來的訊息,用自己的私鑰解密,獲得通話金鑰。

j. 伺服器通過金鑰解密客戶端傳送的被加密資料,得到明文資料。

7.2 Retrofit實現HTTPS雙向認證

對於雙向認證,我們以華為北向平臺登入介面為例來進行學習。想了解華為北向API:請戳此處

我們直接通過瀏覽器訪問登入介面可以看到如下情景: 

哈,驚喜不?直接被拒絕了!這就是雙向認證,沒有證照想訪問伺服器門都沒有。那麼對於雙向認證我們應該做怎樣的配置?我們可以參考華為開源出來的程式碼:

https://github.com/52im/IoT_OceanConnect_North_GUI_APPDemo

原始碼中由兩個證照檔案ca.jks和outgoing.CertwithKey.pkcs12,其中ca.jks是在客戶端配置的證照,outgoing.CertwithKey.pkcs12是在服務端配置的證照。因為我們當前客戶端是Android系統,由於Android系統不支援jks格式的證照,因此需要把jks轉成Android支援的bks格式。轉換方式不再貼出,可自行查閱。 

有了證照,接下來看獲取SSLSocketFactory的程式碼:

/**

   * 雙向認證

   *

   * @return SSLSocketFactory

   */

  publicstaticSSLSocketFactory getSSLSocketFactoryForTwoWay() {

      try{

          InputStream certificate = Utils.getContext().getResources().openRawResource(R.raw.capk);

          //  CertificateFactory certificateFactory = CertificateFactory.getInstance(“X.509”, “BC”);

          KeyStore keyStore = KeyStore.getInstance(CLIENT_TRUST_KEY);

          keyStore.load(certificate, SELF_CERT_PWD.toCharArray());

          KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());

          kmf.init(keyStore, SELF_CERT_PWD.toCharArray());

          try{

              if(certificate != null)

                  certificate.close();

          } catch(IOException e) {

              e.printStackTrace();

          }

          //初始化keystore

          KeyStore clientKeyStore = KeyStore.getInstance(CLIENT_TRUST_KEYSTORE);

          clientKeyStore.load(Utils.getContext().getResources().openRawResource(R.raw.cabks), TRUST_CA_PWD.toCharArray());

          SSLContext sslContext = SSLContext.getInstance(CLIENT_AGREEMENT);

          TrustManagerFactory trustManagerFactory = TrustManagerFactory.

                  getInstance(TrustManagerFactory.getDefaultAlgorithm());

          trustManagerFactory.init(clientKeyStore);

          KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());

          keyManagerFactory.init(clientKeyStore, SELF_CERT_PWD.toCharArray());

          sslContext.init(kmf.getKeyManagers(), trustManagerFactory.getTrustManagers(), newSecureRandom());

          returnsslContext.getSocketFactory();

      } catch(Exception e) {

          e.printStackTrace();

      }

      returnnull;

  }

接下來同樣需要配置OKHttpClient,程式碼如下:

OkHttpClient okHttpClient = newOkHttpClient.Builder()

                .readTimeout(Constants.DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS)

                .connectTimeout(Constants.DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS)

                .addInterceptor(interceptor)

                .addInterceptor(newHttpHeaderInterceptor())

                .addNetworkInterceptor(newHttpCacheInterceptor())

                .sslSocketFactory(SslContextFactory.getSSLSocketFactoryForTwoWay())

                .hostnameVerifier(newSafeHostnameVerifier())

                .cache(cache)

                .build();

這樣就完成了HTTPS的配置,接下來就可以愉快的訪問HTTPS 雙向認證的介面了。由於北向登入介面中需要appId和secret兩個引數,因此,登入相關程式碼就不再貼出。

好了,到此關於HTTPS的學習就結束了,如果有不明白的地方可以參看文末原始碼。以上內容純屬個人對HTTPS的一些認識,如果文中有錯誤之處還請多多包涵,歡迎留言指正。

本文寫成參考了大量的相關文章,在此表示感謝。

(原文連結:https://blog.csdn.net/qq_20521573/article/details/79233793

附錄1:參考原始碼

(請從此連結的附件處下載之:http://www.52im.net/thread-1574-1-1.html

附錄2:參考資料

大型網站的 HTTPS 實踐(1):HTTPS 協議和原理

Retrofit中如何正確的使用https?

Android Https相關完全解析 當OkHttp遇到Https

HTTPS原理及OKHTTP對HTTPS的支援

附錄3:即時通訊乾貨文章

新手入門一篇就夠:從零開發移動端IM

從客戶端的角度來談談移動端IM的訊息可靠性和送達機制

現代移動端網路短連線的優化手段總結:請求速度、弱網適應、安全保障

騰訊技術分享:社交網路圖片的頻寬壓縮技術演進之路

IM開發基礎知識補課:正確理解前置HTTP SSO單點登陸介面的原理

移動端IM中大規模群訊息的推送如何保證效率、實時性?

移動端IM開發需要面對的技術問題

開發IM是自己設計協議用位元組流好還是字元流好?

請問有人知道語音留言聊天的主流實現方式嗎?

IM訊息送達保證機制實現(一):保證線上實時訊息的可靠投遞

IM訊息送達保證機制實現(二):保證離線訊息的可靠投遞

如何保證IM實時訊息的“時序性”與“一致性”?

一個低成本確保IM訊息時序的方法探討

IM單聊和群聊中的線上狀態同步應該用“推”還是“拉”?

IM群聊訊息如此複雜,如何保證不丟不重?

談談移動端 IM 開發中登入請求的優化

移動端IM登入時拉取資料如何作到省流量?

淺談移動端IM的多點登陸和訊息漫遊原理

完全自已開發的IM該如何設計“失敗重試”機制?

通俗易懂:基於叢集的移動端IM接入層負載均衡方案分享

微信對網路影響的技術試驗及分析(論文全文)

即時通訊系統的原理、技術和應用(技術論文)

開源IM工程“蘑菇街TeamTalk”的現狀:一場有始無終的開源秀

QQ音樂團隊分享:Android中的圖片壓縮技術詳解(上篇)

QQ音樂團隊分享:Android中的圖片壓縮技術詳解(下篇)

騰訊原創分享(一):如何大幅提升行動網路下手機QQ的圖片傳輸速度和成功率

騰訊原創分享(二):如何大幅壓縮行動網路下APP的流量消耗(上篇)

騰訊原創分享(三):如何大幅壓縮行動網路下APP的流量消耗(下篇)

如約而至:微信自用的移動端IM網路層跨平臺元件庫Mars已正式開源

基於社交網路的Yelp是如何實現海量使用者圖片的無失真壓縮的?

騰訊技術分享:騰訊是如何大幅降低頻寬和網路流量的(圖片壓縮篇)

騰訊技術分享:騰訊是如何大幅降低頻寬和網路流量的(音視訊技術篇)

>> 更多同類文章 ……

(本文同步釋出於:http://www.52im.net/thread-1574-1-1.html


相關文章