如何存储和加载 cacerts.pem for gSoap-OpenSSL Android 应用程序

How to store and load cacerts.pem for gSoap-OpenSSL Android app

本文关键字:pem cacerts for gSoap-OpenSSL 应用程序 Android 加载 何存储 存储      更新时间:2023-10-16

我正在开发一个C++Android应用程序,该应用程序使用gSOAP和OpenSSL。要启用 OpenSSL 以验证服务器证书,我需要为其提供cacerts.pem文件的位置,否则所有 HTTPS 连接都将失败。

我的问题是:在Android上存储和加载cacerts.pem的正确方法是什么?

  • 我可以通过将cacerts.pem复制到APK根目录中,将其放置在src/resourcesres/raw/assets/中。但是SSL_CTX_load_verify_locations(ctx, "cacerts.pem", nullptr)找不到该文件。如何使OpenSSL能够读取APK中的PEM文件?

  • 有没有办法将cacerts.pem存储为字符串缓冲区,从而避免在APK中携带它?

我的应用需要与 SharePoint 联机网站通信(https://*.example.com/)

在Android上存储和加载cacerts.pem的正确方法是什么?

您必须加载验证主机证书所需的一个 CA。

您可以加载构建从主机到 CA 的路径所需的中间体,但这是可选的。配置良好的服务器将发送所需的中间证书,但并非所有中间证书都配置良好。

您不会加载cacert.pem或等效项。 cacert.pem包含数百个证书,除了一个证书外,所有证书都是错误的。


我是否需要以某种方式将cacerts.pem复制到.APK(如何做到这一点?)并提供文件的路径到SSL_CTX_load_verify_locations()?

不。您通常需要一个证书,即认证主机证书的 CA。它可以是公共CA,如Comodo,也可以是组织内部的私有CA。

您通常将其放在res/assets/文件夹中。另请参阅 堆栈溢出上的/res 和/assets 目录之间的区别。


有没有办法将cacerts.pem存储为字符串缓冲区,从而避免将其携带在..APK?

是的,但您可能必须获取字符串,将其写入文件系统,然后将该文件用于SSL_CTX_load_verify_locations。所以最后,最好把它放在 res/assets/ .


。SharePoint Online sites (https://*.example.com/)

主机名验证的一个重要说明。OpenSSL 1.0.2 及更早版本执行名称匹配。它执行其他常规检查,例如确保存在从最终实体证书(主机)到 CA 证书(信任根)的路径,并且证书未过期。但它执行主机名匹配。

OpenSSL 1.1.0 确实执行主机名匹配。它将很快发布。

作为权宜之计,您通常会从像 cURL 这样的库中获取丢失的代码。 cURL 执行主机名匹配,Daniel Stenberg 很乐意分享代码。我认为它位于ssl.c,如果没记错的话。

另请参阅 OpenSSL wiki 上的 TLS 客户端。

我终于能够让它工作了。我是一个完整的Android新手,所以在这里发布我的笔记,以防它们对像我这样的其他人有用。

正如@jww建议的那样,我将在发货前从我的cacerts.pem文件中删除所有不必要的证书。

我有 3 个选择将我的 cacerts.pem 放在 ..APK。

  1. 在根目录(将文件放在 src\main\resources 中(不是 src\main\res)将导致它被放置在 apk 的根目录)
  2. In res\raw
  3. (把文件放在 src\main\res\raw 中)
  4. 在资产中(将文件放在 src\main\assets 中)

在我的简短研究中,我找不到一种使用 c++ API 直接从 apk 根目录或从 res\raw 读取文件的方法。所以我继续#3。

现在资产文件也不能使用"标准"c++ 函数直接读取(如果我错了,请纠正我),所以我在 Java 层中使用此代码将文件从 assets 文件夹复制到外部位置。

try
{
    AssetManager assetManager = this.getAssets();
    File file = new File(getFilesDir(), "cacerts.pem");
    InputStream in = assetManager.open("cacerts.pem");
    OutputStream out = new FileOutputStream(file);
    byte[] buffer = new byte[65536 * 2];
    int read;
    while ((read = in.read(buffer)) != -1) {
        out.write(buffer, 0, read);
    }
    in.close();
    out.flush();
    out.close();
}
catch (Exception e)
{
}

在此之后,我将此文件位置传递给我的c ++代码,并能够进行此调用

SSL_CTX_load_verify_locations(ctx, "/data/user/0/com.company.app/files/cacerts.pem", NULL)

注意:这不是最终的生产代码。现阶段只是概念验证。请随时指出您看到的任何错误或您的建议。

注意:有一段时间 openssl 仍然无法读取文件,所以我验证了该文件是否已正确创建并使用 ifstream file("/data.../cacerts.pem") 可用,并且它就在那里。这个错误后来以某种方式解决了。