So I'm trying to use libcurl with JNI but it returns CURLE_SSL_CACERT_BADFILE error. This is my code.
JNI side:
static size_t WriteCallback(void *contents, size_t size, size_t nmemb, void *userp)
{
((string*)userp)->append((char*)contents, size * nmemb);
return size * nmemb;
}
//jList is an array containing the certificate.
Java_packageName_MainActivity_Test(JNIEnv *env, jobject thiz, jobject jList)
{
vector<string> certificatesPinning;
// Convert jobject to jobjectArray
// retrieve the java.util.List interface class
jclass cList = env->FindClass("java/util/List");
// retrieve the toArray method and invoke it
jmethodID mToArray = env->GetMethodID(cList, "toArray", "()[Ljava/lang/Object;");
jobjectArray stringArray = (jobjectArray)env->CallObjectMethod(jList, mToArray);
// Add each certificate to the list
int stringCount = (env)->GetArrayLength(stringArray);
for (int i=0; i < stringCount; i++)
{
jstring certificateString = (jstring)(env)-> GetObjectArrayElement(stringArray, i);
const char *cert = (env)->GetStringUTFChars(certificateString, 0);
const jsize len = env->GetStringUTFLength(certificateString);
string certificatePinningObj(cert,len);
certificatesPinning.push_back(certificatePinningObj);
(env)->ReleaseStringUTFChars( certificateString, cert);
}
string readBuffer;
CURL *curl = curl_easy_init();
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST");
curl_easy_setopt(curl, CURLOPT_URL, "https://theapi.com");
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);// Fill the response in the readBuffer
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer);
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 120); // 120 s connect timeout
curl_easy_setopt(curl, CURLOPT_ENCODING, GZIP);
curl_easy_setopt(curl,CURLOPT_SSLCERTTYPE,"der");
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER , 1);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST , 2L);
curl_easy_setopt(curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2);
curl_easy_setopt(curl, CURLOPT_CAINFO,certificatesPinning[0].c_str());//buf
CURLcode res;
res = curl_easy_perform(curl);
if(!readBuffer.empty())
{
printf("success \n");
}
else
{
printf("error \n");
int a = (int)res;// this is 77 = CURLE_SSL_CACERT_BADFILE
}
}
JAVA side:
// Define the function
native void Test(ArrayList<String> certificates);
// Prepare the certificate
ArrayList<String> certificatesPinning = new ArrayList<String>();
certificatesPinning.add(saveCertPemFile());
// Call the function
Test(certificatesPinning);
// Helpers
private String saveCertPemFile()
{
Context context=getApplicationContext();
String assetFileName="certificateName.der";
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;
}
"certificateName.der" is the certificate stored in the assets folder.
And this is the certificate path being sent to the jni:
/data/data/packageName/certificateName.der
Reference
You haven't explained fully what you're using here, but as I will guess that you have a libcurl built against OpenSSL underneath. The CURLOPT_CAINFO option should then be the file name identifying the CA cert bundle, using PEM format. That bundle is all the certs for your trusted CAs.
Your description makes it sound like you have a DER file, but you can't use DER for the CA cert bundle with OpenSSL.
A common way to get a decent CA bundle is to download the PEM version of the bundle that Mozilla ships included in Firefox.
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