Following this guide on OAuth2 support for IMAP/SMTP, I was able to retrieve the access token and authenticate IMAP and SMTP fine, but for some Office 365 accounts like the ones that were bought from GoDaddy, AUTH XOAUTH2 command returns Authentication unsuccessful as shown in logs below.
Connecting to 'smtp.office365.com:587', SSL/TLS: False.
S: 220 AM7PR02CA0017.outlook.office365.com Microsoft ESMTP MAIL Service ready at Fri, 14 Aug 2020 12:24:06 +0000
C: EHLO [127.0.0.1]
S: 250-AM7PR02CA0017.outlook.office365.com Hello [79.185.54.24]
S: 250-SIZE 157286400
S: 250-PIPELINING
S: 250-DSN
S: 250-ENHANCEDSTATUSCODES
S: 250-STARTTLS
S: 250-8BITMIME
S: 250 SMTPUTF8
C: STARTTLS
S: 220 2.0.0 SMTP server ready
C: EHLO [127.0.0.1]
S: 250-AM7PR02CA0017.outlook.office365.com Hello [79.185.54.24]
S: 250-SIZE 157286400
S: 250-PIPELINING
S: 250-DSN
S: 250-ENHANCEDSTATUSCODES
S: 250-AUTH LOGIN XOAUTH2
S: 250-8BITMIME
S: 250 SMTPUTF8
C: AUTH XOAUTH2 *PASSWORD*
S: 535 5.7.3 Authentication unsuccessful [AM7PR02CA0017.eurprd02.prod.outlook.com]
URLs below are used for authorization:
https://login.microsoftonline.com/common/oauth2/v2.0/authorize
https://login.microsoftonline.com/common/oauth2/v2.0/token
Scope for SMTP used: https://outlook.office.com/SMTP.Send (as documented in the guide from Microsoft). In the Azure portal, it is added in Permissions API under Graph API (since it is not available under Exchange anymore).
The app is registered for both personal Outlook.com users and Office 365 organizations in Azure (that is why common is used as a tenant in auth URLs).
It works for our developer license Office 365 organization but for the one that is bought through GoDaddy (on https://www.godaddy.com/email/professional-business-email) it fails to authenticate SMTP (IMAP works fine).
Things already tried:
Limiting scopes to the ones related to Exchange resource (as suggested in https://learn.microsoft.com/en-us/exchange/client-developer/legacy-protocols/how-to-authenticate-an-imap-pop-smtp-application-by-using-oauth)
Changing tenant to organizations in auth URLs (instead of common)
Using https://outlook.office365.com/SMTP.Send as scope for SMTP
Any suggestions?
Related
I get the following exception when trying to send a mail with JavaMail with OAuth2:
javax.mail.MessagingException: Can't send command to SMTP host (javax.net.ssl.SSLHandshakeException: No appropriate protocol (protocol is disabled or cipher suites are inappropriate))
I have already checked dozens of stackOverFlow posts and none of the solutions work.
This is my javamail config (commented out some other configs I have tried):
...
String oauth2_access_token = <procedure to aquire a token>;
Properties props = new Properties();
props.put("mail.host",config.getString("MAILSERVER"));
props.put("mail.smtp.port", config.getString("MAILPORT"));
props.put("mail.smtp.auth", "true");
props.put("mail.transport.protocol", "smtp");
props.put("mail.smtp.starttls.enable", "true");
props.put("mail.smtp.starttls.required", "true");
props.put("mail.smtp.ssl.protocols", "TLSv1.1 TLSv1.2 TLSv1.3");
//props.put("mail.smtp.ssl.trust", "*");
props.put("mail.smtp.auth.mechanisms", "XOAUTH2");
//props.put("mail.smtp.auth.xoauth2.disable", false);
//props.put("mail.smtp.sasl.enable", "true");
//props.put("mail.smtp.auth.login.disable","true");
//props.put("mail.smtp.auth.plain.disable","true");
props.put("mail.debug", "true");
props.put("mail.debug.auth", "true");
// Connect
javax.mail.Session mailSession = javax.mail.Session.getInstance(props);
mailSession.setDebug(true);
SMTPTransport transport = (SMTPTransport) mailSession.getTransport("smtp");
transport.connect(config.getString("MAILSERVER"),
Integer.parseInt(config.getString("MAILPORT")), config.getString("MAILUSER"), oauth2_access_token);
Here is some output:
[apache-tomcat-9.0.54]: DEBUG SMTP: useEhlo true, useAuth false
DEBUG SMTP: trying to connect to host "smtp.office365.com", port 587, isSSL false
[apache-tomcat-9.0.54]: 220 SOMEANONYMIZEDSERVERPREFIX.outlook.office365.com Microsoft ESMTP MAIL Service ready at Wed, 16 Mar 2022 10:16:37 +0000
DEBUG SMTP: connected to host "smtp.office365.com", port: 587
[apache-tomcat-9.0.54]: EHLO MY_COMPUTER_NAME
[apache-tomcat-9.0.54]: 250-SOMEANONYMIZEDSERVERPREFIX.outlook.office365.com Hello [MY_IP_ADRESS]
250-SIZE 157286400
250-PIPELINING
250-DSN
250-ENHANCEDSTATUSCODES
250-STARTTLS
250-8BITMIME
250-BINARYMIME
250-CHUNKING
250 SMTPUTF8
STARTTLS
[apache-tomcat-9.0.54]: 220 2.0.0 SMTP server ready
[apache-tomcat-9.0.54]: EHLO MY_COMPUTER_NAME
[apache-tomcat-9.0.54]: ERROR 2022-03-16 11:16:39,407 [..MyProgramException..] - javax.mail.MessagingException: Can't send command to SMTP host (javax.net.ssl.SSLHandshakeException: No appropriate protocol (protocol is disabled or cipher suites are inappropriate))
I think it has to do with issuing the AUTH command because it is not listed in output. The connection over TLS is established successfully so I think the SSLHandshake Exception might be misleading. So what gives?
Might it have to do with the token? The scope I had to use for the token aquisition is ".default". "Mail.Send" didn't work.
I am using the newest JavaMail version 1.6.2 and AdoptOpenJdk 11.0.12.
I also double checked java.security config. TLS 1v2 and 1v3 algorithms are not disabled.
I found the solution myself. Maybe somone can make use of it:
The code above was not false to my knowledge. The actual reason was, that I should not have used a token acquisition with client credentials but only with username/password and client id.
I am not sure if JavaMail supports this because in the documentation I could not find anything about it. The rare examples about this are usually with client credentials.
As you can see above, no AUTH command was triggered because I had not the permission to do it obviously.
What I needed to do aswell was to use the Microsoft Graph API not only to aquire a token via "password/username provider" (this type) instead of the "client credential provider" (link) but also to actually trigger a send mail command via Microsoft Graph API (link).
The client credential provider was in my case not sufficient because the admin had not specified permissions to send mails.
Thus, I didn't use JavaMail anymore for this and needed another token acquisition provider.
I'm currently developing a React-Native app (first for IOS) and I have to do an API request on a FHIR connector (medical standard). This API uses a TLS certificate generated by the PKI of my state that is by default not trusted by IOS. I added manually the root certificate G1, the second certificate G2, and the certificate of the API URL.
After it, I trusted the Root certificate using this explanation of the apple support : https://support.apple.com/en-us/HT204477
From now I thought that it was ready to be used and I checked with Chrome and now I have the following error: NET::ERR_CERT_VALIDITY_TOO_LONG
I don't have access to the PKI so I can't generate a certificate with a lowest validity time. After some research I found this :
https://support.apple.com/en-us/HT210176
That explains the requirements of certificate TLS to be validated by the OS. I thought that there is no solution except generate a new certificate but I found this :
https://support.apple.com/en-us/HT211025
That explains that the certificate installed manually by a user doesn't have to respect the validity of 398 days.
I'm a bit confuse ...
My certificate validity time is 10 years.
My application React-native work fine with http, but i have the following error with https :
[connection] nw_socket_handle_socket_event [C7:1] Socket SO_ERROR [61: Connection refused]
[connection] nw_connection_get_connected_socket [C7] Client called nw_connection_get_connected_socket on unconnected nw_connection
TCP Conn 0x2835e42c0 Failed : error 0:61 [61]
[native] [GESTURE HANDLER] Initialize gesture handler for root view <RCTRootContentView: 0x106d05890; reactTag: 11; frame = (0 0; 834 1194); gestureRecognizers = <NSArray: 0x280c16640>; layer = <CALayer: 0x280303060>>
Connection 12: default TLS Trust evaluation failed(-9807)
Connection 12: TLS Trust encountered error 3:-9807
Connection 12: encountered error(3:-9807)
Connection 12: unable to determine interface type without an established connection
Task <344A51D7-3F78-47DF-94E4-4A70D6B4E026>.<4> HTTP load failed, 0/0 bytes (error code: -1202 [3:-9807])
Task <344A51D7-3F78-47DF-94E4-4A70D6B4E026>.<4> finished with error [-1202] Error Domain=NSURLErrorDomain Code=-1202 "The certificate for this server is invalid. You might be connecting to a server that is pretending to be “<API URL REQUEST>” which could put your confidential information at risk." UserInfo={NSLocalizedRecoverySuggestion=Would you like to connect to the server anyway?, _kCFStreamErrorDomainKey=3, NSErrorPeerCertificateChainKey=(
"<cert(0x104814000) s: <API URL CERTIF> i: <CERTIF G2>>",
"<cert(0x104811000) s: <CERTIF G2> i: <CERTIF ROOT G1>>",
"<cert(0x10488d000) s: <CERTIF ROOT G1> i: <CERTIF ROOT G1>>"
), NSErrorClientCertificateStateKey=0, NSErrorFailingURLKey=<API URL>, NSErrorFailingURLStringKey=<API UTL>, NSUnderlyingError=0x280c36310 {Error Domain=kCFErrorDomainCFNetwork Code=-1202 "(null)" UserInfo={_kCFStreamPropertySSLClientCertificateState=0, kCFStreamPropertySSLPeerTrust=<SecTrustRef: 0x2830f7330>, _kCFNetworkCFStreamSSLErrorOriginalValue=-9807, _kCFStreamErrorDomainKey=3, _kCFStreamErrorCodeKey=-9807, kCFStreamPropertySSLPeerCertificates=(
"<cert(0x104814000) s: <API URL CERTIF> i: <CERTIF G1>>",
"<cert(0x104811000) s: <CERTIF G2> i: <CERTIF G1>",
"<cert(0x10488d000) s: <CERTIF G1> i: <CERTIF G1>"
)}},
The question is : Am i doing something wrong or is there any way to use this certificate (10 years validity time) for my application ?
Thank you for helping me
If you are using a self signed cert, you have to enable your IOS device to trust it:
For this, open again the iOS Settings app. Then navigate to “General” > “About” > “Certificate Trust Settings”. In the section “Enable Full Trust for Root Certificates”, enable your root certificate. With this, your app can connect now with the self-signed certificate to the backend.
I finally had access to the PKI to generate a TLS certificate with a shorter validity time and this solved my problem. Now the certificate is accepted by IOS.
I strongly think that there is no other solutions.
I’m converting an existing PHPMailer app with Basic (userid and password) Authentication to use OAUTH2. Setup was easy but the app fails on authentication.
2020-05-23 15:14:59 Connection: opening to smtp.office365.com:587, timeout=300, options=array()
2020-05-23 15:14:59 Connection: opened
2020-05-23 15:14:59 SMTP INBOUND: "220 LO2P265CA0358.outlook.office365.com Microsoft ESMTP MAIL Service ready at Sat, 23 May 2020 15:14:58 +0000"
2020-05-23 15:14:59 SERVER -> CLIENT: 220 LO2P265CA0358.outlook.office365.com Microsoft ESMTP MAIL Service ready at Sat, 23 May 2020 15:14:58 +0000
2020-05-23 15:14:59 CLIENT -> SERVER: EHLO [my domain name]
2020-05-23 15:14:59 SMTP INBOUND: "250-LO2P265CA0358.outlook.office365.com Hello [91.208.99.2]"
2020-05-23 15:14:59 SMTP INBOUND: "250-SIZE 157286400"
2020-05-23 15:14:59 SMTP INBOUND: "250-PIPELINING"
2020-05-23 15:14:59 SMTP INBOUND: "250-DSN"
2020-05-23 15:14:59 SMTP INBOUND: "250-ENHANCEDSTATUSCODES"
2020-05-23 15:14:59 SMTP INBOUND: "250-STARTTLS"
2020-05-23 15:14:59 SMTP INBOUND: "250-8BITMIME"
2020-05-23 15:14:59 SMTP INBOUND: "250-BINARYMIME"
2020-05-23 15:14:59 SMTP INBOUND: "250-CHUNKING"
2020-05-23 15:14:59 SMTP INBOUND: "250 SMTPUTF8"
2020-05-23 15:14:59 SERVER -> CLIENT: 250-LO2P265CA0358.outlook.office365.com Hello [91.208.99.2]250-SIZE 157286400250-PIPELINING250-DSN250-ENHANCEDSTATUSCODES250-STARTTLS250-8BITMIME250-BINARYMIME250-CHUNKING250 SMTPUTF8
2020-05-23 15:14:59 CLIENT -> SERVER: STARTTLS
2020-05-23 15:14:59 SMTP INBOUND: "220 2.0.0 SMTP server ready"
2020-05-23 15:14:59 SERVER -> CLIENT: 220 2.0.0 SMTP server ready
2020-05-23 15:14:59 CLIENT -> SERVER: EHLO [my domain name]
2020-05-23 15:14:59 SMTP INBOUND: "250-LO2P265CA0358.outlook.office365.com Hello [91.208.99.2]"
2020-05-23 15:14:59 SMTP INBOUND: "250-SIZE 157286400"
2020-05-23 15:14:59 SMTP INBOUND: "250-PIPELINING"
2020-05-23 15:14:59 SMTP INBOUND: "250-DSN"
2020-05-23 15:14:59 SMTP INBOUND: "250-ENHANCEDSTATUSCODES"
2020-05-23 15:14:59 SMTP INBOUND: "250-AUTH LOGIN XOAUTH2"
2020-05-23 15:14:59 SMTP INBOUND: "250-8BITMIME"
2020-05-23 15:14:59 SMTP INBOUND: "250-BINARYMIME"
2020-05-23 15:14:59 SMTP INBOUND: "250-CHUNKING"
2020-05-23 15:14:59 SMTP INBOUND: "250 SMTPUTF8"
2020-05-23 15:14:59 SERVER -> CLIENT: 250-LO2P265CA0358.outlook.office365.com Hello [91.208.99.2]250-SIZE 157286400250-PIPELINING250-DSN250-ENHANCEDSTATUSCODES250-AUTH LOGIN XOAUTH2250-8BITMIME250-BINARYMIME250-CHUNKING250 SMTPUTF8
2020-05-23 15:14:59 Auth method requested: XOAUTH2
2020-05-23 15:14:59 Auth methods available on the server: LOGIN,XOAUTH2
2020-05-23 15:14:59 CLIENT -> SERVER: AUTH XOAUTH2 [long string of chars ending in == - base 64 encoded?]
2020-05-23 15:15:04 SMTP INBOUND: "535 5.7.3 Authentication unsuccessful [LO2P265CA0358.GBRP265.PROD.OUTLOOK.COM]"
2020-05-23 15:15:04 SERVER -> CLIENT: 535 5.7.3 Authentication unsuccessful [LO2P265CA0358.GBRP265.PROD.OUTLOOK.COM]
2020-05-23 15:15:04 SMTP ERROR: AUTH command failed: 535 5.7.3 Authentication unsuccessful [LO2P265CA0358.GBRP265.PROD.OUTLOOK.COM]
SMTP Error: Could not authenticate.
2020-05-23 15:15:04 CLIENT -> SERVER: QUIT
My composer.json is just:
"require": {
"phpmailer/phpmailer": "~6.1",
"stevenmaguire/oauth2-microsoft": "2.2.0"
which then also drags in the League oauth2-client and others.
To avoid the issue of OAUTH2 only working for free consumer accounts and not O365 (see Issue 3 - stevenmaguire/oauth2-microsoft on github - and I have replicated the issue to confirm it), I have edited /src/Provider/Microsoft.php as follows:
protected $urlAuthorize = 'https://login.microsoftonline.com/common/oauth2/authorize';
protected $urlAccessToken = 'https://login.microsoftonline.com/common/oauth2/token';
(This uses the Graph V1 endpoint. Note that the latest Graph endpoint V2 is accessed via ... oauth2/v2.0/token and ...oauth2/v2.0/authorize and that either of these uses different Scope parameters that are incompatible with the old 'wl' Windows Live ones)
My relevant PHPMailer code is:
$mail = new PHPMailer;
$mail->isSMTP(); // Set mailer to use SMTP
$mail->Host = 'smtp.office365.com'; // Specify main and backup SMTP servers
$mail->SMTPAuth = true; // Enable SMTP authentication
$mail->AuthType = 'XOAUTH2'; // omit this to send using basic authentication
$username = [my email address - as Azure AD signin];
$clientId = [from Azure AD - redacted];
$clientSecret = ‘[from Azure AD - redacted];
$refreshToken = [long char string – from running get_auth_token];
$provider = new Microsoft (
[
'clientId' => $clientId ,
'clientSecret' => $clientSecret
]
);
$mail->setOAuth(
new OAuth(
[
'provider' => $provider,
'clientId' => $clientId,
'clientSecret' => $clientSecret,
'refreshToken' => $refreshToken’,
'userName' =>$username
]
)
);
//$mail->Username = blah; // SMTP username – not needed for OAUTH2
//$mail->Password = blah; // SMTP password – not needed for OAUTH2
$mail->SMTPSecure = 'tls'; // Enable TLS encryption, `ssl` also accepted
$mail->Port = 587;
$mail->SMTPDebug = SMTP::DEBUG_LOWLEVEL;
My get_oauth_token.php contains:
$clientId = [my client ID];
$clientSecret = [my client secret]';
$redirectUri = [the full URI of get_oauth_token.php];
I have created an app in MSFT Azure AD of type ‘Web app’, taken the client ID and client secret from there, and added API permissions for SMTP.Send, Mail.Send, offline_access, and openid
If I can find where in the code the Azure AD authentication codes and messages (vide https://learn.microsoft.com/en-us/azure/active-directory/develop/reference-aadsts-error-codes#lookup-current-error-code-information) are returned, I'll be a step forward! I have tried the AD Monitor for sign-ins, but these particular unsuccessful attempts are not logged - nor the successful getting of the refresh token.
The problem was eventually traced to MSFT's quirky implementation of OIDC/OAuth2 authentication that I know from posts on Stackoverflow and elsewhere has confused many developers. In effect MSFT had not completed the job.
I have described this - and the solution - at some length in https://github.com/decomplexity/SendOauth2/blob/main/MSFT%20OAuth2%20quirks.md
$mail = new PHPMailer;
$mail->isSMTP(); // Set mailer to use SMTP
$mail->Host = 'smtp.office365.com'; // Specify main and backup SMTP servers
$mail->SMTPAuth = true; // Enable SMTP authentication
$mail->AuthType = 'LOGIN'; // omit this to send using basic authentication
$mail->Username = 'sdfdsfds#outlook.com'; // SMTP username – not needed for OAUTH2
$mail->Password = 'fdssdf'; // SMTP password – not needed for OAUTH2
$mail->SMTPSecure = 'starttls'; // Enable TLS encryption, `ssl` also accepted
$mail->Port = 587;
$mail->SMTPDebug = SMTP::DEBUG_LOWLEVEL;
Be careful with phpmailer, I was using 5.2 and in class.smtp it was encoding usermail twice, so my solution was delete that base64_encode in following CASE:
case 'LOGIN':
// Start authentication
if (!$this->sendCommand('AUTH', 'AUTH LOGIN', 334)) {
return false;
}
if (!$this->sendCommand("Username", ($username), 334)) {
return false;
}
if (!$this->sendCommand("Password", base64_encode($password), 235)) {
return false;
}
break;
The application I am currently working on is throwing an error when redirected to Payfort. This issue is seen on iOS but works properly on android. Additionally the redirection error/issue is not seen on the sandbox but rather observed on the production environment.
Error Domain=NSURLErrorDomain Code=-1202 "The certificate for this server is invalid. You might be connecting to a server that is pretending to be “paymentservices.payfort.com” which could put your confidential information at risk." UserInfo={NSURLErrorFailingURLPeerTrustErrorKey=<SecTrustRef: 0x600001894a20>, NSLocalizedRecoverySuggestion=Would you like to connect to the server anyway?, _kCFStreamErrorDomainKey=3, _kCFStreamErrorCodeKey=-9807, NSErrorPeerCertificateChainKey=(
"<cert(0x7f828303b600) s: paymentservices.payfort.com i: Symantec Class 3 Secure Server CA - G4>",
"<cert(0x7f8283185a00) s: Symantec Class 3 Secure Server CA - G4 i: VeriSign Class 3 Public Primary Certification Authority - G5>"
), NSUnderlyingError=0x600002669530 {Error Domain=kCFErrorDomainCFNetwork Code=-1202 "(null)" UserInfo={_kCFStreamPropertySSLClientCertificateState=0, kCFStreamPropertySSLPeerTrust=<SecTrustRef: 0x600001894a20>, _kCFNetworkCFStreamSSLErrorOriginalValue=-9807, _kCFStreamErrorDomainKey=3, _kCFStreamErrorCodeKey=-9807, kCFStreamPropertySSLPeerCertificates=(
"<cert(0x7f828303b600) s: paymentservices.payfort.com i: Symantec Class 3 Secure Server CA - G4>",
"<cert(0x7f8283185a00) s: Symantec Class 3 Secure Server CA - G4 i: VeriSign Class 3 Public Primary Certification Authority - G5>"
)}}, NSLocalizedDescription=The certificate for this server is invalid. You might be connecting to a server that is pretending to be “paymentservices.payfort.com” which could put your confidential information at risk., NSErrorFailingURLKey=https://paymentservices.payfort.com/FortAPI/paymentApi, NSErrorFailingURLStringKey=https://paymentservices.payfort.com/FortAPI/paymentApi, NSErrorClientCertificateStateKey=0}
The certificate for this server is invalid.
Contact the support staff at payfort.com and tell them their Symantec certificate is no longer accepted, due to security concerns. See e.g. https://blog.mozilla.org/security/2018/03/12/distrust-symantec-tls-certificates/
I am working on the server side of a system here, but I have an iOS question. There is a team (Different time zone, so not online now) who are calling a REST API I provide, using an iOS app. iOS 12, I am pretty sure. They mailed me earlier to say that it's "Hanging" and send me the following log. I know iOS doesn't like self-signed certs, so I made a custom CA, and signed a cert for my server. I sent them the Custom CAs, (Issuing and Root) and they appear to have installed them correctly as profiles on iOS. Does the trace below make any sense to anyone? I know this a bit hand-wavy, but we are up against the wire on a regulatory project, and I'd really appreciate any insight that I could offer to my App Development friends.
Error Domain=org.openid.appauth.general Code=-5 "(null)" UserInfo={NSUnderlyingError=0x280d86bb0 {Error Domain=NSURLErrorDomain Code=-1200 "An SSL error has occurred and a secure connection to the server cannot be made." UserInfo={NSURLErrorFailingURLPeerTrustErrorKey=<SecTrustRef: 0x2831eb180>, NSLocalizedRecoverySuggestion=Would you like to connect to the server anyway?, _kCFStreamErrorDomainKey=3, _kCFStreamErrorCodeKey=-9802, NSErrorPeerCertificateChainKey=(
"<cert(0x13e094e00) s: rhaxwayvd1.mid.xxx i: XXX plc Issuing CA 1>",
"<cert(0x13e095800) s: XXX plc Issuing CA 1 i: XXX plc Root CA>",
"<cert(0x13e096200) s: XXX plc Root CA i: XXX plc Root CA>"
), NSUnderlyingError=0x280d85b30 {Error Domain=kCFErrorDomainCFNetwork Code=-1200 "(null)" UserInfo={_kCFStreamPropertySSLClientCertificateState=0, kCFStreamPropertySSLPeerTrust=<SecTrustRef: 0x2831eb180>, _kCFNetworkCFStreamSSLErrorOriginalValue=-9802, _kCFStreamErrorDomainKey=3, _kCFStreamErrorCodeKey=-9802, kCFStreamPropertySSLPeerCertificates=(
"<cert(0x13e094e00) s: rhaxwayvd1.mid.xxx i: XXX plc Issuing CA 1>",
"<cert(0x13e095800) s: XXX plc Issuing CA 1 i: AIB plc Root CA>",
"<cert(0x13e096200) s: XXX plc Root CA i: AIB plc Root CA>"
)}}, NSLocalizedDescription=An SSL error has occurred and a secure connection to the server cannot be made., NSErrorFailingURLKey=https://rhaxwayvd1.mid.xxx:8445/XXXApp/TokenExchange, NSErrorFailingURLStringKey=https://rhaxwayvd1.mid.xxx:8445/XXXApp/TokenExchange, NSErrorClientCertificateStateKey=0}}}
OK, lack of knowledge on my part, but I'll post the answer, in case it helps someone in future. Although I mailed the Custom CA Cert to the iOS tester, and he installed it as a profile, that's not enough.
It is necessary to specify that you trust this CA on the device. This is done by navigating on the device settings to:
settings->general->about->Certificate Trust Settings
There you will see the newly installed Custom CA. It is switched off by default, so needs to be switched on.