> 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 *)&copyMatchingResult );
        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

Reply via email to