Client certificates iOS - iphone

I'm trying to extract a .p12 file and use it for a two way authentication against my own server. when i'm trying to compile i get some linking errors. The errors are refering to:
_kSecImportExportPassphrase
_SecIdentityCopyCertificate
_kSecImportItemTrust
_SecPKCS12Import
_kSecImportItemIdentity
Here is the code i'm using to extract the p12 file:
-(void)clientCert
{
NSString *path = [[NSBundle mainBundle] pathForResource:#"torbix" ofType:#"p12"];
NSData *p12data = [NSData dataWithContentsOfFile:path];
CFDataRef inP12data = (CFDataRef)p12data;
SecIdentityRef myIdentity;
SecTrustRef myTrust;
OSStatus status = extractIdentityAndTrust(inP12data, &myIdentity, &myTrust);
SecCertificateRef myCertificate;
SecIdentityCopyCertificate(myIdentity, &myCertificate);
const void *certs[] = { myCertificate };
CFArrayRef certsArray = CFArrayCreate(NULL, certs, 1, NULL);
}
OSStatus extractIdentityAndTrust(CFDataRef inP12data, SecIdentityRef *identity, SecTrustRef *trust)
{
OSStatus securityError = errSecSuccess;
CFStringRef password = CFSTR("password");
const void *keys[] = { kSecImportExportPassphrase };
const void *values[] = { password };
CFDictionaryRef options = CFDictionaryCreate(NULL, keys, values, 1, NULL, NULL);
CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);
securityError = SecPKCS12Import(inP12data, options, &items);
if (securityError == 0) {
CFDictionaryRef myIdentityAndTrust = CFArrayGetValueAtIndex(items, 0);
const void *tempIdentity = NULL;
tempIdentity = CFDictionaryGetValue(myIdentityAndTrust, kSecImportItemIdentity);
*identity = (SecIdentityRef)tempIdentity;
const void *tempTrust = NULL;
tempTrust = CFDictionaryGetValue(myIdentityAndTrust, kSecImportItemTrust);
*trust = (SecTrustRef)tempTrust;
}
if (options) {
CFRelease(options);
}
return securityError;
}
Why do i get these errors?

Add the Security.framework to your project.
In Xcode 4.2+ in the target go to the "Build Phases" tab, "Link BinariesWith Libraries", Click the "+" and add the "Security.framework".

Related

Authenticate certificate chaining with server in iOS

I have to authenticate certificate chain with my root certificate against server. I get the server certificate's NSData.
We don't have any authenticationChallenge mechanism and need to verify using the NSData.
How do I achieve this, because SecTrustEvaluate always returns kSecTrustResultRecoverableTrustFailure.
Following is the code I am using: -
NSData *aServerCertificateData = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"ABC" ofType:#"cer"]]; // This will actually come from the server
NSString *aRootPath = [[NSBundle mainBundle] pathForResource:#"XYZ" ofType:#"pem"];
CFDataRef aRootCertData = (__bridge CFDataRef)[NSData dataWithContentsOfFile:aRootPath];
CFDataRef myCertData = (__bridge CFDataRef)aServerCertificateData;
SecPolicyRef myPolicy = SecPolicyCreateBasicX509();
SecCertificateRef certArray[1] = {myCert};
CFArrayRef myCerts = CFArrayCreate(NULL, (void *)certArray, 1, NULL);
SecTrustRef myTrust;
OSStatus status = SecTrustCreateWithCertificates(myCerts, myPolicy, &myTrust);
SecCertificateRef rootCertRef[1] = {rootCert};
CFArrayRef rootCerts = CFArrayCreate(NULL, (void *)rootCertRef, 1, NULL);
status = SecTrustSetAnchorCertificates(myTrust, rootCerts);
SecTrustResultType trustResult;
if (status == noErr) {
status = SecTrustEvaluate(myTrust, &trustResult);
if (status == noErr) {
if (trustResult == kSecTrustResultRecoverableTrustFailure) {// 2
NSData *exceptions = (__bridge NSData *)(SecTrustCopyExceptions(myTrust)); // This always gets called
NSLog(#"Exceptions: %#", [NSPropertyListSerialization propertyListWithData:exceptions options:kNilOptions format:nil error:nil]);
}
publicKeyRef = SecTrustCopyPublicKey(myTrust);
}
}

OAuth signature with RSA-SHA1 on iOS

I need help creating an RSA-SHA1 signature to be used in a 3-legged OAuth implementation on iOS.
I was able to do this using HMAC-SHA1 using CommonCrypto.h, but this library doesn't seem to support RSA-SHA1.
Have any of you implemented OAuth signatures with RSA? Could you point me to some resources where I can find more information?
Thanks.
The answer by Erik Villegas was also the solution for me. But there is a bug in the posted code which I encountered when using this solution: secretFile was opened with fopen(), so it must be closed with fclose()
- (NSString *)RSASHA1HashForString:(NSString *)source {
NSLog(#"encrypting %#", source);
if (source == nil) return nil;
OpenSSL_add_all_algorithms();
NSString *signature = nil;
// make a SHA-1 digest of the source string
const char* sourceChars = [source UTF8String];
unsigned char digest[SHA_DIGEST_LENGTH];
SHA1((const unsigned char *)sourceChars, strlen(sourceChars), digest);
NSString *path = [[NSBundle mainBundle] pathForResource:#"privatekey" ofType:#"pem"];
const char *pathCString = [path cStringUsingEncoding:NSUTF8StringEncoding];
FILE *secretFile = fopen(pathCString, "r");
RSA *rsa = NULL;
PEM_read_RSAPrivateKey(secretFile, &rsa, NULL, NULL);
if (rsa != NULL) {
unsigned int sigLen = 0;
unsigned char *sigBuff = malloc(RSA_size(rsa));
int result = RSA_sign(NID_sha1, digest, (unsigned int) sizeof(digest),
sigBuff, &sigLen, rsa);
if (result != 0) {
NSData *sigData = [NSData dataWithBytes:sigBuff length:sigLen];
signature = [self base64forData:sigData];
}
free(sigBuff);
RSA_free(rsa);
}
fclose(secretFile);
NSLog(#"generated signature: %#", signature);
return signature;
}
I finally found the solution. Here is a method that will look for a privatekey.pem file in your bundle and create a RSA-SHA1 signature using the string that is passed in. You will need to add the openssl library. You can use this project as a reference: https://github.com/x2on/OpenSSL-for-iPhone
- (NSString *)RSASHA1HashForString:(NSString *)source {
NSLog(#"encrypting %#", source);
if (source == nil) return nil;
OpenSSL_add_all_algorithms();
NSString *signature = nil;
// make a SHA-1 digest of the source string
const char* sourceChars = [source UTF8String];
unsigned char digest[SHA_DIGEST_LENGTH];
SHA1((const unsigned char *)sourceChars, strlen(sourceChars), digest);
NSString *path = [[NSBundle mainBundle] pathForResource:#"privatekey" ofType:#"pem"];
const char *pathCString = [path cStringUsingEncoding:NSUTF8StringEncoding];
FILE *secretFile = fopen(pathCString, "r");
RSA *rsa = NULL;
PEM_read_RSAPrivateKey(secretFile, &rsa, NULL, NULL);
if (rsa != NULL) {
unsigned int sigLen = 0;
unsigned char *sigBuff = malloc(RSA_size(rsa));
int result = RSA_sign(NID_sha1, digest, (unsigned int) sizeof(digest),
sigBuff, &sigLen, rsa);
if (result != 0) {
NSData *sigData = [NSData dataWithBytes:sigBuff length:sigLen];
signature = [self base64forData:sigData];
}
free(sigBuff);
RSA_free(rsa);
}
NSLog(#"generated signature: %#", signature);
return signature;
}
If you are implementing OAuth you'll need to pass the signature base into this method. More info can be found here: http://oauth.net/core/1.0a/#anchor13
Here is implementation using core security iOS framework.
Key should be converted to pkcs12 format.
Algorythm name is "kSecKeyAlgorithmRSASignatureMessagePKCS1v15SHA1"
- (NSString *)getStringSHA1RSASignature:(NSString *)str
{
NSString *path = [[NSBundle bundleForClass:[self class]]
pathForResource:#"rsaPrivate.pfx" ofType:#"pkcs12"];
NSData *p12data = [NSData dataWithContentsOfFile:path];
NSMutableDictionary * options = [[NSMutableDictionary alloc] init];
[options setObject:#" your password " forKey:(id)kSecImportExportPassphrase];
CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);
OSStatus securityError = SecPKCS12Import((CFDataRef)p12data, (CFDictionaryRef)options, &items);
CFDictionaryRef identityDict = CFArrayGetValueAtIndex(items, 0);
SecIdentityRef identityApp =
(SecIdentityRef)CFDictionaryGetValue(identityDict,
kSecImportItemIdentity);
assert(securityError == noErr);
SecKeyRef privateKeyRef;
SecIdentityCopyPrivateKey(identityApp, &privateKeyRef);
/// have a key
NSData *input = [str dataUsingEncoding:NSUTF8StringEncoding];
CFErrorRef error = nil;
///////////////////////////////// "SHA1withRSA" java
CFDataRef signature = SecKeyCreateSignature(privateKeyRef, kSecKeyAlgorithmRSASignatureMessagePKCS1v15SHA1, (CFDataRef)input, &error);
NSAssert(signature != nil, #"");
NSString *result = [(__bridge NSData *)signature base64EncodedStringWithOptions:0];
CFRelease(signature);
NSAssert(result != nil, #"");
NSLog(#"generated signature: %#", result);
return result;
}

get pfx file included in a mobilecertificate in iOS

I'm trying to connect to a server using a .pfx that is stored in a .mobileconfig file on my iPhone.
When the server ask for it in
-(void)connection:(NSURLConnection*)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge*)challenge{
How can I create the NSURLCredential with the .pfx? Should I use
+ (NSURLCredential *)credentialWithIdentity:(SecIdentityRef)identity certificates:(NSArray *)certArray persistence:(NSURLCredentialPersistence)persistence
If thats the case, how do I extract the .pfx to put it into the array.
Thanks in advance.
So no, there is no way to get the certificate from the mobileconfig file. iOS applications use its own keychain access and storage. Only email and other phone service like internet can make use of those certificates
U can use my code:
- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
NSString *path = [[NSBundle mainBundle] pathForResource:#"torbix" ofType:#"pfx"];
NSData *pfxdata = [NSData dataWithContentsOfFile:path];
CFDataRef inpfxdata = (CFDataRef)pfxdata;
SecIdentityRef myIdentity;
SecTrustRef myTrust;
OSStatus status = extractIdentityAndTrust(inpfxdata, &myIdentity, &myTrust);
SecCertificateRef myCertificate;
SecIdentityCopyCertificate(myIdentity, &myCertificate);
const void *certs[] = { myCertificate };
CFArrayRef certsArray = CFArrayCreate(NULL, certs, 1, NULL);
NSURLCredential *credential = [NSURLCredential credentialWithIdentity:myIdentity
certificates:(NSArray *)myCertificate
persistence:NSURLCredentialPersistencePermanent];
[challenge.sender useCredential:credential forAuthenticationChallenge:challenge];
CFRelease(myIdentity);
CFRelease(myCertificate);
CFRelease(certsArray);
}
//extractIdentityAndTrust method.
-(OSStatus) extractIdentityAndTrust:(CFDataRef)inpfxdata identity:(SecIdentityRef *)identity trust:(SecTrustRef *)trust
{
OSStatus securityError = errSecSuccess;
CFStringRef password = CFSTR("password");
const void *keys[] = { kSecImportExportPassphrase };
const void *values[] = { password };
CFDictionaryRef options = CFDictionaryCreate(NULL, keys, values, 1, NULL, NULL);
CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);
securityError = SecPKCS12Import(inpfxdata, options, &items);
if (securityError == 0) {
CFDictionaryRef myIdentityAndTrust = CFArrayGetValueAtIndex(items, 0);
const void *tempIdentity = NULL;
tempIdentity = CFDictionaryGetValue(myIdentityAndTrust, kSecImportItemIdentity);
*identity = (SecIdentityRef)tempIdentity;
const void *tempTrust = NULL;
tempTrust = CFDictionaryGetValue(myIdentityAndTrust, kSecImportItemTrust);
*trust = (SecTrustRef)tempTrust;
}
if (options) {
CFRelease(options);
}
return securityError;
}
good luck!^-^

How to make iPhoneHTTPServer secure server

I am very new to iPhone development.
I downloaded the iPhoneHTTPServer application from bellow link.
https://github.com/robbiehanson/CocoaHTTPServer/tree/master/Samples/iPhoneHTTPServer
It works fine for HTTP request.
Now I want to make it as a secure server. (use HTTPS)
for that I have override following two methods in MyHTTPConnection.m
I am sure about changes in this method:
/**
* Overrides HTTPConnection's method
**/
- (BOOL)isSecureServer
{
// Create an HTTPS server (all connections will be secured via SSL/TLS)
return YES;
}
I need to apply changes in bellow method: (Please guide me here.)
PROBLEM : DDKeychain and Cocoa.h is not available for iOS.
/**
* Overrides HTTPConnection's method
*
* This method is expected to returns an array appropriate for use in
* kCFStreamSSLCertificates SSL Settings.
* It should be an array of SecCertificateRefs except for the first element in
* the array, which is a SecIdentityRef.
**/
- (NSArray *)sslIdentityAndCertificates
{
NSArray *result = [DDKeychain SSLIdentityAndCertificates];
if([result count] == 0)
{
[DDKeychain createNewIdentity];
return [DDKeychain SSLIdentityAndCertificates];
}
return result;
}
I have solved issue with following steps:
Export certificate from your Keychain Access(Mac OS X)
Open Keychain Access
Select Certificate, Right click and select Export...
Export Certificate with file format : Personal Information Exchange (.p12)
Provide name and password to export file.
FileName: TestCertificate.p12
Password: test123 (* try your admin login pass if not worked)
Import TestCertificate.p12 in you XCode project.
Add Security.framework in your project.
Import Security.h file in you code.
#import <Security/Security.h>
Override and change sslIdentityAndCertificates method as bellow.
/**
* Overrides HTTPConnection's method
*
* This method is expected to returns an array appropriate for use in kCFStreamSSLCertificates SSL Settings.
* It should be an array of SecCertificateRefs except for the first element in the array, which is a SecIdentityRef.
**/
- (NSArray *)sslIdentityAndCertificates
{
SecIdentityRef identityRef = NULL;
SecCertificateRef certificateRef = NULL;
SecTrustRef trustRef = NULL;
NSString *thePath = [[NSBundle mainBundle] pathForResource:#"TestCertificate" ofType:#"p12"];
NSData *PKCS12Data = [[NSData alloc] initWithContentsOfFile:thePath];
CFDataRef inPKCS12Data = (CFDataRef)PKCS12Data;
CFStringRef password = CFSTR("test123");
const void *keys[] = { kSecImportExportPassphrase };
const void *values[] = { password };
CFDictionaryRef optionsDictionary = CFDictionaryCreate(NULL, keys, values, 1, NULL, NULL);
CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);
OSStatus securityError = errSecSuccess;
securityError = SecPKCS12Import(inPKCS12Data, optionsDictionary, &items);
if (securityError == 0) {
CFDictionaryRef myIdentityAndTrust = CFArrayGetValueAtIndex (items, 0);
const void *tempIdentity = NULL;
tempIdentity = CFDictionaryGetValue (myIdentityAndTrust, kSecImportItemIdentity);
identityRef = (SecIdentityRef)tempIdentity;
const void *tempTrust = NULL;
tempTrust = CFDictionaryGetValue (myIdentityAndTrust, kSecImportItemTrust);
trustRef = (SecTrustRef)tempTrust;
} else {
NSLog(#"Failed with error code %d",(int)securityError);
return nil;
}
SecIdentityCopyCertificate(identityRef, &certificateRef);
NSArray *result = [[NSArray alloc] initWithObjects:(id)identityRef, (id)certificateRef, nil];
return result;
}

iOS 4.3 extractIdentityAndTrust linking error

I'm having a problem using extractIdentityAndTrust in iOS and getting the following linking error. I'm just trying to follow the code from the 'Certificate,Key and trust programming guide' and have a PKCS#12 certificate in the bundle.
"_extractIdentityAndTrust", reference from:
[cryptoViewController viewDidLoad] in cryptoViewController.o
Symbol(s) not found
Collect2: Id returneed 1 exit status
I've the following code in the project;
- (void)viewDidLoad {
[super viewDidLoad];
NSString *thePath = [[NSBundle mainBundle]
pathForResource:#"iphone-cert" ofType:#"p12"];
NSData *PKCS12Data = [[NSData alloc] initWithContentsOfFile:thePath];
CFDataRef inPKCS12Data = (CFDataRef)PKCS12Data;
CFDataRef inPKCS12Data1 = (CFDataRef)PKCS12Data;
OSStatus status = noErr;
SecIdentityRef myIdentity;
SecIdentityRef *outIdentity;
SecTrustRef *outTrust;
SecTrustRef myTrust;
status = extractIdentityAndTrust(
inPKCS12Data1,
&myIdentity,
&myTrust);
if (status != 0)
{
}
SecTrustResultType trustResult;
if (status == noErr)
{
status = SecTrustEvaluate(myTrust, &trustResult);
}
if (trustResult == kSecTrustResultRecoverableTrustFailure)
{
}
OSStatus extractIdentityAndTrust(CFDataRef inPKCS12Data,
SecIdentityRef *outIdentity,
SecTrustRef *outTrust);
OSStatus securityError = errSecSuccess;
CFStringRef password = CFSTR("Password");
const void *keys[] = { kSecImportExportPassphrase };
const void *values[] = { password };
CFDictionaryRef optionsDictionary = CFDictionaryCreate(
NULL, keys,
values, 1,
NULL, NULL);
CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);
CFDataRef inPKCS12Data2 = (CFDataRef)PKCS12Data;
securityError = SecPKCS12Import(inPKCS12Data2,
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);
[PKCS12Data release];
}
//Next part
SecCertificateRef myReturnedCertificate = NULL;
SecIdentityRef myReturnedIdentity;
status = SecIdentityCopyCertificate (myReturnedIdentity,
&myReturnedCertificate);
CFStringRef certSummary = SecCertificateCopySubjectSummary
(myReturnedCertificate);
NSString* summaryString = [[NSString alloc]
initWithString:(NSString*)certSummary]; //
NSLog(#"%#", summaryString);
[summaryString release];
}
and the following declaration in the header file;
OSStatus extractIdentityAndTrust(CFDataRef inPKCS12Data,
SecIdentityRef *outIdentity, SecTrustRef *outTrust);
Has anyone got any advice?
I'm not sure, but it seams that this method is not available on iOS.
Anyway the proper way to get the identity and certificates from a p12 file is:
Use the SecPKCS12Import() function to import the p12 data.
This will return an NSArray containing NSDictionary objects.
The identity is stored within the dictionary under the key 'kSecImportItemIdentity'
An NSArray of certificates is stored under 'kSecImportItemCertChain'
The things get a little bit complicated if you have multiple identities in your p12 file. Then you need to have some logic on how to choose the right one. But for start just get the dictionary at index 0 from the array returned at step 1 ;-)
Regards,
Pece