Skip to content

Commit

Permalink
Merge pull request #46 from purejava/SecItem
Browse files Browse the repository at this point in the history
  • Loading branch information
tobihagemann authored Jul 1, 2024
2 parents 3b2ebdd + 0fcd405 commit 5bfab0e
Showing 1 changed file with 64 additions and 78 deletions.
142 changes: 64 additions & 78 deletions src/main/native/org_cryptomator_macos_keychain_MacKeychain_Native.m
Original file line number Diff line number Diff line change
Expand Up @@ -7,117 +7,103 @@
//

#import "org_cryptomator_macos_keychain_MacKeychain_Native.h"
#import <Foundation/Foundation.h>
#import <Security/Security.h>

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;
}

0 comments on commit 5bfab0e

Please sign in to comment.