4

Apple のセキュア トランスポート API で TLS 1.2 を使用して、iOS クライアントを OS X サーバーに接続しようとしています。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 を超えるパケットがない状態ですぐに閉じられます。何が間違っているのでしょうか?

4

1 に答える 1

4

問題は、IO 関数が正しく実装されていなかったことのようです。動作しているように見えるソケット IO 関数を貼り付けていますが、IO 関数の要件が十分に文書化されていないため、何かが不足している可能性があります。

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;
    }
  }
}
于 2014-05-12T14:03:27.680 に答える