我正在开发一个使用gSOAP和OpenSSL的C ++ Android应用程序。要使OpenSSL能够验证服务器证书,我需要为其提供cacerts.pem
文件的位置,否则所有HTTPS连接都会失败。
我的问题是:在Android上存储和加载cacerts.pem
的正确方法是什么?
我可以将cacerts.pem
复制到APK src/resources
或res/raw/
或assets/
中。但SSL_CTX_load_verify_locations(ctx, "cacerts.pem", nullptr)
无法找到该文件。如何让OpenSSL能够读取APK中的PEM文件?
有没有办法将cacerts.pem
存储为字符串缓冲区,从而避免在APK中携带它?
我的应用需要与sharepoint在线网站(https://*.example.com/)
答案 0 :(得分:1)
在Android上存储和加载cacerts.pem的正确方法是什么?
您必须加载验证主机证书所需的CA.
你可以加载构建从主机到CA的路径所需的中间件,但是它是可选的。配置良好的服务器将发送所需的中间证书,但并非所有证书都配置良好。
您没有加载// In login screen before go to home, save the username as key "userID"
let defaults: NSUserDefaults = NSUserDefaults.standardUserDefaults()
defaults.setObject(username, forKey: "userID")
defaults.synchronize()
// In your home, viewDidLoad() retrieve it back
let defaults: NSUserDefaults = NSUserDefaults.standardUserDefaults()
let userID = (defaults.objectForKey("userID") as? String)!
或同等信息。 cacert.pem
包含数百个证书,但所有证书都是错误的。
我是否需要以某种方式将cacerts.pem复制到.APK(如何执行此操作?)并将文件路径提供给SSL_CTX_load_verify_locations()?
没有。您通常需要一个证书,并且该证书是证明主机证书的CA.它可以是公共CA,如Comodo,也可以是组织内部的私有CA.
您通常将其放在 cacert.pem
或 res/
文件夹中。另请参阅Stack Overflow上的Difference between /res and /assets directories。
有没有办法将cacerts.pem存储为字符串缓冲区,从而避免在.APK中携带它?
是的,但您可能必须获取字符串,将其写入文件系统,然后将该文件用于assets/
。最后,最好将其置于SSL_CTX_load_verify_locations
或res/
。
... sharepoint在线网站(https://*.example.com/)
主机名验证的一个重要注意事项。 OpenSSL 1.0.2及更早版本 不 执行名称匹配。它执行其他常规检查,例如确保从最终实体证书(主机)到CA证书(信任根)的路径,并且证书未过期。但 不 执行主机名匹配。
OpenSSL 1.1.0 执行主机名匹配。它很快就会发布。
作为止损,您通常会从assets/
这样的库中提取丢失的代码。 cURL执行主机名匹配,Daniel Stenberg很乐意分享代码。如果记忆能为我服务,我认为它位于cURL
。
另请参阅OpenSSL wiki上的TLS Client。
答案 1 :(得分:1)
我终于让它运转了。我是一个完整的Android新手,所以在这里发布我的笔记,以防它们对像我这样的人有用。
正如@jww建议我在发货前从cacerts.pem文件中删除所有不必要的证书。
我有3个选项可以将我的cacerts.pem放在.APK中。
在我的简短研究中,我找不到使用c ++ apis从apk root或res \ raw直接读取文件的方法。所以我继续#3。
现在资产文件也不能使用'标准'c ++函数直接读取(如果我错了,请纠正我),所以我在我的Java层使用此代码将资产文件夹的文件副本复制到外部位置。
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文件(“/ data ... / cacerts.pem”)可用,并且它就在那里。那个错误后来以某种方式解决了。