Skip to content

Instantly share code, notes, and snippets.

@microshine
Created April 12, 2018 09:33
Show Gist options
  • Save microshine/8b511824d440d4792cc5114e8b92a35e to your computer and use it in GitHub Desktop.
Save microshine/8b511824d440d4792cc5114e8b92a35e to your computer and use it in GitHub Desktop.
OSX add key to keychain
#include <iostream>
#include <CoreFoundation/CoreFoundation.h>
#include <Security/Security.h>
#define SUCCESS printf("%s: OK\n", __FUNCTION__)
static CFStringRef kSecAttrLabelTestApplication = CFSTR("test-application");
template<typename T>
class CFRef {
public:
CFRef() : handle(NULL) { }
CFRef(T value) : handle(value) {}
~CFRef() {
Release();
}
Boolean IsEmpty() {
return !handle;
}
void Release() {
if (!IsEmpty()) {
CFRelease(handle);
handle = NULL;
}
}
T Retain() {
return (T)CFRetain(handle);
}
T Get() {
assert(!IsEmpty());
return handle;
}
T operator*() {
return Get();
}
void Set(T value) {
if (handle != value) {
Release();
handle = value;
}
}
T* Ref() {
return &handle;
}
T* operator&() {
return &handle;
}
CFRef<T>& operator=(const T value) {
Set(value);
return *this;
}
private:
T handle;
};
int OSStatusPrint(const char * funcName, OSStatus status) {
CFRef<CFStringRef> description = SecCopyErrorMessageString(status, NULL);
printf("Error:%s: status:%d description:'%s'\n", funcName, (int)status, CFStringGetCStringPtr(*description, kCFStringEncodingUTF8));
return 0;
}
int CFErrorPrint(const char * funcName, CFErrorRef error) {
CFIndex code = CFErrorGetCode(error);
return OSStatusPrint(funcName, (OSStatus)code);
}
int keySize = 2048;
CFRef<CFNumberRef> kSecKeySize = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &keySize);
// Generates temporary RSA private key
SecKeyRef GenerateRsaKey(CFDictionaryRef attributes) {
CFRef<SecKeyRef> key;
CFRef<CFMutableDictionaryRef> attrs;
if (attributes) {
attrs = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, attributes);
} else {
attrs = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
}
CFDictionarySetValue(*attrs, kSecAttrKeyType, kSecAttrKeyTypeRSA);
CFDictionarySetValue(*attrs, kSecAttrKeySizeInBits, kSecKeySize.Get());
CFRef<CFErrorRef> error;
key = SecKeyCreateRandomKey(*attrs, &error);
if (!error.IsEmpty()) {
assert(CFErrorPrint("SecKeyCreateRandomKey", *error));
}
key.Retain();
return *key;
}
// Adds public key to keychain from CKData
void test_import_public_key() {
CFRef<CFErrorRef> error;
CFRef<SecKeyRef> privateKey = GenerateRsaKey(NULL);
CFRef<SecKeyRef> publicKey = SecKeyCopyPublicKey(*privateKey);
assert(!publicKey.IsEmpty());
CFRef<CFDataRef> keyData = SecKeyCopyExternalRepresentation(*publicKey, &error);
// https://github.com/btnguyen2k/swiftutils/blob/master/SwiftUtils/RSAUtils.swift#L250
CFRef<CFMutableDictionaryRef> attrs = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFDictionarySetValue(*attrs, kSecClass, kSecClassKey);
CFDictionarySetValue(*attrs, kSecAttrKeyType, kSecAttrKeyTypeRSA);
CFDictionarySetValue(*attrs, kSecValueData, *keyData);
CFDictionarySetValue(*attrs, kSecAttrKeyClass, kSecAttrKeyClassPublic);
CFDictionarySetValue(*attrs, kSecReturnPersistentRef, kCFBooleanTrue);
OSStatus status = SecItemAdd(*attrs, NULL);
if (status) {
assert(OSStatusPrint("SecItemAdd", status));
}
SUCCESS;
}
// Adds public key to keychain via SecKeyCreateWithData
void test_import_public_key_2() {
CFRef<CFErrorRef> error;
CFRef<SecKeyRef> privateKey = GenerateRsaKey(NULL);
CFRef<SecKeyRef> publicKey = SecKeyCopyPublicKey(*privateKey);
assert(!publicKey.IsEmpty());
CFRef<CFDataRef> keyData = SecKeyCopyExternalRepresentation(*publicKey, &error);
CFRef<CFMutableDictionaryRef> attrs = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFDictionarySetValue(*attrs, kSecAttrKeyType, kSecAttrKeyTypeRSA);
CFDictionarySetValue(*attrs, kSecAttrKeyClass, kSecAttrKeyClassPublic);
CFRef<SecKeyRef> importedKey = SecKeyCreateWithData(*keyData, *attrs, &error);
if (!error.IsEmpty()) {
assert(CFErrorPrint("SecKeyCreateWithData", *error));
}
// https://github.com/TakeScoop/SwiftyRSA/blob/master/SwiftyRSA/SwiftyRSA.swift#L101
CFDictionaryRemoveAllValues(*attrs);
CFDictionarySetValue(*attrs, kSecClass, kSecClassKey);
CFDictionarySetValue(*attrs, kSecValueRef, *importedKey);
OSStatus status = SecItemAdd(*attrs, NULL);
if (status) {
assert(OSStatusPrint("SecItemAdd", status));
}
SUCCESS;
}
// Adds private key to keychain from CKData
void test_import_private_key() {
CFRef<CFErrorRef> error;
CFRef<SecKeyRef> privateKey = GenerateRsaKey(NULL);
CFRef<CFDataRef> keyData = SecKeyCopyExternalRepresentation(*privateKey, &error);
// https://github.com/btnguyen2k/swiftutils/blob/master/SwiftUtils/RSAUtils.swift#L131
CFRef<CFMutableDictionaryRef> attrs = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFDictionarySetValue(*attrs, kSecClass, kSecClassKey);
CFDictionarySetValue(*attrs, kSecAttrKeyType, kSecAttrKeyTypeRSA);
CFDictionarySetValue(*attrs, kSecValueData, *keyData);
CFDictionarySetValue(*attrs, kSecAttrKeyClass, kSecAttrKeyClassPrivate);
CFDictionarySetValue(*attrs, kSecReturnPersistentRef, kCFBooleanTrue);
OSStatus status = SecItemAdd(*attrs, NULL);
if (status) {
assert(OSStatusPrint("SecItemAdd", status));
}
SUCCESS;
}
// Creates SecAccess with empty application list
SecAccessRef CreateAccess() {
CFRef<CFArrayRef> appList = CFArrayCreate(kCFAllocatorDefault, NULL, 0, &kCFTypeArrayCallBacks);
CFRef<SecAccessRef> access;
OSStatus status = SecAccessCreate(CFSTR("RSA test key"), *appList, &access);
if (status) {
assert(OSStatusPrint("SecAccessCreate", status));
}
access.Retain();
return *access;
}
// Generates temporary RSA key and adds it to keychain via kSecValueRef attribute
void test_add_private_key() {
CFRef<CFMutableDictionaryRef> attrs = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFRef<SecAccessRef> access = CreateAccess();
CFRef<CFMutableDictionaryRef> prvAttrs = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFDictionarySetValue(*prvAttrs, kSecAttrCanSign, kCFBooleanTrue);
CFDictionarySetValue(*prvAttrs, kSecAttrCanDecrypt, kCFBooleanFalse);
CFDictionarySetValue(*prvAttrs, kSecAttrCanUnwrap, kCFBooleanFalse);
CFDictionarySetValue(*prvAttrs, kSecAttrCanDerive, kCFBooleanFalse);
CFDictionarySetValue(*attrs, kSecAttrAccess, *access);
CFDictionarySetValue(*attrs, kSecPrivateKeyAttrs, *prvAttrs);
CFRef<CFMutableDictionaryRef> pubAttrs = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFDictionarySetValue(*pubAttrs, kSecAttrCanVerify, kCFBooleanTrue);
CFDictionarySetValue(*pubAttrs, kSecAttrCanEncrypt, kCFBooleanFalse);
CFDictionarySetValue(*pubAttrs, kSecAttrCanWrap, kCFBooleanFalse);
CFDictionarySetValue(*pubAttrs, kSecAttrCanDerive, kCFBooleanFalse);
CFDictionarySetValue(*attrs, kSecPublicKeyAttrs, *pubAttrs);
CFRef<SecKeyRef> key = GenerateRsaKey(*attrs);
// Check private key attributes
CFRef<CFDictionaryRef> keyAttrs = SecKeyCopyAttributes(*key);
assert(CFDictionaryGetValue(*keyAttrs, kSecAttrCanSign) == kCFBooleanTrue);
assert(CFDictionaryGetValue(*keyAttrs, kSecAttrCanDecrypt) == kCFBooleanFalse);
assert(CFDictionaryGetValue(*keyAttrs, kSecAttrCanUnwrap) == kCFBooleanFalse);
CFDictionaryRemoveAllValues(*attrs);
CFDictionarySetValue(*attrs, kSecValueRef, *key);
CFDictionarySetValue(*attrs, kSecClass, kSecClassKey);
CFDictionarySetValue(*attrs, kSecAttrAccess, *access);
CFDictionarySetValue(*attrs, kSecAttrLabel, kSecAttrLabelTestApplication);
CFDictionarySetValue(*attrs, kSecReturnRef, kCFBooleanTrue);
CFRef<SecKeyRef> newKey;
OSStatus status = SecItemAdd(*attrs, (CFTypeRef*)&newKey);
if (status) {
assert(OSStatusPrint("SecItemAdd", status));
}
// Check new key
assert(!newKey.IsEmpty()); // Check newKey is not empty
CFRef<CFStringRef> typeName = CFCopyTypeIDDescription(CFGetTypeID(*newKey));
char buffer[256] = {0};
CFStringGetCString(*typeName, buffer, 256, kCFStringEncodingUTF8);
printf("newKey type:%s\n", buffer);
assert(CFGetTypeID(*newKey) == SecKeyGetTypeID()); // Must be SecKeyRef
SUCCESS;
}
// Generates RSA signing key with empty application list
void test_generate_private_key() {
OSStatus status;
CFRef<CFMutableDictionaryRef> attrs = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFDictionarySetValue(*attrs, kSecAttrLabel, kSecAttrLabelTestApplication);
CFDictionarySetValue(*attrs, kSecAttrKeyType, kSecAttrKeyTypeRSA);
CFDictionarySetValue(*attrs, kSecAttrKeySizeInBits, *kSecKeySize);
CFRef<SecAccessRef> access = CreateAccess();
CFRef<CFMutableDictionaryRef> prvAttrs = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFDictionarySetValue(*prvAttrs, kSecAttrCanSign, kCFBooleanTrue);
CFDictionarySetValue(*prvAttrs, kSecAttrCanDecrypt, kCFBooleanFalse);
CFDictionarySetValue(*prvAttrs, kSecAttrCanUnwrap, kCFBooleanFalse);
CFDictionarySetValue(*prvAttrs, kSecAttrCanDerive, kCFBooleanFalse);
CFDictionarySetValue(*attrs, kSecAttrAccess, *access);
CFDictionarySetValue(*prvAttrs, kSecAttrIsExtractable, kCFBooleanFalse);
CFDictionarySetValue(*attrs, kSecPrivateKeyAttrs, *prvAttrs);
CFRef<CFMutableDictionaryRef> pubAttrs = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFDictionarySetValue(*pubAttrs, kSecAttrCanVerify, kCFBooleanTrue);
CFDictionarySetValue(*pubAttrs, kSecAttrCanEncrypt, kCFBooleanFalse);
CFDictionarySetValue(*pubAttrs, kSecAttrCanWrap, kCFBooleanFalse);
CFDictionarySetValue(*pubAttrs, kSecAttrCanDerive, kCFBooleanFalse);
CFDictionarySetValue(*attrs, kSecPublicKeyAttrs, *pubAttrs);
CFRef<SecKeyRef> privateKey;
CFRef<SecKeyRef> publicKey;
status = SecKeyGeneratePair(*attrs, &publicKey, &privateKey);
if (status) {
assert(OSStatusPrint("SecKeyGeneratePair", status));
}
SUCCESS;
}
int main(int argc, const char * argv[]) {
test_import_public_key();
test_import_public_key_2();
test_import_private_key();
test_add_private_key();
test_generate_private_key();
SUCCESS;
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment