iOS SecKeyRef to NSData
- 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 keykSecReturnData
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.
Converting NSData to SecKeyRef
Use this function to save your public key. Pass your RAS public key and any name for peername.
- (void)addPeerPublicKey:(NSString *)peerName keyBits:(NSData *)publicKeyData {
OSStatus sanityCheck = noErr;
CFTypeRef persistPeer = NULL;
[self removePeerPublicKey:peerName];
NSData * peerTag = [[NSData alloc] initWithBytes:(const void *)[peerName UTF8String] length:[peerName length]];
NSMutableDictionary * peerPublicKeyAttr = [[NSMutableDictionary alloc] init];
[peerPublicKeyAttr setObject:(id)kSecClassKey forKey:(id)kSecClass];
[peerPublicKeyAttr setObject:(id)kSecAttrKeyTypeRSA forKey:(id)kSecAttrKeyType];
[peerPublicKeyAttr setObject:peerTag forKey:(id)kSecAttrApplicationTag];
[peerPublicKeyAttr setObject:publicKeyData forKey:(id)kSecValueData];
[peerPublicKeyAttr setObject:[NSNumber numberWithBool:YES] forKey:(id)kSecReturnData];
sanityCheck = SecItemAdd((CFDictionaryRef) peerPublicKeyAttr, (CFTypeRef *)&persistPeer);
if(sanityCheck == errSecDuplicateItem){
TRC_DBG(@"Problem adding the peer public key to the keychain, OSStatus == %ld.", sanityCheck );
}
TRC_DBG(@"SecItemAdd OSStATUS = %ld", sanityCheck);
// TRC_DBG(@"PersistPeer privatekey data after import into keychain %@", persistPeer);
persistPeer = NULL;
[peerPublicKeyAttr removeObjectForKey:(id)kSecValueData];
sanityCheck = SecItemCopyMatching((CFDictionaryRef) peerPublicKeyAttr, (CFTypeRef*)&persistPeer);
TRC_DBG(@"SecItemCopying OSStATUS = %ld", sanityCheck);
// TRC_DBG(@"SecItem copy matching returned this public key data %@", persistPeer);
// The nice thing about persistent references is that you can write their value out to disk and
// then use them later. I don't do that here but it certainly can make sense for other situations
// where you don't want to have to keep building up dictionaries of attributes to get a reference.
//
// Also take a look at SecKeyWrapper's methods (CFTypeRef)getPersistentKeyRefWithKeyRef:(SecKeyRef)key
// & (SecKeyRef)getKeyRefWithPersistentKeyRef:(CFTypeRef)persistentRef.
[peerTag release];
[peerPublicKeyAttr release];
if (persistPeer) CFRelease(persistPeer);
}
This is the function to retrieve the public key ref. Pass the same name which one is used for save.
-(SecKeyRef)getPublicKeyReference:(NSString*)peerName{
OSStatus sanityCheck = noErr;
SecKeyRef pubKeyRefData = NULL;
NSData * peerTag = [[NSData alloc] initWithBytes:(const void *)[peerName UTF8String] length:[peerName length]];
NSMutableDictionary * peerPublicKeyAttr = [[NSMutableDictionary alloc] init];
[peerPublicKeyAttr setObject:(id)kSecClassKey forKey:(id)kSecClass];
[peerPublicKeyAttr setObject:(id)kSecAttrKeyTypeRSA forKey:(id)kSecAttrKeyType];
[peerPublicKeyAttr setObject:peerTag forKey:(id)kSecAttrApplicationTag];
[peerPublicKeyAttr setObject:[NSNumber numberWithBool:YES] forKey: (id)kSecReturnRef];
sanityCheck = SecItemCopyMatching((CFDictionaryRef) peerPublicKeyAttr, (CFTypeRef*)&pubKeyRefData);
[peerTag release];
[peerPublicKeyAttr release];
TRC_DBG(@"SecItemCopying OSStATUS = %ld", sanityCheck);
if(pubKeyRefData){
TRC_DBG(@"SecItem copy matching returned this publickeyref %@", pubKeyRefData);
return pubKeyRefData;
}else{
TRC_DBG(@"pubKeyRef is NULL");
return nil;
}
}
Pass your public key data to this function before addPeerPublicKey
- (NSData *)stripPublicKeyHeader:(NSData *)d_key
{
// Skip ASN.1 public key header
if (d_key == nil) return(nil);
unsigned int len = [d_key length];
if (!len) return(nil);
unsigned char *c_key = (unsigned char *)[d_key bytes];
unsigned int idx = 0;
if (c_key[idx++] != 0x30) return(nil);
if (c_key[idx] > 0x80) idx += c_key[idx] - 0x80 + 1;
else idx++;
// PKCS #1 rsaEncryption szOID_RSA_RSA
static unsigned char seqiod[] =
{ 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
0x01, 0x05, 0x00 };
if (memcmp(&c_key[idx], seqiod, 15)) return(nil);
idx += 15;
if (c_key[idx++] != 0x03) return(nil);
if (c_key[idx] > 0x80) idx += c_key[idx] - 0x80 + 1;
else idx++;
if (c_key[idx++] != '\0') return(nil);
// Now make a new NSData from this buffer
return([NSData dataWithBytes:&c_key[idx] length:len - idx]);
}
iPhone: How do you export a SecKeyRef or an NSData containing public key bits to the PEM format?
I think you will want to look at http://www.openssl.org/docs/crypto/pem.html#
maybe:
int PEM_write_PrivateKey(FILE *fp, EVP_PKEY *x, const EVP_CIPHER *enc,
unsigned char *kstr, int klen,
pem_password_cb *cb, void *u);
Related Topics
iOS 6 Xcode 4.5 Unsupported Architecture Armv7S
Resize Mkannotationview Image When Map Zooms in and Out
Unified Uiviewcontroller "Became Frontmost" Detection
Loop Through Subview to Check for Empty Uitextfield - Swift
How to Submit iOS App for Review - "There Are One or More Errors on the Page."
Xcode8 List of Simulators Not Showing
How to Check Whether iPhone and Apple Watch Are Connected
What Does It Mean to "Weak-Link" a Framework
Check If App Is Ad-Hoc|Dev|App-Store Build at Run Time
Swift iOS Add Infinite Scroll Pagination to Uitableview
Hide Navigationbar When Scrolling Tableview in Collectionview
Memory Leak with Large Core Data Batch Insert in Swift
Detect Hash Tags #, Mention Tags @, in iOS Like in Twitter App
Xcode6 Error: "No Matching Provisioning Profiles Found for Application"
iOS Tab Bar Icons Keep Getting Larger
iOS 9.3:An Ssl Error Has Occurred and a Secure Connection to the Server Cannot Be Made