cURL / Mailing Lists / curl-library / Single Mail

curl-library

Using PKCS12 certificate from memory

From: Gilles Vollant <vollant.g_at_gmail.com>
Date: Fri, 5 Aug 2016 10:32:41 +0200

Hello,
I've an application which need use a PKCS12 client side certificate from
memory buffer.
Unfortunately, there is no natural way to do it with libcurl.

Below, I show my method with
1) DarwinSSL (the Apple system SSL library for iOS and MacOSX) : I had to
modify curl/lib/vtls/darwinssl.c source, and I put a string containing the
certificate with "base64:" prefix in CURLOPT_SSLCERT parameter
2) OpenSSL : I don't need modify curl source, using an OpenSSL specific
callback

My suggestion : giving a way to use a certificate from memory buffer in the
different SSL layer.
I think "base64:*" as filename, like my darwinssl patch is the more easy
way.
(If we do that, it can be also useful to create a Curl_base64_encode
function without struct Curl_easy * parameter)

Here are my modification against darwinssl from libcurl 7.50.[0/1] (I also
put on http://gvollant.free.fr/darwinssl_mod_75.c )

*** W:\patchclient\src\PatchClient\curl\lib\vtls\darwinssl.c Wed Aug 03
11:03:15 2016
--- W:\patchclient\src\PatchClient\darwinssl_mod_75.c Thu Jul 21 11:55:27
2016
***************
*** 935,942 ****
                                             SecIdentityRef
*out_cert_and_key)
  {
    OSStatus status = errSecItemNotFound;
! CFURLRef pkcs_url = CFURLCreateFromFileSystemRepresentation(NULL,
! (const UInt8 *)cPath, strlen(cPath), false);
    CFStringRef password = cPassword ? CFStringCreateWithCString(NULL,
      cPassword, kCFStringEncodingUTF8) : NULL;
    CFDataRef pkcs_data = NULL;
--- 935,941 ----
                                             SecIdentityRef
*out_cert_and_key)
  {
    OSStatus status = errSecItemNotFound;
! CFURLRef pkcs_url = NULL;
    CFStringRef password = cPassword ? CFStringCreateWithCString(NULL,
      cPassword, kCFStringEncodingUTF8) : NULL;
    CFDataRef pkcs_data = NULL;
***************
*** 945,952 ****
    /* These constants are documented as having first appeared in 10.6 but
they
       raise linker errors when used on that cat for some reason. */
  #if CURL_BUILD_MAC_10_7 || CURL_BUILD_IOS
! if(CFURLCreateDataAndPropertiesFromResource(NULL, pkcs_url, &pkcs_data,
! NULL, NULL, &status)) {
      const void *cKeys[] = {kSecImportExportPassphrase};
      const void *cValues[] = {password};
      CFDictionaryRef options = CFDictionaryCreate(NULL, cKeys, cValues,
--- 944,979 ----
    /* These constants are documented as having first appeared in 10.6 but
they
       raise linker errors when used on that cat for some reason. */
  #if CURL_BUILD_MAC_10_7 || CURL_BUILD_IOS
!
! Boolean isBase64 = false;
! Boolean resource_imported;
!
! isBase64 = ((cPath!=NULL) && (strlen(cPath)>7) &&
(memcmp(cPath,"base64:",7)==0));
! if (isBase64)
! {
!
! char *outptr=NULL;
! size_t outlen=0;
! if (Curl_base64_decode(cPath+7,&outptr,&outlen) ==
CURLE_OK)
! {
! pkcs_data=CFDataCreate(
! kCFAllocatorDefault,
! (const unsigned char *)outptr,
! outlen);
! status=errSecSuccess;
! resource_imported=true;
! free(outptr);
! }
! }
! else
! {
! pkcs_url = CFURLCreateFromFileSystemRepresentation(NULL,
!
(const UInt8 *)cPath, strlen(cPath), false);
! resource_imported =
CFURLCreateDataAndPropertiesFromResource(NULL, pkcs_url, &pkcs_data,
!
NULL, NULL, &status);
! }
!
! if(resource_imported) {
      const void *cKeys[] = {kSecImportExportPassphrase};
      const void *cValues[] = {password};
      CFDictionaryRef options = CFDictionaryCreate(NULL, cKeys, cValues,
***************
*** 973,979 ****
  #endif /* CURL_BUILD_MAC_10_7 || CURL_BUILD_IOS */
    if(password)
      CFRelease(password);
! CFRelease(pkcs_url);
    return status;
  }

--- 1000,1007 ----
  #endif /* CURL_BUILD_MAC_10_7 || CURL_BUILD_IOS */
    if(password)
      CFRelease(password);
! if (pkcs_url!=NULL)
! CFRelease(pkcs_url);
    return status;
  }

***************
*** 990,995 ****
--- 1018,1027 ----

    if(filename == NULL)
      return false;
+
+ if(strlen(filename)>7)
+ if (memcmp(filename,"base64:",7)==0)
+ return true;

    if(stat(filename, &st) == 0)
      return S_ISREG(st.st_mode);

For using it, I put in char* base64Certif a string with "base64:" prefix
then the certificate encoded as base64

    curl_easy_setopt(curl_handle, CURLOPT_SSLCERTTYPE, "P12");
    curl_easy_setopt(curl_handle, CURLOPT_SSLKEYPASSWD,
certificatePassword);
    curl_easy_setopt(curl_handle, CURLOPT_SSLCERT, base64Certif);

For openssl, in the main function which uses libcurl, with certificate
binary data in memory (pointer certificateData, size certificateSize, handle
curl_handle)

    CertColl certColl;
    certColl.pkey=NULL;
    certColl.cert=NULL;
    certColl.ca = NULL;
    certColl.p12=NULL;
      
      BIO*bp=BIO_new_mem_buf((void*)certificateData,(int)certificateSize);
      
      certColl.p12 = d2i_PKCS12_bio(bp,NULL);
      BIO_free(bp);

      curl_easy_setopt(curl_handle,CURLOPT_SSL_CTX_FUNCTION,
*sslctx_p12_function);
      curl_easy_setopt(curl_handle,CURLOPT_SSL_CTX_DATA,&certColl);

And, I defined before in the source file

typedef struct
{
    EVP_PKEY *pkey ;
    X509 *cert ;
    STACK_OF(X509) *ca ;
    PKCS12 *p12;
    
} CertColl;

static CURLcode sslctx_p12_function(CURL * curl, void * sslctx, void * parm)
{
    CertColl *certColl=(CertColl *)parm;
    
    SSL_CTX* ctx=(SSL_CTX*)sslctx;
    /* get a pointer to the X509 certificate store (which may be empty!) */
    
    if(SSL_CTX_use_certificate(ctx, certColl->cert) != 1) {
        return CURLE_FAILED_INIT;
    }
    
    if(SSL_CTX_use_PrivateKey(ctx, certColl->pkey) != 1) {
        return CURLE_FAILED_INIT;
    }
     
    /* all set to go */
    return CURLE_OK ;
}

-------------------------------------------------------------------
List admin: https://cool.haxx.se/list/listinfo/curl-library
Etiquette: https://curl.haxx.se/mail/etiquette.html
Received on 2016-08-05