Android 应用程序客户端与 java 服务器的双向 TLS

2024-03-12

我正在尝试使用双向 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协议。 跑步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 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;
        }
    }

}

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

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

请参阅此处为服务器和客户端设置相互身份验证的完整示例,包括示例项目spring-boot-mutual-tls-ssl https://github.com/Hakky54/mutual-tls-ssl

2022 年更新

我已在库中提供了上述代码片段和其他实用程序,以便更轻松、更简洁地设置 ssl 配置。除此之外,它还包含一些验证。请参阅此处的图书馆GitHub - SSLContext Kickstart: 下载SSLContext Kickstart https://github.com/Hakky54/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();
    }
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Android 应用程序客户端与 java 服务器的双向 TLS 的相关文章

  • java.lang.Object的hashCode具体使用的算法是什么

    中使用的算法是什么JVM实施java lang Object的隐含的hashCode 方法 OpenJDK or Oracle JDK答案中首选 它依赖于实现 并且在很大程度上 该算法是entirely取决于实施 只要它是一致的 但是 根据
  • Flutter - 删除 ListView 中项目之间的空间

    我正在使用 ListView builder 函数来创建项目列表 然而 iOS 中每个项目之间的空间很大 截图 你知道如何删除项目吗 看来是默认的 因为我没有添加它 code 列表显示 return Scaffold body ListVi
  • 更改 JComboBox 中滚动条的大小

    有谁知道如何手动更改 jComboBox 中的滚动条大小 我已经尝试了一大堆东西 但没有任何效果 好吧 我明白了 您可以实现 PopUpMenuListener 并使用它 public void popupMenuWillBecomeVis
  • 如何使用 swagger-codegen-plugin (maven) 生成客户端代码?

    我需要使用 swagger codegen plugin for maven 在 eclipse 中生成服务器存根代码 你能帮忙怎么做吗 以及需要什么配置 在 pom xml 中 我找到了这个答案 您只需要像下面这样更改 pom xml 即
  • 是否有最新的 Facebook Java SDK? [关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 好像没找到最近更新的 如果没有 是否有一个好的 Java 库来执行与 Facebook 的 API 交
  • 从 Android 访问云存储

    我一直无法找到任何有关如何从 Android 应用程序使用云存储的具体文档 我确实遇到过这个客户端库 https cloud google com storage docs reference libraries然而 Google Clou
  • Android 解析 JSON 卡在 get 任务上

    我正在尝试解析一些 JSON 数据 我的代码工作了一段时间 我不确定我改变了什么突然破坏了代码 当我运行代码时 我没有收到任何运行时错误或警告 我创建一个新的 AsyncTask 并执行它 当我打电话时 get 在这个新任务中 调试器在此行
  • 运行 Android 应用程序时出现错误

    我已经使用 Eclipse 创建了一个 Android 应用程序 但应用程序未在 AVD 上运行 它显示 不幸的是已停止工作 日志猫消息如下 07 29 04 59 50 789 W dalvikvm 784 threadid 1 thre
  • 改变 Java 中凯撒移位的方向

    用户可以通过选择 1 向左或 2 向右移动字母来选择向左或向右移动 左边工作正常 右边不行 现在它显示了完全相同的循环 但我已经改变了所有 and 以不同的方式进行标记 最终我总是得到奇怪的字符 如何让程序将字符向相反方向移动 如果用户输入
  • 如何为 flutter 绘图应用实现橡皮擦功能

    有一个关于通过 flutter 创建绘图应用程序的视频 YouTube https www youtube com watch v yyHhloFMNNA 它支持当用户点击屏幕时绘制线 点 但我找不到像 Android 本机那样擦除用户绘制
  • 配置jmxremote时无法正常停止tomcat

    我添加了一个jmxremotecatalina bat中的配置 set JAVA OPTS Dcom sun management jmxremote port 9004 Dcom sun management jmxremote ssl
  • Jetty Plugin 9启动不喜欢icu4j-2.6.1.jar

    我对 mortbay 的 Maven jetty 插件 6 有相同的配置
  • java swing:向 JTree 项目添加自定义图形按钮

    我想在 JTree 中的项目右侧添加一个带有小图标的附加按钮 这可以做到吗 如果是这样 怎么办 thanks Clamp 你在这方面成功了吗 我想做同样的事情 但很难让 JButton 响应用户 设置渲染器以显示按钮的过程很顺利 但所有鼠标
  • Android项目中使用java获取电脑的IP地址

    我在用ksoap2 android http code google com p ksoap2 android 我需要使用java获取IP地址 这样我就不必每次都手动输入它 我所说的 IP 地址是指 例如 如果我这样做ipconfig使用命
  • Java String ReplaceAll 方法给出非法重复错误?

    我有一个字符串 当我尝试运行时replaceAll方法 我收到这个奇怪的错误 String str something op str str replaceAll o n it works fine str str replaceAll n
  • android 中的 java.net.URL ..新手问题

    我是java新手 正在尝试android开发 以下代码生成 malformedURLException 有人可以帮助我识别异常吗 任何提示都会非常有帮助 package com example helloandroid import and
  • JPA 将 BigDecimal 作为整数保存在数据库中

    我在数据库中有这个字段 ITEMCOST NUMERIC 13 DEFAULT 0 NOT NULL 在JAVA中 Entity中的字段定义如下 Column name ITEMCOST private BigDecimal itemCos
  • 如何将库添加到 LIBGDX 项目的依赖项 gradle

    一切都在问题中 我已经尝试了在 SO 和其他网站中找到的所有答案 但没有运气 这就是我迄今为止尝试过的 adding compile fileTree dir lib include jar 到我的 build gradle adding
  • 如何使用注释处理 Hibernate 和 Spring 中的连接查询?

    我正在使用 Spring 和 Hibernate 以及 MySQL 开发应用程序 我是 Hibernate 新手 完成了基本任务 现在我需要在选择查询中应用联接以使用注释从多个表中获取数据 我已经搜索过但仍然没有任何想法 这是我的数据库表和
  • @Embeddable 中的 @GenerateValue

    我已将实体的 id 分离到一个单独的 Embeddable 类中 该实体如下 Entity Table name users public class Users EmbeddedId private Users pk id private

随机推荐

  • jQuery Lightbox 或具有图像数组的等效项

    我正在尝试实现一个Lightbox http leandrovieira com projects jquery lightbox 样式库 其中单击文本链接会启动从数组加载的图像幻灯片 而不是从页面上的内联内容加载 我能找到的所有示例都使用
  • 显示没有“hitbox”的元素(不接受鼠标/触摸输入)

    我想要实现的是一种通知框 adiv元素 我想用一些不透明度来显示它 我需要这个盒子在事件中 不可见 例如 如果该框位于按钮之上 我仍然可以通过该框单击该按钮 有些人可能建议让用户可以移动它 但当前的 UI 不允许我这样做 可以通过任何方式实
  • 在 JavaFx 标签中显示变化的值

    在JavaFX中 如何使用 标签 显示随时间不断变化的值 有很多方法可以实现这一点 最方便的是使用 JavaFX 的 DataBinding 机制 assuming you have defined a StringProperty cal
  • 将图像从 Matlab 传输到 OpenCV IplImage

    我在 Matlab 中有一张图像 img imopen image jpg 它返回一个 uint8 数组高 x 宽 x 通道 3 个通道 RGB 现在我想使用 openCV 对其进行一些操作 因此我编写了一个 MEX 文件 该文件将图像作为
  • 可以多次访问顶点的 TSP

    我正在寻求解决一个问题 其中我有一个加权有向图 并且必须从原点开始 至少访问所有顶点一次并以尽可能最短的路径返回原点 本质上 这将是 TSP 的一个经典示例 除了我DO NOT具有每个顶点只能被访问一次的约束 在我的例子中 除了原点之外的任
  • 使用 PHP 和 AJAX 保持套接字连接打开

    我正在尝试连接到一个服务器套接字 该套接字将在连接后向我发送一堆数据 从我那里获取响应 然后发送更多数据 重复此过程 直到确定它已经足够了 所以基本上 在第一次连接之后 我们将 并且当前正在 从服务器接收数据 我们想要获取这些数据 在另一个
  • 计算 ISBN 的校验位

    这实际上不是家庭作业 我只是在下周开始计算机科学之前浏览一本离散数学书中的一些问题 不管怎样 其中一个问题要求我编写一个程序来执行这个算法 它解释了 我所困惑的部分是如何获取 9 位数字并将其 拆分 为单个整数 以便可以对每个数字执行计算
  • 如何在 gdb 中使用 python 访问寄存器

    如何访问当前调试实例的cpu寄存器 例如 您可以从 gdb 调用printf 0x x eax and set eax b eax还有一种方法可以通过 gdb 提供的 python 支持来做到这一点吗 或者我应该创建一个可以像这样调用的 p
  • Android:如何更改操作栏背景颜色? [关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 我的 Android 应用程序的颜色是黑色 我想把它改成其他颜色 最简单的方法是什么 我尝试以此为主题 当应用程序打开时 我可以看到操作栏
  • 你能帮我写一个可以自行打印的短代码吗?

    define q k main return puts k nq k q define q k main return puts k nq k 这段代码可以在屏幕上打印出来 但是 我读起来有困难 尤其是那两个 K 它是如何工作的 我知道 d
  • Jooq 将 String 转换为 BigDecimal

    有没有办法在 jooq 查询中将 String 转换为 BigDecimal 而不丢失精度 当我做endResER VALUE cast BigDecimal class where VALUE是数据库中具有字符串值的字段 它返回不带任何小
  • 打印 GUID 变量

    我有一个 GUID 变量 我想在文本文件中写入其值 GUID的定义是 typedef struct GUID size is 16 DWORD Data1 WORD Data2 WORD Data3 BYTE Data4 8 GUID 但我
  • JTextArea 和 JTextField 文本内部填充

    我想增加间距 填充 插入文本字段 http java sun com j2se 1 5 0 docs api javax swing JTextField html and J文本区 http java sun com j2se 1 5 0
  • plpgsql - pgAdmin 4 不显示 RAISE 消息(例如,通知)

    在使用 pgAdmin III 很长一段时间后 我最近安装了 pgAdmin 4 我注意到在使用 RAISE NOTICE 运行 plpgsql 函数后 消息下没有显示任何内容 我去了https www postgresql org doc
  • 使用 Java API 下载 YouTube 视频?

    我在 Stack Overflow 中发现了类似的问题 并尝试了一些解决方案 所有这些都已经过时了 所以我提出了一个新问题 我跟随并尝试 如何在java上从youtube下载视频 https stackoverflow com questi
  • 如何使用 php shell_exec 和 sql 命令行创建数据库

    我正在尝试使用 shell exec 和 mysql 命令创建数据库 由于几个原因 我不想使用 php 内置的 mysql query 但是 我无法让以下命令成功执行 任何人都可以告诉我出了什么问题吗 test shell exec mys
  • 由于“TIMESTAMP withimplicit DEFAULT value is deprecated”错误,我无法启动 Mysql 5.6 服务器? [关闭]

    Closed 这个问题是无关 help closed questions 目前不接受答案 好的 这是我的故事 我访问 mysql com 网站并下载了文件mysql 5 6 11 winx64 zip进入C 然后我解压它 然后我进入 bin
  • 是否有软件可以将大量小数字图像拼接在一起而无需旋转或拉伸它们? [关闭]

    Closed 这个问题是无关 help closed questions 目前不接受答案 我有大量小图像 360x192 这些小图像是从 DOS 2D 电脑游戏中按顺序截取的 它们有相当大的重叠 我想将它们缝合在一起形成一个大的复合材料 由
  • 自动反序列化为 Web.API 控制器中的类似字符串的类

    我有一个 Web API 端点 它将这样的对象作为参数 public class Person public string FirstName get set public string LastName get set public in
  • Android 应用程序客户端与 java 服务器的双向 TLS

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