2

I'm currently using SocketRocket as the WebSocket implementation for my iOS applications and would like to pin my server's CA as a trusted certificate with the SR_SSLPinnedCertificates property. I'm looking for a good example of loading one or more certificates to pass into SocketRocket. I've got the following code to work but I'm not sure it's correct or if there is a more straight-forward approach.

CFArrayRef keyref = NULL;
NSString *path = [[NSBundle mainBundle] pathForResource:@"certificate" ofType:@"p12"];
NSData *data = [[NSData alloc] initWithContentsOfFile:path];
OSStatus status = SecPKCS12Import((__bridge CFDataRef)data, (__bridge CFDictionaryRef)[NSDictionary dictionaryWithObject:@"eftl_key_pass" forKey:(__bridge id)kSecImportExportPassphrase], &keyref);
if (status == noErr) {
    CFDictionaryRef identityDict = CFArrayGetValueAtIndex(keyref, 0);
    SecIdentityRef identityRef = (SecIdentityRef)CFDictionaryGetValue(identityDict, kSecImportItemIdentity);
    SecCertificateRef certRef = NULL;
    SecIdentityCopyCertificate(identityRef, &certRef);
}
user2681040
  • 21
  • 1
  • 3

2 Answers2

5

Certificate pinning with SocketRocket is done thus:

First, we need to initialize SocketRocket from an NSURLRequest, rather than from an NSURL.

NSURL *url = [[NSURL alloc] initWithString:@"wss://path-to-socket:1234"];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];

Then, let's set up the certificate. It's crucial that your certificate be in the binary DER format, rather than the base64-encoded PEM. The certificate file should be in your main bundle.

NSString *cerPath = [[NSBundle mainBundle] pathForResource:@"myOwnCertificate" ofType:@"cer"];
NSData *certData = [[NSData alloc] initWithContentsOfFile:cerPath];
CFDataRef certDataRef = (__bridge CFDataRef)certData;
SecCertificateRef certRef = SecCertificateCreateWithData(NULL, certDataRef);
id certificate = (__bridge id)certRef;

We then set the request's pinned certificates to an array containing just the one we set up previously.

[request setSR_SSLPinnedCertificates:@[certificate]];

And now we can finalize the socket.

SRWebSocket *socket = [[SRWebSocket alloc] initWithURLRequest:request];       
[socket open];
arik
  • 28,170
  • 36
  • 100
  • 156
  • is it also possible to do a SSL handshake with socket rocket (verifying certificate chain) like with the delegates provided by NSURLConnection? –  Oct 23 '14 at 07:50
  • SocketRocket does that by default, actually. The above method is useful if you do not have a certificate whose chain leads to a trusted authority, but if you have your own, possibly self-signed, certificate. – arik Oct 23 '14 at 11:03
  • I am getting nil for this object :SecCertificateRef certRef = SecCertificateCreateWithData(NULL, certDataRef); – Rahul gupta Feb 27 '19 at 11:33
  • My code is : NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url]; NSString *cerPath = [[NSBundle mainBundle] pathForResource:@"ca_chain_cert" ofType:@"pem"]; NSData *certData = [[NSData alloc] initWithContentsOfFile:cerPath]; CFDataRef certDataRef = (__bridge CFDataRef)certData; SecCertificateRef certRef = SecCertificateCreateWithData(nil, certDataRef); id certificate = (__bridge id)certRef; if(certificate!=nil) { [request setSR_SSLPinnedCertificates:@[certificate]]; } – Rahul gupta Feb 27 '19 at 11:50
1

For the code in Swift:

if let pinnedCertificatePath = NSBundle.mainBundle().pathForResource("subdomain.yourwebsite.com", ofType: "der"),
let pinnedCertificateData = NSData(contentsOfFile: pinnedCertificatePath),
let cert = SecCertificateCreateWithData(nil, pinnedCertificateData) {
    request.SR_SSLPinnedCertificates = [cert]

    // make the websocket call!
    let ws = SRWebSocket(URLRequest: request)
    // configure the websocket
    ws.open()
} else {
    NSLog("Failed to open websocket, could not find pinned certificate!")
}
weltan
  • 83
  • 5