diff --git a/src/org/dyorgio/jna/platform/mac/CoreFoundation.java b/src/org/dyorgio/jna/platform/mac/CoreFoundation.java new file mode 100644 index 000000000..80723390c --- /dev/null +++ b/src/org/dyorgio/jna/platform/mac/CoreFoundation.java @@ -0,0 +1,14 @@ +package org.dyorgio.jna.platform.mac; + +import com.sun.jna.Memory; +import com.sun.jna.Native; +import com.sun.jna.Pointer; +import com.sun.jna.PointerType; +import com.sun.jna.ptr.PointerByReference; + +public interface CoreFoundation extends com.sun.jna.platform.mac.CoreFoundation { + CoreFoundation INSTANCE = Native.load("CoreFoundation", CoreFoundation.class); + + void CFDictionaryAddValue(com.sun.jna.platform.mac.CoreFoundation.CFMutableDictionaryRef theDict, PointerType key, PointerType value); + void CFDictionaryGetKeysAndValues(com.sun.jna.platform.mac.CoreFoundation.CFMutableDictionaryRef theDict, PointerByReference keys, Pointer[] values); +} \ No newline at end of file diff --git a/src/org/dyorgio/jna/platform/mac/Security.java b/src/org/dyorgio/jna/platform/mac/Security.java new file mode 100644 index 000000000..d1d5b5867 --- /dev/null +++ b/src/org/dyorgio/jna/platform/mac/Security.java @@ -0,0 +1,304 @@ +/* + * Copyright (C) 2015 QAware GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.dyorgio.jna.platform.mac; + +import com.sun.jna.*; +import com.sun.jna.platform.mac.CoreFoundation; +import com.sun.jna.platform.mac.CoreFoundation.CFStringRef; +import com.sun.jna.ptr.IntByReference; +import com.sun.jna.ptr.PointerByReference; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +/** + * The JNA library interface to access the Security library under MacOS. + * + * @author lreimer + */ +public interface Security extends Library { + Logger log = LogManager.getLogger(Security.class); + Security INSTANCE = Native.load("Security", Security.class); + NativeLibrary NATIVE_INSTANCE = NativeLibrary.getInstance("Security"); + + // String constants. These are needed as reference instead of value, especially kSecValueRef + CFStringRef kSecClass = new CFStringRef(NATIVE_INSTANCE.getGlobalVariableAddress("kSecClass").getPointer(0)); + CFStringRef kSecAttrLabel = new CFStringRef(NATIVE_INSTANCE.getGlobalVariableAddress("kSecAttrLabel").getPointer(0)); + CFStringRef kSecValueRef = new CFStringRef(NATIVE_INSTANCE.getGlobalVariableAddress("kSecValueRef").getPointer(0)); + CFStringRef kSecClassCertificate = new CFStringRef(NATIVE_INSTANCE.getGlobalVariableAddress("kSecClassCertificate").getPointer(0)); + CFStringRef kSecMatchEmailAddressIfPresent = new CFStringRef(NATIVE_INSTANCE.getGlobalVariableAddress("kSecMatchEmailAddressIfPresent").getPointer(0)); + CFStringRef kSecMatchLimit = new CFStringRef(NATIVE_INSTANCE.getGlobalVariableAddress("kSecMatchLimit").getPointer(0)); + CFStringRef kSecMatchLimitAll = new CFStringRef(NATIVE_INSTANCE.getGlobalVariableAddress("kSecMatchLimitAll").getPointer(0)); + CFStringRef kSecUseKeychain = new CFStringRef(NATIVE_INSTANCE.getGlobalVariableAddress("kSecUseKeychain").getPointer(0)); + enum Status { + SUCCESS("errSecSuccess", 0, "No error."), + UNIMPLEMENTED("errSecUnimplemented", -4, "Function or operation not implemented."), + IO("errSecIO", -36, "I/O error (bummers)"), + OPWR("errSecOpWr", -49, "File already open with write permission"), + PARAM("errSecParam", -50, "One or more parameters passed to a function where not valid."), + WRITE_PERMISSION("wrPermErr", -61, "Write permissions error."), + ALLOCATE("errSecAllocate", -108, "Failed to allocate memory."), + USER_CANCELED("errSecUserCanceled", -128, "User canceled the operation."), + BAD_REQ("errSecBadReq", -909, "Bad parameter or invalid state for operation."), + INTERNAL_COMPONENT("errSecInternalComponent", -2070,"Internal component failure"), + NOT_AVAILABLE("errSecNotAvailable", -25291, "No keychain is available. You may need to restart your computer."), + DUPLICATE_ITEM("errSecDuplicateItem", -25299, "The specified item already exists in the keychain."), + ITEM_NOT_FOUND("errSecItemNotFound", -25300, "The specified item could not be found in the keychain."), + INTERACTION_NOT_ALLOWED("errSecInteractionNotAllowed", -25308, "User interaction is not allowed."), + DECODE ("errSecDecode", -26275, "Unable to decode the provided data."), + AUTH_FAILED("errSecAuthFailed", -25293, "The user name or passphrase you entered is not correct."), + INVALID_SET("errAuthorizationInvalidSet", -60001, "The authorization rights are invalid."), + INVALID_REF("errAuthorizationInvalidRef", -60002, "The authorization reference is invalid."), + INVALID_TAG("errAuthorizationInvalidTag", -60003, "The authorization tag is invalid."), + INVALID_POINTER("errAuthorizationInvalidPointer", -60004, "The returned authorization is invalid."), + DENIED("errAuthorizationDenied", -60005, "The authorization was denied."), + CANCELED("errAuthorizationCanceled", -60006, "The authorization was canceled by the user."), + // INTERACTION_NOT_ALLOWED definition collision. added AUTH prefix + AUTH_INTERACTION_NOT_ALLOWED("errAuthorizationInteractionNotAllowed", -60007, "The authorization was denied since no user interaction was possible."), + INTERNAL("errAuthorizationInternal", -60008, "Unable to obtain authorization for this operation."), + EXTERNALIZE_NOT_ALLOWED("errAuthorizationExternalizeNotAllowed", -60009, "The authorization is not allowed to be converted to an external format."), + INTERNALIZE_NOT_ALLOWED("errAuthorizationInternalizeNotAllowed", -60010, "The authorization is not allowed to be created from an external format."), + INVALID_FLAGS("errAuthorizationInvalidFlags", -60011, "The provided option flag(s) are invalid for this authorization operation."), + TOOL_EXECUTE_FAILURE("errAuthorizationToolExecuteFailure", -60031, "The specified program could not be executed."), + TOOL_ENVIRONMENT_ERROR("errAuthorizationToolEnvironmentError", -60032, "An invalid status was returned during execution of a privileged tool."), + BAD_ADDRESS("errAuthorizationBadAddress", -60033, "The requested socket address is invalid (must be 0-1023 inclusive)."), + + UNKNOWN("unknown", -99999, "Code not mapped"); + + int code; + String name; + String message; + Status(String name, int code, String message) { + this.name = name; + this.code = code; + this.message = message; + } + + public static Status parse(int code) { + for(Status status : Status.values()) { + if(code == status.code) { + return status; + } + } + log.warn("Unknown error code {}, falling back to [{}] {} code.", code, UNKNOWN.name, UNKNOWN.code); + return UNKNOWN; + } + + @Override + public String toString() { + return String.format("(%d) [%s] %s", this.code, name, this.message); + } + } + + int SecKeychainCopyDomainDefault(int domain, Pointer SecKeychainRef); + + int SecTrustSettingsSetTrustSettings(PointerType certRef, int domain, PointerType trustSettingsDictOrArray); + + /** + * Returns one or more keychain items that match a search query, or copies attributes of specific keychain items. + *
+ * OSStatus SecItemCopyMatching ( CFDictionaryRef query, CFTypeRef _Nullable *result ); + * + * @param query A dictionary containing an item class specification (Keychain Item Class Keys and Values) and optional attributes for controlling the search. See Keychain Services Constants for a description of currently defined search attributes. + * @param result On return, a reference to the found items. The exact type of the result is based on the search attributes supplied in the query, as discussed below. + * @return A result code. See Keychain Services Result Codes. Call SecCopyErrorMessageString (OS X only) to get a human-readable string explaining the result. + */ + int SecItemCopyMatching(Pointer query, Pointer result); + + /** + * Adds one or more items to a keychain. + *
+ * OSStatus SecItemAdd ( CFDictionaryRef attributes, CFTypeRef _Nullable *result ); + * + * @param attributes A dictionary containing an item class key-value pair (Keychain Item Class Keys and Values) and optional attribute key-value pairs (Attribute Item Keys and Values) specifying the item's attribute values. + * @param result On return, a reference to the newly added items. The exact type of the result is based on the values supplied in attributes, as discussed below. Pass NULL if this result is not required. + * @return A result code. See Keychain Services Result Codes. Call SecCopyErrorMessageString (OS X only) to get a human-readable string explaining the result. + */ + int SecItemAdd(Pointer attributes, PointerByReference result); + + /** + * Modifies items that match a search query. + *
+ * OSStatus SecItemUpdate ( CFDictionaryRef query, CFDictionaryRef attributesToUpdate ); + * + * @param query A dictionary containing an item class specification and optional attributes for controlling the search. Specify the items whose values you wish to change. See Search Keys for a description of currently defined search attributes. + * @param attributesToUpdate A dictionary containing the attributes whose values should be changed, along with the new values. Only real keychain attributes are permitted in this dictionary (no "meta" attributes are allowed.) See Attribute Item Keys and Values for a description of currently defined value attributes. + * @return A result code. See Keychain Services Result Codes. Call SecCopyErrorMessageString (OS X only) to get a human-readable string explaining the result. + */ + int SecItemUpdate(Pointer query, Pointer attributesToUpdate); + + /** + * Deletes items that match a search query. + *
+ * OSStatus SecItemDelete ( CFDictionaryRef query ); + * + * @param query A dictionary containing an item class specification and optional attributes for controlling the search. See Search Keys for a description of currently defined search attributes. + * @return A result code. See Keychain Services Result Codes. Call SecCopyErrorMessageString (OS X only) to get a human-readable string explaining the result. + */ + int SecItemDelete(Pointer query); + + /** + * Returns a string explaining the meaning of a security result code. + *
+ * CFStringRef SecCopyErrorMessageString ( OSStatus status, void *reserved ); + * + * @param status A result code of type OSStatus or CSSM_RETURN, returned by a security or CSSM function. + * @param reserved Reserved for future use. Pass NULL for this parameter. + * @return A human-readable string describing the result, or NULL if no string is available for the specified result code. You must call the CFRelease function to release this object when you are finished using it. + */ + Pointer SecCopyErrorMessageString(int status, Pointer reserved); + + /** + * Opens a keychain. + * + * @param pathName A constant character string representing the POSIX path to the keychain to open. + * @param keychainRef On return, a pointer to the keychain object. You must call the CFRelease function to release this object when you are finished using it. + * @return A result code. See Keychain Services Result Codes. The result code errSecNoDefaultKeychain indicates that no default keychain could be found. The result code errSecDuplicateItem indicates that you tried to add a password that already exists in the keychain. The result code errSecDataTooLarge indicates that you tried to add more data than is allowed for a structure of this type. Call SecCopyErrorMessageString (OS X only) to get a human-readable string explaining the result. + */ + int SecKeychainOpen(String pathName, PointerByReference keychainRef); + + /** + * Adds a new generic password to a keychain. + *
+ * OSStatus SecKeychainAddGenericPassword ( SecKeychainRef keychain, UInt32 serviceNameLength, const char *serviceName, UInt32 accountNameLength, const char *accountName, UInt32 passwordLength, const void *passwordData, SecKeychainItemRef _Nullable *itemRef ); + * + * @param keychain A reference to the keychain in which to store a generic password. Pass NULL to specify the default keychain. + * @param serviceNameLength The length of the serviceName character string. + * @param serviceName A UTF-8 encoded character string representing the service name. + * @param accountNameLength The length of the accountName character string. + * @param accountName A UTF-8 encoded character string representing the account name. + * @param passwordLength The length of the passwordData buffer. + * @param passwordData A pointer to a buffer containing the password data to be stored in the keychain. Before calling this function, allocate enough memory for the buffer to hold the data you want to store. + * @param itemRef On return, a pointer to a reference to the new keychain item. Pass NULL if you don’t want to obtain this object. You must allocate the memory for this pointer. You must call the CFRelease function to release this object when you are finished using it. + * @return A result code. See Keychain Services Result Codes. The result code errSecNoDefaultKeychain indicates that no default keychain could be found. The result code errSecDuplicateItem indicates that you tried to add a password that already exists in the keychain. The result code errSecDataTooLarge indicates that you tried to add more data than is allowed for a structure of this type. Call SecCopyErrorMessageString (OS X only) to get a human-readable string explaining the result. + */ + int SecKeychainAddGenericPassword(Pointer keychain, int serviceNameLength, byte[] serviceName, int accountNameLength, byte[] accountName, int passwordLength, byte[] passwordData, PointerByReference itemRef); + + /** + * Finds the first generic password based on the attributes passed. + *
+ * OSStatus SecKeychainFindGenericPassword ( CFTypeRef keychainOrArray, UInt32 serviceNameLength, const char *serviceName, UInt32 accountNameLength, const char *accountName, UInt32 *passwordLength, void * _Nullable *passwordData, SecKeychainItemRef _Nullable *itemRef ); + * + * @param keychainOrArray A reference to an array of keychains to search, a single keychain, or NULL to search the user’s default keychain search list. + * @param serviceNameLength The length of the serviceName character string. + * @param serviceName A UTF-8 encoded character string representing the service name. + * @param accountNameLength The length of the accountName character string. + * @param accountName A UTF-8 encoded character string representing the account name. + * @param passwordLength On return, the length of the buffer pointed to by passwordData. + * @param passwordData On return, a pointer to a buffer that holds the password data. Pass NULL if you want to obtain the item object but not the password data. In this case, you must also pass NULL in the passwordLength parameter. You should use the SecKeychainItemFreeContent function to free the memory pointed to by this parameter. + * @param itemRef On return, a pointer to the item object of the generic password. You are responsible for releasing your reference to this object. Pass NULL if you don’t want to obtain this object. + * @return A result code. See Keychain Services Result Codes. Call SecCopyErrorMessageString (OS X only) to get a human-readable string explaining the result. + */ + int SecKeychainFindGenericPassword(Pointer keychainOrArray, int serviceNameLength, byte[] serviceName, int accountNameLength, byte[] accountName, IntByReference passwordLength, PointerByReference passwordData, PointerByReference itemRef); + + + /** + * Adds a new Internet password to a keychain. + *
+ * OSStatus SecKeychainAddInternetPassword ( SecKeychainRef keychain, UInt32 serverNameLength, const char *serverName, UInt32 securityDomainLength, const char *securityDomain, UInt32 accountNameLength, const char *accountName, UInt32 pathLength, const char *path, UInt16 port, SecProtocolType protocol, SecAuthenticationType authenticationType, UInt32 passwordLength, const void *passwordData, SecKeychainItemRef _Nullable *itemRef ); + * + * @param keychain A reference to the keychain in which to store an Internet password. Pass NULL to specify the user’s default keychain. + * @param serverNameLength The length of the serverName character string. + * @param serverName A UTF-8 encoded character string representing the server name. + * @param securityDomainLength The length of the securityDomain character string. + * @param securityDomain A UTF-8 encoded character string representing the security domain. This parameter is optional. Pass NULL if the protocol does not require it. + * @param accountNameLength The length of the accountName character string. + * @param accountName A UTF-8 encoded character string representing the account name. + * @param pathLength The length of the path character string. + * @param path A UTF-8 encoded character string representing the path. + * @param port The TCP/IP port number. If no specific port number is associated with this password, pass 0. + * @param protocol The protocol associated with this password. See Keychain Protocol Type Constants for a description of possible values. + * @param authenticationType The authentication scheme used. See Keychain Authentication Type Constants for a description of possible values. Pass the constant kSecAuthenticationTypeDefault, to specify the default authentication scheme. + * @param passwordLength The length of the passwordData buffer. + * @param passwordData A pointer to a buffer containing the password data to be stored in the keychain. + * @param itemRef On return, a pointer to a reference to the new keychain item. Pass NULL if you don’t want to obtain this object. You must allocate the memory for this pointer. You must call the CFRelease function to release this object when you are finished using it. + * @return A result code. See Keychain Services Result Codes. The result code errSecNoDefaultKeychain indicates that no default keychain could be found. The result code errSecDuplicateItem indicates that you tried to add a password that already exists in the keychain. The result code errSecDataTooLarge indicates that you tried to add more data than is allowed for a structure of this type. Call SecCopyErrorMessageString (OS X only) to get a human-readable string explaining the result. + */ + int SecKeychainAddInternetPassword(Pointer keychain, int serverNameLength, byte[] serverName, int securityDomainLength, byte[] securityDomain, int accountNameLength, byte[] accountName, int pathLength, byte[] path, int port, int protocol, int authenticationType, int passwordLength, byte[] passwordData, PointerByReference itemRef); + + /** + * Finds the first Internet password based on the attributes passed. + *
+ * OSStatus SecKeychainFindInternetPassword ( CFTypeRef keychainOrArray, UInt32 serverNameLength, const char *serverName, UInt32 securityDomainLength, const char *securityDomain, UInt32 accountNameLength, const char *accountName, UInt32 pathLength, const char *path, UInt16 port, SecProtocolType protocol, SecAuthenticationType authenticationType, UInt32 *passwordLength, void * _Nullable *passwordData, SecKeychainItemRef _Nullable *itemRef ); + * + * @param keychainOrArray A reference to an array of keychains to search, a single keychain or NULL to search the user’s default keychain search list. + * @param serverNameLength The length of the serverName character string. + * @param serverName A UTF-8 encoded character string representing the server name. + * @param securityDomainLength The length of the securityDomain character string. + * @param securityDomain A UTF-8 encoded character string representing the security domain. This parameter is optional, as not all protocols require it. Pass NULL if it is not required. + * @param accountNameLength The length of the accountName character string. + * @param accountName A UTF-8 encoded character string representing the account name. + * @param pathLength The length of the path character string. + * @param path A UTF-8 encoded character string representing the path. + * @param port The TCP/IP port number. Pass 0 to ignore the port number. + * @param protocol The protocol associated with this password. See Keychain Protocol Type Constants for a description of possible values. + * @param authenticationType The authentication scheme used. See Keychain Authentication Type Constants for a description of possible values. Pass the constant kSecAuthenticationTypeDefault, to specify the default authentication scheme. + * @param passwordLength On return, the length of the buffer pointed to by passwordData. + * @param passwordData On return, a pointer to a buffer containing the password data. Pass NULL if you want to obtain the item object but not the password data. In this case, you must also pass NULL in the passwordLength parameter. You should use the SecKeychainItemFreeContent function to free the memory pointed to by this parameter. + * @param itemRef On return, a pointer to the item object of the Internet password. You are responsible for releasing your reference to this object. Pass NULL if you don’t want to obtain this object. + * @return A result code. See Keychain Services Result Codes. Call SecCopyErrorMessageString (OS X only) to get a human-readable string explaining the result. + */ + int SecKeychainFindInternetPassword(Pointer keychainOrArray, int serverNameLength, byte[] serverName, int securityDomainLength, byte[] securityDomain, int accountNameLength, byte[] accountName, int pathLength, byte[] path, int port, int protocol, int authenticationType, IntByReference passwordLength, PointerByReference passwordData, PointerByReference itemRef); + + /** + * Deletes a keychain item from the default keychain’s permanent data store. + *
+ * OSStatus SecKeychainItemDelete ( SecKeychainItemRef itemRef ); + * + * @param itemRef A keychain item object of the item to delete. You must call the CFRelease function to release this object when you are finished using it. + * @return A result code. See Keychain Services Result Codes. Call SecCopyErrorMessageString (OS X only) to get a human-readable string explaining the result. + */ + int SecKeychainItemDelete(Pointer itemRef); + + + /** + * Updates an existing keychain item after changing its attributes and/or data. + *
+ * OSStatus SecKeychainItemModifyContent ( SecKeychainItemRef itemRef, const SecKeychainAttributeList *attrList, UInt32 length, const void *data ); + * + * @param itemRef A reference to the keychain item to modify. + * @param attrList A pointer to the list of attributes to set and their new values. Pass NULL if you have no need to modify attributes. + * @param length The length of the buffer pointed to by the data parameter. Pass 0 if you pass NULL in the data parameter. + * @param data A pointer to a buffer containing the data to store. Pass NULL if you do not need to modify the data. + * @return A result code. See Keychain Services Result Codes. Call SecCopyErrorMessageString (OS X only) to get a human-readable string explaining the result. + */ + int SecKeychainItemModifyContent(Pointer itemRef, Pointer attrList, int length, byte[] data); + + /** + * Releases the memory used by the keychain attribute list and the keychain data retrieved in a call to the SecKeychainItemCopyContent function. + *
+ * OSStatus SecKeychainItemFreeContent ( SecKeychainAttributeList *attrList, void *data ); + * + * @param attrList A pointer to the attribute list to release. Pass NULL if there is no attribute list to release. + * @param data A pointer to the data buffer to release. Pass NULL if there is no data to release. + * @return A result code. See Keychain Services Result Codes. Call SecCopyErrorMessageString (OS X only) to get a human-readable string explaining the result. + */ + int SecKeychainItemFreeContent(Pointer attrList, Pointer data); + + /** + * Returns a DER representation of a certificate given a certificate object. + * + * @param certificate The certificate object for which you wish to return the DER (Distinguished Encoding Rules) + * representation of the X.509 certificate. + * @return The DER representation of the certificate. Call the CFRelease function to release this object when you + * are finished with it. Returns NULL if the data passed in the certificate parameter is not a valid certificate + * object. + */ + CoreFoundation.CFDataRef SecCertificateCopyData(CoreFoundation.CFDataRef certificate); + + CoreFoundation.CFTypeRef SecCertificateCreateWithData(CoreFoundation.CFAllocatorRef allocator, CoreFoundation.CFDataRef data); + + CFStringRef SecCertificateCopySubjectSummary(CoreFoundation.CFTypeRef certificate); +} \ No newline at end of file diff --git a/src/org/dyorgio/jna/platform/mac/Security_Main_Temp.java b/src/org/dyorgio/jna/platform/mac/Security_Main_Temp.java new file mode 100644 index 000000000..213998ee3 --- /dev/null +++ b/src/org/dyorgio/jna/platform/mac/Security_Main_Temp.java @@ -0,0 +1,154 @@ +package org.dyorgio.jna.platform.mac; + +import com.sun.jna.Memory; +import org.apache.commons.ssl.Base64; +import qz.installer.certificate.MacCertificateNativeAccess; + +import static com.sun.jna.platform.mac.CoreFoundation.*; + +public class Security_Main_Temp { + static int a; + public static void main(String ... args) { + MacCertificateNativeAccess nativeAccess = new MacCertificateNativeAccess(); + nativeAccess.findIdsByEmail("","support@qz.io"); + } + public static void oldMain () { + CFAllocatorRef alloc = INSTANCE.CFAllocatorGetDefault(); + // Keys + CFStringRef cfLabel = CFStringRef.createCFString("My Certificate"); + //CFDataRef cfValueAsData = getCFDataCert(DER_ENCODED_CERT); + + byte[] certBytes = Base64.decodeBase64(CLEAN_CERT); + Memory nativeBytes = new Memory(certBytes.length); + nativeBytes.write(0, certBytes, 0, certBytes.length); + + CFDataRef cfValueAsData = INSTANCE.CFDataCreate(alloc, nativeBytes, new CFIndex(nativeBytes.size())); + CFTypeRef cfValueAsCert = Security.INSTANCE.SecCertificateCreateWithData(null, cfValueAsData); + + CFStringRef summary = Security.INSTANCE.SecCertificateCopySubjectSummary(cfValueAsCert); + System.out.println("SecCertificateCopySubjectSummary: " + summary.stringValue()); + summary.release(); + + //NativeLibrary lib = NativeLibrary.getInstance("Security"); + //Pointer paddr = ((NativeLibrary)(Security.INSTANCE)).getGlobalVariableAddress("kSecValueRef"); + //System.out.println("~~~ " + paddr.getLong(0)); + // Write to dictionary + Memory keystoreRefData = new Memory(8); + Security.INSTANCE.SecKeychainCopyDomainDefault(1, keystoreRefData.share(0)); + CFTypeRef keystoreRef = new CFTypeRef(keystoreRefData.getPointer(0)); + + CFMutableDictionaryRef dict = INSTANCE.CFDictionaryCreateMutable(alloc, new CFIndex(4), null, null); + + CoreFoundation.INSTANCE.CFDictionaryAddValue(dict, Security.kSecClass, Security.kSecClassCertificate); + CoreFoundation.INSTANCE.CFDictionaryAddValue(dict, Security.kSecAttrLabel, cfLabel); + CoreFoundation.INSTANCE.CFDictionaryAddValue(dict, Security.kSecValueRef, cfValueAsCert); + CoreFoundation.INSTANCE.CFDictionaryAddValue(dict, Security.kSecUseKeychain, keystoreRef); + + System.out.println(CoreFoundation.INSTANCE.CFCopyDescription(dict).stringValue()); + + // Call SecAddItem + int retVal = Security.INSTANCE.SecTrustSettingsSetTrustSettings(cfValueAsCert, 1, null); + System.out.println(retVal); + retVal = Security.INSTANCE.SecItemAdd(dict.getPointer(), null); + System.out.println(retVal); + Security.Status code = Security.Status.parse(retVal); + + // Show output + switch(code) { + case SUCCESS: + System.out.println(code); + break; + default: + System.err.println(code); + } + + cfLabel.release(); + cfValueAsData.release(); + + dict.release(); + alloc.release(); + } + + // Attempt to convert data bytes to CFDataRef + private static CFDataRef getCFDataCert(String derEncodedData) { + String certFixed = DER_ENCODED_CERT.split("-----")[2]; + System.out.println(certFixed); + byte[] certBytes = Base64.decodeBase64(certFixed); + Memory nativeBytes = new Memory(certBytes.length); + nativeBytes.write(0, certBytes, 0, certBytes.length); + return INSTANCE.CFDataCreate(null, nativeBytes, new CFIndex(certBytes.length)); + } + + + // SEE ALSO: https://www.andyibanez.com/posts/using-ios-keychain-swift/ + + /** + * let secCert = SecCertificateCreateWithData(nil, certInDer as CFData) // certInDer is Certificate(.der) data + * var keychainQueryDictionary = [String : Any]() + * + * if let tempSecCert = secCert { + * keychainQueryDictionary = [kSecClass as String : kSecClassCertificate, kSecValueRef as String : tempSecCert, kSecAttrLabel as String: "My Certificate"] + * } + * + * let summary = SecCertificateCopySubjectSummary(secCert!)! as String + * print("Cert summary: \(summary)") + * + * let status = SecItemAdd(keychainQueryDictionary as CFDictionary, nil) + * + * guard status == errSecSuccess else { + * print("Error") + * return + * } + * + * print("success") + */ + static String CLEAN_CERT = "MIIELzCCAxegAwIBAgIJALm151zCHDxiMA0GCSqGSIb3DQEBCwUAMIGsMQswCQYD" + + "VQQGEwJVUzELMAkGA1UECAwCTlkxEjAQBgNVBAcMCUNhbmFzdG90YTEbMBkGA1UE" + + "CgwSUVogSW5kdXN0cmllcywgTExDMRswGQYDVQQLDBJRWiBJbmR1c3RyaWVzLCBM" + + "TEMxGTAXBgNVBAMMEHF6aW5kdXN0cmllcy5jb20xJzAlBgkqhkiG9w0BCQEWGHN1" + + "cHBvcnRAcXppbmR1c3RyaWVzLmNvbTAgFw0xNTAzMDEyMzM4MjlaGA8yMTE1MDMw" + + "MjIzMzgyOVowgawxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJOWTESMBAGA1UEBwwJ" + + "Q2FuYXN0b3RhMRswGQYDVQQKDBJRWiBJbmR1c3RyaWVzLCBMTEMxGzAZBgNVBAsM" + + "ElFaIEluZHVzdHJpZXMsIExMQzEZMBcGA1UEAwwQcXppbmR1c3RyaWVzLmNvbTEn" + + "MCUGCSqGSIb3DQEJARYYc3VwcG9ydEBxemluZHVzdHJpZXMuY29tMIIBIjANBgkq" + + "hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuWsBa6uk+RM4OKBZTRfIIyqaaFD71FAS" + + "7kojAQ+ySMpYuqLjIVZuCh92o1FGBvyBKUFc6knAHw5749yhLCYLXhzWwiNW2ri1" + + "Jwx/d83Wnaw6qA3lt++u3tmiA8tsFtss0QZW0YBpFsIqhamvB3ypwu0bdUV/oH7g" + + "/s8TFR5LrDfnfxlLFYhTUVWuWzMqEFAGnFG3uw/QMWZnQgkGbx0LMcYzdqFb7/vz" + + "rTSHfjJsisUTWPjo7SBnAtNYCYaGj0YH5RFUdabnvoTdV2XpA5IPYa9Q597g/M0z" + + "icAjuaK614nKXDaAUCbjki8RL3OK9KY920zNFboq/jKG6rKW2t51ZQIDAQABo1Aw" + + "TjAdBgNVHQ4EFgQUA0XGTcD6jqkL2oMPQaVtEgZDqV4wHwYDVR0jBBgwFoAUA0XG" + + "TcD6jqkL2oMPQaVtEgZDqV4wDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOC" + + "AQEAijcT5QMVqrWWqpNEe1DidzQfSnKo17ZogHW+BfUbxv65JbDIntnk1XgtLTKB" + + "VAdIWUtGZbXxrp16NEsh96V2hjDIoiAaEpW+Cp6AHhIVgVh7Q9Knq9xZ1t6H8PL5" + + "QiYQKQgJ0HapdCxlPKBfUm/Mj1ppNl9mPFJwgHmzORexbxrzU/M5i2jlies+CXNq" + + "cvmF2l33QNHnLwpFGwYKs08pyHwUPp6+bfci6lRvavztgvnKroWWIRq9ZPlC0yVK" + + "FFemhbCd7ZVbrTo0NcWZM1PTAbvlOikV9eh3i1Vot+3dJ8F27KwUTtnV0B9Jrxum" + + "W9P3C48mvwTxYZJFOu0N9UBLLg=="; + + static String DER_ENCODED_CERT = "-----BEGIN CERTIFICATE-----\n" + + "MIIELzCCAxegAwIBAgIJALm151zCHDxiMA0GCSqGSIb3DQEBCwUAMIGsMQswCQYD\n" + + "VQQGEwJVUzELMAkGA1UECAwCTlkxEjAQBgNVBAcMCUNhbmFzdG90YTEbMBkGA1UE\n" + + "CgwSUVogSW5kdXN0cmllcywgTExDMRswGQYDVQQLDBJRWiBJbmR1c3RyaWVzLCBM\n" + + "TEMxGTAXBgNVBAMMEHF6aW5kdXN0cmllcy5jb20xJzAlBgkqhkiG9w0BCQEWGHN1\n" + + "cHBvcnRAcXppbmR1c3RyaWVzLmNvbTAgFw0xNTAzMDEyMzM4MjlaGA8yMTE1MDMw\n" + + "MjIzMzgyOVowgawxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJOWTESMBAGA1UEBwwJ\n" + + "Q2FuYXN0b3RhMRswGQYDVQQKDBJRWiBJbmR1c3RyaWVzLCBMTEMxGzAZBgNVBAsM\n" + + "ElFaIEluZHVzdHJpZXMsIExMQzEZMBcGA1UEAwwQcXppbmR1c3RyaWVzLmNvbTEn\n" + + "MCUGCSqGSIb3DQEJARYYc3VwcG9ydEBxemluZHVzdHJpZXMuY29tMIIBIjANBgkq\n" + + "hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuWsBa6uk+RM4OKBZTRfIIyqaaFD71FAS\n" + + "7kojAQ+ySMpYuqLjIVZuCh92o1FGBvyBKUFc6knAHw5749yhLCYLXhzWwiNW2ri1\n" + + "Jwx/d83Wnaw6qA3lt++u3tmiA8tsFtss0QZW0YBpFsIqhamvB3ypwu0bdUV/oH7g\n" + + "/s8TFR5LrDfnfxlLFYhTUVWuWzMqEFAGnFG3uw/QMWZnQgkGbx0LMcYzdqFb7/vz\n" + + "rTSHfjJsisUTWPjo7SBnAtNYCYaGj0YH5RFUdabnvoTdV2XpA5IPYa9Q597g/M0z\n" + + "icAjuaK614nKXDaAUCbjki8RL3OK9KY920zNFboq/jKG6rKW2t51ZQIDAQABo1Aw\n" + + "TjAdBgNVHQ4EFgQUA0XGTcD6jqkL2oMPQaVtEgZDqV4wHwYDVR0jBBgwFoAUA0XG\n" + + "TcD6jqkL2oMPQaVtEgZDqV4wDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOC\n" + + "AQEAijcT5QMVqrWWqpNEe1DidzQfSnKo17ZogHW+BfUbxv65JbDIntnk1XgtLTKB\n" + + "VAdIWUtGZbXxrp16NEsh96V2hjDIoiAaEpW+Cp6AHhIVgVh7Q9Knq9xZ1t6H8PL5\n" + + "QiYQKQgJ0HapdCxlPKBfUm/Mj1ppNl9mPFJwgHmzORexbxrzU/M5i2jlies+CXNq\n" + + "cvmF2l33QNHnLwpFGwYKs08pyHwUPp6+bfci6lRvavztgvnKroWWIRq9ZPlC0yVK\n" + + "FFemhbCd7ZVbrTo0NcWZM1PTAbvlOikV9eh3i1Vot+3dJ8F27KwUTtnV0B9Jrxum\n" + + "W9P3C48mvwTxYZJFOu0N9UBLLg==\n" + + "-----END CERTIFICATE-----"; +} diff --git a/src/qz/installer/Installer.java b/src/qz/installer/Installer.java index d225110b4..bb16c1347 100644 --- a/src/qz/installer/Installer.java +++ b/src/qz/installer/Installer.java @@ -47,7 +47,9 @@ public enum PrivilegeLevel { public abstract Installer removeLegacyStartup(); public abstract Installer addAppLauncher(); + public abstract Installer removeAppLauncher(); public abstract Installer addStartupEntry(); + public abstract Installer removeStartupEntry(); public abstract Installer addSystemSettings(); public abstract Installer removeSystemSettings(); public abstract void spawn(List args) throws Exception; @@ -108,7 +110,9 @@ public static void uninstall() { TaskKiller.killAll(); getInstance(); log.info("Uninstalling from {}", instance.getDestination()); - instance.removeSharedDirectory() + instance.removeStartupEntry() + .removeAppLauncher() + .removeSharedDirectory() .removeSystemSettings() .removeCerts(); } @@ -333,4 +337,14 @@ public static Properties persistProperties(File oldFile, Properties newProps) { public void spawn(String ... args) throws Exception { spawn(new ArrayList(Arrays.asList(args))); } + + boolean hasVersion_2_0() { + // QZ Tray 2.1 removed the "auth" folder, this should be a valid test + try { + Path p = Paths.get(getDestination(), "auth"); + return p.toFile().isDirectory(); + } + catch(Exception ignore) {} + return false; + } } diff --git a/src/qz/installer/LinuxInstaller.java b/src/qz/installer/LinuxInstaller.java index 185954fc7..796c45d3e 100644 --- a/src/qz/installer/LinuxInstaller.java +++ b/src/qz/installer/LinuxInstaller.java @@ -45,11 +45,21 @@ public Installer addAppLauncher() { return this; } + public Installer removeAppLauncher() { + FileUtils.deleteQuietly(new File(APP_LAUNCHER)); + return this; + } + public Installer addStartupEntry() { addLauncher(STARTUP_LAUNCHER, false); return this; } + public Installer removeStartupEntry() { + FileUtils.deleteQuietly(new File(STARTUP_LAUNCHER)); + return this; + } + private void addLauncher(String location, boolean isStartup) { HashMap fieldMap = new HashMap<>(); // Dynamic fields @@ -69,6 +79,10 @@ private void addLauncher(String location, boolean isStartup) { } public Installer removeLegacyStartup() { + if(!hasVersion_2_0()) { + log.info("{} 2.0 was not found; skipping removal of legacy startup entries", ABOUT_TITLE); + return this; + } log.info("Removing legacy autostart entries for all users matching {} or {}", ABOUT_TITLE, PROPS_FILE); // assume users are in /home String[] shortcutNames = {ABOUT_TITLE, PROPS_FILE}; diff --git a/src/qz/installer/MacInstaller.java b/src/qz/installer/MacInstaller.java index 73b029d2a..a6a6a283d 100644 --- a/src/qz/installer/MacInstaller.java +++ b/src/qz/installer/MacInstaller.java @@ -36,6 +36,11 @@ public Installer addAppLauncher() { return this; } + public Installer removeAppLauncher() { + // not needed; unregistered when "QZ Tray.app" is deleted + return this; + } + public Installer addStartupEntry() { File dest = new File(LAUNCH_AGENT_PATH); HashMap fieldMap = new HashMap<>(); @@ -57,6 +62,13 @@ public Installer addStartupEntry() { return this; } + public Installer removeStartupEntry() { + // Remove startup entry + File dest = new File(LAUNCH_AGENT_PATH); + dest.delete(); + return this; + } + public void setDestination(String destination) { this.destination = destination; } @@ -74,9 +86,7 @@ public Installer addSystemSettings() { return this; } public Installer removeSystemSettings() { - // Remove startup entry - File dest = new File(LAUNCH_AGENT_PATH); - dest.delete(); + // not yet needed return this; } @@ -84,6 +94,10 @@ public Installer removeSystemSettings() { * Removes legacy (<= 2.0) startup entries */ public Installer removeLegacyStartup() { + if(!hasVersion_2_0()) { + log.info("{} 2.0 was not found; skipping removal of legacy startup entries", ABOUT_TITLE); + return this; + } log.info("Removing startup entries for all users matching " + ABOUT_TITLE); String script = "tell application \"System Events\" to delete " + "every login item where name is \"" + ABOUT_TITLE + "\"" diff --git a/src/qz/installer/WindowsInstaller.java b/src/qz/installer/WindowsInstaller.java index 87645882a..61130e23f 100644 --- a/src/qz/installer/WindowsInstaller.java +++ b/src/qz/installer/WindowsInstaller.java @@ -51,6 +51,10 @@ public void setDestination(String destination) { * Cycles through registry keys removing legacy (<= 2.0) startup entries */ public Installer removeLegacyStartup() { + if(!hasVersion_2_0()) { + log.info("{} 2.0 was not found; skipping removal of legacy startup entries", ABOUT_TITLE); + return this; + } log.info("Removing legacy startup entries for all users matching " + ABOUT_TITLE); for (String user : Advapi32Util.registryGetKeys(HKEY_USERS)) { WindowsUtilities.deleteRegValue(HKEY_USERS, user.trim() + "\\Software\\Microsoft\\Windows\\CurrentVersion\\Run", ABOUT_TITLE); @@ -65,7 +69,7 @@ public Installer removeLegacyStartup() { public Installer addAppLauncher() { try { - // Delete old 2.0 launcher + // Delete old 2.0 launcher in case a previous installer forgot to FileUtils.deleteQuietly(new File(COMMON_START_MENU + File.separator + "Programs" + File.separator + ABOUT_TITLE + ".lnk")); Path loc = Paths.get(COMMON_START_MENU.toString(), "Programs", ABOUT_TITLE); loc.toFile().mkdirs(); @@ -79,6 +83,11 @@ public Installer addAppLauncher() { return this; } + public Installer removeAppLauncher() { + removeLaunchers(START_MENU, COMMON_START_MENU, DESKTOP, PUBLIC_DESKTOP, RECENT); + return this; + } + public Installer addStartupEntry() { try { String lnk = WindowsSpecialFolders.COMMON_STARTUP + File.separator + ABOUT_TITLE + ".lnk"; @@ -91,16 +100,15 @@ public Installer addStartupEntry() { } return this; } - public Installer removeSystemSettings() { - // Cleanup registry - WindowsUtilities.deleteRegKey(HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\" + ABOUT_TITLE); - WindowsUtilities.deleteRegKey(HKEY_LOCAL_MACHINE, "Software\\" + ABOUT_TITLE); - WindowsUtilities.deleteRegKey(HKEY_LOCAL_MACHINE, DATA_DIR); - // Chrome protocol handler - WindowsUtilities.deleteRegData(HKEY_LOCAL_MACHINE, "SOFTWARE\\Policies\\Google\\Chrome\\URLWhitelist", String.format("%s://*", DATA_DIR)); + public Installer removeStartupEntry() { + removeLaunchers(COMMON_STARTUP); + return this; + } + + private void removeLaunchers(WindowsSpecialFolders ... folders) { // Cleanup launchers - for(WindowsSpecialFolders folder : new WindowsSpecialFolders[] { START_MENU, COMMON_START_MENU, DESKTOP, PUBLIC_DESKTOP, COMMON_STARTUP, RECENT }) { + for(WindowsSpecialFolders folder : folders) { try { new File(folder + File.separator + ABOUT_TITLE + ".lnk").delete(); // Since 2.1, start menus use subfolder @@ -110,6 +118,15 @@ public Installer removeSystemSettings() { } } catch(InvalidPathException | IOException | Win32Exception ignore) {} } + } + + public Installer removeSystemSettings() { + // Cleanup registry + WindowsUtilities.deleteRegKey(HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\" + ABOUT_TITLE); + WindowsUtilities.deleteRegKey(HKEY_LOCAL_MACHINE, "Software\\" + ABOUT_TITLE); + WindowsUtilities.deleteRegKey(HKEY_LOCAL_MACHINE, DATA_DIR); + // Chrome protocol handler + WindowsUtilities.deleteRegData(HKEY_LOCAL_MACHINE, "SOFTWARE\\Policies\\Google\\Chrome\\URLWhitelist", String.format("%s://*", DATA_DIR)); // Cleanup firewall rules ShellUtilities.execute("netsh.exe", "advfirewall", "firewall", "delete", "rule", String.format("name=%s", ABOUT_TITLE)); diff --git a/src/qz/installer/certificate/MacCertificateInstaller.java b/src/qz/installer/certificate/MacCertificateInstaller.java index b06584d29..3cf5a1dd3 100644 --- a/src/qz/installer/certificate/MacCertificateInstaller.java +++ b/src/qz/installer/certificate/MacCertificateInstaller.java @@ -20,7 +20,10 @@ import java.io.File; import java.io.IOException; import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; public class MacCertificateInstaller extends NativeCertificateInstaller { @@ -35,24 +38,43 @@ public MacCertificateInstaller(Installer.PrivilegeLevel certType) { } public boolean add(File certFile) { - if (certStore.equals(USER_STORE)) { - // This will prompt the user - return ShellUtilities.execute("security", "add-trusted-cert", "-r", "trustRoot", "-k", certStore, certFile.getPath()); - } else { - return ShellUtilities.execute("security", "add-trusted-cert", "-d", "-r", "trustRoot", "-k", certStore, certFile.getPath()); + try { + String s = new String(Files.readAllBytes(certFile.toPath()), StandardCharsets.UTF_8); + MacCertificateNativeAccess.add(s, certStore); + return true; + } catch(Exception e) { + log.error("Failed to write cert to keystore with native access. Falling back to shell commands", e); + if (certStore.equals(USER_STORE)) { + // This will prompt the user + return ShellUtilities.execute("security", "add-trusted-cert", "-r", "trustRoot", "-k", certStore, certFile.getPath()); + } else { + return ShellUtilities.execute("security", "add-trusted-cert", "-d", "-r", "trustRoot", "-k", certStore, certFile.getPath()); + } } } public boolean remove(List idList) { boolean success = true; for (String certId : idList) { - success = success && ShellUtilities.execute("security", "delete-certificate", "-Z", certId, certStore); + try { + //String s = new String(Files.readAllBytes(certFile.toPath()), StandardCharsets.UTF_8); + //MacCertificateNativeAccess.remove(s, certStore); + } + catch(Exception e) { + log.error("Failed to remove cert from keystore with native access. Falling back to shell commands", e); + success = success && ShellUtilities.execute("security", "delete-certificate", "-Z", certId, certStore); + } } return success; } public List find() { ArrayList hashList = new ArrayList<>(); + try { + return MacCertificateNativeAccess.findIdsByEmail(certStore, Constants.ABOUT_EMAIL); + } catch(Exception e) { + log.warn("Could not get certificate list using native, falling back to shell", e); + } try { Process p = Runtime.getRuntime().exec(new String[] {"security", "find-certificate", "-a", "-e", Constants.ABOUT_EMAIL, "-Z", certStore}); BufferedReader in = new BufferedReader(new InputStreamReader(p.getInputStream())); @@ -63,6 +85,7 @@ public List find() { } } in.close(); + return hashList; } catch(IOException e) { log.warn("Could not get certificate list", e); } diff --git a/src/qz/installer/certificate/MacCertificateNativeAccess.java b/src/qz/installer/certificate/MacCertificateNativeAccess.java new file mode 100644 index 000000000..9954f7771 --- /dev/null +++ b/src/qz/installer/certificate/MacCertificateNativeAccess.java @@ -0,0 +1,139 @@ +package qz.installer.certificate; + +import com.sun.jna.*; +import com.sun.jna.ptr.PointerByReference; +import org.apache.commons.ssl.Base64; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.dyorgio.jna.platform.mac.Security; + +import java.util.List; + +import static org.dyorgio.jna.platform.mac.Security.Status.SUCCESS; +import static qz.common.Constants.ABOUT_TITLE; +import static qz.installer.certificate.CoreFoundation.INSTANCE; + +public class MacCertificateNativeAccess { + private static final Logger log = LogManager.getLogger(MacCertificateNativeAccess.class); + + static CoreFoundation.CFAllocatorRef alloc = INSTANCE.CFAllocatorGetDefault(); + public static void add(String cert, String certStore) { + CoreFoundation.CFStringRef cfLabel = CoreFoundation.CFStringRef.createCFString(ABOUT_TITLE + " Certificate"); + + CoreFoundation.CFMutableDictionaryRef dict = INSTANCE.CFDictionaryCreateMutable(alloc, new CoreFoundation.CFIndex(3), null, null); + CoreFoundation.CFTypeRef certRef = getSecCertificateRef(cert); + INSTANCE.CFDictionaryAddValue(dict, Security.kSecClass, Security.kSecClassCertificate); + INSTANCE.CFDictionaryAddValue(dict, Security.kSecAttrLabel, cfLabel); + INSTANCE.CFDictionaryAddValue(dict, Security.kSecValueRef, certRef); + + if (!certStore.equals(MacCertificateInstaller.USER_STORE)) { + Memory keystoreRefData = new Memory(8); + Security.INSTANCE.SecKeychainCopyDomainDefault(1, keystoreRefData.share(0)); + CoreFoundation.CFTypeRef keystoreRef = new CoreFoundation.CFTypeRef(keystoreRefData.getPointer(0)); + INSTANCE.CFDictionaryAddValue(dict, Security.kSecUseKeychain, keystoreRef); + } + + // Call SecAddItem + // todo: import the const for this + int trustRet = Security.INSTANCE.SecTrustSettingsSetTrustSettings(certRef, 1, null); + int addRet = Security.INSTANCE.SecItemAdd(dict.getPointer(), null); + Security.Status trustRetCode = Security.Status.parse(trustRet); + Security.Status addReturnCode = Security.Status.parse(addRet); + + INSTANCE.CFRelease(dict); + INSTANCE.CFRelease(cfLabel); + INSTANCE.CFRelease(certRef); + + if (trustRetCode == SUCCESS && addReturnCode == SUCCESS) { + log.info("Cert added to keystore"); + } else { + log.error("Failed to add cert to keystore. AddItem Code: {} + TrustSetting Code: {}", addReturnCode, trustRetCode); + } + } + + public static List findIdsByEmail(String certStore, String email) { + + CoreFoundation.CFStringRef cfLabel = CoreFoundation.CFStringRef.createCFString(email); + + CoreFoundation.CFMutableDictionaryRef dict = INSTANCE.CFDictionaryCreateMutable(alloc, new CoreFoundation.CFIndex(3), null, null); + + INSTANCE.CFDictionaryAddValue(dict, Security.kSecClass, Security.kSecClassCertificate); + INSTANCE.CFDictionaryAddValue(dict, Security.kSecMatchEmailAddressIfPresent, cfLabel); + INSTANCE.CFDictionaryAddValue(dict, Security.kSecMatchLimit, Security.kSecMatchLimitAll); + + //CoreFoundation.CFMutableArrayRef cfPtrMutableArray = INSTANCE.CFArrayCreateMutable(null, new com.sun.jna.platform.mac.CoreFoundation.CFIndex(1), null); + //INSTANCE.CFArrayAppendValue(cfPtrMutableArray, keychain);{ + + + System.out.println(org.dyorgio.jna.platform.mac.CoreFoundation.INSTANCE.CFCopyDescription(dict).stringValue()); + + Memory pbr = new Memory(8); + int findRet = Security.INSTANCE.SecItemCopyMatching(dict.getPointer(), pbr.share(0)); + + Security.Status findStatus = Security.Status.parse(findRet); + + log.warn(findStatus); + if (INSTANCE.CFGetTypeID(pbr.getPointer(0)) == INSTANCE.CFArrayGetTypeID()) { + // multiple items returned as an array + //matchesFound = CFArrayGetCount(results); + log.warn("found"); + } + else { + // single item returned as either a dictionary or an item reference + //matchesFound = (results) ? 1 : 0; + log.warn("none found"); + } + + + //if (!certStore.equals(MacCertificateInstaller.USER_STORE)) { + // Memory keystoreRefData = new Memory(8); + // Security.INSTANCE.SecKeychainCopyDomainDefault(1, keystoreRefData.share(0)); + // CoreFoundation.CFTypeRef keystoreRef = new CoreFoundation.CFTypeRef(keystoreRefData.getPointer(0)); + // INSTANCE.CFDictionaryAddValue(dict, Security.kSecUseKeychain, keystoreRef); + //} + return null; + } + + + // Attempt to convert data bytes to CFDataRef + private static CoreFoundation.CFTypeRef getSecCertificateRef (String derEncodedData) { + String certFixed = derEncodedData.split("-----")[2]; + byte[] certBytes = Base64.decodeBase64(certFixed); + Memory nativeBytes = new Memory(certBytes.length); + nativeBytes.write(0, certBytes, 0, certBytes.length); + CoreFoundation.CFDataRef cfData = INSTANCE.CFDataCreate(alloc, nativeBytes, new CoreFoundation.CFIndex(nativeBytes.size())); + CoreFoundation.CFTypeRef returnValue = Security.INSTANCE.SecCertificateCreateWithData(alloc, cfData); + INSTANCE.CFRelease(cfData); + return returnValue; + } +} + +interface CoreFoundation extends com.sun.jna.platform.mac.CoreFoundation { + CoreFoundation INSTANCE = Native.load("CoreFoundation", qz.installer.certificate.CoreFoundation.class); + NativeLibrary NATIVE_INSTANCE = NativeLibrary.getInstance("CoreFoundation"); + CFBooleanRef kCFBooleanTrue = new CFBooleanRef(NATIVE_INSTANCE.getGlobalVariableAddress("kCFBooleanTrue").getPointer(0)); + void CFDictionaryAddValue(com.sun.jna.platform.mac.CoreFoundation.CFMutableDictionaryRef theDict, PointerType key, PointerType value); + void CFDictionaryGetKeysAndValues(com.sun.jna.platform.mac.CoreFoundation.CFMutableDictionaryRef theDict, PointerByReference keys, Pointer[] values); + CFMutableArrayRef CFArrayCreateMutable(CFAllocatorRef allocator, CFIndex capacity, Pointer callBacks); + void CFArrayAppendValue(CFMutableArrayRef theArray, PointerType value); + + public static class CFMutableArrayRef extends CFTypeRef { + public CFMutableArrayRef() { + } + + public CFMutableArrayRef(Pointer p) { + super(p); + if (!this.isTypeID(com.sun.jna.platform.mac.CoreFoundation.ARRAY_TYPE_ID)) { + throw new ClassCastException("Unable to cast to CFArray. Type ID: " + this.getTypeID()); + } + } + + //public int getCount() { + // return com.sun.jna.platform.mac.CoreFoundation.INSTANCE.CFArrayGetCount(this).intValue(); + //} + + //public Pointer getValueAtIndex(int idx) { + // return com.sun.jna.platform.mac.CoreFoundation.INSTANCE.CFArrayGetValueAtIndex(this, new CFIndex((long)idx)); + //} + } +} diff --git a/src/qz/installer/certificate/NativeCertificateInstaller.java b/src/qz/installer/certificate/NativeCertificateInstaller.java index 4360f5274..774ba9443 100644 --- a/src/qz/installer/certificate/NativeCertificateInstaller.java +++ b/src/qz/installer/certificate/NativeCertificateInstaller.java @@ -21,6 +21,7 @@ import java.io.IOException; import java.io.OutputStreamWriter; import java.nio.file.Files; +import java.nio.file.Path; import java.nio.file.StandardOpenOption; import java.security.cert.X509Certificate; import java.util.List; @@ -54,13 +55,13 @@ public static NativeCertificateInstaller getInstance(Installer.PrivilegeLevel ty */ public boolean install(X509Certificate cert) { try { - File certFile = File.createTempFile(KeyPairWrapper.getAlias(KeyPairWrapper.Type.CA) + "-", CertificateManager.DEFAULT_CERTIFICATE_EXTENSION); + Path certFile = Files.createTempFile(KeyPairWrapper.getAlias(KeyPairWrapper.Type.CA) + "-", CertificateManager.DEFAULT_CERTIFICATE_EXTENSION); JcaMiscPEMGenerator generator = new JcaMiscPEMGenerator(cert); - JcaPEMWriter writer = new JcaPEMWriter(new OutputStreamWriter(Files.newOutputStream(certFile.toPath(), StandardOpenOption.CREATE))); + JcaPEMWriter writer = new JcaPEMWriter(new OutputStreamWriter(Files.newOutputStream(certFile, StandardOpenOption.CREATE))); writer.writeObject(generator.generate()); writer.close(); - return install(certFile); + return install(certFile.toFile()); } catch(IOException e) { log.warn("Could not install cert from temp file", e); }