2016-09-20 4 views
1

Если вы редактируете следующий код, чтобы иметь допустимые пути сертификатов и URL-адрес, требующий сертификатов клиента, а затем скомпилировать его с помощью clang++ -lcurl curl.cpp на OS X (я использую El Cap, но, думаю, Mavericks или более поздняя версия ведут себя одинаково) и запустите исполняемый файл, вы получите всплывающее окно (показано ниже) из OS X с просьбой разрешить исполняемому файлу использовать закрытый ключ в вашей цепочке ключей. Это раздражает пользователей, которые знают, что происходит (внутренняя завивка на OS X использует инфраструктуру безопасности OS X для загрузки клиентского сертификата), но это пугает для пользователей, которые не знают, что происходит, потому что они думают, что программа пытается получить доступ закрытый ключ в их брелках (как в стороне это пример ужасного UX от Apple, поскольку всплывающее сообщение - полная красная селедка).Как убить всплывающее окно?

Инструмент командной строки curl не создает всплывающее окно, поэтому можно использовать API нижнего уровня, который я мог бы использовать, или это потому, что исполняемый файл подписан. Реальная программа, к которой я пытаюсь добавить эту функцию, часто распространяется как исходный код, поэтому подписка на исполняемый файл не является идеальным подходом, поскольку я не могу распространять ключи подписи или они будут отменены. Кто-нибудь знает, как я могу предотвратить всплывающее окно программно?

Popup with warning text: a wants to sign using key "privateKey" in your keychain. The authenticity of "a" cannot be verified. Do you want to allow access to this item? and buttons for Allow, Deny, and Always Allow

#include <curl/curl.h> 
#include <string> 

using namespace std; 

static size_t receiveResponseBytes(void *buffer, size_t size, size_t nmemb, void *userData) { 
    string *responseData = (string *) userData; 
    responseData->append((const char *) buffer, size * nmemb); 
    return size * nmemb; 
} 

void prepareCurlPOST(CURL *curl, string &bodyJsonString, string *responseData, struct curl_slist **chunk) { 
    curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1); 
    curl_easy_setopt(curl, CURLOPT_URL, "https://example.dev/v1/check.json"); 
    curl_easy_setopt(curl, CURLOPT_HTTPGET, 0); 
    curl_easy_setopt(curl, CURLOPT_POSTFIELDS, bodyJsonString.c_str()); 
    curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, bodyJsonString.length()); 
    *chunk = curl_slist_append(NULL, "Content-Type: application/json"); 
    curl_easy_setopt(curl, CURLOPT_HTTPHEADER, *chunk); 
    curl_easy_setopt(curl, CURLOPT_SSLCERT, "/path/to/client_cert.p12"); 
    curl_easy_setopt(curl, CURLOPT_SSLCERTTYPE, "P12"); 
    curl_easy_setopt(curl, CURLOPT_SSLCERTPASSWD, "1234"); 
    curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L); 
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, receiveResponseBytes); 
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, responseData); 
    curl_easy_setopt(curl, CURLOPT_TIMEOUT, 180); 
    curl_easy_setopt(curl, CURLOPT_CAINFO, "/path/to/ca.crt"); 
} 

int main(){ 
    CURL* curl = curl_easy_init(); 
    struct curl_slist *chunk = NULL; 
    string responseData; 
    long responseCode; 
    string bodyJsonString = "{\"version\": 1}"; 
    prepareCurlPOST(curl, bodyJsonString, &responseData, &chunk); 
    fprintf(stderr,"%s\n",curl_easy_strerror(curl_easy_perform(curl))); 
    curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &responseCode); 
    if (responseCode != 200) { 
    fprintf(stderr, "HTTP %d %s\n", (int) responseCode, responseData.c_str()); 
    } 
    curl_slist_free_all(chunk); 
    curl_easy_cleanup(curl); 
} 

ответ

1

Вот как я решил эту проблему (благодаря людям Phusion позволил мне возиться на этом):

#include <curl/curl.h> 
#include <string> 

#include <CoreFoundation/CoreFoundation.h> 
#include <Security/Security.h> 

using namespace std; 

const char* cert_label = "Name of the certificate as it appears in keychain access"; 
const char* domain = "https://example.dev/v1/check.json"; 
const char* ca_path = "/path/to/ca.crt"; 
const char* cert_path = "/path/to/client_cert.p12"; 
const char* cert_pw = "1234"; 

static OSStatus LookupKeychainItem(const char *label, 
            SecIdentityRef *out_cert_and_key) 
{ 
    OSStatus status = errSecItemNotFound; 

    if(kSecClassIdentity != NULL) { 
    CFTypeRef keys[4]; 
    CFTypeRef values[4]; 
    CFDictionaryRef query_dict; 
    CFStringRef label_cf = CFStringCreateWithCString(NULL, label, 
                kCFStringEncodingUTF8); 

    /* Set up our search criteria and expected results: */ 
    values[0] = kSecClassIdentity; /* we want a certificate and a key */ 
    keys[0] = kSecClass; 
    values[1] = kCFBooleanTrue; /* we need a reference */ 
    keys[1] = kSecReturnRef; 
    values[2] = kSecMatchLimitOne; /* one is enough, thanks */ 
    keys[2] = kSecMatchLimit; 
    /* identity searches need a SecPolicyRef in order to work */ 
    values[3] = SecPolicyCreateSSL(false, label_cf); 
    keys[3] = kSecMatchPolicy; 
    query_dict = CFDictionaryCreate(NULL, (const void **)keys, 
            (const void **)values, 4L, 
            &kCFCopyStringDictionaryKeyCallBacks, 
            &kCFTypeDictionaryValueCallBacks); 
    CFRelease(values[3]); 
    CFRelease(label_cf); 

    /* Do we have a match? */ 
    status = SecItemCopyMatching(query_dict, (CFTypeRef *)out_cert_and_key); 
    CFRelease(query_dict); 
    } 

    return status; 
} 

SecAccessRef createAccess() 
{ 
    SecAccessRef access=NULL; 
    if (SecAccessCreate(CFStringCreateWithCString(NULL, cert_label, kCFStringEncodingUTF8), NULL, &access)){ 
    printf("SecAccessCreate failed\n"); 
    return NULL; 
    } 
    return access; 
} 

static OSStatus CopyIdentityFromPKCS12File(const char *cPath, 
              const char *cPassword, 
              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; 

    if(CFURLCreateDataAndPropertiesFromResource(NULL, pkcs_url, &pkcs_data, 
               NULL, NULL, &status)) { 
    SecAccessRef access = createAccess(); 
    const void *cKeys[] = {kSecImportExportPassphrase,kSecImportExportAccess}; 
    //kSecTrustSettingsKeyUseAny 
    const void *cValues[] = {password,access}; 
    CFDictionaryRef options = CFDictionaryCreate(NULL, cKeys, cValues, 
               2L, NULL, NULL); 
    CFArrayRef items = NULL; 

    /* Here we go: */ 
    status = SecPKCS12Import(pkcs_data, options, &items); 

    if(items) 
     CFRelease(items); 
    CFRelease(options); 
    CFRelease(pkcs_data); 
    } 

    if(password) 
    CFRelease(password); 
    CFRelease(pkcs_url); 
    return status; 
} 

static size_t receiveResponseBytes(void *buffer, size_t size, size_t nmemb, void *userData) { 
    string *responseData = (string *) userData; 
    responseData->append((const char *) buffer, size * nmemb); 
    return size * nmemb; 
} 

void prepareCurlPOST(CURL *curl, string &bodyJsonString, string *responseData, struct curl_slist **chunk) { 
    curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1); 
    curl_easy_setopt(curl, CURLOPT_URL, domain); 
    curl_easy_setopt(curl, CURLOPT_HTTPGET, 0); 
    curl_easy_setopt(curl, CURLOPT_POSTFIELDS, bodyJsonString.c_str()); 
    curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, bodyJsonString.length()); 
    *chunk = curl_slist_append(NULL, "Content-Type: application/json"); 
    curl_easy_setopt(curl, CURLOPT_HTTPHEADER, *chunk); 
    curl_easy_setopt(curl, CURLOPT_SSLCERT, cert_label); 
    curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L); 
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, receiveResponseBytes); 
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, responseData); 
    curl_easy_setopt(curl, CURLOPT_TIMEOUT, 180); 
    curl_easy_setopt(curl, CURLOPT_CAINFO, ca_path); 
} 

void killKey(){ 
    SecIdentityRef id = NULL; 
    if (LookupKeychainItem(cert_label,&id) != errSecItemNotFound){ 

     CFArrayRef itemList = CFArrayCreate(NULL, (const void **)&id, 1, NULL); 
     const void *keys2[] = { kSecClass, kSecMatchItemList, kSecMatchLimit }; 
     const void *values2[] = { kSecClassIdentity, itemList, kSecMatchLimitAll }; 

     CFDictionaryRef dict = CFDictionaryCreate(NULL, keys2, values2, 3, NULL, NULL); 
     OSStatus oserr = SecItemDelete(dict); 
     if (oserr) { 
      CFStringRef str = SecCopyErrorMessageString(oserr, NULL); 
      printf("Removing Passenger Cert from keychain failed: %s Please remove the private key from the certificate labeled %s in your keychain.", CFStringGetCStringPtr(str,kCFStringEncodingUTF8), cert_label); 
      CFRelease(str); 
     } 
     CFRelease(dict); 
     CFRelease(itemList); 

    } 
} 

void preAuthKey(){ 
    SecIdentityRef id = NULL; 
    if(LookupKeychainItem(cert_label,&id) == errSecItemNotFound){ 
    OSStatus status = SecKeychainSetUserInteractionAllowed(false); 
    if(status != errSecSuccess){ 
     printf("%s\n",CFStringGetCStringPtr(SecCopyErrorMessageString(status,NULL),kCFStringEncodingUTF8)); 
    } 
    CopyIdentityFromPKCS12File("/path/to/client_cert.p12","1234",&id); 
    status = SecKeychainSetUserInteractionAllowed(true); 
    if(status != errSecSuccess){ 
     printf("%s\n",CFStringGetCStringPtr(SecCopyErrorMessageString(status,NULL),kCFStringEncodingUTF8)); 
    } 
    } 
} 

int main(){ 
    preAuthKey(); 
    CURL* curl = curl_easy_init(); 
    struct curl_slist *chunk = NULL; 
    string responseData; 
    long responseCode; 
    string bodyJsonString = "{\"version\": 1}"; 
    prepareCurlPOST(curl, bodyJsonString, &responseData, &chunk); 
    fprintf(stderr,"%s\n",curl_easy_strerror(curl_easy_perform(curl))); 
    curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &responseCode); 
    if (responseCode != 200) { 
    fprintf(stderr, "HTTP %d %s\n", (int) responseCode, responseData.c_str()); 
    } 
    curl_slist_free_all(chunk); 
    curl_easy_cleanup(curl); 
    killKey(); 
} 
Смежные вопросы