private static ProtectedKeyStoreData getProtectedKeyStoreData(String keystorePath)
			throws CancelledException, KeyStoreException {

		Msg.info(ApplicationKeyManagerFactory.class, "Using certificate keystore: " + keystorePath);

		String keystorePwd = System.getProperty(KEYSTORE_PASSWORD_PROPERTY);

		int tryCount = 0;

		while (true) {
			char[] password = new char[0];
			char[] oldPassword = null;
			try {
				if (tryCount == 0) {
					// try no password first
				}
				else if (tryCount == 1) {
					// try default password
					Msg.debug(ApplicationKeyManagerFactory.class,
						"Attempting to load keystore without password...");
					password = DEFAULT_PASSWORD.toCharArray();
				}
				else if (tryCount == 2) {
					// try specified password
					if (keystorePwd != null) {
						Msg.debug(ApplicationKeyManagerFactory.class,
							"Attempting to load keystore with property-based password...");
						password = keystorePwd.toCharArray();
					}
					else {
						// no system property password provided
						++tryCount;
						continue;
					}
				}
				else if (customPasswordProvider != null) {
					disposePassword(oldPassword);
					oldPassword = password;
					password =
						customPasswordProvider.getKeyStorePassword(keystorePath, tryCount != 3);
					if (password == null) {
						throw new CancelledException();
					}
					if (Arrays.equals(password, oldPassword)) {
						// Give-up if same password specified twice to avoid infinite loop
						// just in case password provider only supplies one password
						break;
					}
					Msg.debug(ApplicationKeyManagerFactory.class,
						"Attempting to open keystore with user-supplied password...");
				}
				else {
					break; // give-up
				}
				++tryCount;

				// we only support a single password for both keystore and private key
				KeyStore keyStore = ApplicationKeyStore.getKeyStoreInstance(keystorePath, password);
				return new ProtectedKeyStoreData(keyStore, password);
			}
			catch (NoSuchAlgorithmException | CertificateException | FileNotFoundException e) {
				throw new KeyStoreException("Failed to process keystore: " + keystorePath, e);
			}
			catch (KeyStoreException | IOException e) {
				// Java Bug: JDK-6974037 : PKCS12 Keystore load with invalid passwords generates different exceptions
				Exception ioException = getIOException(e);
				if (ioException != null) {
					// Assume all IOExceptions are the result of improperly decrypted keystore
					// since arbitrary and inconsistent errors are produced when attempting to 
					// load an encrypted keystore without a password or with the wrong password.
					continue;
				}
				throw new KeyStoreException(
					"Failed to process keystore (" + tryCount + "): " + keystorePath, e);
			}
			finally {
				disposePassword(oldPassword);
				disposePassword(password);
			}
		}
		throw new KeyStoreException("Failed to unlock key storage: " + keystorePath);
	}
Get protected keystore data for specified keystorePath.  Caller is responsible for
properly disposing returned object.
@param keystorePath protected keystore path
@return protected keystore data
@throws CancelledException password entry was cancelled by user
@throws KeyStoreException error occurred opening/processing keystore
Ghidra/Framework/Generic/src/main/java/ghidra/net/ApplicationKeyManagerFactory.java