Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/jpa/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Android ndk 如何使SSL对等验证在Android上工作?_Android Ndk_Openssl_Libcurl - Fatal编程技术网

Android ndk 如何使SSL对等验证在Android上工作?

Android ndk 如何使SSL对等验证在Android上工作?,android-ndk,openssl,libcurl,Android Ndk,Openssl,Libcurl,我已经在Android上成功地用openssl-1.0.1h构建了libcurl-7.36.0。我运行了一个示例代码来测试HTTPS连接。默认情况下启用SSL_VERIFYPEER。Android上的证书路径是/system/etc/security/cacerts,因此我将CURLOPT_CAPATH设置为/system/etc/security/cacerts ls -l /system/etc/security/cacerts -rw-r--r-- root root

我已经在Android上成功地用openssl-1.0.1h构建了libcurl-7.36.0。我运行了一个示例代码来测试HTTPS连接。默认情况下启用SSL_VERIFYPEER。Android上的证书路径是/system/etc/security/cacerts,因此我将CURLOPT_CAPATH设置为/system/etc/security/cacerts

ls -l /system/etc/security/cacerts
-rw-r--r-- root     root         4767 2012-09-22 11:57 00673b5b.0
-rw-r--r-- root     root         4573 2012-09-22 11:57 03e16f6c.0
-rw-r--r-- root     root         5292 2012-09-22 11:57 08aef7bb.0
......
这是我的一段代码

curl = curl_easy_init();
curl_easy_setopt(curl, CURLOPT_URL, "https://www.google.com:443");
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L);     // default
curl_easy_setopt(curl, CURLOPT_CAPATH, "/system/etc/security/cacerts");
curl_easy_perform(curl);
Curl始终返回一个错误:

== Info: SSL certificate problem: unable to get local issuer certificate  
== Info: Closing connection 0  
curl_easy_perform() failed: Peer certificate cannot be authenticated with given CA certificates
如果我从和
curl\u easy\u setopt(curl,CURLOPT\u CAINFO,“path:/CA bundle.crt”)
下载CA bundle文件CA-bundle.crt,它就工作了


这里是我的问题:有没有办法通过从
/system/etc/security/cacerts
读取证书而不手动下载CA捆绑文件并指定
CURLOPT_CAINFO

编辑:我以前的答案是错误的

应该指向使用该工具为OpenSSL准备的目录。我不知道这是否与Android提供的格式相同


我发现了如何将新证书导入到最近的Android上,它似乎表明该目录中的文件格式与c_hash的格式略有不同…

OpenSSL 0.9.x使用的MD5文件名哈希。OpenSSL 1.0.x使用SHA-1作为文件名哈希。Android正在使用MD5哈希

我尝试了libcurl-7.36.0和openssl-0.9.8zb。它在启用了CURLOPT_SSL_VERIFYPEER的Android上运行。

  • 如果在android应用程序中使用libcurl,CURLOPT_SSL_VERIFYPEER将失败,因此,如果没有CA捆绑,将阻止CURL发送数据。克服这个问题的一个方法是关闭这个选项,这是非常糟糕的。 我们必须提供自己的CA包,并使用CURLOPT_CAINFO选项提供CA包文件的绝对路径
  • 来自的“cacert.pem”文件​ 可以放在资源或资产中,但我更喜欢资产目录
  • CURL需要绝对路径,我们不能给出资产文件夹的绝对路径,因为打包的android APK文件类似于压缩文件夹,因此我们需要将PEM文件从资产复制到内部存储或外部存储,但我更喜欢内部存储,因为它是应用程序的专用文件,并提供内部存储目录的绝对路径在CAINFO。例如,如果应用程序名为com.example.androidtest,则CAINFO路径将为“/data/data/com.example.androidtest/cacert.pem”
  • 使用TLS1.2、openSSL 1.01p、CURL版本7.40.0、cacert.pem捆绑包和verify peer、verify hostname选项实现CURL的示例如所示

  • 上述链接的重要部分如下所示:

JAVA端 JNI侧 旋度码
通过重新编译libcurl并配置证书的默认搜索路径,我在Android上实现了这一点。这可以通过传递选项来完成:

--使用ca path=/system/etc/security/cacerts to./configure


-DCURL\u CA\u PATH=/system/etc/security/cacerts to cmake

问题不在于Curl,而在于openSSL。
openssl-1.1.1x/crypto/x509/by_dir.c:
函数get_cert_by_subject(),它使用与android不兼容的X509_NAME_hash()。
尝试在openssl源代码处修改by_dir.c:

#if defined(__ANDROID__)
  h = X509_NAME_hash_old(name);
#else
  h = X509_NAME_hash(name);
#endif
应该解决这个问题。
补丁:

--- a/openssl-1.1.1k/crypto/x509/by_dir.c
+++ b/openssl-1.1.1k/crypto/x509/by_dir.c
@@ -247,7 +247,11 @@ static int get_cert_by_subject(X509_LOOKUP *xl, 
    
     ctx = (BY_DIR *)xl->method_data;

+#if defined(__ANDROID__)
+    h = X509_NAME_hash_old(name);
+#else
     h = X509_NAME_hash(name);
+#endif
     for (i = 0; i < sk_BY_DIR_ENTRY_num(ctx->dirs); i++) {
         BY_DIR_ENTRY *ent;
         int idx;
——a/openssl-1.1.1k/crypto/x509/by_dir.c
+++b/openssl-1.1.1k/crypto/x509/by_dir.c
@@-247,7+247,11@@static int按主题获取证书(X509\u LOOKUP*xl,
ctx=(按目录*)xl->方法数据;
+#如果已定义(\uuuu ANDROID\uuuu)
+h=X509\u名称\u哈希\u旧(名称);
+#否则
h=X509\u名称\u散列(名称);
+#恩迪夫
对于(i=0;idirs);i++){
由主管输入;
int-idx;

我相信您可以使用
CURL\u CA\u BUNDLE
环境变量来指定
/system/etc/security/cacerts
。请参阅。@jww:这适用于CURL命令行工具,而不是库!根据Nikolay Elenkov在《ICS之前,信任存储是一个文件:/system/etc/security/cacerts.bks,一个有弹性的城堡》(Android中使用的JCE加密提供程序之一)本机密钥库文件…新引入的ICS TrustedCertificateStore类仍然从/system/etc/security读取系统受信任的证书,但添加了两个新的可变位置以将CA证书存储在/data/misc/keychain中:添加的cacerts和删除的cacerts目录。“。“Android正在使用MD5哈希"-是的,但这只是故事的一半。Android附带了OpenSSL 0.9.8。你链接到OpenSSL 1.0.1。你的进程从已经加载了0.9.8的Zygote派生出来。你的进程创建时没有映射到你的1.0.1版本。因为0.9.8和1.0.1不是二进制兼容的,所以出现了很多模糊的bug。有没有办法使OpenSSL 1.0.x成为二进制兼容的在读取文件名散列时使用MD5?谷歌必须以某种方式做到这一点,因为他们在使用OpenSSL 1.0.1时使用1.0.1j。我认为,使用OpenSSL 1.0.1时,唯一的选择是将证书文件从/system/etc/security/cacerts复制到私有内部存储位置,并使用上面提到的SHA-1算法命名,或者将证书文件存储在一个文件中。是否有其他ideas?我认为当您需要openssl-1.1.1x时,它是最好的解决方案。无需下载或转换证书。这个答案是最新的。
CURL* curl = curl_easy_init();
    curl_easy_setopt(curl, CURLOPT_URL, url);
    curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L);
/*  curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, TRUE);
    curl_easy_setopt(curl, CURLOPT_FAILONERROR, TRUE);*/
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &curlCallback);
    curl_easy_setopt(curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, downloadObject);
    curl_easy_setopt(curl,CURLOPT_CAINFO,caCertPtr.c_str());
    curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L);
    curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2L);

    curl_version_info_data * vinfo = curl_version_info( CURLVERSION_NOW );
    if( vinfo->features & CURL_VERSION_SSL )
        // SSL support enabled
         LOGI("SSL support enabled");
    else
    {// No SSL
         LOGI("NO SSL");
    }

    CURLcode res = curl_easy_perform(curl);
    if (res != CURLE_OK){
        LOGI("CURL failed with error code %d", res);
    }

    LOGI("CURL download is OK, result:%d", res);
    curl_easy_cleanup(curl);
    return res == CURLE_OK;
#if defined(__ANDROID__)
  h = X509_NAME_hash_old(name);
#else
  h = X509_NAME_hash(name);
#endif
--- a/openssl-1.1.1k/crypto/x509/by_dir.c
+++ b/openssl-1.1.1k/crypto/x509/by_dir.c
@@ -247,7 +247,11 @@ static int get_cert_by_subject(X509_LOOKUP *xl, 
    
     ctx = (BY_DIR *)xl->method_data;

+#if defined(__ANDROID__)
+    h = X509_NAME_hash_old(name);
+#else
     h = X509_NAME_hash(name);
+#endif
     for (i = 0; i < sk_BY_DIR_ENTRY_num(ctx->dirs); i++) {
         BY_DIR_ENTRY *ent;
         int idx;