iOS KeyChain secItemAdd crash - ios

I'm trying to work with KeyChain and without a wrapper. But my code crashes when I want to read the value.
Code:
CFDictionaryRef attributes = CFDictionaryCreate(NULL, keys, values, 5, NULL, NULL);
CFDataRef result;
OSStatus status = SecItemAdd(attributes, (CFTypeRef *)&result);
if (status == errSecSuccess) {
if (result && CFGetTypeID(result) == CFDataGetTypeID()) { //crashes here
NSLog(#"Data");
}
isSuccess = YES;
} else {
fprintf(stderr, "Error while inserting into keychain osstatus:%ld\n", status);
}
Error: EXC_BAD_ACCESS
What am I doing wrong? I thought SecItemAdd can return the newly add item
Edit:
const void *keys[] = {
kSecClass
, kSecAttrAccessible
, kSecAttrService
, kSecAttrAccount
, kSecValueData
};
const void *values[] = {
kSecClassGenericPassword
, kSecAttrAccessibleWhenUnlocked
, (__bridge CFStringRef)service
, (__bridge CFStringRef)account
, data //CFDataRef
};

From 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.

The answer to why you're getting EXC_BAD_ACCESS, is because the dictionary that you pass into SecItemAdd has to be mutable.
Try something like this:
CFMutableDictionaryRef attributes = CFDictionaryCreateMutable(NULL, size,
&kCFCopyStringDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
CFDictionaryAddValue(attributes, keys[x], values[x]);

Related

Storing RSA public key into iOS keychain using kSecAttrAccessible

So I know that I can store a RSA key into the keychain using my following code:
+ (void)savePublicKeyToKeychain:(NSData *)key tag:(NSString *)tagString deleteExisting:(BOOL)deleteExisting {
NSData *tag = [SecKeyWrapper getKeyTag:tagString];
NSDictionary *saveDict = #{
(__bridge id) kSecClass : (__bridge id) kSecClassKey,
(__bridge id) kSecAttrKeyType : (__bridge id) kSecAttrKeyTypeRSA,
(__bridge id) kSecAttrApplicationTag : tag,
(__bridge id) kSecAttrKeyClass : (__bridge id) kSecAttrKeyClassPublic,
(__bridge id) kSecValueData : key
};
[self saveKeyToKeychain:saveDict tag:tagString deleteExisting:deleteExisting];
}
+ (void)saveKeyToKeychain:(NSDictionary *)saveDict tag:(NSString *)tagString deleteExisting:(BOOL)deleteExisting {
OSStatus sanityCheck = SecItemAdd((__bridge CFDictionaryRef) saveDict, NULL);
if (sanityCheck != errSecSuccess) {
if (sanityCheck == errSecDuplicateItem && deleteExisting) {
// delete the duplicate and save again
SecItemDelete((__bridge CFDictionaryRef) saveDict);
sanityCheck = SecItemAdd((__bridge CFDictionaryRef) saveDict, NULL);
}
if (sanityCheck != errSecSuccess) {
NSLog(#"Problem saving the key to keychain, OSStatus == %d.", (int) sanityCheck);
}
}
// remove from cache
[keyCache removeObjectForKey:tagString];
}
This I can save and retrieve correctly. If I try to set the kSecAttrAccessible value on save:
+ (void)savePublicKeyToKeychain:(NSData *)key tag:(NSString *)tagString deleteExisting:(BOOL)deleteExisting {
NSData *tag = [SecKeyWrapper getKeyTag:tagString];
NSDictionary *saveDict = #{
(__bridge id) kSecClass : (__bridge id) kSecClassKey,
(__bridge id) kSecAttrKeyType : (__bridge id) kSecAttrKeyTypeRSA,
(__bridge id) kSecAttrApplicationTag : tag,
(__bridge id) kSecAttrKeyClass : (__bridge id) kSecAttrKeyClassPublic,
(__bridge id) kSecAttrAccessible: (__bridge id) kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly,
(__bridge id) kSecValueData : key
};
[self saveKeyToKeychain:saveDict tag:tagString deleteExisting:deleteExisting];
}
and then trying to retrieve, I get junk. It's junk because when I retrieve it immediately after I save, the inserted value and the retrieved value are different.
Anyone have a code sample or know how to set the accessibility of the keychain item, specifically for an RSA key?
For public knowledge, it turns out that the attributes used to store the data to the keychain needs to be EXACTLY what is used to retrieve the data from the keychain. If you're missing one of the attributes, even though it seems like an attribute needed only for storing (like kSecAttrAccessible), you'll get the wrong data. You won't even get a errSecItemNotFound. It returns garbage.

SecItemAdd returning status 0 while leaving persistent_ref empty

When trying to save an identity to my keychain, I use the following code. It only works sometimes, but the majority of the times, no persistent reference is returned.
I use the following apple provided function:
CFDataRef persistentRefForIdentity(SecIdentityRef identity)
{
CFTypeRef persistent_ref = NULL;
const void *keys[] = { kSecReturnPersistentRef, kSecValueRef };
const void *values[] = { kCFBooleanTrue, identity };
CFDictionaryRef dict = CFDictionaryCreate(NULL, keys, values, 2, NULL, NULL);
OSStatus s = SecItemAdd(dict, &persistent_ref);
if (dict)
CFRelease(dict);
return (CFDataRef)persistent_ref;
}
The code returned by SecItemAdd is 0, yet persistent_ref stays NULL, or 0x0. The weirdest part of this all is that it sometimes does save the identity. What's going on?

sign data using rsa private key

i know is christmas but i have a huge problem that i need to solve, and i'm looking for my christmas miracle here...
I have read apples documentation, and there were only guides how to create RSA public and private keys from certificates. In my case, i have only RSA private key in .pem file. So my question is his: how should i sign data, using that key? I dont want to use openssl. i have tried it with no luck, and i think it's possible to sign data with RSA, by using apples API's.
This is how my key looks like:
-----BEGIN RSA PRIVATE KEY-----
..............................
-----END RSA PRIVATE KEY-----
This is what i have done so far:
-(NSString *)signing:(NSString *)dataString {
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"PrestaMobilekey" ofType:#"pem"];
NSData *data = [[NSData alloc]initWithContentsOfFile:filePath];
SecKeyRef privateKey = (__bridge SecKeyRef)(data);
uint8_t *signedHashBytes = NULL;
// calculate private key size
size_t signedHashBytesSize = SecKeyGetBlockSize(privateKey);
// create space to put signature
signedHashBytes = (uint8_t *)malloc(signedHashBytesSize * sizeof(uint8_t));
memset((void *)signedHashBytes, 0x0, signedHashBytesSize);
OSStatus status = NULL;
// sign data
status = SecKeyRawSign(privateKey,
kSecPaddingPKCS1SHA1,
[[[dataString dataUsingEncoding:NSUTF8StringEncoding] SHA1] bytes],
CC_SHA1_DIGEST_LENGTH,
signedHashBytes,
&signedHashBytesSize);
if (privateKey) {
CFRelease(privateKey);
}
// get signature hash
NSData *signedHash = [NSData dataWithBytes:(const void *)signedHashBytes length:(NSUInteger)signedHashBytesSize];
// release created space
if (signedHashBytes) {
free(signedHashBytes);
}
if (status != errSecSuccess) {
return #"";
}
// return Base64 encoded signature string
return [Base64 encode:signedHash];
}
I really hope that someone will help me, with some good information and answer.
Thank you.
You don't need to use OpenSSL. You can sign your data using your method with a few tweaks. I don't think you can simply bridge and cast an NSData object to a SecKeyRef. You most likely need to save it to the keychain first.
You can do so with this method:
- (SecKeyRef)saveKeyToKeychain:(NSData *)key keySize:(NSUInteger)keySize private:(BOOL)isPrivate {
OSStatus sanityCheck = noErr;
NSData *tag;
id keyClass;
if (isPrivate) {
tag = privateTag;
keyClass = (__bridge id) kSecAttrKeyClassPrivate;
}
else {
tag = publicTag;
keyClass = (__bridge id) kSecAttrKeyClassPublic;
}
NSDictionary *saveDict = #{
(__bridge id) kSecClass : (__bridge id) kSecClassKey,
(__bridge id) kSecAttrKeyType : (__bridge id) kSecAttrKeyTypeRSA,
(__bridge id) kSecAttrApplicationTag : tag,
(__bridge id) kSecAttrKeyClass : keyClass,
(__bridge id) kSecValueData : key,
(__bridge id) kSecAttrKeySizeInBits : [NSNumber numberWithUnsignedInteger:keySize],
(__bridge id) kSecAttrEffectiveKeySize : [NSNumber numberWithUnsignedInteger:keySize],
(__bridge id) kSecAttrCanDerive : (__bridge id) kCFBooleanFalse,
(__bridge id) kSecAttrCanEncrypt : (__bridge id) kCFBooleanTrue,
(__bridge id) kSecAttrCanDecrypt : (__bridge id) kCFBooleanFalse,
(__bridge id) kSecAttrCanVerify : (__bridge id) kCFBooleanTrue,
(__bridge id) kSecAttrCanSign : (__bridge id) kCFBooleanFalse,
(__bridge id) kSecAttrCanWrap : (__bridge id) kCFBooleanTrue,
(__bridge id) kSecAttrCanUnwrap : (__bridge id) kCFBooleanFalse
};
SecKeyRef savedKey = NULL;
sanityCheck = SecItemAdd((__bridge CFDictionaryRef) saveDict, (CFTypeRef *)&savedKey);
if (sanityCheck != errSecSuccess) {
LOGGING_FACILITY1(sanityCheck != noErr, #"Problem saving the key to keychain, OSStatus == %d.", sanityCheck);
}
return savedKey;
}
If you don't want to get the reference immediately, you can change the method type to void and remove the return statement. Change (CFTypeRef *)&savedKey to NULL.
You can then retrieve the saved key like so:
- (SecKeyRef)getKeyRef:(BOOL)isPrivate {
OSStatus sanityCheck = noErr;
NSData *tag;
id keyClass;
if (isPrivate) {
if (privateKeyRef != NULL) {
// already exists in memory, return
return privateKeyRef;
}
tag = privateTag;
keyClass = (__bridge id) kSecAttrKeyClassPrivate;
}
else {
if (publicKeyRef != NULL) {
// already exists in memory, return
return publicKeyRef;
}
tag = publicTag;
keyClass = (__bridge id) kSecAttrKeyClassPublic;
}
NSDictionary *queryDict = #{
(__bridge id) kSecClass : (__bridge id) kSecClassKey,
(__bridge id) kSecAttrKeyType : (__bridge id) kSecAttrKeyTypeRSA,
(__bridge id) kSecAttrApplicationTag : tag,
(__bridge id) kSecAttrKeyClass : keyClass,
(__bridge id) kSecReturnRef : (__bridge id) kCFBooleanTrue
};
SecKeyRef keyReference = NULL;
sanityCheck = SecItemCopyMatching((__bridge CFDictionaryRef) queryDict, (CFTypeRef *) &keyReference);
if (sanityCheck != errSecSuccess) {
NSLog(#"Error trying to retrieve key from server. isPrivate: %d. sanityCheck: %li", isPrivate, sanityCheck);
}
if (isPrivate) {
privateKeyRef = keyReference;
}
else {
publicKeyRef = keyReference;
}
return keyReference;
}
Also, an easier way to return a base64 encoded string is to do this:
NSString *signatureString = [signedHash base64EncodedStringWithOptions:nil];
About privateTag and publicTag
privateTag and publicTag are used to mark the kSecAttrApplicationTag which defines the application that uses this key. You want to have a separate privateTag and publicTag to differentiate between your private key and public key.
It's a bit convoluted because I followed the sample code, but I defined my privateTag and publicTag this way:
SecKeyWrapper.h
#define kPublicKeyTag "com.sample.app.publickey"
#define kPrivateKeyTag "com.sample.app.privatekey"
SecKeyWrapper.m
// just under #implementation or #synthesize lines
static const uint8_t publicKeyIdentifier[] = kPublicKeyTag;
static const uint8_t privateKeyIdentifier[] = kPrivateKeyTag;
- (id)init {
if (self = [super init]) {
// Tag data to search for keys.
privateTag = [[NSData alloc] initWithBytes:privateKeyIdentifier length:sizeof(privateKeyIdentifier)];
publicTag = [[NSData alloc] initWithBytes:publicKeyIdentifier length:sizeof(publicKeyIdentifier)];
}
return self;
}
Then use the privateTag and publicTag as you would in the code samples I provided above.
Ok, o found a solution for this problem my self. I hope that this will help to others... this is what helped me. I thought that i could do this without openssl, but i was wrong. But still, by doing like in that post, you won't need an extra library in you project. Use terminal

Accessing certificates pushed by MDM

I wanted to know is there a way to access certificate pushed by MDM server through your app?
A few years later - is there still no way to access the MDM issued X.509 certificates?
I'm using this code, but getting zero results. And as far as google helps, there is also no way to see if there is any cert at all?
CFTypeRef certificateRef = NULL; // 1
const char *certLabelString = "XenMobile MDM";
CFStringRef certLabel = CFStringCreateWithCString(
NULL, certLabelString,
kCFStringEncodingUTF8);
const void *keys[] = { kSecClass, kSecAttrLabel, kSecReturnRef };
const void *values[] = { kSecClassCertificate, certLabel, kCFBooleanTrue };
CFDictionaryRef dict = CFDictionaryCreate(NULL, keys,
values, 3,
NULL, NULL);
status = SecItemCopyMatching(dict, &certificateRef);
if (status == errSecItemNotFound) {
_UILabelINFO.text = #"error The item cannot be found (errSecItemNotFound) :";
_UILabelINFO.text = [_UILabelINFO.text stringByAppendingString:(__bridge NSString *)(certLabel)];
} else {
_UILabelINFO.text = #"retrieved keychain reference";
}

SecKeyGeneratePair returns errSecUnimplemented

Im attempting to implement an RSA encryption algorithm into my iOS app, but when I attempt to generate a public and private key pair, the function returns me the errSecUnimplemented error. I am using the 5.1 SDK and targeting 5.1 at the moment.
Can I not use this function, or did I set up something wrong in attempting to generate the pair?
Here is my code for the key generation:
SecKeyRef publicKey, privateKey;
CFDictionaryRef parameters;
const void* keys[] = {kSecAttrKeyType, kSecAttrKeyTypeRSA};
int keySize = 1024;
const void *values[] = {kSecAttrKeySizeInBits, &keySize};
parameters = CFDictionaryCreate(kCFAllocatorDefault, keys, values, 2, NULL, NULL);
OSStatus ret = SecKeyGeneratePair(parameters, &publicKey, &privateKey);
if ( ret == errSecSuccess )
{
NSLog(#"Key success!");
}
else
{
NSLog(#"Key Failure! %li", ret);
}
I've revised it to just complete the solution for you. 1) You need to use a CFNumberRef and not a pointer to an int for the numerical value. 2) The values need to be the values, the keys need to be the keys - you were mixing a key and value in each of "keys" and "values".
SInt32 iKeySize = 1024;
CFNumberRef keySize = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &iKeySize);
const void* values[] = { kSecAttrKeyTypeRSA, keySize };
const void* keys[] = { kSecAttrKeyType, kSecAttrKeySizeInBits };
CFDictionaryRef parameters = CFDictionaryCreate(kCFAllocatorDefault, keys, values, 2, NULL, NULL);
SecKeyRef publicKey, privateKey;
OSStatus ret = SecKeyGeneratePair(parameters, &publicKey, &privateKey);
if ( ret == errSecSuccess )
NSLog(#"Key success!");
else
NSLog(#"Key Failure! %li", ret);
Shouldn't this be:
const void* keys[] = {kSecAttrKeyType, kSecAttrKeySizeInBits};
int keySize = 1024;
const void *values[] = {kSecAttrKeyTypeRSA, &keySize};
i.e., the keys should be the keys of the dict and the values the values, currently you have one (key,value) pair in keys and one in values.
Using kCFAllocatorSystemDefault instead of kCFAllocatorDefault return errSecSuccess.

Resources