使用 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 客户端:
- 一个接受所有证书(
chechServerTrusted(...)
的空 imolementation)的信任管理器被破坏 - 仅针对预期 SSL 握手失败的特定本地网络请求。
- 其余请求的正常请求。
通过这样做,您将向 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 文件的内容。
步骤 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
中的应用程序元素
我正在创建一个简单的 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 客户端:
- 一个接受所有证书(
chechServerTrusted(...)
的空 imolementation)的信任管理器被破坏 - 仅针对预期 SSL 握手失败的特定本地网络请求。 - 其余请求的正常请求。
通过这样做,您将向 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 访问权限,并且可以随心所欲地使用它。
步骤 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 文件的内容。
步骤 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