How do I use my own root certificate in IOS Secure Transport API?
I am trying to use the Apple Secure Transport API on top of an installed transport layer with my own root certificate. I have a root certificate like SecCertificateRef
but I can't figure out how to get it to be trusted.
I've looked at the following features, each of which seems painfully close to what I need:
-
SSLAddDistinguishedName()
not implemented; -
SSLSetTrustedRoots()
deprecated; -
SSLSetCertificateAuthorities()
is for client authentication only.
If I could override SecTrustRef
in SecContextRef
then it SecTrustSetAnchorCertificates()
will do the job. Unfortunately this avenue only led me to SSLGetPeerSecTrust()
, which appears to be the Apple API.
I think what you want is the following:
OSStatus status = SSLSetSessionOption(sslContext, kSSLSessionOptionBreakOnServerAuth, true);
and then during the handshake
OSStatus status = SSLHandshake(sslContext);
if (status == errSSLPeerAuthCompleted)
{
SecTrustRef trust = NULL;
status = SSLCopyPeerTrust(sslContext, &trust);
// Perform your custom trust rules here. e.g.
BOOL bTrusted = NO;
NSArray* anchor = [NSArray arrayWithObject:(id)pCACert];
OSStatus result = SecTrustSetAnchorCertificates(trust, (CFArrayRef)anchor);
/* Uncomment this to enable both system certs and the one we are supplying */
//result = SecTrustSetAnchorCertificatesOnly(trust, NO);
SecTrustResultType trustResult;
result = SecTrustEvaluate(trust, &trustResult);
if (result == errSecSuccess)
{
switch (trustResult)
{
case kSecTrustResultProceed:
case kSecTrustResultUnspecified:
bTrusted = YES;
break;
case kSecTrustResultInvalid:
case kSecTrustResultDeny:
case kSecTrustResultRecoverableTrustFailure:
case kSecTrustResultFatalTrustFailure:
case kSecTrustResultOtherError:
default:
break;
}
}
// If trusted, continue the handshake
if (bTrusted)
status = SSLHandshake(sslContext);
else
{
/* Your trust failure handling here ~ disconnect */
}
}
where pCAcert is the root certificate (SecCertificateRef) you want to trust
If I understand this, you are trying:
{your application} - [ssl session] → {your server}
The server uses some certificate signed by your own CA (root). You are trying to send your CA certificate to your phone and use it as a trusted CA, not the global CAs that are there.
This is similar to what you are looking for:
Preparing for the session
Call SSLNewContext (on OS X) or SSLCreateContext (on iOS and OS X) to create a new SSL session context.
Write the SSLWrite and SSLRead I / O functions and call SSLSetIOFuncs to pass them to the secure transport.
Establish a connection using CFNetwork, BSD Sockets, or Open Transport. Then call SSLSetConnection to specify the connection to which the SSL session context is using.
Call SSLSetPeerDomainName to specify the fully qualified domain name of the partner you want to connect to (optional, but highly recommended).
Call SSLSetCertificate to specify the certificate to be used in authentication (required for backend, optional for client).
OSStatus SSLSetCertificate ( SSLContextRef context, CFArrayRef certRefs );
certRefs
Certificates for installation. This array contains elements of type SecCertificateRef, with the exception of certRefs [0], which is of type SecIdentityRef.
Discussion Installing a certificate or certificates is required for server connections, but optional for clients. Specifying a certificate for the client enables SSL client-side authentication. You must place a SecIdentityRef object in certRefs [0] that identifies the sheet and its associated secret key. The root of the certificate is optional; if not specified, the root certificate that verifies that the certificate chain specified here must be present in the system's trusted anchor certificate set.
from: https://developer.apple.com/library/mac/documentation/Security/Reference/secureTransportRef/