acme.go 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. package acme
  2. import (
  3. "crypto"
  4. "crypto/ecdsa"
  5. "crypto/elliptic"
  6. "crypto/rand"
  7. "crypto/x509"
  8. "encoding/json"
  9. "encoding/pem"
  10. "io/ioutil"
  11. "log"
  12. "net/http"
  13. "os"
  14. "path/filepath"
  15. "strconv"
  16. "strings"
  17. "time"
  18. "github.com/go-acme/lego/v4/certcrypto"
  19. "github.com/go-acme/lego/v4/certificate"
  20. "github.com/go-acme/lego/v4/challenge/http01"
  21. "github.com/go-acme/lego/v4/lego"
  22. "github.com/go-acme/lego/v4/registration"
  23. "imuslab.com/zoraxy/mod/utils"
  24. )
  25. // ACMEUser represents a user in the ACME system.
  26. type ACMEUser struct {
  27. Email string
  28. Registration *registration.Resource
  29. key crypto.PrivateKey
  30. }
  31. // GetEmail returns the email of the ACMEUser.
  32. func (u *ACMEUser) GetEmail() string {
  33. return u.Email
  34. }
  35. // GetRegistration returns the registration resource of the ACMEUser.
  36. func (u ACMEUser) GetRegistration() *registration.Resource {
  37. return u.Registration
  38. }
  39. // GetPrivateKey returns the private key of the ACMEUser.
  40. func (u *ACMEUser) GetPrivateKey() crypto.PrivateKey {
  41. return u.key
  42. }
  43. // ACMEHandler handles ACME-related operations.
  44. type ACMEHandler struct {
  45. email string
  46. acmeServer string
  47. port string
  48. }
  49. func NewACME(email string, acmeServer string, port string) *ACMEHandler {
  50. return &ACMEHandler{
  51. email: email,
  52. acmeServer: acmeServer,
  53. port: port,
  54. }
  55. }
  56. func (a *ACMEHandler) ObtainCert(domains []string, certificateName string) (bool, error) {
  57. log.Println("Obtaining certificate...")
  58. privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
  59. if err != nil {
  60. log.Println(err)
  61. return false, err
  62. }
  63. log.Println(a.acmeServer)
  64. adminUser := ACMEUser{
  65. Email: a.email,
  66. key: privateKey,
  67. }
  68. config := lego.NewConfig(&adminUser)
  69. config.CADirURL = a.acmeServer
  70. config.Certificate.KeyType = certcrypto.RSA2048
  71. client, err := lego.NewClient(config)
  72. if err != nil {
  73. log.Println(err)
  74. return false, err
  75. }
  76. err = client.Challenge.SetHTTP01Provider(http01.NewProviderServer("", a.port))
  77. if err != nil {
  78. log.Println(err)
  79. return false, err
  80. }
  81. // New users will need to register
  82. reg, err := client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true})
  83. if err != nil {
  84. log.Println(err)
  85. return false, err
  86. }
  87. adminUser.Registration = reg
  88. request := certificate.ObtainRequest{
  89. Domains: domains,
  90. Bundle: true,
  91. }
  92. certificates, err := client.Certificate.Obtain(request)
  93. if err != nil {
  94. log.Println(err)
  95. return false, err
  96. }
  97. // Each certificate comes back with the cert bytes, the bytes of the client's
  98. // private key, and a certificate URL. SAVE THESE TO DISK.
  99. err = ioutil.WriteFile("./certs/"+certificateName+".crt", certificates.Certificate, 0777)
  100. if err != nil {
  101. log.Println(err)
  102. return false, err
  103. }
  104. err = ioutil.WriteFile("./certs/"+certificateName+".key", certificates.PrivateKey, 0777)
  105. if err != nil {
  106. log.Println(err)
  107. return false, err
  108. }
  109. return true, nil
  110. }
  111. // Return a list of domains that is in expired certificates
  112. func (a *ACMEHandler) CheckCertificate() []string {
  113. filenames, err := os.ReadDir("./certs/")
  114. expiredCerts := []string{}
  115. if err != nil {
  116. log.Println(err)
  117. return []string{}
  118. }
  119. for _, filename := range filenames {
  120. certFilepath := filepath.Join("./certs/", filename.Name())
  121. certBtyes, err := os.ReadFile(certFilepath)
  122. if err != nil {
  123. //Unable to load this file
  124. continue
  125. } else {
  126. //Cert loaded. Check its expire time
  127. block, _ := pem.Decode(certBtyes)
  128. if block != nil {
  129. cert, err := x509.ParseCertificate(block.Bytes)
  130. if err == nil {
  131. elapsed := time.Since(cert.NotAfter)
  132. //approxMonths := -int(elapsed.Hours() / (24 * 30.44))
  133. //approxDays := -int(elapsed.Hours()/24) % 30
  134. if elapsed > 0 {
  135. //log.Println("Certificate", certFilepath, " expired")
  136. for _, dnsName := range cert.DNSNames {
  137. if !contains(expiredCerts, dnsName) {
  138. expiredCerts = append(expiredCerts, dnsName)
  139. }
  140. }
  141. if !contains(expiredCerts, cert.Subject.CommonName) {
  142. expiredCerts = append(expiredCerts, cert.Subject.CommonName)
  143. }
  144. } else {
  145. //log.Println("Certificate", certFilepath, " will still vaild for the next ", approxMonths, "m", approxDays, "d")
  146. }
  147. }
  148. }
  149. }
  150. }
  151. return expiredCerts
  152. }
  153. func (a *ACMEHandler) Getport() string {
  154. return a.port
  155. }
  156. func contains(slice []string, str string) bool {
  157. for _, s := range slice {
  158. if s == str {
  159. return true
  160. }
  161. }
  162. return false
  163. }
  164. func (a *ACMEHandler) HandleGetExpiredDomains(w http.ResponseWriter, r *http.Request) {
  165. type ExpiredDomains struct {
  166. Domain []string `json:"domain"`
  167. }
  168. info := ExpiredDomains{
  169. Domain: a.CheckCertificate(),
  170. }
  171. js, _ := json.MarshalIndent(info, "", " ")
  172. utils.SendJSONResponse(w, string(js))
  173. }
  174. func (a *ACMEHandler) HandleRenewCertificate(w http.ResponseWriter, r *http.Request) {
  175. domainPara, err := utils.GetPara(r, "domains")
  176. if err != nil {
  177. utils.SendErrorResponse(w, jsonEscape(err.Error()))
  178. return
  179. }
  180. filename, err := utils.GetPara(r, "filename")
  181. if err != nil {
  182. utils.SendErrorResponse(w, jsonEscape(err.Error()))
  183. return
  184. }
  185. domains := strings.Split(domainPara, ",")
  186. result, err := a.ObtainCert(domains, filename)
  187. if err != nil {
  188. utils.SendErrorResponse(w, jsonEscape(err.Error()))
  189. return
  190. }
  191. utils.SendJSONResponse(w, strconv.FormatBool(result))
  192. }
  193. func jsonEscape(i string) string {
  194. b, err := json.Marshal(i)
  195. if err != nil {
  196. panic(err)
  197. }
  198. s := string(b)
  199. return s[1 : len(s)-1]
  200. }