[英]SSLHandshakeException: Certificate Unknown (Java Spring Boot & Android)
我有一個 Spring 啟動 API 在本地運行,帶有自簽名證書,使用 HTTPS 協議。 顯然,當我從瀏覽器發送GET
請求時,我收到io.netty.handler.codec.DecoderException: javax.net.ssl.SSLHandshakeException: Received fatal alert: certificate_unknown
錯誤在服務器端,這是正常的,因為自我-signed 不受瀏覽器信任。 Postman 適用於GET
和POST
。
However, I want to send GET
requests from an Android client to the Spring API but, even I've used a function to allow all SSL traffic (yes, I know it's not recommended), I still can't send requests to the API ,接收如下output:
I/STATUS: 405
I/MSG: Method Not Allowed
我認為我的allowAllSSL()
function ( HttpsTrustManager
類)會解決這個問題,因為如果我刪除 function 調用,我會收到以下錯誤,這似乎與服務器端的錯誤相匹配:
javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
at com.android.org.conscrypt.ConscryptFileDescriptorSocket.startHandshake(ConscryptFileDescriptorSocket.java:239)
現在,您可能認為 Spring 中的GET
請求沒有正確實現,但事實並非如此,因為相同的GET
請求在 Postman 中也可以正常工作。 我相信問題仍然與證書有關,但我無法弄清楚我需要更改什么。 這是我的代碼:
Spring 啟動 Rest Controller
@RestController
@RequestMapping("/post")
public class PostRequest {
@GetMapping("")
public String string(@RequestBody ImageRequest newEmployee){
....
ImageRequest
class 僅包含三個私有String
成員。
HttpsTrustManager class(允許所有 SSL)
package com.example.androidclient;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
public class HttpsTrustManager implements X509TrustManager {
private static TrustManager[] trustManagers;
private static final X509Certificate[] _AcceptedIssuers = new X509Certificate[]{};
@Override
public void checkClientTrusted(
X509Certificate[] x509Certificates, String s)
throws java.security.cert.CertificateException {
}
@Override
public void checkServerTrusted(
X509Certificate[] x509Certificates, String s)
throws java.security.cert.CertificateException {
}
public boolean isClientTrusted(X509Certificate[] chain) {
return true;
}
public boolean isServerTrusted(X509Certificate[] chain) {
return true;
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return _AcceptedIssuers;
}
public static void allowAllSSL() {
HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String arg0, SSLSession arg1) {
return true;
}
});
SSLContext context = null;
if (trustManagers == null) {
trustManagers = new TrustManager[]{new HttpsTrustManager()};
}
try {
context = SSLContext.getInstance("TLS");
context.init(null, trustManagers, new SecureRandom());
} catch (NoSuchAlgorithmException | KeyManagementException e) {
e.printStackTrace();
}
HttpsURLConnection.setDefaultSSLSocketFactory(context != null ? context.getSocketFactory() : null);
}
}
Android 請求
HttpsTrustManager.allowAllSSL();
URL url = new URL("https://192.168.1.106:8443/post");
HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setRequestProperty("Content-Type", "application/json;charset=UTF-8");
conn.setRequestProperty("Accept", "application/json");
conn.setDoOutput(true);
conn.setDoInput(true);
JSONObject jsonParam = new JSONObject();
jsonParam.put("location", "Somewhere");
jsonParam.put("date", "22.05.2020");
jsonParam.put("imageBytes", strings[0]);
Log.i("JSON", jsonParam.toString());
DataOutputStream os = new DataOutputStream(conn.getOutputStream());
//os.writeBytes(URLEncoder.encode(jsonParam.toString(), "UTF-8"));
os.writeBytes(jsonParam.toString());
os.flush();
os.close();
Log.i("STATUS", String.valueOf(conn.getResponseCode()));
Log.i("MSG", conn.getResponseMessage());
conn.disconnect();
} catch (Exception e) {
e.printStackTrace();
}
return "ok";
}
在您的 android 應用程序中使用此 function。
請注意,這將允許所有 ssl 證書無需驗證。 我鼓勵您在處理此處概述的自簽名證書時遵循推薦的方法: https://developer.android.com/training/articles/security-ssl#java
// Create a trust manager that does not validate certificate chains
final TrustManager[] trustAllCerts = new TrustManager[] {
new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {}
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
}
};
// Install the all-trusting trust manager
final SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
// Create an ssl socket factory with our all-trusting manager
final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
我自己找到了解決方案。 顯然, Connection.setDoOutput(true)
方法僅適用於POST
和PUT
請求,但不適用於GET
。
因此,我已將我的RequestMapping
更改為在POST
上工作,例如:
@RequestMapping(
value = "/post",
produces = "application/json",
method = RequestMethod.POST)
現在我收到200 OK
。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.