acme.go 5.6 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. // NewACME creates a new ACMEHandler instance.
  50. func NewACME(email string, acmeServer string, port string) *ACMEHandler {
  51. return &ACMEHandler{
  52. email: email,
  53. acmeServer: acmeServer,
  54. port: port,
  55. }
  56. }
  57. // ObtainCert obtains a certificate for the specified domains.
  58. func (a *ACMEHandler) ObtainCert(domains []string, certificateName string) (bool, error) {
  59. log.Println("Obtaining certificate...")
  60. privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
  61. if err != nil {
  62. log.Println(err)
  63. return false, err
  64. }
  65. log.Println(a.acmeServer)
  66. adminUser := ACMEUser{
  67. Email: a.email,
  68. key: privateKey,
  69. }
  70. config := lego.NewConfig(&adminUser)
  71. config.CADirURL = a.acmeServer
  72. config.Certificate.KeyType = certcrypto.RSA2048
  73. client, err := lego.NewClient(config)
  74. if err != nil {
  75. log.Println(err)
  76. return false, err
  77. }
  78. err = client.Challenge.SetHTTP01Provider(http01.NewProviderServer("", a.port))
  79. if err != nil {
  80. log.Println(err)
  81. return false, err
  82. }
  83. // New users will need to register
  84. reg, err := client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true})
  85. if err != nil {
  86. log.Println(err)
  87. return false, err
  88. }
  89. adminUser.Registration = reg
  90. request := certificate.ObtainRequest{
  91. Domains: domains,
  92. Bundle: true,
  93. }
  94. certificates, err := client.Certificate.Obtain(request)
  95. if err != nil {
  96. log.Println(err)
  97. return false, err
  98. }
  99. // Each certificate comes back with the cert bytes, the bytes of the client's
  100. // private key, and a certificate URL. SAVE THESE TO DISK.
  101. err = ioutil.WriteFile("./certs/"+certificateName+".crt", certificates.Certificate, 0777)
  102. if err != nil {
  103. log.Println(err)
  104. return false, err
  105. }
  106. err = ioutil.WriteFile("./certs/"+certificateName+".key", certificates.PrivateKey, 0777)
  107. if err != nil {
  108. log.Println(err)
  109. return false, err
  110. }
  111. return true, nil
  112. }
  113. // CheckCertificate returns a list of domains that are in expired certificates.
  114. func (a *ACMEHandler) CheckCertificate() []string {
  115. filenames, err := os.ReadDir("./certs/")
  116. expiredCerts := []string{}
  117. if err != nil {
  118. log.Println(err)
  119. return []string{}
  120. }
  121. for _, filename := range filenames {
  122. certFilepath := filepath.Join("./certs/", filename.Name())
  123. certBtyes, err := os.ReadFile(certFilepath)
  124. if err != nil {
  125. // Unable to load this file
  126. continue
  127. } else {
  128. // Cert loaded. Check its expiry time
  129. block, _ := pem.Decode(certBtyes)
  130. if block != nil {
  131. cert, err := x509.ParseCertificate(block.Bytes)
  132. if err == nil {
  133. elapsed := time.Since(cert.NotAfter)
  134. // approxMonths := -int(elapsed.Hours() / (24 * 30.44))
  135. // approxDays := -int(elapsed.Hours()/24) % 30
  136. if elapsed > 0 {
  137. // log.Println("Certificate", certFilepath, " expired")
  138. for _, dnsName := range cert.DNSNames {
  139. if !contains(expiredCerts, dnsName) {
  140. expiredCerts = append(expiredCerts, dnsName)
  141. }
  142. }
  143. if !contains(expiredCerts, cert.Subject.CommonName) {
  144. expiredCerts = append(expiredCerts, cert.Subject.CommonName)
  145. }
  146. } else {
  147. // log.Println("Certificate", certFilepath, " will still be valid for the next ", approxMonths, "m", approxDays, "d")
  148. }
  149. }
  150. }
  151. }
  152. }
  153. return expiredCerts
  154. }
  155. // return the current port number
  156. func (a *ACMEHandler) Getport() string {
  157. return a.port
  158. }
  159. // contains checks if a string is present in a slice.
  160. func contains(slice []string, str string) bool {
  161. for _, s := range slice {
  162. if s == str {
  163. return true
  164. }
  165. }
  166. return false
  167. }
  168. func (a *ACMEHandler) HandleGetExpiredDomains(w http.ResponseWriter, r *http.Request) {
  169. type ExpiredDomains struct {
  170. Domain []string `json:"domain"`
  171. }
  172. info := ExpiredDomains{
  173. Domain: a.CheckCertificate(),
  174. }
  175. js, _ := json.MarshalIndent(info, "", " ")
  176. utils.SendJSONResponse(w, string(js))
  177. }
  178. func (a *ACMEHandler) HandleRenewCertificate(w http.ResponseWriter, r *http.Request) {
  179. domainPara, err := utils.GetPara(r, "domains")
  180. if err != nil {
  181. utils.SendErrorResponse(w, jsonEscape(err.Error()))
  182. return
  183. }
  184. filename, err := utils.GetPara(r, "filename")
  185. if err != nil {
  186. utils.SendErrorResponse(w, jsonEscape(err.Error()))
  187. return
  188. }
  189. domains := strings.Split(domainPara, ",")
  190. result, err := a.ObtainCert(domains, filename)
  191. if err != nil {
  192. utils.SendErrorResponse(w, jsonEscape(err.Error()))
  193. return
  194. }
  195. utils.SendJSONResponse(w, strconv.FormatBool(result))
  196. }
  197. func jsonEscape(i string) string {
  198. b, err := json.Marshal(i)
  199. if err != nil {
  200. panic(err)
  201. }
  202. s := string(b)
  203. return s[1 : len(s)-1]
  204. }