gpt4 book ai didi

java - Android 应用程序客户端与 java 服务器的相互 TLS

转载 作者:行者123 更新时间:2023-11-29 22:46:08 28 4
gpt4 key购买 nike

我正在尝试使用双向 TLS 向我的服务器发送 https 请求。我使用 TLS 成功运行的服务器。但我不知道如何在客户端(Android 应用程序)执行此操作。我在 java 服务器上使用 spring。来自 Android 应用程序的请求是使用 HttpsUrlConnection() 发出的。

我成功地调用了 HttpsUrlConnection() 我的代码看起来是这样的:

public void test() {
try {
URL url = new URL(this.apiUrl);
HttpsURLConnection urlConnection = (HttpsURLConnection) url.openConnection();
urlConnection.setSSLSocketFactory(sslContext.getSocketFactory());
InputStream in = urlConnection.getInputStream();
System.out.print(in);
} catch (Exception e) {
e.printStackTrace();
}
}

我的服务器配置为使用 TLSv1.2 协议(protocol)。运行 test() 会抛出这个错误:

W/System.err: javax.net.ssl.SSLHandshakeException: Handshake failed
at com.android.org.conscrypt.ConscryptFileDescriptorSocket.startHandshake(ConscryptFileDescriptorSocket.java:288)
at com.android.okhttp.internal.io.RealConnection.connectTls(RealConnection.java:196)
at com.android.okhttp.internal.io.RealConnection.connectSocket(RealConnection.java:153)
at com.android.okhttp.internal.io.RealConnection.connect(RealConnection.java:116)
at com.android.okhttp.internal.http.StreamAllocation.findConnection(StreamAllocation.java:186)
at com.android.okhttp.internal.http.StreamAllocation.findHealthyConnection(StreamAllocation.java:128)
at com.android.okhttp.internal.http.StreamAllocation.newStream(StreamAllocation.java:97)
at com.android.okhttp.internal.http.HttpEngine.connect(HttpEngine.java:289)
at com.android.okhttp.internal.http.HttpEngine.sendRequest(HttpEngine.java:232)
W/System.err: at com.android.okhttp.internal.huc.HttpURLConnectionImpl.execute(HttpURLConnectionImpl.java:465)
at com.android.okhttp.internal.huc.HttpURLConnectionImpl.getResponse(HttpURLConnectionImpl.java:411)
at com.android.okhttp.internal.huc.HttpURLConnectionImpl.getInputStream(HttpURLConnectionImpl.java:248)
at com.android.okhttp.internal.huc.DelegatingHttpsURLConnection.getInputStream(DelegatingHttpsURLConnection.java:211)
W/System.err: at com.android.okhttp.internal.huc.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:30)
at nl.management.finance.client.RaboClient.test(RaboClient.java:64)
at nl.management.finance.MainActivity$RESTTask.doInBackground(MainActivity.java:31)
at nl.management.finance.MainActivity$RESTTask.doInBackground(MainActivity.java:25)
at android.os.AsyncTask$3.call(AsyncTask.java:378)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:289)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:919)
Caused by: javax.net.ssl.SSLProtocolException: SSL handshake aborted: ssl=0x703daa2ff448: Failure in SSL library, usually a protocol error
error:10000412:SSL routines:OPENSSL_internal:SSLV3_ALERT_BAD_CERTIFICATE (external/boringssl/src/ssl/tls_record.cc:587 0x703daa2b1148:0x00000001)
at com.android.org.conscrypt.NativeCrypto.SSL_do_handshake(Native Method)
at com.android.org.conscrypt.NativeSsl.doHandshake(NativeSsl.java:387)
at com.android.org.conscrypt.ConscryptFileDescriptorSocket.startHandshake(ConscryptFileDescriptorSocket.java:226)
... 22 more

为什么我在堆栈跟踪中看到 SSLV3?它没有使用TLSv1.2吗? Wireshark 显示此 https://ibb.co/27mpG4r

此代码(来自@Hakan54)使 SSLContext:

public class SSLTrustManagerHelper {

private InputStream keyStore;
private String keyStorePassword;
private InputStream trustStore;
private String trustStorePassword;

public SSLTrustManagerHelper(InputStream keyStore,
String keyStorePassword,
InputStream trustStore,
String trustStorePassword) throws ClientException {
if (keyStore == null || keyStorePassword.trim().isEmpty() || trustStore == null || trustStorePassword.trim().isEmpty()) {
throw new ClientException("TrustStore or KeyStore details are empty, which are required to be present when SSL is enabled");
}

this.keyStore = keyStore;
this.keyStorePassword = keyStorePassword;
this.trustStore = trustStore;
this.trustStorePassword = trustStorePassword;
}

public SSLContext clientSSLContext() throws ClientException {
try {
TrustManagerFactory trustManagerFactory = getTrustManagerFactory(trustStore, trustStorePassword);
KeyManagerFactory keyManagerFactory = getKeyManagerFactory(keyStore, keyStorePassword);
this.keyStore.close();
this.trustStore.close();

return getSSLContext(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers());
} catch (UnrecoverableKeyException | NoSuchAlgorithmException | CertificateException | KeyStoreException | IOException | KeyManagementException e) {
e.printStackTrace();
throw new ClientException(e);
}
}

private static SSLContext getSSLContext(KeyManager[] keyManagers, TrustManager[] trustManagers) throws NoSuchAlgorithmException, KeyManagementException {
SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
sslContext.init(keyManagers, trustManagers, null);
return sslContext;
}

private static KeyManagerFactory getKeyManagerFactory(InputStream keystore, String keystorePassword) throws NoSuchAlgorithmException, KeyStoreException, IOException, CertificateException, UnrecoverableKeyException, ClientException {
KeyStore keyStore = loadKeyStore(keystore, keystorePassword);
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
keyManagerFactory.init(keyStore, keystorePassword.toCharArray());
return keyManagerFactory;
}

private static TrustManagerFactory getTrustManagerFactory(InputStream truststore, String truststorePassword) throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException, ClientException {
KeyStore trustStore = loadKeyStore(truststore, truststorePassword);
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(trustStore);
return trustManagerFactory;
}

private static KeyStore loadKeyStore(InputStream keystoreStream, String keystorePassword) throws ClientException, IOException, KeyStoreException, NoSuchAlgorithmException, CertificateException {
if (keystoreStream == null) {
throw new ClientException("keystore was null.");
}

KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
keystore.load(keystoreStream, keystorePassword.toCharArray());
return keystore;
}

}

最佳答案

您正在寻找的是基于证书的相互身份验证。服务器和客户端都需要相互信任才能进行通信。如果服务器只信任特定的客户端,则任何其他客户端都不可能发出请求。

上面的例子看起来没问题,但是用下面的例子配置会更容易:

import static java.util.Objects.isNull;
import static org.apache.commons.lang3.StringUtils.isBlank;

import java.io.IOException;
import java.io.InputStream;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;

import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;

public class SSLTrustManagerHelper {

private String keyStore;
private String keyStorePassword;
private String trustStore;
private String trustStorePassword;

public SSLTrustManagerHelper(String keyStore,
String keyStorePassword,
String trustStore,
String trustStorePassword) {
if (isBlank(keyStore) || isBlank(keyStorePassword) || isBlank(trustStore) || isBlank(trustStorePassword)) {
throw new ClientException("TrustStore or KeyStore details are empty, which are required to be present when SSL is enabled");
}

this.keyStore = keyStore;
this.keyStorePassword = keyStorePassword;
this.trustStore = trustStore;
this.trustStorePassword = trustStorePassword;
}

public SSLContext clientSSLContext() {
try {
TrustManagerFactory trustManagerFactory = getTrustManagerFactory(trustStore, trustStorePassword);
KeyManagerFactory keyManagerFactory = getKeyManagerFactory(keyStore, keyStorePassword);

return getSSLContext(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers());
} catch (UnrecoverableKeyException | NoSuchAlgorithmException | CertificateException | KeyStoreException | IOException | KeyManagementException e) {
throw new ClientException(e);
}
}

private static SSLContext getSSLContext(KeyManager[] keyManagers, TrustManager[] trustManagers) throws NoSuchAlgorithmException, KeyManagementException {
SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
sslContext.init(keyManagers, trustManagers, null);
return sslContext;
}

private static KeyManagerFactory getKeyManagerFactory(String keystorePath, String keystorePassword) throws NoSuchAlgorithmException, KeyStoreException, IOException, CertificateException, UnrecoverableKeyException {
KeyStore keyStore = loadKeyStore(keystorePath, keystorePassword);
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
keyManagerFactory.init(keyStore, keystorePassword.toCharArray());
return keyManagerFactory;
}

private static TrustManagerFactory getTrustManagerFactory(String truststorePath, String truststorePassword) throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException {
KeyStore trustStore = loadKeyStore(truststorePath, truststorePassword);
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(trustStore);
return trustManagerFactory;
}

private static KeyStore loadKeyStore(String keystorePath, String keystorePassword) throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException {
try(InputStream keystoreInputStream = SSLTrustManagerHelper.class.getClassLoader().getResourceAsStream(keystorePath)) {
if (isNull(keystoreInputStream)) {
throw new ClientException(String.format("Could not find the keystore file with the given location %s", keystorePath));
}

KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
keystore.load(keystoreInputStream, keystorePassword.toCharArray());
return keystore;
}
}

}

在这里您需要提供 keystore 和信任库的位置,以及密码。公共(public)类将为您提供 ssl 上下文,您可以将其加载到您的 http 客户端中。

确保您有一个包含私钥和公钥的客户端 keystore ,以及一个包含服务器公钥的信任库。并确保服务器在其信任库中具有客户端的公钥。您还需要在 application.yml 文件中为您的服务器提供额外的属性,以强制服务器验证客户端。属性是:client-auth: need

在此处查看为服务器和客户端设置相互身份验证的完整示例,包括示例项目 spring-boot-mutual-tls-sll

2022 年更新

我已经在一个库中提供了上面的代码 fragment 和其他实用程序,以便更容易和更简洁地设置 ssl 配置。除此之外,它还包含一些验证。图书馆请看这里 GitHub - SSLContext Kickstart

我一开始提供的例子可以替换为:

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;

class App {
public static void main(String[] args) {
SSLFactory sslFactory = SSLFactory.builder()
.withIdentityMaterial("/path/to/resource/identity.jks", "password".toCharArray())
.withTrustMaterial("/path/to/resource/truststore.jks", "password".toCharArray())
.build();

SSLContext sslContext = sslFactory.getSslContext();
SSLSocketFactory sslSocketFactory = sslFactory.getSslSocketFactory();
}
}

关于java - Android 应用程序客户端与 java 服务器的相互 TLS,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58438885/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com