Created
April 12, 2018 09:33
-
-
Save microshine/8b511824d440d4792cc5114e8b92a35e to your computer and use it in GitHub Desktop.
OSX add key to keychain
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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