XMPPFramework - Connect via SSL on Openfire
Asked Answered
A

4

6

I'm trying to connect my users via SSL from my iOS XMPP chat client to Openfire server.

In my iOS client:

- (void)setupStream 
{
    ...
    // BOOL values for security settings
    customCertEvaluation = NO;
    allowSelfSignedCertificates = YES;
    allowSSLHostNameMismatch = NO;
}

In my Openfire server's Security Settings > Client Connection Security, I've set:

Required - Clients can only connect to the server using secured connections.

Thus, the following delegate method will be called:

- (void)xmppStream:(XMPPStream *)sender willSecureWithSettings:(NSMutableDictionary *)settings 
{
    NSString *expectedCertName = [xmppStream.myJID domain];

    if (customCertEvaluation)
        [settings setObject:@(YES) forKey:GCDAsyncSocketManuallyEvaluateTrust];

    if (allowSelfSignedCertificates)
        [settings setObject:[NSNumber numberWithBool:YES] forKey:(NSString *)kCFStreamSSLAllowsAnyRoot];

    if (allowSSLHostNameMismatch)
        [settings setObject:[NSNull null] forKey:(NSString *)kCFStreamSSLPeerName];

    else
        if (expectedCertName)
            [settings setObject:expectedCertName forKey:(NSString *)kCFStreamSSLPeerName];
}

I attempted this solution from this thread: XMPPFramework TLS/SSL connection with Openfire

However, when I run my application and attempt to connect to the server, I'd receive this error:

Security option unavailable - kCFStreamSSLAllowsAnyRoot - You must use manual trust evaluation

I looked through the GCDAsyncSocket class and realized kCFStreamSSLAllowsAnyRoot is stated as deprecated. An NSAssert was implemented to deliberately throw the error.

Next, I decided to change my BOOL values as such:

- (void)setupStream 
{
    ...
    // BOOL values for security settings
    // Manually evaluate trust
    customCertEvaluation = YES;
    allowSelfSignedCertificates = NO;
    allowSSLHostNameMismatch = NO;
}

This time, again, no connection could be made to the server but, no error was prompted.

I could connect to Openfire fine if I changed the Client Connection Security back to the original setting > Optional. But, I wouldn't be connected via SSL as indicated by a lock icon beside every user's status in Client Sessions.

My Android client (using Smack API for XMPP) connects to Openfire via SSL without issues. So I'm wondering if there's workaround I have to implement for my iOS client using XMPPFramework.

I would greatly appreciate any advices.

Applause answered 19/6, 2014 at 4:41 Comment(0)
A
8

Explanation

In the latest version of XMPP (after April 22), you can no longer use allowSelfSignedCertificates = YES with the following:

if (allowSelfSignedCertificates)
    [settings setObject:[NSNumber numberWithBool:YES] forKey:(NSString *)kCFStreamSSLAllowsAnyRoot];`

This is because kCFStreamSSLAllowsAnyRoot & SSLSetAllowsAnyRoot have been deprecated.

 /* 
  * ==== The following UNAVAILABLE KEYS are: (with throw an exception)
  * - kCFStreamSSLAllowsAnyRoot (UNAVAILABLE)
  *     You MUST use manual trust evaluation instead (see GCDAsyncSocketManuallyEvaluateTrust).
  *     Corresponding deprecated method: SSLSetAllowsAnyRoot
  */

See XMPPFramework/GCDAsyncSocket.h & Deprecated Secure Transport Functions.


Solution

  1. Go to Openfire server > Security Settings > Client Connection Security

    Check: Required - Clients can only connect to the server using secured connections.

  2. Define variable in AppDelegate

    BOOL customCertEvaluation;
    
  3. Set variable in setupStream

    - (void)setupStream 
    {
        ...
        customCertEvaluation = YES;
    }
    
  4. Set security settings in willSecureWithSettings

    - (void)xmppStream:(XMPPStream *)sender willSecureWithSettings:(NSMutableDictionary *)settings
    {
        /*
         * Properly secure your connection by setting kCFStreamSSLPeerName 
         * to your server domain name
         */
        [settings setObject:xmppStream.myJID.domain forKey:(NSString *)kCFStreamSSLPeerName];
    
        /*
         * Use manual trust evaluation
         * as stated in the XMPPFramework/GCDAsyncSocket code documentation
         */
        if (customCertEvaluation)
            [settings setObject:@(YES) forKey:GCDAsyncSocketManuallyEvaluateTrust];
    }
    
  5. Validate peer manually

    /*
     * This is only called if the stream is secured with settings that include:
     * - GCDAsyncSocketManuallyEvaluateTrust == YES
     * That is, if a delegate implements xmppStream:willSecureWithSettings:, and plugs in that key/value pair.
     */
     - (void)xmppStream:(XMPPStream *)sender didReceiveTrust:(SecTrustRef)trust completionHandler:(void (^)(BOOL shouldTrustPeer))completionHandler
     {
         /* Custom validation for your certificate on server should be performed */
    
         completionHandler(YES); // After this line, SSL connection will be established
     }
    
Applause answered 9/7, 2014 at 15:51 Comment(0)
O
1

I was having the same issue, after i updated my XMPPFramework. After days of trying to find out what went wrong i came across this question, but the solution didn't work for me.

Here is what worked for me. The problem seems to originate from your xmppStream.startTLSPolicy. Setting startTLSPolicy explicitly worked for me.

xmppStream.startTLSPolicy = XMPPStreamStartTLSPolicyPreferred; // or
xmppStream.startTLSPolicy = XMPPStreamStartTLSPolicyRequired;

Here is an EXPLANATION of why it works.

In XMPPStream's handleStreamFeatures method, it turns out that. If your XMPP Server doesn't return starttls as 'required' and you don't set startTLSPolicy(default=XMPPStreamStartTLSPolicyAllowed) explicitly. The client will just do a normal connection and not a TLS one.

Here is section of code(for reference) in XMPPStream that is doing the checks.

/**
 * This method is called anytime we receive the server's stream features.
 * This method looks at the stream features, and handles any requirements so communication can continue.
**/
- (void)handleStreamFeatures
{
    NSAssert(dispatch_get_specific(xmppQueueTag), @"Invoked on incorrect queue");

    XMPPLogTrace();

    // Extract the stream features
    NSXMLElement *features = [rootElement elementForName:@"stream:features"];

    // Check to see if TLS is required
    // Don't forget about that NSXMLElement bug you reported to apple (xmlns is required or element won't be found)
    NSXMLElement *f_starttls = [features elementForName:@"starttls" xmlns:@"urn:ietf:params:xml:ns:xmpp-tls"];

    if (f_starttls)
    {
        if ([f_starttls elementForName:@"required"] || [self startTLSPolicy] >= XMPPStreamStartTLSPolicyPreferred)
        {
            // TLS is required for this connection

            // Update state
            state = STATE_XMPP_STARTTLS_1;

            // Send the startTLS XML request
            [self sendStartTLSRequest];

            // We do not mark the stream as secure yet.
            // We're waiting to receive the <proceed/> response from the
            // server before we actually start the TLS handshake.

            // We're already listening for the response...
            return;
        }
    }
    else if (![self isSecure] && [self startTLSPolicy] == XMPPStreamStartTLSPolicyRequired)
    {
        // We must abort the connection as the server doesn't support our requirements.

        NSString *errMsg = @"The server does not support startTLS. And the startTLSPolicy is Required.";
        NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];

        otherError = [NSError errorWithDomain:XMPPStreamErrorDomain code:XMPPStreamUnsupportedAction userInfo:info];

        // Close the TCP connection.
        [self disconnect];

        // The socketDidDisconnect:withError: method will handle everything else
        return;
    }

    // Check to see if resource binding is required
    // Don't forget about that NSXMLElement bug you reported to apple (xmlns is required or element won't be found)
    NSXMLElement *f_bind = [features elementForName:@"bind" xmlns:@"urn:ietf:params:xml:ns:xmpp-bind"];

    if (f_bind)
    {
        // Start the binding process
        [self startBinding];

        // We're already listening for the response...
        return;
    }

    // It looks like all has gone well, and the connection should be ready to use now
    state = STATE_XMPP_CONNECTED;

    if (![self isAuthenticated])
    {
        [self setupKeepAliveTimer];

        // Notify delegates
        [multicastDelegate xmppStreamDidConnect:self];
    }
}
Oversubtlety answered 14/4, 2015 at 6:53 Comment(0)
B
0

You are trying to use outdated API, check iPhoneXMPP sample for the new one - https://github.com/robbiehanson/XMPPFramework/commit/73f3c35a930b91d27e62bc19e91d9cdcc02c6e42

Brewhouse answered 19/6, 2014 at 20:48 Comment(1)
Nope, I'm using the latest API. That's why I've got kCFStreamSSLAllowsAnyRoot deprecated. Old API, specifically before April still uses it. Instead, I'm using an old solution in my question above. I read the issues page in XMPPFramework Github - after support for SSL Pinning was introduced, old Apple deprecated methods is no longer supported since April 22. See github.com/robbiehanson/XMPPFramework/issues/333. Anyway, I've worked out a solution. Will share it here after more testing is done.Applause
R
0
customCertEvaluation = YES;
allowSelfSignedCertificates = YES;
allowSSLHostNameMismatch = NO;  

try these this might help

Rizo answered 9/7, 2014 at 11:10 Comment(10)
Please explain why this "might help".Mindymine
Because it worked for me!!! .May be the reason for not establishing a connection is because the allowSelfSignedCertifates function should be set to yes, Only then certificates are allowed for signing for a secure connection. Try this,nothing wrong in giving it a try.Rizo
@Rizo You're most likely using an outdated API. Because your corresponding method for allowSelfSignedCertificates has been deprecated. Check out my answer above.Applause
@KeithOYS Can you explain how actually certificate is created and how it is transferred.How do we see if the connection is secure.Rizo
@Rizo See: Create a self-signed server certificate - igniterealtime.org/builds/openfire/docs/latest/documentation/… | If a user is connected via SSL, you'll see a lock icon beside the user's name in Sessions page.Applause
@KeithOYS If i do all the steps listed in the link will it be enough or should i do any code change.I am new to this so bare me.What to do for a TLS connectionRizo
@Rizo In Openfire, ensure that you create a self-signed certificate (preferable to follow the steps in the link). In Xcode, you may perform the code changes according to my answer below.Applause
Let us continue this discussion in chat.Rizo
@KeithOYS thanks for your time dude.hope this solves my doubt and help the actually poster too.Rizo
@Rizo Sure about that. :)Applause

© 2022 - 2024 — McMap. All rights reserved.