public static final KeyStore createKeyStore(String alias, String dn, int durationDays,
			PrivateKeyEntry caEntry, File keyFile, String keystoreType,
			Collection<String> subjectAlternativeNames, char[] protectedPassphrase)
			throws KeyStoreException {

		PasswordProtection pp = new PasswordProtection(protectedPassphrase);

		LoadStoreParameter loadStoreParameter = null;
		if (keyFile != null && keyFile.exists()) {
			loadStoreParameter = new LoadStoreParameter() {
				@Override
				public ProtectionParameter getProtectionParameter() {
					return pp;
				}
			};
		}

		try {
			KeyStore keyStore = KeyStore.getInstance(keystoreType);
			keyStore.load(loadStoreParameter);

			SecureRandom random = SecureRandomFactory.getSecureRandom();

			KeyPairGenerator rsa = KeyPairGenerator.getInstance(RSA_TYPE);
			rsa.initialize(KEY_SIZE);

			KeyPair keyPair = rsa.generateKeyPair();
			PrivateKey issuerKey = keyPair.getPrivate();

			byte[] encodedPublicKey = keyPair.getPublic().getEncoded();
			SubjectPublicKeyInfo bcPk = SubjectPublicKeyInfo.getInstance(encodedPublicKey);

			X500Name x500Name = new X500Name(dn);
			X500Name caX500Name = x500Name; // self-signed CA if caEntry is null
			KeyUsage keyUsage = new KeyUsage(
				KeyUsage.digitalSignature | KeyUsage.keyEncipherment | KeyUsage.keyCertSign);
			if (caEntry != null) {
				// derive CA X500Name from caEntry
				Certificate caCert = caEntry.getCertificate();
				if (!(caCert instanceof X509Certificate)) {
					throw new CertificateException(
						"Unsupported certificate type: " + caCert.getType());
				}
				X509Certificate caX509Cert = (X509Certificate) caCert;
				caX500Name =
					new X500Name(caX509Cert.getSubjectX500Principal().getName());
				keyUsage = new KeyUsage(KeyUsage.digitalSignature | KeyUsage.keyEncipherment);
				issuerKey = caEntry.getPrivateKey();
			}
			Date notBefore = new Date();
			long durationMs = (long) durationDays * MILLISECONDS_PER_DAY;
			Date notAfter = new Date(notBefore.getTime() + durationMs);
			BigInteger serialNumber = new BigInteger(128, random);

			X509v3CertificateBuilder certificateBuilder = new X509v3CertificateBuilder(caX500Name,
				serialNumber, notBefore, notAfter, x500Name, bcPk);
			certificateBuilder.addExtension(Extension.keyUsage, true, keyUsage);
			if (subjectAlternativeNames != null && !subjectAlternativeNames.isEmpty()) {
				List<GeneralName> nameList = new ArrayList<GeneralName>();
				for (String altName : subjectAlternativeNames) {
					int nameType =
						IPAddress.isValid(altName) ? GeneralName.iPAddress : GeneralName.dNSName;
					nameList.add(new GeneralName(nameType, altName));
				}
				GeneralName[] altNames = nameList.toArray(GeneralName[]::new);
				certificateBuilder.addExtension(Extension.subjectAlternativeName, false,
					new GeneralNames(altNames));
			}
			if (caEntry == null) {
				certificateBuilder.addExtension(Extension.basicConstraints, true,
					new BasicConstraints(1));
			}

			ContentSigner contentSigner =
				new JcaContentSignerBuilder(SIGNING_ALGORITHM).build(issuerKey);

			X509Certificate certificate = new JcaX509CertificateConverter()
					.getCertificate(certificateBuilder.build(contentSigner));

			Certificate[] chain;
			if (caEntry == null) {
				chain = new Certificate[] { certificate };
			}
			else {
				chain = makeCertificateChain(certificate, caEntry.getCertificateChain());
			}

			keyStore.setKeyEntry(alias, keyPair.getPrivate(), protectedPassphrase, chain);

			if (keyFile != null) {
				FileOutputStream out = new FileOutputStream(keyFile);
				try {
					keyStore.store(out, protectedPassphrase);
					out.flush();
					out.getFD().sync();
					Msg.debug(ApplicationKeyManagerUtils.class,
						out.getChannel().size() + " bytes written to key/cert file: " + keyFile);
				}
				catch (SyncFailedException e) {
					// ignore
				}
				finally {
					out.close();
				}
				keyFile.setReadable(true, true);
				keyFile.setWritable(false);
			}

			Msg.debug(ApplicationKeyManagerUtils.class,
				"Certificate Generated (" + alias + "): " + dn);

			return keyStore;
		}
		catch (GeneralSecurityException | OperatorException | IOException e) {
			throw new KeyStoreException("Failed to generate/store certificate (" + dn + ")", e);
		}
		finally {
			try {
				pp.destroy();
			}
			catch (DestroyFailedException e) {
				throw new AssertException(e); // unexpected for simple password clearing
			}
		}
	}
Generate a new {@link X509Certificate} with RSA {@link KeyPair} and create/update a {@link KeyStore}
optionally backed by a keyFile.  
@param alias entry alias with keystore
@param dn distinguished name (e.g., "CN=Ghidra Test, O=Ghidra, OU=Test, C=US" )
@param durationDays number of days which generated certificate should remain valid
@param caEntry optional CA private key entry.  If null, a self-signed CA certificate will be 
			generated.
@param keyFile optional file to load/store resulting {@link KeyStore} (may be null)
@param keystoreType support keystore type (e.g., "JKS", "PKCS12")
@param subjectAlternativeNames an optional list of subject alternative names to be included 
			in certificate (may be null)
@param protectedPassphrase key and keystore protection password
@return keystore containing newly generated certification with key pair
@throws KeyStoreException if error occurs while updating keystore
Ghidra/Framework/Generic/src/main/java/ghidra/net/ApplicationKeyManagerUtils.java