c++ - 我如何杀死弹出窗口?

标签 c++ macos libcurl

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

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

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

最佳答案

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

关于c++ - 我如何杀死弹出窗口?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39602553/

相关文章:

c++ - 如何在所有测试用例 BOOST c++ 之后运行一个函数

C++ 内存模型 - 此示例是否包含数据竞争?

c - 亚马逊 dynamoDb ValidationException

linux - 如何指定 libcurl SFTP hmac 选项

php - 图片上传 CURL 命令到 PHP Curl

c++ - tm_wday 返回 0-6 范围之外的大整数

C++:在 vector 中插入什么 - 指针或引用, vector 何时复制其元素?

swift - 如何同时调用多个操作

macos - 如何从 Mac 中的 Redis 桌面管理器连接到 Docker 容器中的 Redis 服务器?

ios - 从 Mac 读取 iPhone 日志文件内容