Introduction
Starting with Release 3.1.0, the Android and iOS platforms have added functionality to support SSL Certificate Stores with the Titanium.Network.HTTPClient. This document provides information on the changes made to the platforms and the related functionality.
Please note that neither platform includes functionality to actually parse certificates. This functionality can be implemented by developers in native modules to extend the HTTPClient.
iOS Platform Notes
The HTTPClient on iOS exposes two properties:
clientCertificateIdentity
- sets aSecIdentityRef
, which is a C structure containing a private key and certificateclientCertificates
- sets an array whose elements are of typeSecCertificateRef
These are concrete C structures of the native iOS SDK and cannot be exposed on the JavaScript side. Modules must set these properties on the HTTPClient object in native code. These properties must be set before calling open on the HTTPClient.
The clientCertificateIdentity
property must be set for the clientCertificates
to be honored.
These two properties can be used to set client-side certificates for secure HTTP connections. These are useful for environments where the server requires the client to present valid certificates as part of the SSL handshake.
Validation of server certificates is controlled by the validatesSecureCertificate property of the HTTPClient and must be set to true validate the secure certificate.
For more information about using the native iOS SDK APIs for authentication processes, see Mac Developer Guide: Certificate, Key, and Trust Services Tasks for iOS and iOS Developer Library: HTTPS Server Trust Evaluation.
iOS Example
The following example code shows how to load an external PKCS #12 file and use it with an HTTP client. This example contains two separate pieces: the Titanium application written in JavaScript and the iOS module written in Objective-C and using some of the native iOS SDK APIs. The Titanium application needs to use the iOS module to perform necessary functions as mentioned earlier.
In the Titanium application:
- Require in the module.
- Create an
HTTPClient
with thevalidatesSecureCertificate
property set to true. - Call the module method to parse the PKCS #12 file and set the
clientCertificateIdentity
property. - Call the
open
andsend
methods to initiate the HTTP request.
// Require the module var certificateStore = require( 'com.certtest' ); var httpClient = Ti.Network.createHTTPClient({ onload: function (e) { Ti.API.info( "Received text: " + this .responseText); alert( 'success' ); }, onerror: function (e) { Ti.API.info( "Error: " + e.error); alert( 'error' ); }, timeout: 5000, // this property must be set to true validatesSecureCertificate: true }); // References a file called server.p12 in the Resources directory var cert = Ti.Filesystem.getFile(Ti.Filesystem.resourcesDirectory, "server.p12" ); // Call the module method and send the HTTPClient object // to set the clientCertificateIdentity property certificateStore.loadP12Cert(httpClient, cert, 'password' ); httpClient.open( "GET" , url); httpClient.send(); |
In an iOS module, the loadP12Cert
method receives the HTTPClient object, the PKCS #12 file and the password
to the file, then it:
- Loads the external PKCS #12 file.
- Parses it with the native
SecPKCS12Import
method. - Sets the
clientCertificateIdentity
of the HTTPClient object.
// Omitted boiler plate code created for a new module // Note that this example does not perform type or error checking -(id)loadP12Cert:(id)args { // Parse the arguments NSArray* theArg = args; // Titanium.Network.HTTPClient object TiProxy* theClient = [theArg objectAtIndex:0]; // Titanium.Filesystem.File object (p12 file) TiFile *theFile = [theArg objectAtIndex:1]; // Password of the file NSString* thePassword = [theArg objectAtIndex:2]; NSString* path = [theFile path]; // Load the file data NSData *PKCS12Data = [[NSData alloc] initWithContentsOfFile:path]; CFDataRef inPKCS12Data = (CFDataRef)PKCS12Data; SecIdentityRef myIdentity; SecTrustRef myTrust; OSStatus securityError = noErr; // Parse the file data securityError = extractIdentityAndTrust(inPKCS12Data, &myIdentity,
&myTrust, thePass); if (securityError == 0) { // Set the clientCertificateIdentity property [theClient setValue:(id)myIdentity forUndefinedKey:@ "clientCertificateIdentity" ]; } [PKCS12Data release]; return securityError; } // Example code taken from the Mac Developer Guide: Certificate, Key, and
Trust Services Tasks OSStatus extractIdentityAndTrust(CFDataRef inPKCS12Data, SecIdentityRef
*outIdentity, SecTrustRef *outTrust, NSString* thePass) { OSStatus securityError = errSecSuccess; CFStringRef password = (CFStringRef)thePass; const void *keys[] = { kSecImportExportPassphrase }; const void *values[] = { password }; CFDictionaryRef optionsDictionary = CFDictionaryCreate(NULL, keys, values,
1, NULL, NULL); CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL); securityError = SecPKCS12Import(inPKCS12Data, optionsDictionary, &items); if (securityError == 0) { CFDictionaryRef myIdentityAndTrust = CFArrayGetValueAtIndex (items, 0); const void *tempIdentity = NULL; tempIdentity = CFDictionaryGetValue (myIdentityAndTrust, kSecImportItemIdentity); *outIdentity = (SecIdentityRef)tempIdentity; const void *tempTrust = NULL; tempTrust = CFDictionaryGetValue (myIdentityAndTrust, kSecImportItemTrust); *outTrust = (SecTrustRef)tempTrust; } if (optionsDictionary) CFRelease(optionsDictionary); return securityError; } |
Android Platform Notes
The HTTPClient on Android has exposed two new methods:
addKeyManager(X509KeyManager)
addTrustManager(X509TrustManager)
Users can add custom KeyManager and TrustManager implementations to use with the HTTPClient connection. These methods must be called before calling open on the HTTPClient.
You need to use an Android module to implement the X509KeyManager or X509TrustManager interface and return these objects to the Titanium application. The application uses
the addKeyManager
and addTrustManager
methods to add support for these objects.
When either of these methods are used to initialize a custom SSLContext, the validatesSecureCertificate property of the HTTPClient is ignored. For more information about custom SSL, see Custom SSL for advanced JSSE developers.
Android Example
For an example of writing an Android module that implements and returns an X509TrustManager interface, see https://github.com/appcelerator-modules/ti.certificatestore. This module receives an external PKCS #12 file, parses it, then creates an X509TrustManager instance.
To use the module, in the Titanium application:
- Require in the module.
- Create an
HTTPClient
. - Make a call to the module by providing it a PKCS#12 file, which is used to create the X509TrustManager.
- Retrieve the X509TrustManager interface from the module and add it to the
HTTP client with the
addTrustManager
method. - Call the
open
andsend
methods to initiate the HTTP request.
var certificateStore = require( 'ti.certificatestore' ); var httpClient = Ti.Network.createHTTPClient({ onload: function (e) { Ti.API.info( "Received text: " + this .responseText); alert( 'success' ); }, onerror: function (e) { Ti.API.info( "Error: " + e.error); alert( 'error' ); }, timeout: 5000, // this property must be set to true // unless you are using a custom SSLContext validatesSecureCertificate: true }); // Add the certificate to the certificate store certificateStore.addCertificate( 'server.p12' , 'password' ); // Add the trust manager to the HTTP client httpClient.addTrustManager(certificateStore.getTrustManager()); httpClient.open( "GET" , url); httpClient.send(); |