我在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
它的工作,如果我请从CA包文件CA-bundle.crt http://curl.haxx.se/docs/caextract.html和curl_easy_setopt(curl, CURLOPT_CAINFO, "path:/ca-bundle.crt")
.
这是我的问题:有没有办法通过从/system/etc/security/cacerts
手动下载CA捆绑文件并指定CURLOPT_CAINFO
?来读取证书来使SSL对等验证工作?
如果在Android应用程序中使用libcurl,CURLOPT_SSL_VERIFYPEER将失败,因此如果没有CA捆绑,则阻止CURL发送数据.解决这个问题的一种方法是关闭这个非常非常糟糕的选项.我们必须提供自己的CA捆绑包,并使用CURLOPT_CAINFO选项提供CA捆绑包文件的绝对路径.
从"cacert.pem"文件http://curl.haxx.se/docs/caextract.html可以放在资源或资产,但我更喜欢的资源目录.
CURL期望绝对路径,我们不能给出资产文件夹的绝对路径,因为打包的android APK文件就像一个压缩文件夹,因此我们需要将PEM文件从资产复制到内部存储或外部存储,但我更喜欢内部存储,因为它是私有的app并提供CAINFO中内部存储目录的绝对路径.例如,如果应用名称为com.example.androidtest,则CAINFO路径为"/data/data/com.example.androidtest/cacert.pem".
CURL的示例实现使用TLS1.2,openSSL 1.01p,curl版本7.40.0,带验证对等的cacert.pem包,验证主机名选项显示在https://github.com/vyshas/CURL-Android-with-verify -peer-
以上链接的重要部分如下所示:
public native void setDir(String caCertDir); setDir(saveCertPemFile()); private String saveCertPemFile() { Context context=getApplicationContext(); String assetFileName="cacert.pem"; if(context==null || !FileExistInAssets(assetFileName,context)) { Log.i("TestActivity", "Context is null or asset file doesnt exist"); return null; } //destination path is data/data/packagename String destPath=getApplicationContext().getApplicationInfo().dataDir; String CertFilePath =destPath + "/" +assetFileName; File file = new File(CertFilePath); if(file.exists()) { //delete file file.delete(); } //copy to internal storage if(CopyAssets(context,assetFileName,CertFilePath)==1) return CertFilePath; return CertFilePath=null; } private int CopyAssets(Context context,String assetFileName, String toPath) { AssetManager assetManager = context.getAssets(); InputStream in = null; OutputStream out = null; try { in = assetManager.open(assetFileName); new File(toPath).createNewFile(); out = new FileOutputStream(toPath); byte[] buffer = new byte[1024]; int read; while ((read = in.read(buffer)) != -1) { out.write(buffer, 0, read); } in.close(); in = null; out.flush(); out.close(); out = null; return 1; } catch(Exception e) { Log.e("tag", "CopyAssets"+e.getMessage()); } return 0; } private boolean FileExistInAssets(String fileName,Context context) { try { return Arrays.asList(context.getResources().getAssets().list("")).contains(fileName); } catch (IOException e) { // TODO Auto-generated catch block Log.e("tag", "FileExistInAssets"+e.getMessage()); } return false; }
JNIEXPORT void JNICALL Java_com_example_androidtest_TestActivity_setDir(JNIEnv*env,jobject obj,jstring caCertDir){if(!caCertDir)return;
JNIEXPORT void JNICALL Java_com_example_androidtest_TestActivity_setDir(JNIEnv* env, jobject obj, jstring caCertDir) { if(!caCertDir) return; const char* caCertDir_c = env->GetStringUTFChars(caCertDir, NULL); if (!caCertDir_c) return ; const jsize len = env->GetStringUTFLength(caCertDir); LOGI( "CaCertDir: %s", caCertDir_c ); std::string caCert(caCertDir_c,len); caCertPtr=caCert; LOGI( "CaCertDirptr in std string: %s", caCertPtr.c_str()); env->ReleaseStringUTFChars(caCertDir, caCertDir_c); }
}
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;