iOS RSA Decryption - ios

I'm using this website to generate public and private key
http://travistidwell.com/jsencrypt/demo/ and I'm trying to figure it out how to create SecKeyRef from the private key
I've found this project which looks very promising but it doesn't work for me.
Here is my code which try to create the SecKeyRef
NSString* publicKey = #"MIGeMA0GCSqGSIb3DQEBAQUAA4GMADCBiAKBgHKzKc/6vphvntLiP1r/YvxjSLolPeDeOCy48ao5ymwNU2Nqbfeir/qHqbqSAhclAO8TGq8QIpE5ObAKNp2j01pu8Cu9AqwdtZ6EZa/NYahfITKS8iYGs6cHzk2LGw8AqFOEJqHrW/xR8MOS1J765KeZOBCSrWZ5Ag/lpb5jxiDlAgMBAAE=";
[[RSA sharedInstance] setPublicKey:publicKey];
and
- (BOOL)setPublicKey: (NSString *)keyAsBase64 {
NSData *extractedKey =
[[NSData alloc] initWithBase64EncodedString:keyAsBase64 options:0];
/* Load as a key ref */
OSStatus error = noErr;
CFTypeRef persistPeer = NULL;
NSData * refTag = [self.serverPublicIdentifier dataUsingEncoding:NSUTF8StringEncoding];
NSMutableDictionary * keyAttr = [[NSMutableDictionary alloc] init];
[keyAttr setObject:(__bridge id)kSecClassKey forKey:(__bridge id)kSecClass];
[keyAttr setObject:(__bridge id)kSecAttrKeyTypeRSA forKey:(__bridge id)kSecAttrKeyType];
[keyAttr setObject:refTag forKey:(__bridge id)kSecAttrApplicationTag];
/* First we delete any current keys */
error = SecItemDelete((__bridge CFDictionaryRef) keyAttr);
[keyAttr setObject:extractedKey forKey:(__bridge id)kSecValueData];
[keyAttr setObject:[NSNumber numberWithBool:YES] forKey:(__bridge id)kSecReturnPersistentRef];
error = SecItemAdd((__bridge CFDictionaryRef) keyAttr, (CFTypeRef *)&persistPeer);
if (persistPeer == nil || ( error != noErr && error != errSecDuplicateItem)) {
NSLog(#"Problem adding public key to keychain");
return FALSE;
}
CFRelease(persistPeer);
serverPublicRef = nil;
/* Now we extract the real ref */
[keyAttr removeAllObjects];
/*
[keyAttr setObject:(id)persistPeer forKey:(id)kSecValuePersistentRef];
[keyAttr setObject:[NSNumber numberWithBool:YES] forKey:(id)kSecReturnRef];
*/
[keyAttr setObject:(__bridge id)kSecClassKey forKey:(__bridge id)kSecClass];
[keyAttr setObject:refTag forKey:(__bridge id)kSecAttrApplicationTag];
[keyAttr setObject:(__bridge id)kSecAttrKeyTypeRSA forKey:(__bridge id)kSecAttrKeyType];
[keyAttr setObject:[NSNumber numberWithBool:YES] forKey:(__bridge id)kSecReturnRef];
// Get the persistent key reference.
error = SecItemCopyMatching((__bridge CFDictionaryRef)keyAttr, (CFTypeRef *)&serverPublicRef);
if (serverPublicRef == nil || ( error != noErr && error != errSecDuplicateItem)) {
NSLog(#"Error retrieving public key reference from chain");
return FALSE;
}
return TRUE;
}
I'm getting serverPublicRef == nil but the error is 0 (which is ok.)

The kSecClassKey does not have a kSecValueData field. You should look into using SecPKCS12Import().
Some related questions:
How to create private key from file in Objective C?
How to make a valid p12 file to be correctly imported by SecPKCS12Import

Related

How can we update NEVPNProtocol params like serverAddress and username?

My app acts like a VPN client to an IPSec VPN server (strongSwan).
I have used NEVPNManager to setup the VPN profile. The code is something like this:
#define KEY_PASSWORD #"password"
#define PASSWORD #"password"
#define VPN_SERVER #"10.1.1.1"
#define KEY_USERNAME #"username"
#define USERNAME #"myusername"
#define KEY_SHARED_SECRET #"sharedSecret"
#define SHARED_SECRET #"thisisthesecretekey"
#define LOCAL_IDENTIFIER #"myserver.com.client"
#define REMOTE_IDENTIFIER #"myserver.com.server"
-(void) setupVPNProfile {
NEVPNProtocolIKEv2 *protocol = [[NEVPNProtocolIKEv2 alloc] init];
protocol.username = USERNAME;
NSData *passwdRef = [self getData:KEY_PASSWORD];
if (passwdRef == nil) {
[self storeData:KEY_PASSWORD data:[PASSWORD dataUsingEncoding:NSUTF8StringEncoding]];
passwdRef = [self getData:PASSWORD];
NSLog(#"passwdRef: %#", [[NSString alloc] initWithData:passwdRef encoding:NSUTF8StringEncoding]);
}
protocol.passwordReference = passwdRef;
protocol.serverAddress = VPN_SERVER;
protocol.authenticationMethod = NEVPNIKEAuthenticationMethodSharedSecret;
NSData *sharedSecretRef = [self getData:KEY_SHARED_SECRET];
if (sharedSecretRef == nil) {
[self storeData:KEY_SHARED_SECRET data:[SHARED_SECRET dataUsingEncoding:NSUTF8StringEncoding]];
sharedSecretRef = [self getData:KEY_SHARED_SECRET];
NSLog(#"sharedSecretRef: %#", [[NSString alloc] initWithData:sharedSecretRef encoding:NSUTF8StringEncoding]);
}
protocol.sharedSecretReference = sharedSecretRef;
protocol.localIdentifier = LOCAL_IDENTIFIER;
protocol.remoteIdentifier = REMOTE_IDENTIFIER;
protocol.useExtendedAuthentication = YES;
protocol.disconnectOnSleep = NO;
self.manager.protocolConfiguration = protocol;
self.manager.enabled = YES;
}
#pragma mark - Keychain methods
- (void) storeData: (NSString * )key data:(NSData *)data {
NSLog(#"Store Data");
NSMutableDictionary * dict = [[NSMutableDictionary alloc] init];
[dict setObject:(__bridge id)kSecClassGenericPassword forKey:(__bridge id)kSecClass];
NSData *encodedKey = [key dataUsingEncoding:NSUTF8StringEncoding];
[dict setObject:encodedKey forKey:(__bridge id)kSecAttrGeneric];
[dict setObject:encodedKey forKey:(__bridge id)kSecAttrAccount];
[dict setObject:#"VPN" forKey:(__bridge id)kSecAttrService];
[dict setObject:(__bridge id)kSecAttrAccessibleAlwaysThisDeviceOnly forKey:(__bridge id)kSecAttrAccessible];
[dict setObject:data forKey:(__bridge id)kSecValueData];
OSStatus status = SecItemAdd((__bridge CFDictionaryRef)dict, NULL);
if(errSecSuccess != status) {
NSLog(#"Unable add item with key =%# error:%d",key,(int)status);
}
}
- (NSData *) getData: (NSString *)key {
NSMutableDictionary * dict = [[NSMutableDictionary alloc] init];
[dict setObject:(__bridge id)kSecClassGenericPassword forKey:(__bridge id)kSecClass];
NSData *encodedKey = [key dataUsingEncoding:NSUTF8StringEncoding];
[dict setObject:encodedKey forKey:(__bridge id)kSecAttrGeneric];
[dict setObject:encodedKey forKey:(__bridge id)kSecAttrAccount];
[dict setObject:#"VPN" forKey:(__bridge id)kSecAttrService];
[dict setObject:(__bridge id)kSecAttrAccessibleAlwaysThisDeviceOnly forKey:(__bridge id)kSecAttrAccessible];
[dict setObject:(__bridge id)kSecMatchLimitOne forKey:(__bridge id)kSecMatchLimit];
[dict setObject:(id)kCFBooleanTrue forKey:(__bridge id)kSecReturnPersistentRef];
CFTypeRef result = NULL;
OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)dict,&result);
if( status != errSecSuccess) {
NSLog(#"Unable to fetch item for key %# with error:%d",key,(int)status);
return nil;
}
NSData *resultData = (__bridge NSData *)result;
return resultData;
}
- (BOOL) removeData: (NSString *) key {
NSMutableDictionary * dict = [[NSMutableDictionary alloc] init];
[dict setObject:(__bridge id)kSecClassGenericPassword forKey:(__bridge id)kSecClass];
NSData *encodedKey = [key dataUsingEncoding:NSUTF8StringEncoding];
[dict setObject:encodedKey forKey:(__bridge id)kSecAttrGeneric];
[dict setObject:encodedKey forKey:(__bridge id)kSecAttrAccount];
[dict setObject:#"VPN" forKey:(__bridge id)kSecAttrService];
[dict setObject:(__bridge id)kSecAttrAccessibleAlwaysThisDeviceOnly forKey:(__bridge id)kSecAttrAccessible];
OSStatus status = SecItemDelete((__bridge CFDictionaryRef)dict);
if( status != errSecSuccess) {
NSLog(#"Unable to fetch item for key %# with error:%d",key,(int)status);
return NO;
}
return YES;
}
Here values like username, password server-address are stored in MACROS defined above. Per the required design, password and sharedSecret is to be stored as Persistent KeyChain items.
In this setup, how can I gracefully change values like username, password, serverAddress, sharedSecret?
What I have tried so far:
I changed the values like server address, username in the code. I
used storeData functions to overwrite the password and sharedSecret
persistent keychain items. However when I ran the app after making
these changes I wasn't able to connect to new VPN server. Please
note that I didn't make any mistake with the new values I had to
use. I double checked before updating. Please also note that I was
able to connect to VPN server when I created a .mobileconfig file
out of those new params. Its just that my VPN client app wasn't
able to connect to server anymore. It would try connecting and would
disconnect again. Uninstalling the app had no effect as well.
Setting manager.protocolConfiguration = nil. Doing this has no effect. The installed protocolConfiguration stays intact.
I do not want to remove the VPN profile by calling manager removePreferencesWithCompletionHandler:] since the user would see the VPN related pop-up again when he tries to connect to VPN server.
If anyone has done this before please help.
Thanks.
To change VPN account you should be able to simply change the VPN account parameters and then start VPNManager with the new values. You don't need to stop the currently running VPN Session and you certainly don't need to delete the profile, just call NEVPNManager.shared.locadFromPreferences, make the changes, then .saveToPreferences and startVPNTunnel as normal.
Since you're not able to get VPN tunnel to start even with the known good credentials using variables instead of macros, then you are probably passing values incorrectly from the keychain or not what you expect. I would compare every parameter using print statements, first with your current working solution that uses macros then with the non-working keychain code.

Use SecItemUpdate in simulator and get an error -50

Can anybody tell me where is the miastake? I have no idea now.
//Get a query Dic
+ (NSMutableDictionary *)keychainQueryDictionary:(NSString *)service
{
return [NSMutableDictionary dictionaryWithObjectsAndKeys:
(__bridge id)kSecClassGenericPassword,(__bridge id)kSecClass,
service, (__bridge id)kSecAttrService,
service, (__bridge id)kSecAttrAccount,
(__bridge id)kSecAttrAccessibleAfterFirstUnlock,(__bridge id)kSecAttrAccessible,
nil];
}
//Here i save some data to keychain
+ (OSStatus)saveData:(id)data service:(NSString *)serviceIdentify
{
NSMutableDictionary *keychainQuery = [self keychainQueryDictionary:serviceIdentify];
SecItemDelete((__bridge CFDictionaryRef)keychainQuery);
[keychainQuery setObject:[NSKeyedArchiver archivedDataWithRootObject:data] forKey:(__bridge id)kSecValueData];
return SecItemAdd((__bridge CFDictionaryRef)keychainQuery, NULL);
}
//delete opration
+ (OSStatus)deleteData:(NSString *)serviceIdentify
{
NSMutableDictionary *keychainQuery = [self keychainQueryDictionary:serviceIdentify];
return SecItemDelete((__bridge CFDictionaryRef)keychainQuery);
}
//Here i try to update one item in keychian, but i get an error -50, but i have no idea where is the wrong param
+ (OSStatus)updataData:(id)data service:(NSString *)serviceIdentify
{
NSMutableDictionary *keychainQuery = [self keychainQueryDictionary:serviceIdentify];
[keychainQuery setObject:(id)kCFBooleanTrue forKey:(__bridge id)kSecReturnData];
[keychainQuery setObject:(__bridge id)kSecMatchLimitOne forKey:(__bridge id)kSecMatchLimit];
CFDataRef keyData = NULL;
OSStatus status = errSecNotAvailable;
if (SecItemCopyMatching((__bridge CFDictionaryRef)keychainQuery, (CFTypeRef *)&keyData) == noErr) {
NSMutableDictionary *queryDict = nil;
NSDictionary *ret = [NSKeyedUnarchiver unarchiveObjectWithData:(__bridge NSData *)keyData];
queryDict = [NSMutableDictionary dictionaryWithDictionary:ret];
[queryDict setObject:[keychainQuery objectForKey:(__bridge id)kSecClass] forKey:(__bridge id)kSecClass];
NSMutableDictionary *attributesToUpdate = [self keychainQueryDictionary:serviceIdentify];
[attributesToUpdate setObject:[NSKeyedArchiver archivedDataWithRootObject:data] forKey:(__bridge id)kSecValueData];
status = SecItemUpdate((__bridge CFDictionaryRef)queryDict,(__bridge CFDictionaryRef)attributesToUpdate);
}
return status;
}
//read opration
+ (id)loadData:(NSString *)serviceIdentify
{
id ret = nil;
NSMutableDictionary *keychainQuery = [self keychainQueryDictionary:serviceIdentify];
[keychainQuery setObject:(id)kCFBooleanTrue forKey:(__bridge id)kSecReturnData];
[keychainQuery setObject:(__bridge id)kSecMatchLimitOne forKey:(__bridge id)kSecMatchLimit];
CFDataRef keyData = NULL;
if (SecItemCopyMatching((__bridge CFDictionaryRef)keychainQuery, (CFTypeRef *)&keyData) == noErr) {
ret = [NSKeyedUnarchiver unarchiveObjectWithData:(__bridge NSData *)keyData];
}
return ret;
}
-50 is errSecParam. I think you have the parameters flipped for SecItemUpdate. The first param is the search query to find the matching item that needs to be updated. This probably should be created by calling your keychainQueryDictionary method. The second parameter should contain the change - in your example this is the new data for kSecValueData like you are doing, but the dictionary generally doesn't include any of search attributes. In your example it looks like it also contains all the search attributes again which will cause an errSecParam.
As a separate point, also note that any values in the keychainQuery dictionary that are not applicable to the update will also cause errSecParam. If these values are present they can simply be removed, for example:
CFDictionaryRemoveValue(keychainQuery, kSecReturnData);
CFDictionaryRemoveValue(keychainQuery, kSecMatchLimit);

Get the bytes of generated RSA private and public key

I am generating a RSA keypair on the iphone, but how can I store the generated private and public key in a file? I need to encrypt the private key with AES and transmit it to my server, but I can't find any methods to get the bytes of the keys.
My code for generating the keypair:
-(void)generateKeyPair {
OSStatus sanityCheck = noErr;
SecKeyRef localPublicKey = NULL;
SecKeyRef localPrivateKey = NULL;
self.publicTag = [#"com.crap.pub" dataUsingEncoding:NSUTF8StringEncoding];
self.privateTag = [#"com.crap.pri" dataUsingEncoding:NSUTF8StringEncoding];
// Container dictionaries.
NSMutableDictionary * privateKeyAttr = [NSMutableDictionary dictionaryWithCapacity:0];
NSMutableDictionary * publicKeyAttr = [NSMutableDictionary dictionaryWithCapacity:0];
NSMutableDictionary * keyPairAttr = [NSMutableDictionary dictionaryWithCapacity:0];
// Set top level dictionary for the keypair.
[keyPairAttr setObject:(__bridge id)kSecAttrKeyTypeRSA forKey:(__bridge id)kSecAttrKeyType];
[keyPairAttr setObject:[NSNumber numberWithUnsignedInteger:2048] forKey:(__bridge id)kSecAttrKeySizeInBits];
// Set the private key dictionary.
[privateKeyAttr setObject:[NSNumber numberWithBool:YES] forKey:(__bridge id)kSecAttrIsPermanent];
[privateKeyAttr setObject:self.privateTag forKey:(__bridge id)kSecAttrApplicationTag];
// See SecKey.h to set other flag values.
// Set the public key dictionary.
[publicKeyAttr setObject:[NSNumber numberWithBool:YES] forKey:(__bridge id)kSecAttrIsPermanent];
[publicKeyAttr setObject:self.publicTag forKey:(__bridge id)kSecAttrApplicationTag];
// See SecKey.h to set other flag values.
// Set attributes to top level dictionary.
[keyPairAttr setObject:privateKeyAttr forKey:(__bridge id)kSecPrivateKeyAttrs];
[keyPairAttr setObject:publicKeyAttr forKey:(__bridge id)kSecPublicKeyAttrs];
// SecKeyGeneratePair returns the SecKeyRefs just for educational purposes.
sanityCheck = SecKeyGeneratePair((__bridge CFDictionaryRef)keyPairAttr, &localPublicKey, &localPrivateKey);
}

Encrypt a NSString using RSA algorithm

I have a exponent and a modulus. How do i encrypt a NSString using RSA algorithm. I have gone through many forums. But still find it confusing. Can anyone give me the right way to encrypt a NSString using RSA algorithm with the exponent and modulus?
I currently try this. But still get a wrong encrypted string
publicTag = [self PublicKeyItems];
SecKeyRef publicKeyData = [self getPublicKeyRef];
NSString* result = (NSString*)[self encryptRSA:#"Shob" key:publicKeyData];
And the following implementations
- (NSData *)PublicKeyItems
{
NSMutableArray *publicarray = [[NSMutableArray alloc] init];
[publicarray addObject:encryptionExponent];
[publicarray addObject:encryptionModulus];
NSData *testData = [publicarray berData];
NSLog(#"testdata = %#",testData);
return testData;
}
-(SecKeyRef)getPublicKeyRef
{
OSStatus sanityCheck = noErr;
SecKeyRef publicKeyReference = NULL;
if (publicKeyReference == NULL) {
[self generateKeyPair:512];
NSMutableDictionary *queryPublicKey = [[NSMutableDictionary alloc] init];
// Set the public key query dictionary.
[queryPublicKey setObject:(__bridge id)kSecClassKey forKey:(__bridge id)kSecClass];
[queryPublicKey setObject:publicTag forKey:(__bridge id)kSecAttrApplicationTag];
[queryPublicKey setObject:(__bridge id)kSecAttrKeyTypeRSA forKey:(__bridge id)kSecAttrKeyType];
[queryPublicKey setObject:[NSNumber numberWithBool:YES] forKey:(__bridge id)kSecReturnRef];
// Get the key.
sanityCheck = SecItemCopyMatching((__bridge CFDictionaryRef)queryPublicKey, (CFTypeRef *)&publicKeyReference);
if (sanityCheck != noErr)
{
publicKeyReference = NULL;
}
// [queryPublicKey release];
} else { publicKeyReference = publicKey; }
return publicKeyReference;
}
- (void)generateKeyPair:(NSUInteger)keySize {
OSStatus sanityCheck = noErr;
publicKey = NULL;
privateKey = NULL;
// LOGGING_FACILITY1( keySize == 512 || keySize == 1024 || keySize == 2048, #"%d is an invalid and unsupported key size.", keySize );
// First delete current keys.
// [self deleteAsymmetricKeys];
// Container dictionaries.
NSMutableDictionary * privateKeyAttr = [[NSMutableDictionary alloc] init];
NSMutableDictionary * publicKeyAttr = [[NSMutableDictionary alloc] init];
NSMutableDictionary * keyPairAttr = [[NSMutableDictionary alloc] init];
// Set top level dictionary for the keypair.
[keyPairAttr setObject:(__bridge id)kSecAttrKeyTypeRSA forKey:(__bridge id)kSecAttrKeyType];
[keyPairAttr setObject:[NSNumber numberWithUnsignedInteger:keySize] forKey:(__bridge id)kSecAttrKeySizeInBits];
// Set the public key dictionary.
[publicKeyAttr setObject:[NSNumber numberWithBool:YES] forKey:(__bridge id)kSecAttrIsPermanent];
[publicKeyAttr setObject:publicTag forKey:(__bridge id)kSecAttrApplicationTag];
// See SecKey.h to set other flag values.
// Set attributes to top level dictionary.
[keyPairAttr setObject:publicKeyAttr forKey:(__bridge id)kSecPublicKeyAttrs];
// SecKeyGeneratePair returns the SecKeyRefs just for educational purposes.
sanityCheck = SecKeyGeneratePair((__bridge CFDictionaryRef)keyPairAttr, &publicKey, &privateKey);
// LOGGING_FACILITY( sanityCheck == noErr && publicKey != NULL && privateKey != NULL, #"Something really bad went wrong with generating the key pair." );
if(sanityCheck == noErr && publicKey != NULL && privateKey != NULL)
{
NSLog(#"Successful");
}
// [privateKeyAttr release];
// [publicKeyAttr release];
// [keyPairAttr release];
}
-(NSString *)encryptRSA:(NSString *)plainTextString key:(SecKeyRef)publicKeyNext {
size_t cipherBufferSize = SecKeyGetBlockSize(publicKeyNext);
uint8_t *cipherBuffer = malloc(cipherBufferSize);
uint8_t *nonce = (uint8_t *)[plainTextString UTF8String];
SecKeyEncrypt(publicKeyNext,
kSecPaddingOAEP,
nonce,
strlen( (char*)nonce ),
&cipherBuffer[0],
&cipherBufferSize);
NSData *encryptedData = [NSData dataWithBytes:cipherBuffer length:cipherBufferSize];
NSString* encryptedString = [NSString stringWithFormat:#"%#",encryptedData];
return encryptedString;
}
The easy way
Use Chilkat iOS RSA Library. One major downside: cost $189! :O
The hard way
Parse the XML, use SCZ-BasicEncodingRules-iOS to generate a public key data out of the modulus and exponent. If that works, create a dummy keychain using that public key (follow sample code here), extract the public key now in SecKeyRef format and pass it to the encryptRSA method in the question. Finally, cleanup, remove the dummy keychain. Sounds good in theory, but I have never tested this thoroughly, if you do, let me know!

iOS SecKeyRef to NSData

I have two keys, public and private, that are both stored in SecKeyRef-variables. For simplicity's sake, let's start with the public one. What I wanna do is export it to an NSData object. For that, there is an almost famous code snippet provide by Apple, which is here:
- (NSData *)getPublicKeyBits {
OSStatus sanityCheck = noErr;
NSData * publicKeyBits = nil;
NSMutableDictionary * queryPublicKey = [[NSMutableDictionary alloc] init];
// Set the public key query dictionary.
[queryPublicKey setObject:(id)kSecClassKey forKey:(id)kSecClass];
[queryPublicKey setObject:publicTag forKey:(id)kSecAttrApplicationTag];
[queryPublicKey setObject:(id)kSecAttrKeyTypeRSA forKey:(id)kSecAttrKeyType];
[queryPublicKey setObject:[NSNumber numberWithBool:YES] forKey:(id)kSecReturnData];
// Get the key bits.
sanityCheck = SecItemCopyMatching((CFDictionaryRef)queryPublicKey, (CFTypeRef *)&publicKeyBits);
if (sanityCheck != noErr)
{
publicKeyBits = nil;
}
[queryPublicKey release];
return publicKeyBits;
}
I have Xcode 4.6.2, however, and the code appears erroneous ("__bridge" is added before each conversion to id). The new version looks like this:
- (NSData *)getPublicKeyBitsFromKey:(SecKeyRef)givenKey {
OSStatus sanityCheck = noErr;
NSData * publicKeyBits = nil;
NSMutableDictionary * queryPublicKey = [[NSMutableDictionary alloc] init];
// Set the public key query dictionary.
[queryPublicKey setObject:(__bridge id)kSecClassKey forKey:(__bridge id)kSecClass];
[queryPublicKey setObject:publicTag forKey:(__bridge id)kSecAttrApplicationTag];
[queryPublicKey setObject:(__bridge id)kSecAttrKeyTypeRSA forKey:(__bridge id)kSecAttrKeyType];
[queryPublicKey setObject:[NSNumber numberWithBool:YES] forKey:(__bridge id)kSecReturnData];
// Get the key bits.
sanityCheck = SecItemCopyMatching((__bridge CFDictionaryRef)queryPublicKey, (CFTypeRef *)&publicKeyBits);
if (sanityCheck != noErr)
{
publicKeyBits = nil;
}
return publicKeyBits;
}
There are still two errors, though:
use of undeclared identifier 'publicTag'
Cast of an indirect pointer to an Objective-C pointer to 'CFTypeRef ' (aka 'const void *') is disallowed with ARC
Now, I hope that after your help, the first issue will no longer be a problem, for I do not want to build a query or whatnot to extract the key from the keychain. I have it in a variable and I wish to extract it from there. The variable's name is givenPublicKey, and that's the key I wish to convert to NSData.
So, how would I go about doing this and solving this ARC-issue?
Follow-up: How can I export a private key to NSData, since I've read several time that the function I'm trying to work with only works for public keys.
use of undeclared identifier 'publicTag'
The publicTag is just some unique identifier added to the Keychain items. In the CryptoExercise sample project it is defined as
#define kPublicKeyTag "com.apple.sample.publickey"
static const uint8_t publicKeyIdentifier[] = kPublicKeyTag;
NSData *publicTag = [[NSData alloc] initWithBytes:publicKeyIdentifier length:sizeof(publicKeyIdentifier)];
Cast of an indirect pointer to an Objective-C pointer to 'CFTypeRef ' (aka 'const void *') is disallowed with ARC
This can be solved by using a temporary CFTypeRef variable:
CFTypeRef result;
sanityCheck = SecItemCopyMatching((__bridge CFDictionaryRef)queryPublicKey, &result);
if (sanityCheck == errSecSuccess) {
publicKeyBits = CFBridgingRelease(result);
}
I do not want to build a query or whatnot to extract the key from the keychain. I have it in a variable and I wish to extract it from there ...
As far as I know, you have to store the SecKeyRef to the Keychain temporarily. SecItemAdd
has the option to return the added item as data. From the documentation:
To obtain the data of the added item as an object of type CFDataRef,
specify the return type key kSecReturnData with a value of
kCFBooleanTrue.
Putting all that together, the following code should do what you want:
- (NSData *)getPublicKeyBitsFromKey:(SecKeyRef)givenKey {
static const uint8_t publicKeyIdentifier[] = "com.your.company.publickey";
NSData *publicTag = [[NSData alloc] initWithBytes:publicKeyIdentifier length:sizeof(publicKeyIdentifier)];
OSStatus sanityCheck = noErr;
NSData * publicKeyBits = nil;
NSMutableDictionary * queryPublicKey = [[NSMutableDictionary alloc] init];
[queryPublicKey setObject:(__bridge id)kSecClassKey forKey:(__bridge id)kSecClass];
[queryPublicKey setObject:publicTag forKey:(__bridge id)kSecAttrApplicationTag];
[queryPublicKey setObject:(__bridge id)kSecAttrKeyTypeRSA forKey:(__bridge id)kSecAttrKeyType];
// Temporarily add key to the Keychain, return as data:
NSMutableDictionary * attributes = [queryPublicKey mutableCopy];
[attributes setObject:(__bridge id)givenKey forKey:(__bridge id)kSecValueRef];
[attributes setObject:#YES forKey:(__bridge id)kSecReturnData];
CFTypeRef result;
sanityCheck = SecItemAdd((__bridge CFDictionaryRef) attributes, &result);
if (sanityCheck == errSecSuccess) {
publicKeyBits = CFBridgingRelease(result);
// Remove from Keychain again:
(void)SecItemDelete((__bridge CFDictionaryRef) queryPublicKey);
}
return publicKeyBits;
}
I hope that this works, I cannot test it at the moment.
Follow-up: How can I export a private key to NSData, since I've read several time that the function I'm trying to work with only works for public keys.
I don't know.

Resources