truststore_java.go 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. // Copyright 2018 The mkcert Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. package main
  5. import (
  6. "bytes"
  7. "crypto/sha1"
  8. "crypto/sha256"
  9. "crypto/x509"
  10. "encoding/hex"
  11. "hash"
  12. "os"
  13. "os/exec"
  14. "path/filepath"
  15. "runtime"
  16. "strings"
  17. )
  18. var (
  19. hasJava bool
  20. hasKeytool bool
  21. javaHome string
  22. cacertsPath string
  23. keytoolPath string
  24. storePass string = "changeit"
  25. )
  26. func init() {
  27. if runtime.GOOS == "windows" {
  28. keytoolPath = filepath.Join("bin", "keytool.exe")
  29. } else {
  30. keytoolPath = filepath.Join("bin", "keytool")
  31. }
  32. if v := os.Getenv("JAVA_HOME"); v != "" {
  33. hasJava = true
  34. javaHome = v
  35. if pathExists(filepath.Join(v, keytoolPath)) {
  36. hasKeytool = true
  37. keytoolPath = filepath.Join(v, keytoolPath)
  38. }
  39. if pathExists(filepath.Join(v, "lib", "security", "cacerts")) {
  40. cacertsPath = filepath.Join(v, "lib", "security", "cacerts")
  41. }
  42. if pathExists(filepath.Join(v, "jre", "lib", "security", "cacerts")) {
  43. cacertsPath = filepath.Join(v, "jre", "lib", "security", "cacerts")
  44. }
  45. }
  46. }
  47. func (m *mkcert) checkJava() bool {
  48. if !hasKeytool {
  49. return false
  50. }
  51. // exists returns true if the given x509.Certificate's fingerprint
  52. // is in the keytool -list output
  53. exists := func(c *x509.Certificate, h hash.Hash, keytoolOutput []byte) bool {
  54. h.Write(c.Raw)
  55. fp := strings.ToUpper(hex.EncodeToString(h.Sum(nil)))
  56. return bytes.Contains(keytoolOutput, []byte(fp))
  57. }
  58. keytoolOutput, err := exec.Command(keytoolPath, "-list", "-keystore", cacertsPath, "-storepass", storePass).CombinedOutput()
  59. fatalIfCmdErr(err, "keytool -list", keytoolOutput)
  60. // keytool outputs SHA1 and SHA256 (Java 9+) certificates in uppercase hex
  61. // with each octet pair delimitated by ":". Drop them from the keytool output
  62. keytoolOutput = bytes.Replace(keytoolOutput, []byte(":"), nil, -1)
  63. // pre-Java 9 uses SHA1 fingerprints
  64. s1, s256 := sha1.New(), sha256.New()
  65. return exists(m.caCert, s1, keytoolOutput) || exists(m.caCert, s256, keytoolOutput)
  66. }
  67. func (m *mkcert) installJava() {
  68. args := []string{
  69. "-importcert", "-noprompt",
  70. "-keystore", cacertsPath,
  71. "-storepass", storePass,
  72. "-file", filepath.Join(m.CAROOT, rootName),
  73. "-alias", m.caUniqueName(),
  74. }
  75. out, err := execKeytool(exec.Command(keytoolPath, args...))
  76. fatalIfCmdErr(err, "keytool -importcert", out)
  77. }
  78. func (m *mkcert) uninstallJava() {
  79. args := []string{
  80. "-delete",
  81. "-alias", m.caUniqueName(),
  82. "-keystore", cacertsPath,
  83. "-storepass", storePass,
  84. }
  85. out, err := execKeytool(exec.Command(keytoolPath, args...))
  86. if bytes.Contains(out, []byte("does not exist")) {
  87. return // cert didn't exist
  88. }
  89. fatalIfCmdErr(err, "keytool -delete", out)
  90. }
  91. // execKeytool will execute a "keytool" command and if needed re-execute
  92. // the command with commandWithSudo to work around file permissions.
  93. func execKeytool(cmd *exec.Cmd) ([]byte, error) {
  94. out, err := cmd.CombinedOutput()
  95. if err != nil && bytes.Contains(out, []byte("java.io.FileNotFoundException")) && runtime.GOOS != "windows" {
  96. origArgs := cmd.Args[1:]
  97. cmd = commandWithSudo(cmd.Path)
  98. cmd.Args = append(cmd.Args, origArgs...)
  99. cmd.Env = []string{
  100. "JAVA_HOME=" + javaHome,
  101. }
  102. out, err = cmd.CombinedOutput()
  103. }
  104. return out, err
  105. }