我如何杀死弹出窗口?

How do I kill the popup?

如果您编辑以下代码以获得有效的证书路径和需要客户端证书的 url,然后在 OS X 上使用 clang++ -lcurl curl.cpp 编译它(我使用的是 El Cap,但我认为 Mavericks 或更高版本的行为方式相同)和 运行 可执行文件,您会从 OS X 获得一个弹出窗口(如下所示),询问您是否要允许可执行文件使用私有钥匙串中的钥匙。这对知道发生了什么的用户来说很烦人(OS X 上的内部 curl 使用 OS X 安全框架来加载客户端证书)但对于不知道的用户来说这很可怕知道发生了什么,因为他们认为该程序正在尝试访问其钥匙串中的私钥(顺便说一句,这是 Apple 糟糕的 UX 示例,因为弹出消息完全是转移注意力)。

curl 命令行工具不会生成弹出窗口,所以要么是我可以使用较低级别的 API,要么是因为可执行文件已签名。我尝试将此功能添加到的真实程序通常作为源代码分发,因此对可执行文件进行签名不是理想的方法,因为我无法分发签名密钥,否则它们将被撤销。有谁知道我如何以编程方式防止弹出窗口?

#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);
}

这就是我解决这个问题的方法(感谢 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();
}