今天來介紹,數位憑證,講一點點數位簽章,以及最重要的憑證綁定原理及用途
避免有人透過手機使用 Burp Suite 或 Charles 攔截,即可發現憑證內容不符合,而拒絕連線進而達到保護的目的
一般我們在說的網頁用憑證 SSL / TLS 憑證,也稱為 數位憑證(digital certificate)用來證明公開金鑰擁有者的身分。
此檔案包含了公鑰資訊、擁有者身分資訊(主體)、以及數位憑證認證機構(發行者)
且對這份檔案做了數位簽章,確保檔案內容正常沒有問題
數位簽章(英語:Digital Signature,又稱公鑰數位簽章)
數位簽章是能夠實現『身分鑒別』和『檢查訊息完整性』兩種功能的訊息鑒別碼 外也加入了不可抵賴性機制
作法可看前面的『訊息鑒別碼』章節,加密流程相同,差別採用公開金鑰機制,因此可以確定訊息的製作者
數位憑證就是用來確保使用者使用的伺服器憑證是伺服器提供的
為了確保是咩的公鑰,需要跟憑證機構(certification authority) CA 申請發行憑證證明金鑰 P咩 是咩的
憑證機構機構本身也具有自己的公開金鑰PCa 及私密金鑰Sca
接下來 咩 將自己的,個人資訊加上公開金鑰,傳送給憑證機構
憑證機構確認過資訊後,使用憑證機構的 私密金鑰 Sca 進行簽章
憑證機構將製作完成的數位簽章跟數據合成一個電子檔
最後憑證機構把電子檔案傳回給 咩,這個電子檔就是咩的數位憑證
咩接著就可以傳送自己的數位憑證,取代只有傳送公鑰
使用者/客戶端進行動作確認憑證有效
憑證用途非常的廣,國人使用的自然人憑證,工商憑證,健保卡等都是屬於數位憑證的用途
另外我們使用再伺服器尚的數位憑證又稱為『伺服器憑證』、HTTPS/SSL憑證
下圖為維基百科的憑證(數位憑證)
得知以下資訊
可以整理出 wikipedia.org 網域,使用 ecc - secp256r1 公鑰 ,並且透過 Let's Encrypt 將這些資訊進行簽章
當我們已經使用數位憑證再我們的Server上為什麼還需要憑證?
因為原本方式只能確保伺服器的公鑰是伺服器原始提供的
並無法確認,客戶端/使用者是否有進行憑證的驗證
先前的章節提過我們可以使用公開金鑰系統對資料進行保護,看起來萬無一失,但黑客卻不直接對演算法進行攻擊,而是使用竊聽(eavesdrop)和電子欺騙(spoofing)方式進行偽裝
這方式又稱為 中間人攻擊(Man-in-the-middle attack,MITM)
取代掉原本的數位憑證,使用者端若是沒有進行驗證將會被劫持傳輸的內容
在處理方式上,普遍是使用憑證綁定 (certificate pinning) 的方式,把需要比對的憑證預先存放在應用程式裡,等要進行 SSL Handshake 的時候再與伺服器的憑證做比對。
當遇到有被取代掉的憑證,使用者用了 Burp Suite 或 Charles 想要攔截流量,由於憑證會更換為Burp或Charles
我們APP再進行連線時,即可發現憑證內容不符合,而拒絕連線進而達到保護的目的
import Alamofire
//自定 manager
fileprivate static var manager: Alamofire.SessionManager = {
// Create custom manager
let configuration = URLSessionConfiguration.default
configuration.httpAdditionalHeaders = Alamofire.SessionManager.defaultHTTPHeaders
return Alamofire.SessionManager(
configuration: configuration,
serverTrustPolicyManager: CustomServerTrustPoliceManager()
)
}()
class CustomServerTrustPoliceManager : ServerTrustPolicyManager {
//自訂信任協議
override func serverTrustPolicy(forHost host: String) -> ServerTrustPolicy? {
//如果網域符合
if host.hasSuffix("www.example.com") {
//從檔案資料夾取出憑證
let dir_bundle:Bundle = Bundle.init(path: "/certificates")!
let serverTrustPolicy = ServerTrustPolicy.pinCertificates(
//進行比對的參數
certificates: ServerTrustPolicy.certificates(in:dir_bundle),
validateCertificateChain: true,
validateHost: true
)
return serverTrustPolicy
}
return .performDefaultEvaluation(validateHost: true)
}
public init() {
super.init(policies: [:])
}
}
Android 新的OS 可以使用網絡安全配置 功能,可以直接使用此功能定義網路安全設定,而無需使用程式碼
res/xml/network_security_config.xml
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<domain-config>
<domain includeSubdomains="true">example.com</domain>
<pin-set expiration="2018-01-01">
<pin digest="SHA-256">7HIpactkIAq2Y49orFOOQKurWxmmSFZhBCoQYcRhJ3Y=</pin>
<!-- backup pin -->
<pin digest="SHA-256">fwza0LRMXouZHRC8Ei+4PyuldPDcf3UKgO/04cDM1oE=</pin>
</pin-set>
</domain-config>
</network-security-config>
或是使用程式碼的方式,將金鑰放入KeyStore,再連線的時候進行比對
KeyStore keyStore = ...;
String algorithm = TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory tmf = TrustManagerFactory.getInstance(algorithm);
tmf.init(keyStore);
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, tmf.getTrustManagers(), null);
URL url = new URL("https://www.example.com/");
HttpsURLConnection urlConnection = (HttpsURLConnection) url.openConnection();
urlConnection.setSSLSocketFactory(context.getSocketFactory());
InputStream in = urlConnection.getInputStream();
Android 跟 iOS 系統本身內無法直接像 Http 請求可以直接設定憑證綁定驗證
但可以自己實作,可以攔截請求連線,改成自己定義Http連線,並檢查憑證是否有檢驗通過
https://developer.android.com/training/articles/security-config.html#CertificatePinning
https://owasp.org/www-community/controls/Certificate_and_Public_Key_Pinning
https://devco.re/blog/2014/08/15/ssl-mishandling-on-mobile-app-development/
https://developer.android.com/training/articles/security-ssl.html#UnknownCa