本文不對具體的算法作深刻研究,只是講解各類安全算法的原理和使用場景。html
數據校驗,是爲保護數據的完整性,用一種指定的算法對原始數據計算出的一個校驗值。當接收方用一樣的算法再算一次校驗值,若是兩次校驗值同樣,表示數據完整。android
一、奇偶校驗git
能檢測出信息傳輸過程中的一位誤碼。出現錯誤不能檢測出錯誤,只能要求重發。算法
二、 CRC循環冗餘校驗數組
經過增長若干冗餘位,能夠檢測出傳輸過程當中的錯誤。檢錯和糾錯能力強,在通訊領域運用較普遍。瀏覽器
三、MD5校驗安全
MD5算法是一種信息摘要算法,是經過哈希映射的原理獲得一個大文件簡短的MD5值。該算法是一種不可逆算法,也就是說開發者不能經過MD5值獲得原始文件的數據。這裏有一種可能性,不一樣的數據文件獲得相同的MD5值,可是這種狀況通常開發過程中都不予考慮(數據碰撞)。服務器
四、 SHA網絡
SHA(Secure Hash Algorithm)是由美國專門制定密碼算法的標準機構——美國國家標準技術研究院(NIST)制定的,SHA系列算法的摘要長度分別爲:SHA爲20字節(160位)、SHA256爲32字節(256位)、 SHA384爲48字節(384位)、SHA512爲64字節(512位),因爲它產生的數據摘要的長度更長,所以更難以發生碰撞,所以也更爲安全,它是將來數據摘要算法的發展方向。因爲SHA系列算法的數據摘要長度較長,所以其運算速度與MD5相比,也相對較慢。dom
同MD5算法相同,他也是一種不可逆的算法。
該算法只能稱爲一種校驗,是對原始的數據進行了一個編碼的過程。 有編碼就有解碼,該過程是一個可逆的。該算法安全性較差,能夠很輕鬆的經過解碼將密文轉換爲明文,從而獲取信息。
是對稱加密算法領域中的典型算法,如今認爲是一種不安全的加密算法,由於如今已經有用窮舉法攻破DES密碼的報道了。儘管如此,該加密算法仍是運用很是廣泛,是一種標準的加密算法。3DES是DES的增強版本。
DES加密的祕鑰是 56位,而3DES加密算法的祕鑰是 112位或者168位。3DES加密處理速度較慢、密鑰計算時間較長、加密效率不高。
Advanced Encryption Standard ,高級數據加密標準,AES算法能夠有效抵制針對DES的攻擊算法。
密鑰創建時間短、靈敏性好、內存需求低、安全性高
DES/3DES/AES 三種加密算法的對好比下:
異或運算中,若是某個字符(或數值)x 與 一個數值m 進行異或運算獲得y,則再用y 與 m 進行異或運算就能夠還原爲 x ,所以應用這個原理能夠實現數據的加密解密功能。
這種加密算法較簡單,只是簡單的將明文轉換爲不易看出的密文,破解的複雜度徹底取決於祕鑰的長度。
公開密鑰加密,又稱 asymmetric cryptography (非對稱加密),即存在兩把不一樣的密鑰,分別稱爲公鑰 Pu 和私鑰 Pr,公鑰一般用來加密明文 M,只有私鑰才能解密密文 C,若是用 E 和 D 分別表示加密和解密算法,那麼有:
C = E(M,Pu); // 加密公式,M爲明文,C爲密文,
M = D(C,Pr); // 解密公式
傳統的對稱加密需雙方共享相同的密鑰,通訊安全很大程度依賴雙方是否能妥善的管理密鑰。公開密鑰加密發明是密碼學最爲重要的里程碑之一,它從數學的角度保證了通訊安全。公開密鑰加密體系有三大範疇:
Encryption/Decryption:即加密與解密,發送方用接收方的公鑰加密消息(祕鑰由接收方生成,傳送給發送方)
Digital Signature:數字簽名,發送方用公鑰加密消息摘要生成簽名,保證消息的完整性和可靠性
Key Exchange:安全的交換密鑰,一般用於交換對稱加密的密鑰(迪菲-赫爾曼密鑰交換算法)
一、RSA加密
這種算法以歐拉函數爲基礎,這裏不具體展開:
這種算法很是可靠,密鑰越長,它就越難破解。根據已經披露的文獻,目前被破解的最長RSA密鑰是768個二進制位。也就是說,長度超過768位的密鑰,還沒法破解(至少沒人公開宣佈,由於大數的因式分解計算量特別大)。所以能夠認爲,1024位的RSA密鑰基本安全,2048位的密鑰極其安全。
具體原理能夠見:公鑰私鑰的生成及加解密過程
http://www.ruanyifeng.com/blog/2013/06/rsa_algorithm_part_one.htm
https://www.kancloud.cn/kancloud/rsa_algorithm/48488
二、SSL/TLS協議
具體來講,SSL/TLS 並非一種算法,而是爲了保證通訊安全的一種協議,固然裏面用到了相關加密算法如非對稱加密算法、數字證書、HD祕鑰交換算法等。Https多使用這種協議進行網絡數據的傳輸。
經過上述這張圖大體瞭解這種協議的通訊過程。上述過程的目的只是爲了獲取比較安全的Session Key,固然這個Key須要Client Random + Server Random + Permaster secret 三者共同生成,安全性較高。Session key 用於後面通訊對數據進行對稱加密。 雙方得到Session Key 後經過http協議進行數據傳輸,Session Key 用來對http傳輸的內容進行加密。
關於SSL/TLS的詳細講解請參考 阮一峯的博客連接。
http://www.ruanyifeng.com/blog/2014/02/ssl_tls.html
http://www.ruanyifeng.com/blog/2014/09/illustration-ssl.html
TOTP 的全稱是"基於時間的一次性密碼"(Time-based One-time Password)。它是公認的可靠解決方案,已經寫入國際標準 RFC6238。
它的步驟以下。
第一步:用戶開啓雙因素認證後,服務器生成一個密鑰。
第二步:服務器提示用戶掃描二維碼(或者使用其餘方式),把密鑰保存到用戶的手機。也就是說,服務器和用戶的手機,如今都有了同一把密鑰。
第四步,服務器也使用密鑰和當前時間戳,生成一個哈希,跟用戶提交的哈希比對。只要二者不一致,就拒絕登陸。
注意,密鑰必須跟手機綁定。一旦用戶更換手機,就必須生成全新的密鑰。
算法原理:
TC = floor((unixtime(now) − unixtime(T0)) / TS)
上面的公式中,TC 表示一個時間計數器,unixtime(now)
是當前 Unix 時間戳,unixtime(T0)
是約定的起始時間點的時間戳,默認是0
,也就是1970年1月1日。TS 則是哈希有效期的時間長度,默認是30秒。所以,上面的公式就變成下面的形式。
TC = floor(unixtime(now) / 30)
因此,只要在 30 秒之內,TC 的值都是同樣的。前提是服務器和手機的時間必須同步。
接下來,就能夠算出哈希了。
TOTP = HASH(SecretKey, TC)
TOTP 有硬件生成器和軟件生成器之分,都是採用上面的算法。
http://www.ruanyifeng.com/blog/2011/08/what_is_a_digital_signature.html
一、經過公鑰私鑰進行加密通訊,發送方發送的內容只有經過私鑰才能打開,因此保證了發送信息的私密性。
二、第三方能夠把僞造的公鑰給發送方,再截獲發送的信息,經過本身的私鑰解密信息。 這裏Https協議中引入的CA(certificate authority)能夠很好的解決這個問題。證書中心用本身的私鑰爲傳輸的公鑰和一些信息進行加密,發送者能夠經過證書中的公鑰解密證書中的信息,這些信息中包含了須要傳送的公鑰。(暫且認爲證書中的信息都是對的,通常證書中心都是可靠性比較高的機構頒發的)
經過Chrome打開百度網頁的時候,F12建, Security菜單欄能夠看到百度的證書。證書裏面包含:頒發者、頒給者、公鑰、有效期等信息。
a) 客戶端向服務器發出加密請求
b) 服務器用本身的私鑰加密網頁之後,連同自己的數字證書,一塊兒發送給客戶端
c) 客戶端(瀏覽器)的"證書管理器",有"受信任的根證書頒發機構"列表。客戶端會根據這張列表,查看解開數字證書的公鑰是否在列表以內。
Android端如何使用https發送請求
網上關於https的使用文章大多數是關於瀏覽器如何經過簽名進行驗證的,瀏覽器會保存各個網站由CA認證中心頒發的數字簽名並進行校驗。那麼問題來了,當客戶端和服務端握手完成後,客戶端如何判斷服務端發送的公鑰和相關信息沒有被第三方篡改(對應c圖)? 其實,客戶端則須要提早將CA證書導入進來,當建立請求時將證書的相關信息添加進去便可完成https請求和驗證。
android端實現https請求具體流程:https://developer.android.com/training/articles/security-ssl
客戶端添加證書實現:
InputStream is = getAssets().open("srca.cer"); private SSLSocketFactory getSslSocketFactory(InputStream... certificates){ try{ CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509"); KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); keyStore.load(null); int index = 0; for (InputStream certificate : certificates) { String certificateAlias = Integer.toString(index++); keyStore.setCertificateEntry(certificateAlias, certificateFactory.generateCertificate(certificate)); if (certificate != null) certificate.close(); } SSLContext sslContext = SSLContext.getInstance("TLS"); TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); trustManagerFactory.init(keyStore); sslContext.init ( null, trustManagerFactory.getTrustManagers(), new SecureRandom() ); return sslContext.getSocketFactory(); }catch (Exception e){ } return null; }
建立HttpsURLConnection完成後將證書信息設置進去再發送請求:
httpsConn.setSSLSocketFactory(sslSocketFactory);
http網絡請求中的token
咱們都是知道HTTP協議是無狀態的,這種無狀態意味着程序須要驗證每一次請求,從而辨別客戶端的身份,那麼如何經過只登錄一次就能實現後續已登陸狀態呢?
1.用戶登陸校驗,校驗成功後就返回Token給客戶端。
2.客戶端收到數據後保存在客戶端
3.客戶端每次訪問API是攜帶Token到服務器端。
4.服務器端採用filter過濾器校驗。校驗成功則返回請求數據,校驗失敗則返回錯誤碼
那麼這裏有個問題: 服務端怎麼知道攜帶的token就是這個用戶的token而不是僞造的? 後續補充
如今有三個參數(A、B、C 封裝在一個類Data中)須要傳輸到服務端,那麼如何保證 這三個數據不是別人僞造發送的,能夠在傳這三個數據數據以外再添加一個數據 sign(String類型)。
客戶端sign的生成算法:
第一步:對三個參數名稱(與服務端提早約定好傳輸變量的名稱)進行字典排序,拼接成字符串StringA(key1=value1&key2=value2&key3=value3),同時字符串的拼接遵循指定的規則。
第二步:在stringA最後拼接祕鑰key(只有服務端和客戶端知道,第三方不能得到,這也是簽名的關鍵)獲得finalStringA
第三部: 對finalStringA 作MD5計算,並將字符數組轉換成16進制的字符串。
自此sign生成過程結束。
生成根據Data 中A、B、C三個參數的值以及Key 生成了sign以後,將sign值賦給Data中的sign。將Data轉換成JSONObject格式再轉換成字符數組
經過 HttpURLConnection傳送到服務端。服務端經過拿到A、B、C的數值後會本身經過一樣的算法生成sign,若是和客戶端傳送的Sign不同則此次傳送多是第三方僞造的。
這種簽名就保證了第三方不能僞造數據和服務端進行通訊。(具體的流程以下圖所示)
可是這裏有個問題,其它人員獲取到了通訊祕鑰secret,則能夠僞造數據,畢竟客戶端的代碼頗有可能被破解的(就算放在native層均可以被破解),可是對於安全等級較低通訊這種加密措施也足夠了。