So now my code is:
privateKey:(NSString *)pemKey
settings:(NSDictionary *)settings {
RCTLogWarn(@"createIdentity: Starting identity creation");
SecIdentityRef identity = NULL;
// Strip PEM headers and convert to data
NSString *cleanedCert = [self stripPEMHeader:pemCert prefix:@"CERTIFICATE"];
NSString *cleanedPrivateKey = [self stripPEMHeader:pemKey prefix:@"PRIVATE KEY"];
NSString *certAlias = settings[@"certAlias"];
NSString *keyAlias= settings[@"keyAlias"];
NSData *certData = [[NSData alloc] initWithBase64EncodedString:cleanedCert
options:NSDataBase64DecodingIgnoreUnknownCharacters];
NSData *pkcs8KeyData = [[NSData alloc] initWithBase64EncodedString:cleanedPrivateKey
options:NSDataBase64DecodingIgnoreUnknownCharacters];
if (!certData || !pkcs8KeyData) {
RCTLogWarn(@"createIdentity: Failed to create data from base64");
return NULL;
}
// Creates a certificate object from its DER representation
SecCertificateRef cert = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certData);
if (!cert) {
RCTLogWarn(@"createIdentity: Failed to create certificate from data");
return NULL;
}
// Extract RSA key from PKCS#8 - TODO use a ASN1 decoder to detect the key format ...
// For my own use I know it's a pem but I prefer not to trust a file extension and
// it's better to check from asn1 data
NSData *rsaKeyData = [self extractRSAKeyFromPKCS8:pkcs8KeyData];
if (!rsaKeyData) {
RCTLogWarn(@"Failed to extract RSA key from PKCS#8");
CFRelease(cert);
return NULL;
}
NSDictionary *privateKeyAttributes = @{
//(__bridge id)kSecClass: (__bridge id)kSecClassKey,
(__bridge id)kSecAttrKeyType: (__bridge id)kSecAttrKeyTypeRSA,
(__bridge id)kSecAttrKeyClass: (__bridge id)kSecAttrKeyClassPrivate,
//(__bridge id)kSecAttrLabel: kKeychainPrivateKeyLabel,
//(__bridge id)kSecAttrApplicationTag: [kKeychainApplicationTag dataUsingEncoding:NSUTF8StringEncoding],
};
CFErrorRef error = NULL;
SecKeyRef privateKey = SecKeyCreateWithData((__bridge CFDataRef)rsaKeyData,
(__bridge CFDictionaryRef)privateKeyAttributes,
&error);
if (!privateKey) {
RCTLogWarn(@"createIdentity: Failed to create private key: %@", error);
CFRelease(cert);
if (error) CFRelease(error);
return NULL;
}
NSDictionary *deleteKeyQuery = @{
(__bridge id)kSecClass: (__bridge id)kSecClassKey,
(__bridge id)kSecAttrLabel: keyAlias
};
SecItemDelete((__bridge CFDictionaryRef)deleteKeyQuery);
// Import certificate in keychain
NSDictionary *deleteCertQuery = @{
(__bridge id)kSecClass: (__bridge id)kSecClassCertificate,
(__bridge id)kSecAttrLabel: certAlias
};
SecItemDelete((__bridge CFDictionaryRef)deleteCertQuery);
NSDictionary *certAttributes = @{
(__bridge id)kSecClass: (__bridge id)kSecClassCertificate,
(__bridge id)kSecValueRef: (__bridge id)cert,
(__bridge id)kSecAttrLabel: certAlias
};
OSStatus status = SecItemAdd((__bridge CFDictionaryRef)certAttributes, NULL);
if (status != errSecSuccess && status != errSecDuplicateItem) {
RCTLogWarn(@"createIdentity: Failed to store certificate, status: %d", (int)status);
CFRelease(cert);
CFRelease(privateKey);
return NULL;
}
// Add the private key to keychain
NSDictionary *keyAttributes = @{
(__bridge id)kSecClass: (__bridge id)kSecClassKey,
(__bridge id)kSecAttrKeyType: (__bridge id)kSecAttrKeyTypeRSA,
(__bridge id)kSecAttrKeyClass: (__bridge id)kSecAttrKeyClassPrivate,
//(__bridge id)kSecAttrAccessible: (__bridge id)kSecAttrAccessibleAfterFirstUnlock,
(__bridge id)kSecAttrLabel: keyAlias,
//(__bridge id)kSecAttrApplicationTag: [kKeychainApplicationTag dataUsingEncoding:NSUTF8StringEncoding],
};
status = SecItemAdd((__bridge CFDictionaryRef)keyAttributes, NULL);
if (status != errSecSuccess && status != errSecDuplicateItem) {
RCTLogWarn(@"createIdentity: Failed to store private key, status: %d", (int)status);
CFRelease(cert);
CFRelease(privateKey);
return NULL;
}
//------ PRIVATE API: need to find the proper way of doing it -------
if (YES) {
identity = SecIdentityCreate(NULL, cert, privateKey);
} else {
NSDictionary *identityQuery = @{
(__bridge id)kSecClass: (__bridge id)kSecClassIdentity,
(__bridge id)kSecReturnRef: @YES,
(__bridge id)kSecMatchItemList:@[(__bridge id)cert],
(__bridge id)kSecMatchLimit: (__bridge id)kSecMatchLimitOne,
//(__bridge id)kSecAttrLabel: @"My Certificate",
//(__bridge id)kSecUseDataProtectionKeychain: @YES
};
status = SecItemCopyMatching((__bridge CFDictionaryRef)identityQuery, (CFTypeRef *)&identity);
if (status != errSecSuccess || !identity) {
RCTLogWarn(@"createIdentity: Failed to find identity, status: %d", (int)status);
} else {
RCTLogWarn(@"createIdentity: Successfully found identity");
}
}
// Clean up
CFRelease(cert);
CFRelease(privateKey);
return identity;
}
One last effort and it should be ok ...