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
Related
I am basically follow this guide to generated a private key, copy the public key, and then encrypt a message. However, it gives me the error (OSStatus error -67712 - CSSM Exception: -2147415791 CSSMERR_CSP_INVALID_KEY_REFERENCE).
Initially, I thought I set the attributes incorrectly. However, if I create the public key (with the same attributes) by the SecKeyGeneratePair() function, everything works perfectly. Is it weird?
void TestEncryptDecrpt() {
OSStatus status;
NSData* tag = [#"com.example.keys.mykey" dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary* attributes =
#{ (id)kSecAttrKeyType: (id)kSecAttrKeyTypeRSA,
(id)kSecAttrKeySizeInBits: #1024,
(id)kSecPrivateKeyAttrs:
#{ (id)kSecAttrIsPermanent: #YES,
(id)kSecAttrApplicationTag: tag,
},
};
CFErrorRef error = NULL;
SecKeyRef privateKey = SecKeyCreateRandomKey((__bridge CFDictionaryRef)attributes, &error);
SecKeyRef publicKey = SecKeyCopyPublicKey(privateKey);
// *** it will work if I generate the key by SecKeyGeneratePair ***
// status = SecKeyGeneratePair( (__bridge CFDictionaryRef)attributes, &publicKey, &privateKey );
// start encrypt and decrypt a message
static char const kMessage[] = "This is a secret!\n";
SecKeyAlgorithm algorithm = kSecKeyAlgorithmRSAEncryptionRaw;
BOOL canEncrypt = SecKeyIsAlgorithmSupported(publicKey, kSecKeyOperationTypeEncrypt, algorithm);
NSData* plainData = [NSData dataWithBytes:kMessage length:sizeof(kMessage)];
canEncrypt &= ([plainData length] < (SecKeyGetBlockSize(publicKey)-130));
NSData* cipherText = nil;
if (canEncrypt) {
CFErrorRef error = NULL;
cipherText = (NSData*)CFBridgingRelease( SecKeyCreateEncryptedData(publicKey, algorithm, (__bridge CFDataRef)plainData, &error));
if (!cipherText) {
NSError *err = CFBridgingRelease(error); // ARC takes ownership
// Handle the error. . .
NSLog(#"error = %#, %#", [err userInfo], [err localizedDescription]);
}
}
}
Problem solved. You need the "kSecAttrIsPermanent" property as well in the public key setting.
Not sure why this is not mentioned in the example.
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.
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.
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]);
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