Repository: FiloSottile/mkcert
File: truststore_java.go (Go)

Code before:
		fp := []byte(strings.ToUpper(hex.EncodeToString(h.Sum(nil))))
		return bytes.Contains(keytoolOutput, fp)
	}

	keytoolOutput, err := exec.Command(keytoolPath, "-list", "-keystore", cacertsPath, "-storepass", storePass).CombinedOutput()
	if err != nil {
		hasKeytool = false
		return false
	}
	keytoolOutput = bytes.Replace(keytoolOutput, []byte(":"), nil, -1) // drop : in whole thing

	// pre-Java 9 uses SHA1 fingerprints
	s1, s256 := sha1.New(), sha256.New()
	return exists(m.caCert, s1, keytoolOutput) || exists(m.caCert, s256, keytoolOutput)
}

func (m *mkcert) installJava() {
	tmp, err := ioutil.TempFile("", "mkcert-java")
	fatalIfErr(err, "failed creating temp file for java install")
	defer os.Remove(tmp.Name())

	err = ioutil.WriteFile(tmp.Name(), pem.EncodeToMemory(&pem.Block{
		Type:  "CERTIFICATE",
		Bytes: m.caCert.Raw,
	}), 0644)
	fatalIfErr(err, "failed to write java cert into temp file")

	args := []string{
		"-importcert", "-noprompt",
		"-keystore", cacertsPath,
		"-storepass", storePass,
		"-file", tmp.Name(),
		"-alias", m.caUniqueName(),
	}

	out, err := m.execKeytool(exec.Command(keytoolPath, args...))
	fatalIfCmdErr(err, "keytool -importcert", out)
}

func (m *mkcert) uninstallJava() {
	args := []string{
		"-delete",
		"-alias", fmt.Sprintf("'%s'", m.caUniqueName()),
		"-keystore", cacertsPath,
		"-storepass", storePass,
	}
	out, err := m.execKeytool(exec.Command(keytoolPath, args...))
	if bytes.Contains(out, []byte("does not exist")) {
		return // cert didn't exist
	}
	fatalIfCmdErr(err, "keytool -delete", out)

Review comment (security):
Could you use the certificate file on disk? See for example the macOS `security` input.

Code after:

	keytoolOutput, err := exec.Command(keytoolPath, "-list", "-keystore", cacertsPath, "-storepass", storePass).CombinedOutput()
	fatalIfCmdErr(err, "keytool -list", keytoolOutput)
	// keytool outputs SHA1 and SHA256 (Java 9+) certificates in uppercase hex
	// with each octet pair delimitated by ":". Drop them from the keytool output
	keytoolOutput = bytes.Replace(keytoolOutput, []byte(":"), nil, -1)

	// pre-Java 9 uses SHA1 fingerprints
	s1, s256 := sha1.New(), sha256.New()
	return exists(m.caCert, s1, keytoolOutput) || exists(m.caCert, s256, keytoolOutput)
}

func (m *mkcert) installJava() {
	args := []string{
		"-importcert", "-noprompt",
		"-keystore", cacertsPath,
		"-storepass", storePass,
		"-file", filepath.Join(m.CAROOT, rootName),
		"-alias", m.caUniqueName(),
	}

	out, err := m.execKeytool(exec.Command(keytoolPath, args...))
	fatalIfCmdErr(err, "keytool -importcert", out)
}

func (m *mkcert) uninstallJava() {
	args := []string{
		"-delete",
		"-alias", m.caUniqueName(),
		"-keystore", cacertsPath,
		"-storepass", storePass,
	}
	out, err := m.execKeytool(exec.Command(keytoolPath, args...))
	if bytes.Contains(out, []byte("does not exist")) {
		return // cert didn't exist
	}
	fatalIfCmdErr(err, "keytool -delete", out)
}

// execKeytool will execute a "keytool" command and if needed re-execute
// the command wrapped in 'sudo' to work around file permissions.
func (m *mkcert) execKeytool(cmd *exec.Cmd) ([]byte, error) {
	out, err := cmd.CombinedOutput()
	if err != nil && bytes.Contains(out, []byte("java.io.FileNotFoundException")) {
		origArgs := cmd.Args[1:]
		cmd = exec.Command("sudo", keytoolPath)
		cmd.Args = append(cmd.Args, origArgs...)
		cmd.Env = []string{
			"JAVA_HOME=" + javaHome,
		}
		out, err = cmd.CombinedOutput()