| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411 |
- package dbdata
- import (
- "crypto"
- "crypto/ecdsa"
- "crypto/elliptic"
- "crypto/rand"
- "crypto/rsa"
- "crypto/tls"
- "crypto/x509"
- "crypto/x509/pkix"
- "encoding/pem"
- "errors"
- "fmt"
- "math/big"
- "net"
- "os"
- "strings"
- "sync"
- "time"
- "github.com/pion/dtls/v2/pkg/crypto/selfsign"
- "github.com/bjdgyc/anylink/base"
- "github.com/go-acme/lego/v4/certcrypto"
- "github.com/go-acme/lego/v4/certificate"
- "github.com/go-acme/lego/v4/challenge"
- "github.com/go-acme/lego/v4/challenge/dns01"
- "github.com/go-acme/lego/v4/lego"
- "github.com/go-acme/lego/v4/providers/dns/alidns"
- "github.com/go-acme/lego/v4/providers/dns/cloudflare"
- "github.com/go-acme/lego/v4/providers/dns/tencentcloud"
- "github.com/go-acme/lego/v4/registration"
- )
- var (
- // nameToCertificate mutex
- ntcMux sync.RWMutex
- nameToCertificate = make(map[string]*tls.Certificate)
- tempCert *tls.Certificate
- )
- func init() {
- c, _ := selfsign.GenerateSelfSignedWithDNS("localhost")
- tempCert = &c
- }
- type SettingLetsEncrypt struct {
- Domain string `json:"domain"`
- Legomail string `json:"legomail"`
- Name string `json:"name"`
- Renew bool `json:"renew"`
- DNSProvider
- }
- type DNSProvider struct {
- AliYun struct {
- APIKey string `json:"apiKey"`
- SecretKey string `json:"secretKey"`
- } `json:"aliyun"`
- TXCloud struct {
- SecretID string `json:"secretId"`
- SecretKey string `json:"secretKey"`
- } `json:"txcloud"`
- CfCloud struct {
- AuthToken string `json:"authToken"`
- } `json:"cfcloud"`
- }
- type LegoUserData struct {
- Email string `json:"email"`
- Registration *registration.Resource `json:"registration"`
- Key []byte `json:"key"`
- }
- type LegoUser struct {
- Email string
- Registration *registration.Resource
- Key *ecdsa.PrivateKey
- }
- type LeGoClient struct {
- mutex sync.Mutex
- Client *lego.Client
- Cert *certificate.Resource
- LegoUserData
- }
- func GetDNSProvider(l *SettingLetsEncrypt) (Provider challenge.Provider, err error) {
- switch l.Name {
- case "aliyun":
- if Provider, err = alidns.NewDNSProviderConfig(&alidns.Config{APIKey: l.DNSProvider.AliYun.APIKey, SecretKey: l.DNSProvider.AliYun.SecretKey, PropagationTimeout: 60 * time.Second, PollingInterval: 2 * time.Second, TTL: 600}); err != nil {
- return
- }
- case "txcloud":
- if Provider, err = tencentcloud.NewDNSProviderConfig(&tencentcloud.Config{SecretID: l.DNSProvider.TXCloud.SecretID, SecretKey: l.DNSProvider.TXCloud.SecretKey, PropagationTimeout: 60 * time.Second, PollingInterval: 2 * time.Second, TTL: 600}); err != nil {
- return
- }
- case "cfcloud":
- if Provider, err = cloudflare.NewDNSProviderConfig(&cloudflare.Config{AuthToken: l.DNSProvider.CfCloud.AuthToken, PropagationTimeout: 60 * time.Second, PollingInterval: 2 * time.Second, TTL: 600}); err != nil {
- return
- }
- }
- return
- }
- func (u *LegoUser) GetEmail() string {
- return u.Email
- }
- func (u LegoUser) GetRegistration() *registration.Resource {
- return u.Registration
- }
- func (u *LegoUser) GetPrivateKey() crypto.PrivateKey {
- return u.Key
- }
- func (l *LegoUserData) SaveUserData(u *LegoUser) error {
- key, err := x509.MarshalECPrivateKey(u.Key)
- if err != nil {
- return err
- }
- l.Email = u.Email
- l.Registration = u.Registration
- l.Key = key
- if err := SettingSet(l); err != nil {
- return err
- }
- return nil
- }
- func (l *LegoUserData) GetUserData(d *SettingLetsEncrypt) (*LegoUser, error) {
- if err := SettingGet(l); err != nil {
- return nil, err
- }
- if l.Email != "" {
- key, err := x509.ParseECPrivateKey(l.Key)
- if err != nil {
- return nil, err
- }
- return &LegoUser{
- Email: l.Email,
- Registration: l.Registration,
- Key: key,
- }, nil
- }
- privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
- if err != nil {
- return nil, err
- }
- return &LegoUser{
- Email: d.Legomail,
- Key: privateKey,
- }, nil
- }
- func ReNewCert() {
- _, certtime, err := ParseCert()
- if err != nil {
- base.Error(err)
- return
- }
- if certtime.AddDate(0, 0, -7).Before(time.Now()) {
- config := &SettingLetsEncrypt{}
- if err := SettingGet(config); err != nil {
- base.Error(err)
- return
- }
- if config.Renew {
- client := &LeGoClient{}
- if err := client.NewClient(config); err != nil {
- base.Error(err)
- return
- }
- if err := client.RenewCert(base.Cfg.CertFile, base.Cfg.CertKey); err != nil {
- base.Error(err)
- return
- }
- base.Info("证书续期成功")
- }
- } else {
- base.Info(fmt.Sprintf("证书过期时间:%s", certtime.Local().Format("2006-1-2 15:04:05")))
- }
- }
- func (c *LeGoClient) NewClient(l *SettingLetsEncrypt) error {
- c.mutex.Lock()
- defer c.mutex.Unlock()
- legouser, err := c.GetUserData(l)
- if err != nil {
- return err
- }
- config := lego.NewConfig(legouser)
- config.CADirURL = lego.LEDirectoryProduction
- config.Certificate.KeyType = certcrypto.RSA2048
- client, err := lego.NewClient(config)
- if err != nil {
- return err
- }
- Provider, err := GetDNSProvider(l)
- if err != nil {
- return err
- }
- if err := client.Challenge.SetDNS01Provider(Provider, dns01.AddRecursiveNameservers([]string{"114.114.114.114", "114.114.115.115"})); err != nil {
- return err
- }
- if legouser.Registration == nil {
- reg, err := client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true})
- if err != nil {
- return err
- }
- legouser.Registration = reg
- c.SaveUserData(legouser)
- }
- c.Client = client
- return nil
- }
- func (c *LeGoClient) GetCert(domain string) error {
- // 申请证书
- certificates, err := c.Client.Certificate.Obtain(
- certificate.ObtainRequest{
- Domains: []string{domain},
- Bundle: true,
- })
- if err != nil {
- return err
- }
- c.Cert = certificates
- // 保存证书
- if err := c.SaveCert(); err != nil {
- return err
- }
- return nil
- }
- func (c *LeGoClient) RenewCert(certFile, keyFile string) error {
- cert, err := os.ReadFile(certFile)
- if err != nil {
- return err
- }
- key, err := os.ReadFile(keyFile)
- if err != nil {
- return err
- }
- // 续期证书
- renewcert, err := c.Client.Certificate.Renew(certificate.Resource{
- Certificate: cert,
- PrivateKey: key,
- }, true, false, "")
- if err != nil {
- return err
- }
- c.Cert = renewcert
- // 保存更新证书
- if err := c.SaveCert(); err != nil {
- return err
- }
- return nil
- }
- func (c *LeGoClient) SaveCert() error {
- err := os.WriteFile(base.Cfg.CertFile, c.Cert.Certificate, 0600)
- if err != nil {
- return err
- }
- err = os.WriteFile(base.Cfg.CertKey, c.Cert.PrivateKey, 0600)
- if err != nil {
- return err
- }
- if tlscert, _, err := ParseCert(); err != nil {
- return err
- } else {
- LoadCertificate(tlscert)
- }
- return nil
- }
- func ParseCert() (*tls.Certificate, *time.Time, error) {
- _, errCert := os.Stat(base.Cfg.CertFile)
- _, errKey := os.Stat(base.Cfg.CertKey)
- if os.IsNotExist(errCert) || os.IsNotExist(errKey) {
- err := PrivateCert()
- if err != nil {
- return nil, nil, err
- }
- }
- cert, err := tls.LoadX509KeyPair(base.Cfg.CertFile, base.Cfg.CertKey)
- if err != nil || errors.Is(err, os.ErrNotExist) {
- PrivateCert()
- return nil, nil, err
- }
- parseCert, err := x509.ParseCertificate(cert.Certificate[0])
- if err != nil {
- return nil, nil, err
- }
- return &cert, &parseCert.NotAfter, nil
- }
- func PrivateCert() error {
- // 创建一个RSA密钥对
- priv, _ := rsa.GenerateKey(rand.Reader, 2048)
- pub := &priv.PublicKey
- // 生成一个自签名证书
- template := x509.Certificate{
- SerialNumber: big.NewInt(1658),
- Subject: pkix.Name{CommonName: "localhost"},
- NotBefore: time.Now(),
- NotAfter: time.Now().Add(time.Hour * 24 * 365),
- KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
- ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
- BasicConstraintsValid: true,
- IPAddresses: []net.IP{},
- }
- derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, pub, priv)
- if err != nil {
- return err
- }
- // 将证书编码为PEM格式并将其写入文件
- certOut, _ := os.OpenFile(base.Cfg.CertFile, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0600)
- pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
- certOut.Close()
- // 将私钥编码为PEM格式并将其写入文件
- keyOut, _ := os.OpenFile(base.Cfg.CertKey, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
- pem.Encode(keyOut, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(priv)})
- keyOut.Close()
- cert, err := tls.LoadX509KeyPair(base.Cfg.CertFile, base.Cfg.CertKey)
- if err != nil {
- return err
- }
- LoadCertificate(&cert)
- return nil
- }
- func getTempCertificate() (*tls.Certificate, error) {
- var err error
- var cert tls.Certificate
- if tempCert == nil {
- cert, err = selfsign.GenerateSelfSignedWithDNS("localhost")
- tempCert = &cert
- }
- return tempCert, err
- }
- func GetCertificateBySNI(commonName string) (*tls.Certificate, error) {
- ntcMux.RLock()
- defer ntcMux.RUnlock()
- // Copy from tls.Config getCertificate()
- name := strings.ToLower(commonName)
- if cert, ok := nameToCertificate[name]; ok {
- return cert, nil
- }
- if len(name) > 0 {
- labels := strings.Split(name, ".")
- labels[0] = "*"
- wildcardName := strings.Join(labels, ".")
- if cert, ok := nameToCertificate[wildcardName]; ok {
- return cert, nil
- }
- }
- // TODO 默认证书 兼容不支持 SNI 的客户端
- if cert, ok := nameToCertificate["default"]; ok {
- return cert, nil
- }
- return getTempCertificate()
- }
- func LoadCertificate(cert *tls.Certificate) {
- buildNameToCertificate(cert)
- }
- // Copy from tls.Config BuildNameToCertificate()
- func buildNameToCertificate(cert *tls.Certificate) {
- ntcMux.Lock()
- defer ntcMux.Unlock()
- // TODO 设置默认证书
- nameToCertificate["default"] = cert
- x509Cert, err := x509.ParseCertificate(cert.Certificate[0])
- if err != nil {
- return
- }
- startTime := x509Cert.NotBefore.String()
- expiredTime := x509Cert.NotAfter.String()
- if x509Cert.Subject.CommonName != "" && len(x509Cert.DNSNames) == 0 {
- commonName := x509Cert.Subject.CommonName
- fmt.Printf("┏ Load Certificate: %s\n", commonName)
- fmt.Printf("┠╌╌ Start Time: %s\n", startTime)
- fmt.Printf("┖╌╌ Expired Time: %s\n", expiredTime)
- nameToCertificate[commonName] = cert
- }
- for _, san := range x509Cert.DNSNames {
- fmt.Printf("┏ Load Certificate: %s\n", san)
- fmt.Printf("┠╌╌ Start Time: %s\n", startTime)
- fmt.Printf("┖╌╌ Expired Time: %s\n", expiredTime)
- nameToCertificate[san] = cert
- }
- }
- // func Scrypt(passwd string) string {
- // salt := []byte{0xc8, 0x28, 0xf2, 0x58, 0xa7, 0x6a, 0xad, 0x7b}
- // hashPasswd, err := scrypt.Key([]byte(passwd), salt, 1<<15, 8, 1, 32)
- // if err != nil {
- // return err.Error()
- // }
- // return base64.StdEncoding.EncodeToString(hashPasswd)
- // }
|