sslcert.go 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. package flexCdn
  2. import (
  3. "context"
  4. "crypto/tls"
  5. "crypto/x509"
  6. "encoding/json"
  7. "fmt"
  8. v1 "github.com/go-nunu/nunu-layout-advanced/api/v1"
  9. "github.com/go-nunu/nunu-layout-advanced/internal/repository/api/waf"
  10. "github.com/go-nunu/nunu-layout-advanced/internal/service"
  11. )
  12. type SslCertService interface {
  13. ParseCert(ctx context.Context, httpsCert string, httpKey string) (serverName string, commonName []string, DNSNames []string, before int64, after int64, isSelfSigned bool, err error)
  14. AddSSLCert(ctx context.Context, req v1.SSL) (int64, error)
  15. EditSSLCert(ctx context.Context, req v1.SSL) error
  16. AddSslPolicy(ctx context.Context, CertIds []int64) (sslPolicyId int64, err error)
  17. EditSslPolicy(ctx context.Context, sslPolicyId int64, CertIds []int64, action string) error
  18. }
  19. func NewSslCertService(
  20. service *service.Service,
  21. webForwardingRep waf.WebForwardingRepository,
  22. cdn CdnService,
  23. ) SslCertService {
  24. return &sslCertService{
  25. Service: service,
  26. webForwardingRep: webForwardingRep,
  27. cdn: cdn,
  28. }
  29. }
  30. type sslCertService struct {
  31. *service.Service
  32. webForwardingRep waf.WebForwardingRepository
  33. cdn CdnService
  34. }
  35. func (s *sslCertService) ParseCert(ctx context.Context, httpsCert string, httpKey string) (serverName string, commonName []string, DNSNames []string, before int64, after int64, isSelfSigned bool, err error) {
  36. cert, err := tls.X509KeyPair([]byte(httpsCert), []byte(httpKey))
  37. if err != nil {
  38. return "", nil, nil, 0, 0, false, fmt.Errorf("无法从字符串加载密钥对: %v", err)
  39. }
  40. if len(cert.Certificate) == 0 {
  41. return "", nil, nil, 0, 0, false, fmt.Errorf("提供的证书数据中没有找到证书。")
  42. }
  43. // 解析第一个证书(通常是叶子证书)
  44. x509Cert, err := x509.ParseCertificate(cert.Certificate[0])
  45. if err != nil {
  46. return "", nil, nil, 0, 0, false, fmt.Errorf("无法解析证书: %v", err)
  47. }
  48. // 1. 获取 Common Name (通用名称)
  49. // Common Name 位于 Subject 字段内. [1]
  50. serverName = x509Cert.Subject.CommonName
  51. // 2. 获取 DNS Names (备用主题名称中的DNS条目)
  52. // DNS Names 直接是证书结构体的一个字段. [1]
  53. DNSNames = x509Cert.DNSNames
  54. // 检查证书是否为自签名
  55. // 判断条件:颁发者(Issuer)和主题(Subject)相同,并且证书的签名可以由其自身的公钥验证
  56. if err := x509Cert.CheckSignatureFrom(x509Cert); err == nil {
  57. isSelfSigned = true
  58. }
  59. // 将CommonName放入一个切片,以匹配[]string的类型要求
  60. var commonNames []string
  61. if x509Cert.Subject.CommonName != "" {
  62. commonNames = []string{x509Cert.Subject.CommonName}
  63. }
  64. return serverName, commonNames, DNSNames, x509Cert.NotBefore.Unix(), x509Cert.NotAfter.Unix(), isSelfSigned, nil
  65. }
  66. func (s *sslCertService) AddSslPolicy(ctx context.Context, CertIds []int64) (sslPolicyId int64, err error) {
  67. // 构造策略中引用的证书列表
  68. type sslCerts struct {
  69. IsOn bool `json:"isOn" form:"isOn"`
  70. CertId int64 `json:"certId" form:"certId"`
  71. }
  72. var sslCertsSlice []sslCerts
  73. for _, certId := range CertIds {
  74. sslCertsSlice = append(sslCertsSlice, sslCerts{
  75. IsOn: true,
  76. CertId: certId,
  77. })
  78. }
  79. sslCertsJson, err := json.Marshal(sslCertsSlice)
  80. if err != nil {
  81. return 0, fmt.Errorf("序列化SSL证书引用失败: %w", err)
  82. }
  83. // 调用CDN服务创建策略
  84. newSslPolicyId, err := s.cdn.AddSSLPolicy(ctx, v1.AddSSLPolicy{
  85. Http2Enabled: true,
  86. SslCertsJSON: sslCertsJson,
  87. MinVersion: "TLS 1.1", // 可根据安全要求调整
  88. })
  89. if err != nil {
  90. // 如果策略创建失败,需要考虑回滚或记录错误,这里直接返回错误
  91. return 0, fmt.Errorf("通过CDN添加SSL策略失败: %w", err)
  92. }
  93. return newSslPolicyId, nil
  94. }
  95. func (s *sslCertService) EditSslPolicy(ctx context.Context, sslPolicyId int64, CertIds []int64, action string) error {
  96. type sslCerts struct {
  97. IsOn bool `json:"isOn" form:"isOn"`
  98. CertId int64 `json:"certId" form:"certId"`
  99. }
  100. oldCertIds, err := s.webForwardingRep.GetSslCertId(ctx, sslPolicyId)
  101. if err != nil {
  102. return fmt.Errorf("获取SSL证书失败: %w", err)
  103. }
  104. var sslCertsSlice []sslCerts
  105. newCertIdSet := make(map[int64]struct{}, len(CertIds))
  106. for _, certId := range CertIds {
  107. newCertIdSet[certId] = struct{}{}
  108. }
  109. oldCertIdSet := make(map[int64]struct{}, len(oldCertIds))
  110. for _, oldCert := range oldCertIds {
  111. oldCertIdSet[oldCert.CertId] = struct{}{}
  112. }
  113. switch action {
  114. case "add":
  115. for _, certId := range CertIds {
  116. // 使用 oldCertIdSet 进行 O(1) 复杂度的查找。
  117. if _, found := oldCertIdSet[certId]; !found {
  118. // 如果在旧的集合中没找到,说明是新增的。
  119. sslCertsSlice = append(sslCertsSlice, sslCerts{
  120. IsOn: true,
  121. CertId: certId,
  122. })
  123. }
  124. }
  125. case "del":
  126. for _, oldCert := range oldCertIds {
  127. // 使用 newCertIdSet 进行 O(1) 复杂度的查找。
  128. if _, found := newCertIdSet[oldCert.CertId]; !found {
  129. // 如果在新的集合中没找到,说明被删除了。
  130. sslCertsSlice = append(sslCertsSlice, sslCerts{
  131. IsOn: false,
  132. CertId: oldCert.CertId,
  133. })
  134. }
  135. }
  136. }
  137. sslCertsJson, err := json.Marshal(sslCertsSlice)
  138. if err != nil {
  139. return fmt.Errorf("序列化SSL证书引用失败: %w", err)
  140. }
  141. // 调用CDN服务创建策略
  142. err = s.cdn.EditSSLPolicy(ctx, v1.SSLPolicy{
  143. SslPolicyId: sslPolicyId,
  144. Http2Enabled: true,
  145. SslCertsJSON: sslCertsJson,
  146. MinVersion: "TLS 1.1", // 可根据安全要求调整
  147. })
  148. if err != nil {
  149. // 如果策略创建失败,需要考虑回滚或记录错误,这里直接返回错误
  150. return fmt.Errorf("通过CDN添加SSL策略失败: %w", err)
  151. }
  152. return nil
  153. }
  154. func (s *sslCertService) AddSSLCert(ctx context.Context, req v1.SSL) (int64, error) {
  155. // 1. 解析证书文件,提取元数据
  156. serverName, commonNames, DNSNames, before, after, isSelfSigned, err := s.ParseCert(ctx, req.CertData, req.KeyData)
  157. if err != nil {
  158. return 0, fmt.Errorf("解析证书失败: %w", err)
  159. }
  160. // 2. 将证书添加到CDN提供商
  161. // 这是获取可以在策略中引用的 `sslCertId` 的前提
  162. newSslCertId, err := s.cdn.AddSSLCert(ctx, v1.SSlCert{
  163. IsOn: true,
  164. UserId: int64(req.CdnUserId),
  165. Name: req.Domain, // 使用域名作为证书名称
  166. ServerName: serverName,
  167. Description: req.Description,
  168. CertData: []byte(req.CertData),
  169. KeyData: []byte(req.KeyData),
  170. TimeBeginAt: before,
  171. TimeEndAt: after,
  172. DnsNames: DNSNames,
  173. CommonNames: commonNames,
  174. IsSelfSigned: isSelfSigned,
  175. })
  176. if err != nil {
  177. return 0, fmt.Errorf("添加SSL证书到CDN失败: %w", err)
  178. }
  179. return newSslCertId, nil
  180. }
  181. func (s *sslCertService) EditSSLCert(ctx context.Context, req v1.SSL) error {
  182. if req.CertData == "" && req.KeyData == "" {
  183. return nil
  184. }
  185. // 1. 解析证书文件,提取元数据
  186. serverName, commonNames, DNSNames, before, after, isSelfSigned, err := s.ParseCert(ctx, req.CertData, req.KeyData)
  187. if err != nil {
  188. return fmt.Errorf("解析证书失败: %w", err)
  189. }
  190. // 2. 将证书添加到CDN提供商
  191. err = s.cdn.EditSSLCert(ctx, v1.SSlCert{
  192. SslCertId: int64(req.CertId),
  193. IsOn: true,
  194. UserId: int64(req.CdnUserId),
  195. Name: req.Domain, // 使用域名作为证书名称
  196. ServerName: serverName,
  197. Description: req.Description,
  198. CertData: []byte(req.CertData),
  199. KeyData: []byte(req.KeyData),
  200. TimeBeginAt: before,
  201. TimeEndAt: after,
  202. DnsNames: DNSNames,
  203. CommonNames: commonNames,
  204. IsSelfSigned: isSelfSigned,
  205. })
  206. if err != nil {
  207. return fmt.Errorf("添加SSL证书到CDN失败: %w", err)
  208. }
  209. return nil
  210. }