Итак, в iOS связка ключей находится в песочнице, AFAIK. Это означает, что все, что вы помещаете в цепочку для ключей, доступно только вашему приложению и только вашему приложению, если не указано иное. Вы должны включить Общий доступ к связке ключей в Возможности в настройках проекта.
Теперь, когда это не так, вы, безусловно, можете импортировать данные. Поскольку они NSString
объекты, вам сначала нужно преобразовать их в NSData
объекты, чтобы импортировать их правильно. Скорее всего, они закодированы в Base64, так что вам придется изменить это:
NSData *decodedData = [[NSData alloc] initWithBase64EncodedString:base64String options:0];
Теперь, когда это сделано, вы можете использовать этот метод, чтобы сохранить ключ в цепочке для ключей и получить SecKeyRef:
/**
* key: the data you're importing
* keySize: the length of the key (512, 1024, 2048)
* isPrivate: is this a private key or public key?
*/
- (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 savedKeyRef = NULL;
sanityCheck = SecItemAdd((__bridge CFDictionaryRef) saveDict, (CFTypeRef *)&savedKeyRef);
if (sanityCheck != errSecSuccess) {
LOGGING_FACILITY1(sanityCheck != noErr, @"Problem saving the key to keychain, OSStatus == %d.", sanityCheck);
}
return savedKeyRef;
}
Позже, если вы хотите получить SecKeyRef из цепочки для ключей, вы можете использовать это:
- (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;
}
О privateTag и publicTag
privateTag
и publicTag
используются для обозначения kSecAttrApplicationTag
, который определяет приложение, использующее этот ключ. Вы хотите иметь отдельные privateTag
и publicTag
, чтобы различать ваш закрытый ключ и открытый ключ.
Это немного запутанно, потому что я следовал примеру кода, но я определил свои privateTag
и publicTag
следующим образом:
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;
}
Затем используйте privateTag
и publicTag
, как и в приведенных выше примерах кода.