I am trying to read certificates from various URLs in iOS. My code however is not working well - the array that should return the information I need always returns null.
What am I missing?
- (void)findCertificate:(NSString *)url
{
NSInputStream*input = [[NSInputStream inputStreamWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:@"https://store.writeitstudios.com"]]] retain];
[input setDelegate:self];
[input scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
[input open];
NSLog(@"Status: %i",[input streamStatus]);
}
- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode
{
NSLog(@"handle Event: %i",eventCode);
if (eventCode == NSStreamStatusOpen)
{
NSArray *certificates = (NSArray*)CFReadStreamCopyProperty((CFReadStreamRef)aStream, kCFStreamPropertySSLPeerCertificates);
NSLog(@"Certs: %@",CFReadStreamCopyProperty((CFReadStreamRef)aStream, kCFStreamPropertySSLPeerCertificates));
if ([certificates count] > 0) {
SecCertificateRef certificate = (SecCertificateRef)[certificates objectAtIndex:0];
NSString *description = (NSString*)SecCertificateCopySubjectSummary(certificate);
NSData *data = (NSData *)SecCertificateCopyData(certificate);
NSLog(@"Description: %@",description);
}
}
}
And yes, I am aware that I am leaking memory. This is just a snippet.
Let me explain what you're doing here and why it's wrong:
NSData (a data buffer). Note that you are not loading any certificates (well, technically NSURL will load them internally, but this code is most definitely not putting them into the NSData)NSStream's delegate method stream:handleEvent: and are attempting to read the kCFStreamPropertySSLPeerCertificates property. This property will be empty since the stream contains only a bit of HTML data, nothing else.NSArray.NULL.Using NSStream/CFStream is not necessary for the task at hand. And most definitely you don't have to go through NSURLConnection first and then through NSStream.
To retrieve SSL server certificates, stick to a simple, asynchronous NSURLConnection and use its delegate methods to access the certificates:
// Method to begin the asynchronous download
- (void)beginCertificateDownload:(NSURL *)url
{
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
[connection start];
}
// NSURLConnection Delegate Methods
- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace
{
return [[protectionSpace authenticationMethod] isEqualToString:NSURLAuthenticationMethodServerTrust];
}
- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
// extract the certificates
SecTrustRef trustRef = [[challenge protectionSpace] serverTrust];
CFIndex count = SecTrustGetCertificateCount(trustRef);
for (CFIndex i = 0; i < count; i++) {
SecCertificateRef certRef = SecTrustGetCertificateAtIndex(trustRef, i);
CFStringRef certSummary = SecCertificateCopySubjectSummary(certRef);
NSLog(@"%@", certSummary);
// do whatever you need with the certificates here
// don't forget to copy them if you need to keep them
// around beyond the scope of this method
}
// I'm assuming you're not interested in actually loading the contents of the URL, so cancel
[[challenge sender] cancelAuthenticationChallenge:challenge];
// you'll also want to release the connection object at some point
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With