使用 Android Native、Android Studio 和 retrofit 在本地网络中信任我自己的自签名证书

Trust my own self-signed certificate in local network using Android Native, Android Studio and retrofit

我正在创建一个简单的 android 应用程序,它将在封闭的本地网络中使用。在这个本地网络中,flask 服务器是 运行,它被配置为通过 nginx 代理使用自签名证书。服务器的后端应用程序使用自签名证书工作正常,我已经使用我的浏览器和邮递员验证了这一点。 (显然,我必须明确要求浏览器信任我的证书)。

几天来,我一直试图在网上找到一些关于如何让我的 android 应用程序接受我的证书的明确答案,但我尝试过的所有事情都让我陷入了死胡同。有时解决方案已被弃用,有时对于这种微不足道的事情来说太复杂了。

使用Retrofit发送http请求;据我了解,我必须以某种方式配置改造实例的 http 客户端以接受我的证书。

我设法使用了一个接受任何证书的客户端,但这不是我想要的。理想情况下,我的证书将添加到官方 CA 默认信任的“证书集”中,这样​​应用程序也可以向外部资源发送请求。

因此,假设后端应用程序是 运行,例如192.168.1.10:443,我该怎么做?

注意:我已阅读此处给出的说明https://developer.android.com/training/articles/security-config.html#TrustingAdditionalCas 并添加了

android:networkSecurityConfig="@xml/network_security_config"

到我的清单文件,但出现以下错误:

Hostname 192.168.1.10 not verified: certificate sha256/...../..... 并继续列出证书的信息,如通用名称等

最佳解决方案

拉取字符串以使服务器所有者使用由自定义根 CA 签名的证书,然后使用 networkSecurityConfig 固定此证书。 在我看来,这应该是一项阻止功能发布的要求。

备选

我会创建 2 个 http 客户端:

  1. 一个接受所有证书(chechServerTrusted(...) 的空 imolementation)的信任管理器被破坏 - 仅针对预期 SSL 握手失败的特定本地网络请求。
  2. 其余请求的正常请求。

通过这样做,您将向 man-in-a-middle 攻击公开此请求,以防攻击者进入本地网络 - 攻击者和真实服务器证书均不受信任。 它们还可以模仿证书元数据,因此您不会区分它们。 只有您知道您的系统是否可以承受风险(以及风险的可能性有多大)。

更多见解:

  • networkSecurityConfig 用于众所周知的证书,因此您可以对 public 密钥进行硬编码,因此我认为它无法解决您的问题。
  • 如果没有实际的自签名证书,您将无法在设备上安装证书颁发机构。

更新

根据Android Developers,你需要如下配置:

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <base-config>
        <trust-anchors>
            <certificates src="@raw/my_custom_cas"/>
            <certificates src="system"/>
        </trust-anchors>
    </base-config>
</network-security-config>

他们声称 res/raw/my_custom_cas 接受所有常见格式:

Add the trusted CAs, in PEM or DER format, to res/raw/trusted_roots. Note that if using PEM format the file must contain only PEM data and no extra text. You can also provide multiple <certificates> elements instead of one.

Shlomi Katriel 的回答是我已经尝试过的,但它间接地引导我找到了解决方案。请记住,我能够解决这个问题的唯一原因是因为我拥有服务器的 root 访问权限,并且可以随心所欲地使用它。

答案基本上是解决整个问题的关键。我将 post 所有步骤,以防其他人需要。

步骤 1 创建您的 self-signed 证书。就我而言,我使用了 openssl 实用程序。包含 -addext 标志非常重要。这是直接取自我上面链接的答案的示例:

openssl req \
-newkey rsa:2048 \
-nodes \
-x509 \
-days 36500 -nodes \
-addext "subjectAltName = IP.1:1.2.3.4" \
-keyout /etc/ssl/private/nginx-selfsigned2.key \
-out /etc/ssl/certs/nginx-selfsigned2.crt

将1.2.3.4替换成你服务器的本地ip。这将在证书的 subjectAltName 属性 中包含服务器的本地 ip。否则,您将不断收到错误“主机名...未验证...”

在 运行 nginx 实例中使用证书是一个不同的主题,可以很容易地完成并且网上有很多资源。

步骤 2 打开 android studio 并在 [=12= 下创建一个新的 android 资源目录(如果您还没有的话) ] 名为 raw。在此目录中,复制上述命令生成的 .crt 文件的内容。

步骤 3res 下创建一个名为 xml 的 android 资源目录。在那里,创建一个新的 xml 文件,在我的例子中我称之为 network_security_config.xml。在那里,我插入了以下内容:

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <base-config cleartextTrafficPermitted="false">
        <trust-anchors>
            <certificates src="@raw/certificate"/>
            <certificates src="system"/>
        </trust-anchors>
    </base-config>
</network-security-config>

不要使用“证书”,而是使用您在第 2 步中复制的证书文件的文件名。

步骤 4android:networkSecurityConfig="@xml/network_security_config" 添加到 AndroidManifest.xml

中的应用程序元素