diff --git a/src/main/native/org_cryptomator_macos_keychain_MacKeychain_Native.m b/src/main/native/org_cryptomator_macos_keychain_MacKeychain_Native.m index e04bb0a..5309050 100644 --- a/src/main/native/org_cryptomator_macos_keychain_MacKeychain_Native.m +++ b/src/main/native/org_cryptomator_macos_keychain_MacKeychain_Native.m @@ -7,117 +7,103 @@ // #import "org_cryptomator_macos_keychain_MacKeychain_Native.h" +#import #import JNIEXPORT jint JNICALL Java_org_cryptomator_macos_keychain_MacKeychain_00024Native_storePassword(JNIEnv *env, jobject thisObj, jbyteArray service, jbyteArray key, jbyteArray password) { - const int serviceLen = (*env)->GetArrayLength(env, service); jbyte *serviceStr = (*env)->GetByteArrayElements(env, service, NULL); - const int keyLen = (*env)->GetArrayLength(env, key); jbyte *keyStr = (*env)->GetByteArrayElements(env, key, NULL); - const int pwLen = (*env)->GetArrayLength(env, password); jbyte *pwStr = (*env)->GetByteArrayElements(env, password, NULL); + jsize length = (*env)->GetArrayLength(env, password); // find existing: - SecKeychainItemRef itemRef = NULL; - OSStatus status = SecKeychainFindGenericPassword( - NULL, // default keychain - serviceLen, // length of service name - (char *)serviceStr, // service name - keyLen, // length of account name - (char *)keyStr, // account name - NULL, // length of password - NULL, // pointer to password data - &itemRef // the item reference - ); - if (status == errSecSuccess) { + NSDictionary *query = @{ + (__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword, + (__bridge id)kSecAttrService: [NSString stringWithCString:(char *)serviceStr encoding:NSUTF8StringEncoding], + (__bridge id)kSecAttrAccount: [NSString stringWithCString:(char *)keyStr encoding:NSUTF8StringEncoding], + (__bridge id)kSecReturnAttributes: @YES, + (__bridge id)kSecReturnData: @YES, + (__bridge id)kSecMatchLimit: (__bridge id)kSecMatchLimitOne + }; + CFDictionaryRef result = NULL; + OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, (CFTypeRef *)&result); + if (status == errSecSuccess && result != NULL) { // update existing: - status = SecKeychainItemModifyAttributesAndData( - itemRef, // the item reference - NULL, // no change to attributes - pwLen, // length of password - pwStr // pointer to password data - ); + NSDictionary *attributesToUpdate = @{ + (__bridge id)kSecValueData: [NSData dataWithBytes:pwStr length:length] + }; + status = SecItemUpdate((__bridge CFDictionaryRef)query, (__bridge CFDictionaryRef)attributesToUpdate); } else if (status == errSecItemNotFound) { // add new: - status = SecKeychainAddGenericPassword( - NULL, // default keychain - serviceLen, // length of service name - (char *)serviceStr, // service name - keyLen, // length of account name - (char *)keyStr, // account name - pwLen, // length of password - pwStr, // pointer to password data - NULL // the item reference - ); + NSDictionary *attributes = @{ + (__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword, + (__bridge id)kSecAttrService: [NSString stringWithCString:(char *)serviceStr encoding:NSUTF8StringEncoding], + (__bridge id)kSecAttrAccount: [NSString stringWithCString:(char *)keyStr encoding:NSUTF8StringEncoding], + (__bridge id)kSecValueData: [NSData dataWithBytes:pwStr length:length] + }; + status = SecItemAdd((__bridge CFDictionaryRef)attributes, NULL); + } else { + NSLog(@"Error storing item in keychain. Status code: %d", (int)status); } + (*env)->ReleaseByteArrayElements(env, service, serviceStr, JNI_ABORT); (*env)->ReleaseByteArrayElements(env, key, keyStr, JNI_ABORT); (*env)->ReleaseByteArrayElements(env, password, pwStr, JNI_ABORT); - if (itemRef) { - CFRelease(itemRef); - } return status; } JNIEXPORT jbyteArray JNICALL Java_org_cryptomator_macos_keychain_MacKeychain_00024Native_loadPassword(JNIEnv *env, jobject thisObj, jbyteArray service, jbyteArray key) { - const int serviceLen = (*env)->GetArrayLength(env, service); jbyte *serviceStr = (*env)->GetByteArrayElements(env, service, NULL); - const int keyLen = (*env)->GetArrayLength(env, key); jbyte *keyStr = (*env)->GetByteArrayElements(env, key, NULL); - void *pwStr = NULL; - UInt32 pwLen; - OSStatus status = SecKeychainFindGenericPassword( - NULL, // default keychain - serviceLen, // length of service name - (char *)serviceStr, // service name - keyLen, // length of account name - (char *)keyStr, // account name - &pwLen, // length of password - &pwStr, // pointer to password data - NULL // the item reference - ); - jbyteArray result; - if (status == errSecSuccess) { - result = (*env)->NewByteArray(env, pwLen); - (*env)->SetByteArrayRegion(env, result, 0, pwLen, pwStr); - } else { - result = NULL; + // find existing: + NSDictionary *query = @{ + (__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword, + (__bridge id)kSecAttrService: [NSString stringWithCString:(char *)serviceStr encoding:NSUTF8StringEncoding], + (__bridge id)kSecAttrAccount: [NSString stringWithCString:(char *)keyStr encoding:NSUTF8StringEncoding], + (__bridge id)kSecReturnAttributes: @YES, + (__bridge id)kSecReturnData: @YES, + (__bridge id)kSecMatchLimit: (__bridge id)kSecMatchLimitOne + }; + CFDictionaryRef result = NULL; + OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, (CFTypeRef *)&result); + jbyteArray password = NULL; + if (status == errSecSuccess && result != NULL) { + // retrieve password: + NSDictionary *attributes = (__bridge_transfer NSDictionary *)result; + NSData *passwordData = attributes[(__bridge id)kSecValueData]; + password = (*env)->NewByteArray(env, (int)passwordData.length); + (*env)->SetByteArrayRegion(env, password, 0, (int)passwordData.length, (jbyte *)passwordData.bytes); + } else if (status != errSecItemNotFound) { + NSLog(@"Error retrieving item from keychain. Status code: %d", (int)status); } + (*env)->ReleaseByteArrayElements(env, service, serviceStr, JNI_ABORT); (*env)->ReleaseByteArrayElements(env, key, keyStr, JNI_ABORT); - if (pwStr) { - SecKeychainItemFreeContent(NULL, pwStr); - } - return result; + return password; } JNIEXPORT jint JNICALL Java_org_cryptomator_macos_keychain_MacKeychain_00024Native_deletePassword(JNIEnv *env, jobject thisObj, jbyteArray service, jbyteArray key) { - const int serviceLen = (*env)->GetArrayLength(env, service); jbyte *serviceStr = (*env)->GetByteArrayElements(env, service, NULL); - const int keyLen = (*env)->GetArrayLength(env, key); jbyte *keyStr = (*env)->GetByteArrayElements(env, key, NULL); - SecKeychainItemRef itemRef = NULL; - OSStatus status = SecKeychainFindGenericPassword( - NULL, // default keychain - serviceLen, // length of service name - (char *)serviceStr, // service name - keyLen, // length of account name - (char *)keyStr, // account name - NULL, // length of password - NULL, // pointer to password data - &itemRef // the item reference - ); - if (status == errSecSuccess) { - status = SecKeychainItemDelete( - itemRef // the item reference - ); + // find existing: + NSDictionary *query = @{ + (__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword, + (__bridge id)kSecAttrService: [NSString stringWithCString:(char *)serviceStr encoding:NSUTF8StringEncoding], + (__bridge id)kSecAttrAccount: [NSString stringWithCString:(char *)keyStr encoding:NSUTF8StringEncoding], + (__bridge id)kSecMatchLimit: (__bridge id)kSecMatchLimitOne + }; + CFDictionaryRef result = NULL; + OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, (CFTypeRef *)&result); + if (status == errSecSuccess && result != NULL) { + // delete: + status = SecItemDelete((__bridge CFDictionaryRef)query); + } else if (status != errSecItemNotFound) { + NSLog(@"Error deleting item from keychain. Status code: %d", (int)status); } + (*env)->ReleaseByteArrayElements(env, service, serviceStr, JNI_ABORT); (*env)->ReleaseByteArrayElements(env, key, keyStr, JNI_ABORT); - if (itemRef) { - CFRelease(itemRef); - } return status; }