How to Add a Cert to System.keychain Without SecKeychainCopyDomainDefault - objective-c

As of MacOS 10.10 SecKeychainCopyDomainDefault was deprecated. The SecItemAdd key kSecUseKeychain is however not deprecated. kSecUseKeychain requires a SecKeychainRef as a value, but I do not see any non-deprecated way of obtaining one.
I need to add a CA to the System keychain. The following code is a working example when ran as root.
SecKeychainRef system;
OSStatus keychainError = SecKeychainCopyDomainDefault(kSecPreferencesDomainSystem, &system);
CFDataRef data = CFDataCreate(kCFAllocatorDefault, nsdataCert.bytes, nsdataCert.length);
SecCertificateRef test = SecCertificateCreateWithData(kCFAllocatorDefault, data);
const void *keys[] = { kSecClass, kSecAttrLabel, kSecValueRef, kSecUseKeychain};
const void *values[] = { kSecClassCertificate , #"Test Certificate", test, system};
CFDictionaryRef query = CFDictionaryCreate(kCFAllocatorDefau1lt, keys, values, 4, NULL, NULL);
int status = SecItemAdd ((CFDictionaryRef)query, NULL);
NSLog(#"status=%d", status);

Related

Remove All trusted applications from a keychain item

I wrote the below code to add a generic password from my app. SecKeychainAddGenericPassword() adds the app as a trusted application in the keychain by design.
I want to remove my app from the list of trusted apps. I called SecKeychainItemSetAccess () to do that but I still see my app listed as a trusted app.
addgenericpassword(const std::string& service,const std::string& account,
const std::string& password) {
SecKeychainItemRef item_ref;
OSStatus status = SecKeychainAddGenericPassword(NULL,
service.length(),
service.data(),
account.length(),
account.data(),
password.length(),
password.data(),
&item_ref);
//Creating an secAccess object that has an empty trusted application list
//https://developer.apple.com/documentation/security/1393522-secaccesscreate?language=objc
CFArrayRef applicationList=CFArrayCreate (NULL,NULL,0,NULL);
SecAccessRef accessref;
CFStringRef description=CFStringCreateWithCString(NULL, "Generic description", kCFStringEncodingASCII);
status = SecAccessCreate(description,applicationList,&accessref);
//Set the access of a keychain item "item_ref".
status = SecKeychainItemSetAccess(item_ref,accessref);
CFRelease(item_ref);
CFRelease(accessref);
CFRelease(applicationList);
CFRelease(description);
return 0;
}
Update:
Changed description to match the service name. Still no luck
CFStringRef description=CFStringCreateWithCString(NULL, service.data(), kCFStringEncodingASCII);
I have been able to get the functionality I was looking for.I am not sure if this is the right method to do it though.
SecAccessRef accessref;
SecKeychainItemCopyAccess(item_ref, &accessref);
CFArrayRef aclList;
SecAccessCopyACLList(accessref, &aclList);
CFIndex count = CFArrayGetCount(aclList);
//Array with 0 Applications / Empty Array . Not the same as passing NULL
CFArrayRef zero_applications = CFArrayCreate(NULL, NULL, 0, NULL);
for (int i = 0; i < count; i++) {
SecACLRef acl = (SecACLRef) CFArrayGetValueAtIndex(aclList, i);
CFArrayRef applicationList;
CFStringRef description;
CSSM_ACL_KEYCHAIN_PROMPT_SELECTOR promptSelector;
SecACLCopySimpleContents(acl, &applicationList, &description,
&promptSelector);
if (applicationList == NULL) {
continue;
}
CFIndex appCount = CFArrayGetCount(applicationList);
for (int j = 0; j < appCount; j++) {
status= SecACLSetContents(acl, zero_applications, description, 1);
break;
}
CFRelease(applicationList);
CFRelease(description);
}
// Set the modified copy to the item now
status = SecKeychainItemSetAccess(item_ref, accessref);

RSA public key decryption on OS X using SecTransform API (or other system API)

I'm trying to replace my use of OpenSSL, which was long ago deprecated and has been removed from the 10.11 SDK with the Security Transform API. My use of OpenSSL is simply for license key verification. The problem I've run into is that license keys are generated (server side) using OpenSSL's rsa_private_encrypt() function, rather than the (probably more appropriate) rsa_sign(). In the current OpenSSL code, I verify them using rsa_public_decrypt() like so:
int decryptedSize = RSA_public_decrypt([signature length], [signature bytes], checkDigest, rsaKey, RSA_PKCS1_PADDING);
BOOL success = [[NSData dataWithBytes:checkDigest length:decryptedSize] isEqualToData:[digest sha1Hash]])
Unfortunately, I'm unable to replicate this using the SecTransform APIs. I have the following:
SecTransformRef decryptor = CFAutorelease(SecDecryptTransformCreate(pubKey, &error));
if (error) { showSecError(error); return NO; }
SecTransformSetAttribute(decryptor, kSecTransformInputAttributeName, (CFDataRef)signatureData, &error);
if (error) { showSecError(error); return NO; }
CFDataRef result = SecTransformExecute(decryptor, &error);
if (error) { showSecError(error); return NO; }
return CFEqual(result, (CFDataRef)[digest sha1Hash]);
The call to SecTransformExecute() fails with a CSSMERR_CSP_INVALID_KEY_CLASS error.
Am I missing something, or is there no equivalent to OpenSSL's RSA_public_decrypt() in Security.framework? Perhaps a SecVerifyTransform can be used (I have been unable to get this to work either, but then the same is true of OpenSSL's RSA_sign()). I am certainly willing to use another system API (e.g. CDSA/CSSM) if it will enable me to do this.
Unfortunately, as this code needs to verify existing license codes, I can not simply change my license generation code to use RSA_sign() or similar instead.
I figured out how to do this using CDSA/CSSM. Code below:
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
NSData *ORSDecryptDataWithPublicKey(NSData *dataToDecrypt, SecKeyRef publicKey)
{
const CSSM_KEY *cssmPubKey = NULL;
SecKeyGetCSSMKey(publicKey, &cssmPubKey);
CSSM_CSP_HANDLE handle;
SecKeyGetCSPHandle(publicKey, &handle);
CSSM_DATA inputData = {
.Data = (uint8_t *)[dataToDecrypt bytes],
.Length = [dataToDecrypt length],
};
CSSM_DATA outputData = {
.Data = NULL,
.Length = 0,
};
CSSM_ACCESS_CREDENTIALS credentials;
memset(&credentials, 0, sizeof(CSSM_ACCESS_CREDENTIALS));
CSSM_CC_HANDLE contextHandle;
CSSM_RETURN result = CSSM_CSP_CreateAsymmetricContext(handle, cssmPubKey->KeyHeader.AlgorithmId, &credentials, cssmPubKey, CSSM_PADDING_PKCS1, &contextHandle);
if (result) { NSLog(#"Error creating CSSM context: %i", result); return nil; }
CSSM_CONTEXT_ATTRIBUTE modeAttribute = {
.AttributeType = CSSM_ATTRIBUTE_MODE,
.AttributeLength = sizeof(UInt32),
.Attribute.Uint32 = CSSM_ALGMODE_PUBLIC_KEY,
};
result = CSSM_UpdateContextAttributes(contextHandle, 1, &modeAttribute);
if (result) { NSLog(#"Error setting CSSM context mode: %i", result); return nil; }
CSSM_SIZE numBytesDecrypted = 0;
CSSM_DATA remData = {
.Data = NULL,
.Length = 0,
};
result = CSSM_DecryptData(contextHandle, &inputData, 1, &outputData, 1, &numBytesDecrypted, &remData);
if (result) { NSLog(#"Error decrypting data using CSSM: %i", result); return nil; }
CSSM_DeleteContext(contextHandle);
outputData.Length = numBytesDecrypted;
return [NSData dataWithBytesNoCopy:outputData.Data length:outputData.Length freeWhenDone:YES];
}
#pragma clang diagnostic pop
Note that as documented here, while CDSA is deprecated, Apple recommends its use "if none of the other cryptographic service APIs support what you are trying to do". I have filed radar #23063471 asking for this functionality to be added to Security.framework.

how to set Authentication setting data? VPN Mac OS Programatically

i am using this https://github.com/halo/macosvpn. Code run successfully and create new network but it's not showing Authentication Setting data which i Password e.g Shared Secret.
- (CFDictionaryRef) L2TPPPPConfig {
CFStringRef keys[4] = { NULL, NULL, NULL, NULL };
CFStringRef vals[4] = { NULL, NULL, NULL, NULL };
CFIndex count = 0;
keys[count] = kSCPropNetPPPCommRemoteAddress;
vals[count++] = (__bridge CFStringRef)self.endpoint;
keys[count] = kSCPropNetPPPAuthName;
vals[count++] = (__bridge CFStringRef)self.username;
keys[count] = kSCPropNetPPPAuthPassword;
vals[count++] = (__bridge CFStringRef)self.serviceID;
keys[count] = kSCPropNetPPPAuthPasswordEncryption;
vals[count++] = kSCValNetPPPAuthPasswordEncryptionKeychain;
return CFDictionaryCreate(NULL, (const void **)&keys, (const void **)&vals, count, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
Furthermore Server Address and Account Name set Successfully. i
These two lines look very suspicious:
keys[count] = kSCPropNetPPPAuthPassword;
vals[count++] = (__bridge CFStringRef)self.serviceID;
Shouldn't that second line be something like: "self.password"?
serviceID is something totally different than a password.

How to programmatically retrieve UUID of root disk partition in OS X?

I can retrieve an OS X disk partition UUID by this code:
void PrintUUID()
{
DADiskRef disk;
CFDictionaryRef descDict;
DASessionRef session = DASessionCreate(NULL);
if (session) {
disk = DADiskCreateFromBSDName(NULL, session, "/dev/disk0s2");
if (disk) {
descDict = DADiskCopyDescription(disk);
if (descDict) {
CFTypeRef value = (CFTypeRef)CFDictionaryGetValue(descDict,
CFSTR("DAVolumeUUID"));
CFStringRef strValue = CFStringCreateWithFormat(NULL, NULL,
CFSTR("%#"), value);
print(strVal); <------------- here is the output
CFRelease(strValue);
CFRelease(descDict);
}
CFRelease(disk);
}
}
}
Above code retrieve UUID of disk0, I want to retrieve UUID of root disk (mount point = /),
if I use "/" instead "/dev/disk0s2" then DADiskCopyDescription returns NULL.
Also I know I can do it in Terminal by this command:
diskutil info /
Briefly how can I retrieve BSD Name of root disk? (to use it in DADiskCreateFromBSDName)
Anybody has an idea?
Thanks.
Use DADiskCreateFromVolumePath instead of DADiskCreateFromBSDName:
char *mountPoint = "/";
CFURLRef url = CFURLCreateFromFileSystemRepresentation(NULL, (const UInt8 *)mountPoint, strlen(mountPoint), TRUE);
disk = DADiskCreateFromVolumePath(NULL, session, url);
CFRelease(url);
Swift code:
let mountPoint = "/"
let url = URL(fileURLWithPath: mountPoint)
if let session = DASessionCreate(nil),
let disk = DADiskCreateFromVolumePath(nil, session, url as CFURL),
let desc = DADiskCopyDescription(disk) as? [String: CFTypeRef] {
if let uuid = desc["DAVolumeUUID"], CFGetTypeID(uuid) == CFUUIDGetTypeID() {
if let uuidString = CFUUIDCreateString(nil, (uuid as! CFUUID)) {
print(uuidString)
}
}
}
DADiskCreateFromVolumePath is only included in OS 10.7 and above, so if you need to support older platforms like OS 10.4 and above (like me!) the only option is to use statfs to generate a BSD name from the posix name, so the entire function then becomes:
#include <sys/param.h>
#include <sys/mount.h>
void PrintUUID()
{
DADiskRef disk;
CFDictionaryRef descDict;
DASessionRef session = DASessionCreate (kCFAllocatorDefault);
if (session) {
struct statfs statFS;
statfs ("/", &statFS);
disk = DADiskCreateFromBSDName (kCFAllocatorDefault, session, statFS.f_mntfromname);
if (disk) {
descDict = DADiskCopyDescription (disk);
if (descDict) {
CFTypeRef value = (CFTypeRef) CFDictionaryGetValue (descDict, CFSTR("DAVolumeUUID"));
CFStringRef strValue = CFStringCreateWithFormat (NULL, NULL, CFSTR("%#"), value);
print (strValue) <------------- here is the output
CFRelease (strValue);
CFRelease (descDict);
}
CFRelease (disk);
}
CFRelease (session);
}
}

SecKeychain load item

I want to store SMTP-Data from my Mac OSX application using the keychain. I read the Keychain Services Programming Guide of Apple and wrote this method to store the data:
- (BOOL)saveSMPTData
{
OSStatus err;
SecKeychainItemRef item = nil;
SecProtocolType protocol = kSecProtocolTypeSMTP;
const char *accessLabelUTF8 = [KEYCHAIN_NAME UTF8String];
const char *serverNameUTF8 = [self.serverName UTF8String];
const char *usernameUTF8 = [self.username UTF8String];
const char *passwordUTF8 = [self.password UTF8String];
SecAccessRef access = createAccess(KEYCHAIN_NAME);
SecKeychainAttribute attrs[] = {
{ kSecLabelItemAttr, (int)strlen(accessLabelUTF8), (char *)accessLabelUTF8 },
{ kSecAccountItemAttr, (int)strlen(usernameUTF8), (char *)usernameUTF8 },
{ kSecServerItemAttr, (int)strlen(serverNameUTF8), (char *)serverNameUTF8 },
{ kSecProtocolItemAttr, sizeof(SecProtocolType), (SecProtocolType *)&protocol }
};
SecKeychainAttributeList attributes = { sizeof(attrs) / sizeof(attrs[0]), attrs };
err = SecKeychainItemCreateFromContent(kSecInternetPasswordItemClass,
&attributes,
(int)strlen(passwordUTF8),
passwordUTF8,
NULL,
access,
&item);
if (access) CFRelease(access);
if (item) CFRelease(item);
return (err == noErr);
}
SecAccessRef createAccess(NSString *accessLabel)
{
OSStatus err;
SecAccessRef access = nil;
NSArray *trustedApplications = nil;
SecTrustedApplicationRef myself;
err = SecTrustedApplicationCreateFromPath(NULL, &myself);
trustedApplications = [NSArray arrayWithObjects:(__bridge id)myself, nil];
err = SecAccessCreate((__bridge CFStringRef)accessLabel,
(__bridge CFArrayRef)trustedApplications, &access);
if (err) return nil;
return access;
}
Of course I also want to load them. My first try looks like this:
- (BOOL)loadDataFromKeychain
{
uint32_t serverNameLength = 0;
const char *serverName = NULL;
uint32_t usernameLength = 0;
const char *username = NULL;
uint32_t passwordLength = 0;
void **password = NULL;
OSStatus err = SecKeychainFindInternetPassword(NULL,
serverNameLength, serverName,
0, NULL,
usernameLength, username,
0, NULL,
0, 0,
0,
&passwordLength, password,
NULL); // How do I get the ItemRef?
return (err == noErr);
}
But this does not work, and I think I know why not. I don’t know how to get the SecKeychainItemRef for the SecKeychainFindInternetPassword method.
Maybe anyone can help me?
Instead of declaring password a void **, declare it a void * and pass &password for the second-to-last parameter.
You probably don't need the SecKeychainItemRef for what you're trying to accomplish.
By the way, have you tried using Keychain Access to verify the items are getting into the keychain?