> On 26 Jun 2016, at 05:12, Jens Alfke <j...@mooseyard.com> wrote: > > >> On Jun 18, 2016, at 2:34 AM, Gerriet M. Denkmann <gerr...@mdenkmann.de> >> wrote: >> >> 1. (important) the client really wants to know that: >> (1a) it is talking to the right server and not to some evil entity >> masquerading as the real server. >> (1b) the data it receives has not been tampered with on the way. > > You want an SSL (aka TLS) connection, with the server providing a certificate > (the typical setup.) > >> 2. (less important) the server might want to know that the client connecting >> to it is a valid client. >> This might help if there are thousands of fake clients overwhelming the >> server with fake requests. >> But this is a kind of unlikely scenario. > > For that you’d need the SSL client to provide a certificate too. This is > supported by the CFStream APIs. > >> 3. (hardly important at all) no one can read the data exchanged. >> The data exchanged it really not sensitive. > > Well, you get that for free with SSL anyway :) > > Accomplishing (1b) and (3) is straightforward, I think. The doc-comment for > NSNetService says how to enable SSL/TLS for the sockets by setting the stream > properties:
Following TN2326 I created a (self signed) Certificate Authority and a Digital Identity called "MyServerId". The server gets MyServerId from my keychain: - (void)netService:(NSNetService *)sender didAcceptConnectionWithInputStream:(NSInputStream *)inputStream outputStream:(NSOutputStream *)outputStream { NSString *certificateName = @"MyServerId"; NSDictionary *d = @{ (__bridge id) kSecClass: (__bridge id) kSecClassIdentity, (__bridge id) kSecReturnRef: @YES, (__bridge id) kSecMatchSubjectWholeString: certificateName, }; CFArrayRef copyMatchingResult; OSStatus status = SecItemCopyMatching( (__bridge CFDictionaryRef)di, (CFTypeRef *)©MatchingResult ); if (status != errSecSuccess) ... id mySecIdentityRef = (__bridge id)copyMatchingResult; NSArray *certs = @[ mySecIdentityRef ]; NSDictionary *settings = @{ (__bridge NSString *)kCFStreamSSLValidatesCertificateChain: @NO, (__bridge NSString *)kCFStreamSSLIsServer: @YES, (__bridge NSString *)kCFStreamSSLCertificates: certs, }; BOOL ok = [ inputStream setProperty: settings forKey: (__bridge NSString *)kCFStreamPropertySSLSettings ]; if ( !ok ) ... ... for both streams: scheduleInRunLoop, open } The client has a copy of MyServerId.cer (exported from my keychain) in its bundle. - (void)netService:(NSNetService *)sender didAcceptConnectionWithInputStream:(NSInputStream *)inputStream outputStream:(NSOutputStream *)outputStream { NSDictionary *settings = @{ (__bridge NSString *)kCFStreamSSLValidatesCertificateChain: @NO }; BOOL ok = [ inputStream setProperty: settings forKey: (__bridge NSString *)kCFStreamPropertySSLSettings ]; if ( !ok ) ... ... for both streams: scheduleInRunLoop, open } The NSStreamDelegate of the client compares the certificate in inputStream with the certificate in its bundle: - (void)stream:(NSStream *)aStream handleEvent: (NSStreamEvent)streamEvent { if ( streamEvent & NSStreamEventHasSpaceAvailable ) { if ( firstTimeHere ) { firstTimeHere = NO; NSString *kSSLPeerTrust = (__bridge NSString *)kCFStreamPropertySSLPeerTrust; id trusT = [ inputStream propertyForKey: kSSLPeerTrust ]; if ( trusT == nil ) … error badServer SecTrustRef trust = (__bridge SecTrustRef)trusT; SecTrustResultType trustResult; // will be: recoverable trust failure; probably because certificate is based on self signed Certificate Authority OSStatus err = SecTrustEvaluate(trust, &trustResult); if (err != errSecSuccess) … error badServer if trustResult ≠ kSecTrustResultProceed, kSecTrustResultUnspecified, kSecTrustResultRecoverableTrustFailure ... error badServer SecCertificateRef certificate = SecTrustGetCertificateAtIndex ( trust, 0 ); if ( certificate == nil ) ... error badServer CFDataRef dac = SecCertificateCopyData( certificate ); if ( dac == nil ) … error badServer NSData *certificateDataOfServer = (NSData *)CFBridgingRelease(dac); NSData *realCertificateData = get data from mainBundle; if ( ![ realCertificateData isEqualToData: certificateDataOfServer ] ) ... error badServer // trust the server }; // write to aStream ... } ... } Absolutely not sure whether the code above is correct, but it seems to be working. > We are now falling into the rabbit hole that is peer-to-peer trust & > identity. How is your server going to identify it so that a client will know > that it’s the server it expects? I don’t know whether you’ve given any > thought to this; the answer affects how you’d implement this part of the app. I have thought about this, but I am not at all sure that my thoughts are correct. Currently (as indicated in the code above) my client has a copy of the real server certificate and compares it with the certificate obtained from its inputStream. I am not sure whether putting the server certificate into the client is ok or a breach of security. That is: the client will accept any server which has signed with the server certificate. When the server does AcceptConnectionWithInputStream I get a panel: <name of app> wants to sign using key "MyServerId" in your keychain. Do you want to allow access to this item? Deny / Accept. If <name of app> is “My Real Server" I will click "Accept". If I have foolishly installed a malicious app calling itself “My Real Server” the client will be out of luck. Also: If the LAN the client is connected to (client runs on an iOS Device) has a malicious server app which somehow has obtained a copy of my server certificate, the client will be in trouble. Not sure how to solve this problem. Kind regards, Gerriet. _______________________________________________ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: https://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com