我是如何解決java.security.cert.CertPathValidatorException異常的


問題來了

昨天,我還在我的工位上愉快的敲的代碼,有位開發組的同事Z給我發消息。

開發組同事Z:大哥,這個PKI的登錄功能是你做的嗎?

我:是呀!N年前的事了。。。

開發組同事Z:PKI登錄功能出問題了!有位客戶使用他的PKI登錄我們做的系統,登錄時報錯,換了幾台電腦也不行。但是他使用他的PKI能登錄其他的系統。能幫忙看一下嗎,我找過好多同事看過了,他們看了下,都不知道如何入手

我:報錯是報什么錯?能具體的描述下嗎?

開發組同事Z:訪問系統時,能彈出證書的選擇框,選擇完證書后,Chrome瀏覽器的報錯信息是:ERR_SSL_PROTOCOL_ERROR

我:行吧,我待會過去瞧瞧

問題分析

之前在配置tomcat的SSL時,我已經把客戶證書的根證書已經導入到服務器信任證書列表內(配置見附錄),“能彈出證書的選擇框”說明服務器端是能識別出客戶證書,但是為什么在登錄的過程中,就報錯了,可能有如下的幾種原因:

  1. 服務器端的證書和服務器端的信任證書配置有誤;(我一直以為是服務器端證書中的IP地址和服務器的IP不一致,后來才發現這個是不可能的)
  2. 登錄過程中,tomcat能讀取到客戶端的證書,在程序處理的過程中,拒絕該證書;(客戶端的證書的格式不被服務器端接受)
  3. 客戶端證書過期了?(通過上面的描述好像不是這個錯誤)
  4. tomcat或者JDK有問題?(這個不太可能,其他用戶使用PKI能登錄呀)

解決問題

博主當時也是有點懵逼,不知道該怎么解決這個問題。好在博主有敢於嘗試的精神,決定一個一個試一下。

重新安裝服務器端證書

首先使用keytool工具生成服務器端證書,再使用keytool導入客戶的根證書到服務器端的信任證書庫中。然后把服務器端證書和服務器端的信任證書庫放入tomcat,配置、重啟tomcat(這里省略了具體的配置過程,具體的配置步驟見附錄)。經過一番嘗試,發現選擇完證書還是報錯。又經過一番折騰,還是沒有找過是啥原因,正在灰心喪氣的時候,靈機一動,程序員不是應該從日志中找錯誤嗎?於是從網上找了一下java中如何開啟SSL的日志信息。

日志帶來曙光

在tomcat中加入JVM參數: -Djavax.net.debug=SSL,handshake,data,trustmanager,重啟tomcat。使用客戶的證書重新登錄,看到后台打印出了很多的日志信息,瀏覽了一遍后,發現了如下的異常:

http-nio-8443-exec-4, fatal error: 46: General SSLEngine problem
sun.security.validator.ValidatorException: PKIX path validation failed: java.security.cert.CertPathValidatorException: algorithm constraints check failed
%% Invalidated:  [Session-7, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256]
http-nio-8443-exec-4, SEND TLSv1.2 ALERT:  fatal, description = certificate_unknown
http-nio-8443-exec-4, WRITE: TLSv1.2 Alert, length = 2
http-nio-8443-exec-4, fatal: engine already closed.  Rethrowing javax.net.ssl.SSLHandshakeException: General SSLEngine problem
http-nio-8443-exec-4, called closeOutbound()
http-nio-8443-exec-4, closeOutboundInternal()

拿着這個異常信息java.security.cert.CertPathValidatorException: algorithm constraints check failed去google上搜索了一下,找到了如下的這兩個網站上的解決方法:

於是乎,按照上面的描述修改了JDK的配置信息,重啟tomcat,使用證書登錄,居然能正常登錄了。心中暗自高興了好一會兒,滿滿的成就感,想不到居然修改了一點配置就解決了......在高興之余,想着搞清楚這到底是怎么一回事!接下來,容我慢慢道來。

刨根到底

到底是做了什么神奇的操作,就解決這問題了呢!首先找到JDK安裝目錄下的這個文件:

$JAVA_HOME/jre/lib/security/java.security

然后修改其中的兩項配置(為了確保不出報錯,把這兩項禁用的算法全置為空):

# 處理證書路徑時禁用的算法
jdk.certpath.disabledAlgorithms=
# 處理SSL/TLS時禁用的算法
jdk.tls.disabledAlgorithms=

然后重啟tomcat就行了。

先看一下禁用的算法的語法,如下:

# 總體的語法
禁用的算法1, 禁用的算法2, 禁用的算法3...
# 禁用的每一項算法語法
算法名稱 keySize 操作符 數值值

例如:DSA表示禁用DSA算法,RSA keySize < 2048表示禁用密鑰長度小於2048的RSA算法,RSA keySize > 1024, RSA keySize < 2048表示禁用密鑰長度大於1024小於2048的RSA算法。

后來,仔細看了一下日志,發現在客戶證書的信息有這么一行:

Signature Algorithm: SHA1withRSA, OID = 1.2.***.11***9.1.1.*

Key:  Sun RSA public key, 1023 bits

客戶證書的密鑰長度居然是1023,而JDK8中為了安全性默認禁用了密鑰長度小於2048的算法。最終修改的配置如下:

jdk.certpath.disabledAlgorithms=MD2, RSA keySize < 512

問題就這樣解決了,哈哈!!!

總結

有時遇到問題,真的是讓人摸不到頭腦,這時別着急,把自己能想到的方法先嘗試一遍,說不定就能行了呢。遇到問題首先可以借助日志來分析問題(就像上面那樣打開JDK的SSL的調試日志的開關),通過日志大概就能定位到時哪里出問題了。在實際的開發中,很多程序猿基本上不會記錄日志,稍微好一點的可能會在控制台使用System.out.println(xxxx)輸出日志,不習慣使用像log4j這樣的日志框架來記錄日志。有人會說,出了問題,我可以直接debug呀!你有沒想過在生產環境中,你還能用IDE來進行調試嗎?

附錄

tomcat的SSL配置

編輯conf/server.xml文件加入如下的配置:

<Connector port="8443" protocol="org.apache.coyote.http11.Http11NioProtocol" SSLEnabled="true"
               maxThreads="150" scheme="https" secure="true"
               clientAuth="true" sslProtocol="TLS"
               keystoreFile="${catalina.base}/server.ks" keystorePass="123456"
               truststoreFile ="${catalina.base}/server_trust.ks" 
           	   truststorePass="123456"/>

說明:

  • clientAuth為true表示開啟SSL雙向認證
  • keystoreFile指定服務器端的證書位置
  • truststoreFile指定服務器端信任證書庫

服務器端證書配置

  1. 生成服務器端證書

    keytool -genkeypair -v -alias server -keyalg RSA -validity 3650 -keystore ./server.ks  -storepass 123456 -keypass 123456 -dname "CN=192.168.2.89,OU=zfx,O=zfx,L=gz,ST=gd,C=cn"
    
  2. 導出服務器端證書

    keytool -exportcert -alias server  -keystore ./server.ks  -file ./server.cer  -storepass 123456
    
  3. 導入服務器端證書到服務器信任證書列表

    keytool -importcert -alias serverca  -keystore ./server_trust.ks  -file ./server.cer  -storepass 123456
    
  4. 導入客戶根證書到服務器信任證書列表

    keytool -importcert -alias urootca -keystore ./server_trust.ks  -file ./uroot.cer  -storepass 123456
    

使用如下的命令查看信任的證書信息:

keytool -list -keystore ./server_trust.ks -storepass 123456

Keytool命令常用參數

  • -genkeypair在用戶主目錄中創建一個默認文件”.keystore”,還會產生一個mykey的別名,mykey中包含用戶的公鑰、私鑰和證書(在沒有指定生成位置的情況下,keystore會存在用戶系統默認目錄)
  • -alias 產生別名 每個keystore都關聯這一個獨一無二的alias,這個alias通常不區分大小寫
  • -keystore 指定密鑰庫的路徑(產生的各類信息將不在.keystore文件中)
  • -keyalg 指定密鑰的算法 (如 RSA,DSA,默認值為:DSA)
  • -validity 指定創建的證書有效期多少天(默認 90)
  • -keysize 指定密鑰長度 (默認 1024
  • -storepass 指定密鑰庫的密碼(獲取keystore信息所需的密碼)
  • -keypass 指定別名條目的密碼(私鑰的密碼)
  • -dname 指定證書發行者信息 其中: “CN=名字與姓氏,OU=組織單位名稱,O=組織名稱,L=城市或區域名 稱,ST=州或省份名稱,C=單位的兩字母國家代碼”
  • -list 顯示密鑰庫中的證書信息如:keytool -list -v –keystore path/to/keystore -storepass password
  • -v 顯示密鑰庫中的證書詳細信息
  • -exportcert 導出指定別名的證書,如:keytool - exportcert -alias theAlias -keystore path/to/keystore -file path/to/keystore/cert -storepass pass
  • -file 參數指定導出到文件的文件名
  • -delete 刪除密鑰庫中某條目 keytool -delete -alias theAlias -keystore path/to/keystore –storepass pass
  • -printcert 控制台打印證書的詳細信息,如:keytool -printcert -file path/to/keystore/cert -v
  • -keypasswd 修改密鑰庫中指定條目口令 keytool -keypasswd -alias theAlias -keypass oldPass -new newPass -storepass keystorePass -keystore path/to/keystore
  • -storepasswd 修改keystore口令 keytool -storepasswd -keystore path/to/keystore -storepass oldPass -new newPass
  • -importcert 將已簽名數字證書導入密鑰庫 keytool -importcert -alias certAlias -keystore path/to/keystore -file path/to/keystore/cert


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM