最近碰到一些 SSL 的小問題,特記錄下。 我們有個 Java 實現的 SSL TCP 服務端,為客戶端(PC、Android 和 iOS)提供 SSL 接入連接服務。最近有用戶反饋其手機上 App 不能正常連接登錄,別人手機上都可以。經過單獨回訪調查該用戶使用的手機操作系統是 Android 6.
最近碰到一些 SSL 的小問題,特記錄下。
我們有個 Java 實現的 SSL TCP 服務端,為客戶端(PC、Android 和 iOS)提供 SSL 接入連接服務。最近有用戶反饋其手機上 App 不能正常連接登錄,別人手機上都可以。經過單獨回訪調查該用戶使用的手機操作系統是 Android 6.0,經搜索瞭解了 Android 6.0 之後 Google 使用了自家的 BoringSSL 替換了原來的 OpenSSL,懷疑是這裡在搗鬼。
繼續搜索類似問題解決方案,在參考[1] 中找到答案:
SSL/TLS握手過程中,假如選中了諸如 TLS_DHE_RSA_WITH_AES_128_CBC_SHA 這樣使用 deffie-hellman 密鑰的 cipher,那麼在 deffie-hellman 密鑰交換過程中會使用的一個P參數(prime number),伺服器側提供的 P 參數在 JDK8 之前都只用了 768bit 的長度,小於 1024bit 存在安全漏洞可導致 logjam attack,會被最新本版的瀏覽器和 BoringSSL 拒絕。
明瞭了原因後我們只好把 JDK 從 6 升級到了 8,順利解決 Android6.0 SSL 握手失敗問題。但解決完這個後,沒多久又發現 APNS iOS 推送又不可用了,和蘋果推送伺服器建立 SSL 連接失敗,無法推送消息。唯一的變化就是升級到了 JDK8 自然就將懷疑目標對準了 JDK8。
繼續 Google 一把找了同類受害者,他已經搞明白了原因,見參考[2]
The problem was the exported keystore (in PKCS12 format) contained the private key as well as the production certificate and the development certificate for push notifications. Java can use keystores in the PKCS12 format. But Java 6 and Java 8 did not read-in the keystore the same way. It looks like Java 6 read in the production certificate for the private key and Java 8 read in the development certificate.
解決辦法也很簡單,先用 JDK6 提供的 keytool 將 .p12 格式的證書轉換為 .jks 格式。再用 JDK8 提供的 keytool 將剛生成的 .jks 證書轉換為 .p12 格式。轉換命令如下:
.p12 -> .jks
/JDK6/keytool -importkeystore -destkeystore apns.jks -srckeystore apns_jdk6.p12 -srcstoretype PKCS12
.jks -> .p12
/JDK8/keytool -importkeystore -srckeystore apns.jks -srcstoretype JKS -deststoretype PKCS12 -destkeystore apns_jdk8.p12
參考
[1] liuxian233. Android 6.0 HTTPS連接ssl3_get_server_key_exchange:BAD_DH_P_LENGTH錯誤問題
[2] szediwy. Apple Push Notification with Java
[3] ASHISH PARAB. APPLE PUSH NOTIFICATION SERVICE CERTIFICATE ISSUE WITH JDK 7
寫點文字,畫點畫兒,「瞬息之間」一切都變了。覺得不錯,掃描二維碼關註。