I am not sure why this code fails. I am always getting errSecDuplicateItem
First I am trying to call SecPKCS12Import.
CFDataRef inId = (CFDataRef)certToImport_;
OSStatus securityError = errSecSuccess;
CFStringRef pw = (CFStringRef)password;
const void *keys[] = { kSecImportExportPassphrase };
const void *values[] = { pw };
CFDictionaryRef myDict = CFDictionaryCreate(NULL, keys, values, 1, NULL, NULL);
CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);
securityError = SecPKCS12Import(inId, myDict, &items);
if (securityError == errSecSuccess) {
securityError = [self addIdentityToKeychain:items];
if (securityError == errSecSuccess)
{
securityError = [self addRootCaToKeychain:items];
....
So from the code above, I am calling addIdentityToKeychain which returns success.
- (OSStatus)addIdentityToKeychain:(CFArrayRef)importDict
{
CFDictionaryRef myId = CFArrayGetValueAtIndex(importDict, 0);
const void *tempId = NULL;
tempId = CFDictionaryGetValue(myId,kSecImportItemIdentity);
SecIdentityRef secId = (SecIdentityRef)tempId;
SecCertificateRef certRef;
OSStatus securityError = SecIdentityCopyCertificate(secId, &certRef);
if (securityError == errSecSuccess) {
uniqueLabel_ = [[NSString alloc] initWithString:[self GetUniqueLabel:certRef]];
NSMutableDictionary *secIdentityParams = [[NSMutableDictionary alloc] init];
[secIdentityParams setObject:(id)secId forKey:(id)kSecValueRef];
securityError = SecItemAdd((CFDictionaryRef) secIdentityParams, NULL);
[secIdentityParams release];
}
return securityError;
}
But when I am trying finally to call addRootCaToKeychain fails, complaining that we have a duplicate entry. it failed the very first time with a random certificate that could not have been imported in any case.
- (OSStatus)addRootCaToKeychain:(CFArrayRef)importDict
{
OSStatus securityError = errSecSuccess;
CFDictionaryRef myId = CFArrayGetValueAtIndex(importDict, 0);
NSArray *certs = (NSArray*)CFDictionaryGetValue(myId, kSecImportItemCertChain);
CFIndex cnt = [certs count];
if (cnt)
{
CFIndex i = 0;
while ((i < cnt)) {
SecCertificateRef certRef = (SecCertificateRef)[certs objectAtIndex:i];
NSMutableDictionary *secCertParams = [[NSMutableDictionary alloc] init];
[secCertParams setObject:(id)certRef forKey:(id)kSecValueRef];
[secCertParams setObject:(id)uniqueLabel_ forKey:(id)kSecAttrLabel];
securityError = SecItemAdd((CFDictionaryRef) secCertParams, NULL);
[secCertParams release];
// if we get something other than success or duplicate, we will quit right here and report the error.
if ((securityError != errSecSuccess) && (securityError != errSecDuplicateItem)) {
return securityError;
}
i++;
}
}
return securityError;
}
What is wrong with the code? Any idea?
In addRootCaToKeychain check before adding in SecItemAdd if Item already there like below:
//adding access key
[secCertParams setObject:(id)key forKey:(id) kSecValueRef];
[secCertParams setObject:(id)uniqueLabel_ forKey:(id) kSecAttrLabel];
//check and removing item if it exists
SecItemDelete((CFDictionaryRef) secCertParams);
//setting data (private key)
[secCertParams setObject:(id) secCertParams forKey:(id)kSecValueData];
Also check answer here for more detail: https://stackoverflow.com/a/12387678/5575752
Related
I want to get the mac Address,IP Address,Host Name, device Name And Brand Name of the devices which are connected to my wifi router.I will Get all these except the host name.
I have tried MMLAnscan,AFNetworking,LANScanMaster,etc it will provide me all the details except host name
+(NSString *)getHostFromIPAddress:(NSString*)ipAddress {
struct addrinfo *result = NULL;
struct addrinfo hints;
memset(&hints, 0, sizeof(hints));
hints.ai_flags = AI_NUMERICHOST;
hints.ai_family = PF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = 0;
//"www.kame.net", "http"
int errorStatus = getaddrinfo([ipAddress cStringUsingEncoding:NSUTF8StringEncoding], NULL, &hints, &result);
//NSUTF8StringEncoding instead of NSASCIIStringEncoding
if (errorStatus != 0) {
return nil;
}
CFDataRef addressRef = CFDataCreate(NULL, (UInt8 *)result->ai_addr, result->ai_addrlen);
if (addressRef == nil) {
return nil;
}
freeaddrinfo(result);
CFHostRef hostRef = CFHostCreateWithAddress(kCFAllocatorDefault, addressRef);
if (hostRef == nil) {
return nil;
}
CFRelease(addressRef);
BOOL succeeded = CFHostStartInfoResolution(hostRef, kCFHostNames, NULL);
if (!succeeded) {
return nil;
}
NSMutableArray *hostnames = [NSMutableArray array];
CFArrayRef hostnamesRef = CFHostGetNames(hostRef, NULL);
for (int currentIndex = 0; currentIndex < [(__bridge NSArray *)hostnamesRef count]; currentIndex++) {
[hostnames addObject:[(__bridge NSArray *)hostnamesRef objectAtIndex:currentIndex]];
NSLog(#"hostt:%#",hostnames);
}
NSLog(#"hostname:%#",hostnames[0]);
return hostnames[0];
}
I want hostnames of the devices which are connected to my wifi.
I am using this class to load and save a userid and password to the ios keychain.
Every once in a while either the load or save is failing on a real device. I can't tell which.
Am I doing anything wrong in this code?
Is there a better way I should be doing this?
This code is based on code at https://gist.github.com/mlapointe/6e88a9f059f1cc0dabec
#import <Foundation/Foundation.h>
#interface KeychainHelper : NSObject
#property (strong, nonatomic) NSMutableDictionary * keychainItem;
- (void)saveKeychainWithUserId:(NSString *)userId andPassword: (NSString *)password;
- (NSString *)getPassword;
- (NSString *)getUserId;
#end
#import <Security/Security.h>
#interface KeychainHelper ()
#end
#implementation KeychainHelper
- (NSMutableDictionary *)createEmptyKeychainItem {
//Let's create an empty mutable dictionary:
_keychainItem = [NSMutableDictionary dictionary];
//Populate it with the data and the attributes we want to use.
_keychainItem[(__bridge id)kSecClass] = (__bridge id)kSecClassInternetPassword;
_keychainItem[(__bridge id)kSecAttrAccessible] = (__bridge id)kSecAttrAccessibleAfterFirstUnlock;
_keychainItem[(__bridge id)kSecAttrServer] = #"myapp.com";
_keychainItem[(__bridge id)kSecReturnData] = (__bridge id)kCFBooleanTrue;
_keychainItem[(__bridge id)kSecReturnAttributes] = (__bridge id)kCFBooleanTrue;
return nil;
}
- (void)saveKeychainWithUserId: (NSString *) userId andPassword: (NSString *)password {
[self createEmptyKeychainItem];
// Check if this keychain item already exists.
if(SecItemCopyMatching((__bridge CFDictionaryRef)_keychainItem, NULL) == noErr) {
// Item Already Exists == Update instead
[self updateKeychainWithUserId:userId andPassword:password];
} else {
//Item does not exist -- create new
_keychainItem[(__bridge id)kSecValueData] = [password dataUsingEncoding:NSUTF8StringEncoding];
_keychainItem[(__bridge id)kSecAttrAccount] = userId;
OSStatus sts = SecItemAdd((__bridge CFDictionaryRef)_keychainItem, NULL);
if (sts != noErr) {
NSLog(#"Error Code while inserting keychain: %d", (int)sts);
}
}
}
- (void)updateKeychainWithUserId:(NSString *)userId andPassword:(NSString *)password {
NSMutableDictionary *attributesToUpdate = [NSMutableDictionary dictionary];
attributesToUpdate[(__bridge id)kSecValueData] = [password dataUsingEncoding:NSUTF8StringEncoding];
attributesToUpdate[(__bridge id)kSecAttrAccount] = userId;
// Must set these back to false for SecItemUpdate to work
_keychainItem[(__bridge id)kSecReturnData] = (__bridge id)kCFBooleanFalse;
_keychainItem[(__bridge id)kSecReturnAttributes] = (__bridge id)kCFBooleanFalse;
OSStatus sts = SecItemUpdate((__bridge CFDictionaryRef)_keychainItem, (__bridge CFDictionaryRef)attributesToUpdate);
if (sts != noErr) {
NSLog(#"Error Code while updating keychain: %d", (int)sts);
}
}
- (NSString *)getPassword {
CFDictionaryRef result = nil;
[self createEmptyKeychainItem];
OSStatus sts = SecItemCopyMatching((__bridge CFDictionaryRef)_keychainItem, (CFTypeRef *)&result);
if (sts == noErr) {
NSDictionary *resultDict = (__bridge_transfer NSDictionary *)result;
NSData *passwordData = resultDict[(__bridge id)kSecValueData];
NSString *password = [[NSString alloc] initWithData:passwordData encoding:NSUTF8StringEncoding];
return password;
} else {
NSLog(#"No Keychain found while getting password, Error Code: %d", (int)sts);
return nil;
}
}
- (NSString *)getUserId {
CFDictionaryRef result = nil;
[self createEmptyKeychainItem];
OSStatus sts = SecItemCopyMatching((__bridge CFDictionaryRef)_keychainItem, (CFTypeRef *)&result);
if (sts == noErr) {
NSDictionary *resultDict = (__bridge_transfer NSDictionary *)result;
return resultDict[(__bridge id)kSecAttrAccount];
} else {
NSLog(#"No Keychain found while getting userId, Error Code: %d", (int)sts);
return nil;
}
}
#end
I have a class which saves an NSDictionary into KeyChain. It worked fine but suddenly, when I try to load the NSDictionary I get nil value.
This is the class:
//
// KeyChainHandler.m
//
//
#import "KeyChainHandler.h"
#define IDENTIFIER #"Identifier"
#interface KeyChainHandler ()
#property (strong, nonatomic, readwrite) NSDictionary *applicationData;
#end
#implementation KeyChainHandler
// Make this class a singleton
static KeyChainHandler *instance = nil;
+ (KeyChainHandler*)sharedKeyChain
{
#synchronized(self)
{
if (!instance) {
instance = [[self alloc] init];
}
}
return instance;
}
- (id)init
{
self = [super init];
if (self)
{
[self load];
}
return self;
}
- (void)saveObject:(NSDictionary*)data
{
self.applicationData = data;
[self storeDictionary:data toKeychainWithKey:IDENTIFIER];
}
- (NSDictionary*)load
{
NSDictionary *data = [KeyChainHandler dictionaryFromKeychainWithKey:IDENTIFIER];
self.applicationData = data;
return data;
}
- (void)remove
{
[self deleteDictionaryFromKeychainWithKey:IDENTIFIER];
}
- (void)storeDictionary:(NSDictionary*)data toKeychainWithKey:(NSString*)aKey
{
// serialize dict
NSData *serializedDictionary = [NSKeyedArchiver archivedDataWithRootObject:data];
// encrypt in keychain
// first, delete potential existing entries with this key (it won't auto update)
[self remove];
// setup keychain storage properties
NSDictionary *storageQuery = #{
(__bridge id)kSecAttrAccount: aKey,
(__bridge id)kSecValueData: serializedDictionary,
(__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
(__bridge id)kSecAttrAccessible: (__bridge id)kSecAttrAccessibleWhenUnlocked
};
OSStatus osStatus = SecItemAdd((__bridge CFDictionaryRef)storageQuery, nil);
if(osStatus != noErr) {
// do someting with error
}
}
+ (NSDictionary*)dictionaryFromKeychainWithKey:(NSString *)aKey
{
// setup keychain query properties
NSDictionary *readQuery = #{
(__bridge id)kSecAttrAccount: aKey,
(__bridge id)kSecReturnData: (id)kCFBooleanTrue,
(__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword
};
CFDataRef serializedDictionary = NULL;
OSStatus osStatus = SecItemCopyMatching((__bridge CFDictionaryRef)readQuery, (CFTypeRef *)&serializedDictionary);
if(osStatus == noErr) {
// deserialize dictionary
NSData *data = (__bridge NSData *)serializedDictionary;
NSDictionary *storedDictionary = [NSKeyedUnarchiver unarchiveObjectWithData:data];
return storedDictionary;
}
else {
// do something with error
return nil;
}
}
- (void)deleteDictionaryFromKeychainWithKey:(NSString*)aKey
{
// setup keychain query properties
NSDictionary *deletableItemsQuery = #{
(__bridge id)kSecAttrAccount: aKey,
(__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
(__bridge id)kSecMatchLimit: (__bridge id)kSecMatchLimitAll,
(__bridge id)kSecReturnAttributes: (id)kCFBooleanTrue
};
CFArrayRef itemList = nil;
OSStatus osStatus = SecItemCopyMatching((__bridge CFDictionaryRef)deletableItemsQuery, (CFTypeRef *)&itemList);
// each item in the array is a dictionary
NSArray *itemListArray = (__bridge NSArray *)itemList;
for (NSDictionary *item in itemListArray) {
NSMutableDictionary *deleteQuery = [item mutableCopy];
[deleteQuery setValue:(__bridge id)kSecClassGenericPassword forKey:(__bridge id)kSecClass];
// do delete
osStatus = SecItemDelete((__bridge CFDictionaryRef)deleteQuery);
if(osStatus != noErr) {
// do something with error
}
}
}
#end
At AppDelegate when I print [[KeyChainHandler sharedHandler] load]; I get the correct data, then on login screen I try to do it again and I get nil. Then when I restart (just with CMD + R) the app, I don't get nil, I get the correct data again..
What seems to be the problem? maybe it's some kind of Apple's bug?
Why: call [[KeyChainHandler sharedHandler] load];, the property is already loaded at singleton creation and if changed the property would also be updated.
You do need to set the property to nil in remove.
Instead just use:
NSDictionary *dict = [KeyChainHandler sharedKeyChain].applicationData;
Note: the code is: sharedKeyChain, the example call is: sharedHandler.
I'm trying to add a certificate to the keychain. I saw several posts that make this from a file, but I want to create one from a NSString.
My NSString is on RSA - 64base and is like:
-----BEGIN CERTIFICATE-----
MIIDoDCCAoigAwIBAgIJAL8qgXMVVVhPMA0GCSqGSIb3DQEBBQUAMGwxCzAJBgNVBAYTAkJSMRIw
...
FT70at8bty9ocDaXuI3j6mfw2SI=
-----END CERTIFICATE-----
And I'm trying to do something like this:
+ (NSMutableDictionary *)newSearchDictionary:(NSString *)identifier {
NSMutableDictionary *searchDictionary = [[NSMutableDictionary alloc] init];
[searchDictionary setObject:(__bridge id)kSecClassCertificate forKey:(__bridge id)kSecClass];
NSData *encodedIdentifier = [identifier dataUsingEncoding:NSUTF8StringEncoding];
[searchDictionary setObject:encodedIdentifier forKey:(__bridge id)kSecAttrGeneric];
[searchDictionary setObject:encodedIdentifier forKey:(__bridge id)kSecAttrAccount];
[searchDictionary setObject:SERVICE_NAME forKey:(__bridge id)kSecAttrService];
return searchDictionary;
}
+ (BOOL)createKeychainValue:(NSString *)certificado forIdentifier:(NSString *)identifier {
NSMutableDictionary *dictionary = [self newSearchDictionary:identifier];
NSData *certificadoData = [certificado dataUsingEncoding:NSUTF8StringEncoding];
SecCertificateRef cert = SecCertificateCreateWithData(NULL, (__bridge CFDataRef) certificadoData);
[dictionary setObject:(__bridge id)(cert) forKey:(__bridge id<NSCopying>)(kSecValueRef)];
OSStatus status = SecItemAdd((__bridge CFDictionaryRef)dictionary, NULL);
CFRelease(cert);
if (status == errSecSuccess) {
return YES;
}
return NO;
}
But is getting back the cert as nil. Probably because my certificate is PEM and I need a DER. How can I convert? I'm using openssl on my project.
The function that works for me and created the SecCertificateRef was:
+ (NSData *)derFromPem:(NSString *)pem {
BIO *certBio = BIO_new(BIO_s_mem());
BIO_write(certBio, [pem UTF8String], strlen([pem UTF8String]));
X509 *x = PEM_read_bio_X509(certBio,NULL,0,NULL);
BIO *outBio = BIO_new(BIO_s_mem());
i2d_X509_bio(outBio, x);
int len = BIO_pending(outBio);
char *out = calloc(len + 1, 1);
int i = BIO_read(outBio, out, len);
return [NSData dataWithBytes:out length:i];
}
Convert RSA public key from PEM to DER:
UPDATE
- (NSData *)derFromPem:(NSString *)pem
{
if (pem.length == 0) {
return nil;
}
NSData *result = nil;
const char *pem_str = [pem UTF8String];
BIO *bio;
RSA *rsa;
// X509 *x509;
bio = BIO_new_mem_buf(pem_str, strlen(pem_str));
if (bio) {
rsa = PEM_read_bio_RSAPublicKey(bio, &rsa, NULL, NULL);
// x509 = PEM_read_bio_X509(bio, &x509, NULL/*password*/, NULL);
if (rsa) { // or if (x509)
uint8_t *buf, *bufp;
int len = i2d_RSAPublicKey(rsa, NULL);
// int len = i2d_X509(x509, NULL);
if (len >= 0) {
buf = bufp = malloc(len);
i2d_RSAPublicKey(rsa, &bufp);
// i2d_X509(x509, &bufp);
}
if (len >= 0) {
result = [NSData dataWithBytes:buf length:len];
free(buf);
}
RSA_free(rsa);
// X509_free(x509);
}
BIO_free(bio);
}
return result;
}
One task of my program is to scan local wi-fi network for any devices/computers in same network. I found solution to get all working devices IPs, but did not managed to get names of them. I did not find any clue to solve this problem.
Any suggestions?
In order to perform a reverse DNS lookup, you need to call the CFHostGetNames function, like this:
+ (NSArray *)hostnamesForIPv4Address:(NSString *)address
{
struct addrinfo *result = NULL;
struct addrinfo hints;
memset(&hints, 0, sizeof(hints));
hints.ai_flags = AI_NUMERICHOST;
hints.ai_family = PF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = 0;
int errorStatus = getaddrinfo([address cStringUsingEncoding:NSASCIIStringEncoding], NULL, &hints, &result);
if (errorStatus != 0) {
return nil;
}
CFDataRef addressRef = CFDataCreate(NULL, (UInt8 *)result->ai_addr, result->ai_addrlen);
if (addressRef == nil) {
return nil;
}
freeaddrinfo(result);
CFHostRef hostRef = CFHostCreateWithAddress(kCFAllocatorDefault, addressRef);
if (hostRef == nil) {
return nil;
}
CFRelease(addressRef);
BOOL succeeded = CFHostStartInfoResolution(hostRef, kCFHostNames, NULL);
if (!succeeded) {
return nil;
}
NSMutableArray *hostnames = [NSMutableArray array];
CFArrayRef hostnamesRef = CFHostGetNames(hostRef, NULL);
for (int currentIndex = 0; currentIndex < [(__bridge NSArray *)hostnamesRef count]; currentIndex++) {
[hostnames addObject:[(__bridge NSArray *)hostnamesRef objectAtIndex:currentIndex]];
}
return hostnames;
}
BOOL succeeded = CFHostStartInfoResolution(hostRef, kCFHostNames, NULL); Now I encounter that always failed at this line, and I tried to use getnameinfo function, it is still can't get the hostname