SecItemAdd on OS X not working, on iOS is - ios

I have a problem.
I have working Code for iOS to add a private Key to the Keychain by SecItemAdd. It works without any error.
On OS X with the same attributes and values, it does not work.
Any ideas, whats the problem. Here is the part of the Code:
NSData * keyData = ...
NSString * name = #"TestKey"
NSString * keyID = #"TestKey"
const id keys[] = {
(__bridge id)(kSecClass),
(__bridge id)(kSecAttrKeyClass),
(__bridge id)(kSecAttrLabel),
(__bridge id)(kSecAttrApplicationLabel),
(__bridge id)(kSecAttrIsPermanent),
(__bridge id)(kSecAttrAccessible),
(__bridge id)(kSecValueData) };
const id values[] = {
(__bridge id)(kSecClassKey),
(__bridge id)(kSecAttrKeyClassPrivate),
name,
keyID,
(id)kCFBooleanTrue,
(__bridge id)(kSecAttrAccessibleAfterFirstUnlock),
keyData };
NSMutableDictionary * attributes = [[NSMutableDictionary alloc] initWithObjects:values forKeys:keys count:ATTR_COUNT(keys)];
CFTypeRef result;
NSError * error = nil;
OSStatus osStatus = SecItemAdd((__bridge CFDictionaryRef)attributes, &result);
The error is:
25303 (errKCNoSuchAttr / errSecNoSuchAttr: / The attribute does not
exist.).)

The Error code specifies The attribute does not exist, it is due to the attribute : kSecAttrKeyClass. Try removing this attribute, and use tag names to distinguish the different keys. I was also getting similar issue in my code.

Which OS X version are you trying to support? OS X Keychain Services are different than iOS Keychain Services. For example, kSecClass is only available as of OS X 10.7, and kSecAttrAccessible 10.9.

Related

Why SecItemAdd return me -50 (invalid params)

I want to store in the keychain the value "MyKeyValue" and I do like this :
NSData* key = [#"MyKeyValue" dataUsingEncoding:NSUTF8StringEncoding];
NSData* tag = [#"com.example.MyKey" dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary* addquery = #{ (id)kSecValueRef: key,
(id)kSecClass: (id)kSecClassKey,
(id)kSecAttrApplicationTag: tag,
};
OSStatus status = SecItemAdd((__bridge CFDictionaryRef)addquery, NULL);
but this failed with error -50 (Invalid params)
What i did wrong ?
I would like to store in the keychain a string that can be retrieved if the user uninstall and reinstall my app.
The error is occurring because of kSecValueRef, as per Apple's guideline kSecValueRef accepts a cryptographic key which can be generated through SecKeyRef, Please find below,
NSData* tag = [#"com.example.keys.mykey" dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary* attributes =
#{ (id)kSecAttrKeyType: (id)kSecAttrKeyTypeRSA,
(id)kSecAttrKeySizeInBits: #2048,
(id)kSecPrivateKeyAttrs:
#{ (id)kSecAttrIsPermanent: #YES,
(id)kSecAttrApplicationTag: tag,
},
};
CFErrorRef error = NULL;
SecKeyRef privateKey = SecKeyCreateRandomKey((__bridge CFDictionaryRef)attributes,
&error);
SecKeyRef publicKey = SecKeyCopyPublicKey(privateKey);
NSDictionary* addquery = #{ (id)kSecValueRef: (__bridge id)publicKey,
(id)kSecClass: (id)kSecClassKey,
(id)kSecAttrApplicationTag: tag,
};
OSStatus status = SecItemAdd((__bridge CFDictionaryRef)addquery, NULL);
For more info please refer Storing Keys in the Keychain

Are there new device identifiers in iOS 10?

Has anyone found a new way to uniquely identify a device in iOS 10? I haven't seen any documentation mentioning changes in that area and I wanted to ask before I surrendered to identifier for vendor.
If you're submitting to the store, the only real identifier you have left is the Advertising Identifier for the AdSupport framework.
If you want to go down the rabbit hole a little further and potentially go into some unsafe API territory, you could hook into IOKit and try to get the battery identifier on the device. This is taken from Anthony Agatiello gist of UIDevice extensions:
- (NSString *)batteryID {
void *IOKit = dlopen("/System/Library/Frameworks/IOKit.framework/Versions/A/IOKit", RTLD_LAZY);
NSParameterAssert(IOKit);
mach_port_t *kIOMasterPortDefault = dlsym(IOKit, "kIOMasterPortDefault");
NSParameterAssert(kIOMasterPortDefault);
CFMutableDictionaryRef (*IOServiceNameMatching)(const char *name) = dlsym(IOKit, "IOServiceNameMatching");
NSParameterAssert(IOServiceNameMatching);
mach_port_t (*IOServiceGetMatchingService)(mach_port_t masterPort, CFDictionaryRef matching) = dlsym(IOKit, "IOServiceGetMatchingService");
NSParameterAssert(IOServiceGetMatchingService);
kern_return_t (*IORegistryEntryCreateCFProperties)(mach_port_t entry, CFMutableDictionaryRef *properties, CFAllocatorRef allocator, UInt32 options) = dlsym(IOKit, "IORegistryEntryCreateCFProperties");
NSParameterAssert(IORegistryEntryCreateCFProperties);
kern_return_t (*IOObjectRelease)(mach_port_t object) = dlsym(IOKit, "IOObjectRelease");
NSParameterAssert(IOObjectRelease);
CFMutableDictionaryRef properties = NULL;
mach_port_t service = IOServiceGetMatchingService(*kIOMasterPortDefault, IOServiceNameMatching("charger"));
IORegistryEntryCreateCFProperties(service, &properties, kCFAllocatorDefault, 0);
IOObjectRelease(service);
service = 0;
NSDictionary *dictionary = (__bridge NSDictionary *)properties;
NSData *batteryIDData = [dictionary objectForKey:#"battery-id"];
CFRelease(properties);
properties = NULL;
dlclose(IOKit);
return [NSString stringWithUTF8String:[batteryIDData bytes]];
}
This still works on iOS 10.

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.

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";
}

Resources