Search code examples
iphoneobjective-ciosbouncycastle

Using . bks keystores certificate in iPhone app


I am building an iPhone app that needs .bks keystores to authenticate. I did not find anything about this information for iOS apps .

I would like to know if apple permits the use of keystores in their apps and how to get started on iOS. The certificates are created using BouncyCastle. I DID find information about it for android but for iOS i had no luck. Any help will be appreciated.


Solution

  • You could export the certificate(s) you need from the keystore like this

    keytool -exportcert -keystore <keystore> -file some.cer
    

    You may need to tell keytool about the store type and store provider, look here.

    You can read that .cer file into the iOS keychain with this code:

    - (void) importCertToKeyChain: (NSData *) data
    {
        // Delete the old certificate, otherwise SecItemAdd complains.
        OSStatus oss = SecItemDelete((__bridge CFDictionaryRef)([self clientCertificateQuery]));
    
        // Import the certificate
        SecCertificateRef certRef = NULL;
        certRef = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)(data));
    
        NSDictionary *att = [NSDictionary dictionaryWithObjectsAndKeys: (__bridge id)(kSecClassCertificate), kSecClass, (__bridge id) certRef, kSecValueRef, nil];
    
        oss = SecItemAdd((__bridge CFDictionaryRef)(att), NULL);
    }
    

    When you need the certificate you can get from the keychain like this:

    - (SecCertificateRef) getCertFromKeyChain
    {
        CFTypeRef ref = NULL;
        SecItemCopyMatching((__bridge CFDictionaryRef)([self clientCertificateQuery]), &ref);
    
        return (SecCertificateRef) ref;
    }
    

    The clientCertificateQuery looks like this.

    static NSString *clientCertSubject = @"TestSubjectClient";
    
    -(NSMutableDictionary *) clientCertificateQuery
    {
        NSMutableDictionary *query = [[NSMutableDictionary alloc] init];
        [query setObject:(__bridge id) kSecClassCertificate forKey:(__bridge id)kSecClass];
        [query setObject:clientCertSubject forKey:(__bridge id<NSCopying>)(kSecMatchSubjectContains)];
        [query setObject:[NSNumber numberWithBool:YES] forKey:(__bridge id)kSecReturnRef];
     id)kSecAttrKeyType];
        return query;
    }
    

    There is also a function to read a PCKS12 store (you would still need to transform your BKS store into that format). It's called SecPKCS12Import and with it you won't need to import the cert into your iOS keychain. I had no luck with it and needed the cert in the keychain anyway, but here is something about this.

    Update:

    As camdaochemgio pointed out in the comments using above method is not recommend when including a certificate that contains secret information (like private keys) in an app. Because the .cer file is not protected and can easily be extracted from the .ipa.

    PKCS#P12 supports password protection so it's better to use this.

    You can covert your keystore to PKCS#P12 like this (taken from here):

     keytool -importkeystore -srckeystore KEYSTORE.jks -destkeystore KEYSTORE.p12 -srcstoretype BKS -deststoretype PKCS12 -srcstorepass mysecret -deststorepass mysecret -srcalias myalias -destalias myalias -srckeypass mykeypass -destkeypass mykeypass -noprompt
    

    Then you can load the .p12 file like this (credits go here)

    // Load Certificate
    NSString *path = [[NSBundle mainBundle] pathForResource:@"cert" ofType:@"p12"];
    NSData *p12data = [NSData dataWithContentsOfFile:path];
    CFDataRef inP12data = (__bridge CFDataRef)p12data;
    
    // Only password based PKCS#12 blobs are supported
    CFStringRef password = CFSTR("Password");
    const void *keys[] = { kSecImportExportPassphrase };
    const void *values[] = { password };
    CFDictionaryRef options = CFDictionaryCreate(NULL, keys, values, 1, NULL, NULL);
    
    // The import
    CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);
    OSStatus securityError = SecPKCS12Import(inP12data, options, &items);
    
    if (securityError == 0)
    {
        // Exploring the content
        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);
    }
    

    Last but not least some links regarding this topic: