I've successfully built libcurl-7.36.0 with openssl-1.0.1h on Android. I ran a sample code to test HTTPS connection. The SSL_VERIFYPEER is enabled by default. The certificates path on Android is /system/etc/security/cacerts, so I set CURLOPT_CAPATH to /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
......
Here is a snippet of my codes..
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 always returns an error:
== 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
It's working if I download the CA bundle file ca-bundle.crt from http://curl.haxx.se/docs/caextract.html and curl_easy_setopt(curl, CURLOPT_CAINFO, "path:/ca-bundle.crt")
.
Here is my question: Is there any way to make SSL peer verification work by reading the certificate from /system/etc/security/cacerts
without manually downloading the CA bundle file and specifying CURLOPT_CAINFO
?
Sample implementation of CURL using TLS1.2 ,openSSL 1.01p,curl version 7.40.0 ,cacert.pem bundle with verify peer ,verify hostname option is shown in https://github.com/vyshas/CURL-Android-with-verify-peer-
Important parts from the above link is shown below:
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;
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;
OpenSSL 0.9.x used MD5 filename hash. OpenSSL 1.0.x used SHA-1 for the filename hash. Android is using MD5 hash. Why old hash?
I tried libcurl-7.36.0 with openssl-0.9.8zb. It's working on Android with CURLOPT_SSL_VERIFYPEER enabled.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With