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

发布于 2025-01-15 17:44:24 字数 925 浏览 3 评论 0原文

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

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

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

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

那么,假设后端应用程序运行在例如 192.168.1.10:443 上,我该如何处理呢?

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

android:networkSecurityConfig="@xml/network_security_config"

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

主机名 192.168.1.10 未验证:证书 sha256/.... ./..... 并继续列出证书的信息,如通用名称等。

I am creating a simple android app that will be used in a closed local network. In this local network, a flask server is running which is configured to use a self-signed certificate via nginx proxying. The backend application of the server works fine using the self-signed certificate, I have verified this both using my browser and postman. (Obviously, I had to explicitly ask the browser to trust my certificate).

For days, I have been trying to find some definitive answer online on how to make my android app accept my certificate, but all the things I have tried have led me to a dead end. Sometimes the solutions where deprecated, and other times just too complicated for such a trivial thing.

The http requests are sent using Retrofit; as I understand, I must somehow configure my retrofit instance's http client to accept my certificate.

I have managed to use a client that accepts any certificate, but this is not what I want. Ideally, my certificate would be added to the "set" of certificates that are trusted by default by official CAs, so that the app can possibly send requests to outside resources as well.

So, given that the backend application is running on e.g. 192.168.1.10:443, how would I go about this?

Note: I have read the instructions given here https://developer.android.com/training/articles/security-config.html#TrustingAdditionalCas
and have added

android:networkSecurityConfig="@xml/network_security_config"

to my manifest file, but I am getting the following error:

Hostname 192.168.1.10 not verified: certificate sha256/...../.....
and continues to list the information of the certificate like common name etc.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

烛影斜 2025-01-22 17:44:24

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

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

第 1 步
创建您的自签名证书。就我而言,我使用了 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 并在 res 下创建一个名为 raw 的新 Android 资源目录(如果您还没有)。在此目录中,复制上述命令生成的 .crt 文件的内容。

第3步res下创建一个名为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 中复制的证书文件的文件名,而不是“证书”。

步骤 4 添加 android:networkSecurityConfig="@xml/network_security_config" 到 AndroidManifest.xml 中的应用程序元素

Shlomi Katriel's answer was something I had already tried, but it led me to the solution indirectly. Please keep in mind that the only reason I was able to solve this was because I have root access to the server and can do with it as I please.

This
answer was basically the key to solving the whole thing. I will post all the steps in case someone else needs this.

Step 1
Create your self-signed certificate. In my case, I used the openssl utility. It is very important to include the -addext flag. Here is an example taken directly from the answer I linked above:

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

Replace 1.2.3.4 with the local ip of your server. This will include the server's local ip in the subjectAltName property of the certificate. Without this, you will keep getting the error "Hostname ... not verified ..."

Using the certificate in a running nginx instance is a different subject which can be done easily and there are plenty of resources online.

Step 2 Open android studio and create a new android resource directory (if you don't already have it) under res named raw. Inside this directory, copy the contents of the .crt file that the command above produced.

Step 3 Create an android resource directory under res named xml. In there, create a new xml file which in my case I called network_security_config.xml. In there, I inserted the following:

<?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>

Instead of "certificate", use the filename of the certificate file you copied in step 2.

Step 4 Add android:networkSecurityConfig="@xml/network_security_config" to the application element in your AndroidManifest.xml

你是暖光i 2025-01-22 17:44:24

最佳解决方案是

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

另一种方法

是创建 2 个 http 客户端:

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

通过这样做,您将暴露此中间人攻击请求,以防攻击者进入本地网络 - 攻击者和真实服务器证书都不可信。
他们还可以模仿证书元数据,这样您就无法分辨出差异。
只有您知道您的系统是否能够承受风险(以及风险的可能性有多大)。

更多见解:

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

更新

根据 Android 开发人员,您需要以下配置:

<?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 接受所有常见格式:

将 PEM 或 DER 格式的可信 CA 添加到 res/raw/trusted_roots。请注意,如果使用 PEM 格式,文件必须仅包含 PEM 数据,不得包含额外文本。您还可以提供多个 元素,而不是一个。

Best solution

pull the strings to make the owner of the server use a certificate, which is signed by a custom root CA, then pin this certificate using networkSecurityConfig.
In my opinion it should be a requirement that should prevent a feature from being released.

Alternative

I would create 2 http clients:

  1. One with breached trust manager that accepts all certificates (empty imolementation of chechServerTrusted(...)) - only for that specific local network request that expected to fail the SSL handshake.
  2. Normal one for the rest of the requests.

By doing so, you're exposing this request for man-in-a-middle attack in case an attacker got into the local network - both attacker and real server certificates are not trusted.
They can also mimic the certificate metadata so you won't tell the difference.
Only you know if your system can afford the risk (and how probable is the risk anyway).

More insights:

  • networkSecurityConfig is meant for well known certificates so you can hard code public keys, so I don't believe it can solve your case.
  • You cannot install certificate authority on device without the actual self signed certificate.

Update

According to Android Developers, you need the following configuration:

<?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>

They claim res/raw/my_custom_cas accepts all common formats:

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文