程式狂想筆記

一個攻城師奮鬥史

0%

Java 因憑證 SNI 問題驗證不過

最近在做 line webhook
但是做測試的時候
發現沒有呼叫我的程式
但我手機、其他電腦都可以連線
這又非常詭異

使用外部網站檢查

我在SSL Server Test (Powered by Qualys SSL Labs)做測試
是在這裡面找到的edelahozuah/awesome-tls-security: A collection of (not-so, yet) awesome resources related to TLS, PKI and related stuff

這個網站可以找到很多問題
但主要不能連我是遇到的問題是Client aborts on SNI unrecognized_name warning

使用 Java SSLTEST

不多說,紀錄安裝過程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 安裝 jdk
sudo apt install default-jdk #openjdk-11-jdk

# 下載 ssltest
git clone https://github.com/ChristopherSchultz/ssltest.git

cd ssltest
./build.sh # 記得 socure 跟 target 要改11 因為我使用的是11
cd build
java -jar ssltest.jar google.com
# 我第一次的實用適用 jdk 7 跑,會有 sni Exception
# 但我現在用 jdk 11 沒有跑出 Exception
# 不過可以看到沒有 Accepted
java -jar ssltest.jar google.com.tw | grep Accepted

自己來寫一隻來驗證
其實用 postman 產生範例
搭配我之前 maven 執行

1
2
3
4
5
6
7
8
9
10
OkHttpClient client = new OkHttpClient();

Request request = new Request.Builder()
.url("http://httpbin.org/get?test=123%20123")
.get()
.addHeader("cache-control", "no-cache")
.addHeader("postman-token", "b0cbc43f-9a70-1056-951e-e5b0c6b2dd33")
.build();

Response response = client.newCall(request).execute();

maven 不知道為什麼執行要加這段-Dexec.cleanupDaemonThreads=false
运行 mvn java:exec 如何避免 IllegalThreadStateException - 尚码园

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
[WARNING] 
javax.net.ssl.SSLHandshakeException: received handshake warning: unrecognized_name
at sun.security.ssl.Alert.createSSLException (Alert.java:131)
at sun.security.ssl.Alert.createSSLException (Alert.java:117)
at sun.security.ssl.TransportContext.fatal (TransportContext.java:313)
at sun.security.ssl.TransportContext.fatal (TransportContext.java:269)
at sun.security.ssl.TransportContext.fatal (TransportContext.java:260)
at sun.security.ssl.Alert$AlertConsumer.consume (Alert.java:272)
at sun.security.ssl.TransportContext.dispatch (TransportContext.java:186)
at sun.security.ssl.SSLTransport.decode (SSLTransport.java:164)
at sun.security.ssl.SSLSocketImpl.decode (SSLSocketImpl.java:1144)
at sun.security.ssl.SSLSocketImpl.readHandshakeRecord (SSLSocketImpl.java:1055)
at sun.security.ssl.SSLSocketImpl.startHandshake (SSLSocketImpl.java:395)
at okhttp3.internal.connection.RealConnection.connectTls (RealConnection.kt:367)
at okhttp3.internal.connection.RealConnection.establishProtocol (RealConnection.kt:325)
at okhttp3.internal.connection.RealConnection.connect (RealConnection.kt:197)
at okhttp3.internal.connection.ExchangeFinder.findConnection (ExchangeFinder.kt:249)
at okhttp3.internal.connection.ExchangeFinder.findHealthyConnection (ExchangeFinder.kt:108)
at okhttp3.internal.connection.ExchangeFinder.find (ExchangeFinder.kt:76)
at okhttp3.internal.connection.RealCall.initExchange$okhttp (RealCall.kt:245)
at okhttp3.internal.connection.ConnectInterceptor.intercept (ConnectInterceptor.kt:32)
at okhttp3.internal.http.RealInterceptorChain.proceed (RealInterceptorChain.kt:100)
at okhttp3.internal.cache.CacheInterceptor.intercept (CacheInterceptor.kt:96)
at okhttp3.internal.http.RealInterceptorChain.proceed (RealInterceptorChain.kt:100)
at okhttp3.internal.http.BridgeInterceptor.intercept (BridgeInterceptor.kt:83)
at okhttp3.internal.http.RealInterceptorChain.proceed (RealInterceptorChain.kt:100)
at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept (RetryAndFollowUpInterceptor.kt:76)
at okhttp3.internal.http.RealInterceptorChain.proceed (RealInterceptorChain.kt:100)
at okhttp3.internal.connection.RealCall.getResponseWithInterceptorChain$okhttp (RealCall.kt:197)
at okhttp3.internal.connection.RealCall.execute (RealCall.kt:148)
at test.App.main (App.java:23)
at org.codehaus.mojo.exec.ExecJavaMojo$1.run (ExecJavaMojo.java:254)
at java.lang.Thread.run (Thread.java:834)

正在就能證明 JAVA 程式會有問題

Java 其他驗證

這個也可以直接看到 Exception 錯誤內容

UniconLabs/java-keystore-ssl-test: A simple script to verify SSL/HTTPS functionality of a remote host through JDK keystore.

1
2
3
mvn clean package
cd target
java -jar java-keystore-test-0.1.0.jar https://google.com.tw

其他程式

1
curl https://xxx

golang 就用 postman 產生出來吧

解決方法

apache VirtualServer 設定

ServerName xxx

1
2
3
4
<VirtualHost xxx:443>
ServerName www.example.com
ServerAlias example.com www.example.com
...

Client aborts on SNI unrecognized_name warning
Qualys Labs SSL Test - Incorrect SNI alerts - JERVIS DOT WS

其他相關
java - Handshake alert: Unrecognized_name Liberty Profile - Stack Overflow
10 Online Tool to Test SSL, TLS and Latest Vulnerability - Geekflare

drwetter/testssl.sh: Testing TLS/SSL encryption anywhere on any port
git clone https://github.com/drwetter/testssl.sh
chmod a+x testssh.sh
./testssh.sh google.com

pornin/TestSSLServer
tord and lion’s lair | 各式心得 | 頁 4

感想

之前萬用憑證(WildCard)和中間憑證小記 | 程式狂想筆記就讓我以為就不會有其他問題
沒想到 Java 也有憑證這一關(好像是 Java 獨立的)
QQ 希望之後不要遇到類似這個問題

2020-07-28

Java SSLPoke 檢查

檢查也有找到這個方法可用[JAVA] SSL handshake連線測試工具 SSLPoke | 阿輝的零碎筆記 - 點部落

SSLPoke.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
import java.io.PrintStream;
import javax.net.ssl.SSLHandshakeException;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;

public class SSLPoke
{
public SSLPoke() {}

public static void main(String[] paramArrayOfString)
{
if (paramArrayOfString.length != 2) {
System.err.println("Utility to debug Java connections to SSL servers");
System.err.println("Usage: ");
System.err.println(" java " + SSLPoke.class.getName() + " <host> <port>");
System.err.println("or for more debugging:");
System.err.println(" java -Djavax.net.debug=ssl " + SSLPoke.class.getName() + " <host> <port>");
System.err.println();
System.err.println("Eg. to test the SSL certificate at https://localhost, use");
System.err.println(" java " + SSLPoke.class.getName() + " localhost 443");
System.exit(1);
}
try {
SSLSocketFactory localSSLSocketFactory = (SSLSocketFactory)SSLSocketFactory.getDefault();
SSLSocket localSSLSocket = (SSLSocket)localSSLSocketFactory.createSocket(paramArrayOfString[0], Integer.parseInt(paramArrayOfString[1]));

java.io.InputStream localInputStream = localSSLSocket.getInputStream();
java.io.OutputStream localOutputStream = localSSLSocket.getOutputStream();


localOutputStream.write(1);

while (localInputStream.available() > 0) {
System.out.print(localInputStream.read());
}
System.out.println("Successfully connected");
System.exit(0);
}
catch (SSLHandshakeException localSSLHandshakeException) {
if (localSSLHandshakeException.getCause() != null) {
localSSLHandshakeException.getCause().printStackTrace();
} else {
localSSLHandshakeException.printStackTrace();
}
} catch (Exception localException) {
localException.printStackTrace();
}
System.exit(1);
}
}

1
2
3
## 先打包成class
$ javac SSLPoke.java
$ java SSLPoke www.google.com 443

連線成功會顯示

1
2
$ java SSLPoke www.google.com 443
Successfully connected

失敗就會出現一大堆錯

handling exception: javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

1
2
 handling exception: javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

不知道是哪裡出問題
但後來發現是根憑證沒有這個

  1. 指定jks(加好根憑證)或自己加

    1
    2
    3
    4
    java -Djavax.net.ssl.trustStore= 

    # 吃公司內部jks 無可以設定
    #java -Djavax.net.debug=ssl -Djavax.net.ssl.trustStore=./jssecacerts.jks SSLPoke xxxx.aaa.com.tw 443

    获取访问目标主机的有效SSL/TLS证书 (无法直接得到证书时)_iihero@CSDN-CSDN博客

  2. 程式忽略掉
    证书不安全解决HttpClient 如何忽略证书验证 - ALLOW_ALL_HOSTNAME_VERIFIER_tiantianchuqiji的博客-CSDN博客