tlscert.go 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. package tlscert
  2. import (
  3. "crypto/tls"
  4. "crypto/x509"
  5. "embed"
  6. "encoding/pem"
  7. "io"
  8. "io/ioutil"
  9. "log"
  10. "os"
  11. "path/filepath"
  12. "strings"
  13. "imuslab.com/zoraxy/mod/utils"
  14. )
  15. type Manager struct {
  16. CertStore string
  17. verbal bool
  18. }
  19. //go:embed localhost.crt localhost.key
  20. var buildinCertStore embed.FS
  21. func NewManager(certStore string, verbal bool) (*Manager, error) {
  22. if !utils.FileExists(certStore) {
  23. os.MkdirAll(certStore, 0775)
  24. }
  25. thisManager := Manager{
  26. CertStore: certStore,
  27. verbal: verbal,
  28. }
  29. return &thisManager, nil
  30. }
  31. func (m *Manager) ListCertDomains() ([]string, error) {
  32. filenames, err := m.ListCerts()
  33. if err != nil {
  34. return []string{}, err
  35. }
  36. //Remove certificates where there are missing public key or private key
  37. filenames = getCertPairs(filenames)
  38. return filenames, nil
  39. }
  40. func (m *Manager) ListCerts() ([]string, error) {
  41. certs, err := ioutil.ReadDir(m.CertStore)
  42. if err != nil {
  43. return []string{}, err
  44. }
  45. filenames := make([]string, 0, len(certs))
  46. for _, cert := range certs {
  47. if !cert.IsDir() {
  48. filenames = append(filenames, cert.Name())
  49. }
  50. }
  51. return filenames, nil
  52. }
  53. func (m *Manager) GetCert(helloInfo *tls.ClientHelloInfo) (*tls.Certificate, error) {
  54. //Check if the domain corrisponding cert exists
  55. pubKey := "./tmp/localhost.crt"
  56. priKey := "./tmp/localhost.key"
  57. //Check if this is initial setup
  58. if !utils.FileExists(pubKey) {
  59. buildInPubKey, _ := buildinCertStore.ReadFile(filepath.Base(pubKey))
  60. os.WriteFile(pubKey, buildInPubKey, 0775)
  61. }
  62. if !utils.FileExists(priKey) {
  63. buildInPriKey, _ := buildinCertStore.ReadFile(filepath.Base(priKey))
  64. os.WriteFile(priKey, buildInPriKey, 0775)
  65. }
  66. if utils.FileExists(filepath.Join(m.CertStore, helloInfo.ServerName+".crt")) && utils.FileExists(filepath.Join(m.CertStore, helloInfo.ServerName+".key")) {
  67. pubKey = filepath.Join(m.CertStore, helloInfo.ServerName+".crt")
  68. priKey = filepath.Join(m.CertStore, helloInfo.ServerName+".key")
  69. } else {
  70. domainCerts, _ := m.ListCertDomains()
  71. cloestDomainCert := matchClosestDomainCertificate(helloInfo.ServerName, domainCerts)
  72. if cloestDomainCert != "" {
  73. //There is a matching parent domain for this subdomain. Use this instead.
  74. pubKey = filepath.Join(m.CertStore, cloestDomainCert+".crt")
  75. priKey = filepath.Join(m.CertStore, cloestDomainCert+".key")
  76. } else if m.DefaultCertExists() {
  77. //Use default.crt and default.key
  78. pubKey = filepath.Join(m.CertStore, "default.crt")
  79. priKey = filepath.Join(m.CertStore, "default.key")
  80. if m.verbal {
  81. log.Println("No matching certificate found. Serving with default")
  82. }
  83. } else {
  84. if m.verbal {
  85. log.Println("Matching certificate not found. Serving with build-in certificate. Requesting server name: ", helloInfo.ServerName)
  86. }
  87. }
  88. }
  89. //Load the cert and serve it
  90. cer, err := tls.LoadX509KeyPair(pubKey, priKey)
  91. if err != nil {
  92. log.Println(err)
  93. return nil, nil
  94. }
  95. return &cer, nil
  96. }
  97. // Check if both the default cert public key and private key exists
  98. func (m *Manager) DefaultCertExists() bool {
  99. return utils.FileExists(filepath.Join(m.CertStore, "default.crt")) && utils.FileExists(filepath.Join(m.CertStore, "default.key"))
  100. }
  101. // Check if the default cert exists returning seperate results for pubkey and prikey
  102. func (m *Manager) DefaultCertExistsSep() (bool, bool) {
  103. return utils.FileExists(filepath.Join(m.CertStore, "default.crt")), utils.FileExists(filepath.Join(m.CertStore, "default.key"))
  104. }
  105. // Delete the cert if exists
  106. func (m *Manager) RemoveCert(domain string) error {
  107. pubKey := filepath.Join(m.CertStore, domain+".crt")
  108. priKey := filepath.Join(m.CertStore, domain+".key")
  109. if utils.FileExists(pubKey) {
  110. err := os.Remove(pubKey)
  111. if err != nil {
  112. return err
  113. }
  114. }
  115. if utils.FileExists(priKey) {
  116. err := os.Remove(priKey)
  117. if err != nil {
  118. return err
  119. }
  120. }
  121. return nil
  122. }
  123. // Check if the given file is a valid TLS file
  124. func IsValidTLSFile(file io.Reader) bool {
  125. // Read the contents of the uploaded file
  126. contents, err := io.ReadAll(file)
  127. if err != nil {
  128. // Handle the error
  129. return false
  130. }
  131. // Parse the contents of the file as a PEM-encoded certificate or key
  132. block, _ := pem.Decode(contents)
  133. if block == nil {
  134. // The file is not a valid PEM-encoded certificate or key
  135. return false
  136. }
  137. // Parse the certificate or key
  138. if strings.Contains(block.Type, "CERTIFICATE") {
  139. // The file contains a certificate
  140. cert, err := x509.ParseCertificate(block.Bytes)
  141. if err != nil {
  142. // Handle the error
  143. return false
  144. }
  145. // Check if the certificate is a valid TLS/SSL certificate
  146. return cert.IsCA == false && cert.KeyUsage&x509.KeyUsageDigitalSignature != 0 && cert.KeyUsage&x509.KeyUsageKeyEncipherment != 0
  147. } else if strings.Contains(block.Type, "PRIVATE KEY") {
  148. // The file contains a private key
  149. _, err := x509.ParsePKCS1PrivateKey(block.Bytes)
  150. if err != nil {
  151. // Handle the error
  152. return false
  153. }
  154. return true
  155. } else {
  156. return false
  157. }
  158. }