Почему происходит сбой рукопожатия SSL перед отправкой каких-либо сообщений SSL?
Я пытаюсь подключить клиент iOS к серверу OS X, используя TLS 1.2 с API-интерфейсами безопасного транспорта Apple. У меня была правильная связь через сокеты BSD, и у меня возникли большие проблемы, связанные с TLS. Насколько я могу судить по выводу Wireshark, рукопожатие SSL даже на самом деле не начинается, поэтому возможно, что я неправильно настраиваю SSL с одной или другой стороны, но я не уверен, что могу делать неправильно,
Сторона сервера:
void establish_connection(int sockfd) {
SSLContexRef sslContext = SSLCreateContext(kCFAllocatorDefault, kSSLServerSide, kSSLStreamType);
SSLSetIOFuncs(sslContext, readFromSocket, writeToSocket);
SSLSetConnection(sslContext, (SSLConnectionRef)(long)sockfd);
SSLSetProtocolVersionMin(sslContext, kTLSProtocol12);
// Get self-signed certificate from p12 data
CFDataRef cert_data = CFDataCreate(kCFAllocatorDefault, cert_p12, cert_p12_len);
CFArrayRef items = NULL;
const void *options_keys[] = { kSecImportExportPassphrase };
const void *options_values[] = { CFSTR("password") };
CFDictionaryRef options = CFDictionaryCreate(kCFAllocatorDefault, options_keys, options_values, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
SecPKCS12Import(cert_data, options, &items);
CFRelease(options);
CFDictionaryRef item = CFArrayGetValueAtIndex(items, 0);
SecIdentityRef identity = (SecIdentityRef)CFDictionaryGetValue(item, kSecImportItemIdentity);
CFArrayRef certs = CFArrayCreate(kCFAllocatorDefault, (const void **)&identity, 1, NULL);
SSLSetCertificate(sslContext, certs);
// Fails with errSSLProtocol
SSLHandshake(sslContext);
...
}
Сторона клиента:
void establish_connection(int server_sockfd) {
SSLContextRef sslContext = SSLCreateContext(kCFAllocatorDefault, kSSLClientSide, kSSLStreamType);
SSLSetIOFuncs(sslContext, readFromSocket, writeToSocket);
SSLSetConnection(sslContext, (SSLConnectionRef)server_sockfd);
SSLSetProtocolVersionMin(sslContext, kTLSProtocol12);
// Fails with errSSLProtocol
SSLHandshake(sslContext);
...
}
Свалка Wireshark от попытки рукопожатия:
Source Destination Protocol Length Info
client server TCP 78 50743 > 49754 [SYN] Seq=0 Win=65535 Len=0 MSS=1460 WS=16 TSval=365690143 TSecr=0 SACK_PERM=1
server client TCP 78 49754 > 50743 [SYN, ACK] Seq=0 Ack=1 Win=65535 Len=0 MSS=1460 WS=16 TSval=666304222 TSecr=365690143 SACK_PERM=1
client server TCP 66 50743 > 49754 [ACK] Seq=1 Ack=1 Win=131760 Len=0 TSval=365690468 TSecr=666304222
server client TCP 66 [TCP Window Update] 49754 > 50743 [ACK] Seq=1 Ack=1 Win=131760 Len=0 TSval=666304252 TSecr=365690468
server client TCP 66 49754 > 50743 [FIN, ACK] Seq=1 Ack=1 Win=131760 Len=0 TSval=666304281 TSecr=365690468
client server TCP 66 50743 > 49754 [ACK] Seq=1 Ack=2 Win=131760 Len=0 TSval=365690498 TSecr=666304281
server client TCP 66 [TCP Dup ACK 9099#1] 49754 > 50743 [ACK] Seq=2 Ack=1 Win=131760 Len=0 TSval=666304283 TSecr=365690498
Используя Wireshark, чтобы попытаться диагностировать проблему, я даже не вижу никаких сообщений о рукопожатии SSL. Я вижу, что TCP-соединение установлено, а затем немедленно закрыто без пакетов длиной более 0. Что я могу сделать неправильно?
1 ответ
Похоже, проблема была в том, что мои функции ввода-вывода были реализованы неправильно. Я вставляю в сокет функции ввода-вывода, которые, кажется, работают, но требования к функциям ввода-вывода не так хорошо документированы, поэтому я могу что-то упустить.
OSStatus readFromSocket(SSLConnectionRef connection, void *data, size_t *dataLength) {
int sockfd = (int)connection;
size_t bytesRequested = *dataLength;
ssize_t status = read(sockfd, data, bytesRequested);
if (status > 0) {
*dataLength = status;
if (bytesRequested > *dataLength)
return errSSLWouldBlock;
else
return noErr;
} else if (0 == status) {
*dataLength = 0;
return errSSLClosedGraceful;
} else {
*dataLength = 0;
switch (errno) {
case ENOENT:
return errSSLClosedGraceful;
case EAGAIN:
return errSSLWouldBlock;
case ECONNRESET:
return errSSLClosedAbort;
default:
return errSecIO;
}
return noErr;
}
}
OSStatus writeToSocket(SSLConnectionRef connection, const void *data, size_t *dataLength) {
int sockfd = (int)connection;
size_t bytesToWrite = *dataLength;
ssize_t status = write(sockfd, data, bytesToWrite);
if (status > 0) {
*dataLength = status;
if (bytesToWrite > *dataLength)
return errSSLWouldBlock;
else
return noErr;
} else if (0 == status) {
*dataLength = 0;
return errSSLClosedGraceful;
} else {
*dataLength = 0;
if (EAGAIN == errno) {
return errSSLWouldBlock;
} else {
return errSecIO;
}
}
}