Сервер защищенных сокетов Ocj-C с реальным сертификатом — ошибка протокола SSL (на основе GCDAsyncSockets)

Я ломал голову над странной проблемой - чем-то, о чем, кажется, не говорят (или называют совсем по-другому). Удивительно, но я не могу найти пример HTTPS — все, что я нахожу, имеет это «задание»!

Вкратце:

Я пытаюсь создать «сервер», использующий TLS, используя действительный сертификат.

Проблема:

Как только я успешно импортирую сертификат, настрою настройки для GCDAsyncSockets, затем вызову startTLS, соединение немедленно прервется с «Ошибка SecureTransport -9800» — Chrome называет это «ERR_SSL_PROTOCOL_ERROR».

Тест:

  1. Запустить сервер.
  2. Подключитесь через Chrome или Safari (https://localhost или https://localhost:somefreeport).
  3. Попробуйте наблюдать за запросом страницы из полученных данных.
  4. Обратите внимание на сбой на этапе рукопожатия.

Я выбрал Chrome для тестирования, так как это упражнение позже будет обрабатывать трафик WebSocket, поэтому я использую действительный сертификат, а не самозаверяющий.

Длинное описание:

Используя GCDAsyncSocket для прослушивания порта, затем, как только я получаю соединение, я беру этот сокет и «startTLS» для его обработки. (Я не ограничиваюсь использованием GCDAsyncSocket, просто я знаком с ним в клиентском сокете, использую открытый текст и TLS, а также открытый текст сервера - только серверный TLS ставит меня в тупик).

Сертификат представляет собой подписанный сертификат с известным общедоступным центром сертификации (Comodo или GoDaddy). Я уже успешно использую его в .Net. Приведенный ниже источник успешно откроет сертификат и сообщит имя узла. .Net, однако, многое упрощает, поэтому сравнение не тривиально. (загрузить сертификат с пропуском в объект x509, затем применить к новому сокету. Принимая во внимание, что здесь я также должен определить шифры и версии SSL).

Упрощенный пример кода:

- (void)socket:(GCDAsyncSocket *)sock didAcceptNewSocket:(GCDAsyncSocket *)newSocket
{   
    //Get certificate
    NSString* p12Path = [[NSBundle bundleForClass:[self class]] pathForResource:@"mySignedCert"
                                                                         ofType:@"pfx"];
    CFDataRef inPKCS12Data = (CFDataRef)CFBridgingRetain([[NSData alloc] initWithContentsOfFile:p12Path]);

    CFStringRef password = CFSTR("myCertPassword"); //Cert Password
    const void *keys[] = { kSecImportExportPassphrase };
    const void *values[] = { password };
    CFDictionaryRef options = CFDictionaryCreate(NULL, keys, values, 1, NULL, NULL);

    CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);

    OSStatus securityError = SecPKCS12Import(inPKCS12Data, options, &items);
    CFRelease(options);
    CFRelease(password);

    if(securityError == errSecSuccess)
        NSLog(@"Success opening p12 certificate."); //All good!
    else
        return nil;

    CFDictionaryRef identityDict = CFArrayGetValueAtIndex(items, 0);
    SecIdentityRef myIdent = (SecIdentityRef)CFDictionaryGetValue(identityDict,
                                                                  kSecImportItemIdentity);

    SecIdentityRef  certArray[1] = { myIdent };
    CFArrayRef myCerts = CFArrayCreate(NULL, (void *)certArray, 1, NULL);

    //Build TLS settings dictionary
    [self willSecureWithSettings:settings]; //This just uses SSLGetNumberSupportedCiphers & SSLGetSupportedCiphers to create a cipher array and appends to settings
    settings[(NSString *)kCFStreamSSLCertificates] = (__bridge id)(myCerts);
    settings[(NSString *)kCFStreamSSLPeerName] = @"my.peer.address";

    /* Valid values for protocol types
     kSSLProtocolUnknown = 0,               no protocol negotiated/specified; use default
    kSSLProtocol3       = 2,                SSL 3.0
    kTLSProtocol1       = 4,                TLS 1.0
    kTLSProtocol11      = 7,                TLS 1.1
    kTLSProtocol12      = 8,                TLS 1.2
    kDTLSProtocol1      = 9,                DTLS 1.0
     */

    //Tried 0,2,4,7,8 - 9 throws another error, possibly unsupported.
    settings[GCDAsyncSocketSSLProtocolVersionMin] = [NSNumber numberWithInteger:8];
    settings[GCDAsyncSocketSSLProtocolVersionMax] = [NSNumber numberWithInteger:8];

    [newSocket startTLS:settings];
    [newSocket readDataWithTimeout:-1 tag:0];
}

Я нашел много старых примеров, ни один из них больше не применим (без использования более старого GCDAsyncSocket или более старого Xcode). Самое близкое, что я нашел, упоминает о более позднем «GCDAsyncSocketSSLProtocolVersionMin/Max», установленном на 2, который, например, не поддерживается в Chrome.

Кто-нибудь был успешным и готов поделиться некоторыми указателями?

С уважением, КФ.


person KattenFodder    schedule 01.09.2015    source источник


Ответы (1)


Ничего себе - я потратил приличное время на это, а затем обратился сюда за советами.

Прикинул через 10 минут!

Мои изменения:

//THIS! Tried prior, but GCDAsyncSocket docs stated not-needed, plus was getting different handshake errors.
settings[(NSString *)kCFStreamSSLIsServer] = [NSNumber numberWithBool:YES];

Другие изменения, которые могут оказаться полезными для других пользователей SO:

//Apparently prefereable
settings[(NSString*)kCFStreamPropertyShouldCloseNativeSocket] = [NSNumber numberWithBool:YES];
//Above example was both matching for test. 4-8 was always preferable.
settings[GCDAsyncSocketSSLProtocolVersionMin] = [NSNumber numberWithInteger:4];
settings[GCDAsyncSocketSSLProtocolVersionMax] = [NSNumber numberWithInteger:8];

ТАКОЕ спасибо - даже если бы вы были за Rubber Ducking!

person KattenFodder    schedule 01.09.2015