mirror of
https://github.com/TeaOSLab/EdgeCommon.git
synced 2026-01-25 10:25:48 +08:00
272 lines
6.1 KiB
Go
272 lines
6.1 KiB
Go
|
|
package sslconfigs
|
|||
|
|
|
|||
|
|
import (
|
|||
|
|
"crypto/tls"
|
|||
|
|
"crypto/x509"
|
|||
|
|
"crypto/x509/pkix"
|
|||
|
|
"encoding/pem"
|
|||
|
|
"errors"
|
|||
|
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/configutils"
|
|||
|
|
"github.com/iwind/TeaGo/Tea"
|
|||
|
|
"github.com/iwind/TeaGo/files"
|
|||
|
|
"github.com/iwind/TeaGo/lists"
|
|||
|
|
"github.com/iwind/TeaGo/utils/string"
|
|||
|
|
"io/ioutil"
|
|||
|
|
"strings"
|
|||
|
|
"time"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
// SSL证书
|
|||
|
|
type SSLCertConfig struct {
|
|||
|
|
Id string `yaml:"id" json:"id"`
|
|||
|
|
IsOn bool `yaml:"isOn" json:"isOn"`
|
|||
|
|
Description string `yaml:"description" json:"description"` // 说明
|
|||
|
|
CertFile string `yaml:"certFile" json:"certFile"`
|
|||
|
|
KeyFile string `yaml:"keyFile" json:"keyFile"`
|
|||
|
|
IsLocal bool `yaml:"isLocal" json:"isLocal"` // 是否为本地文件
|
|||
|
|
TaskId string `yaml:"taskId" json:"taskId"` // 生成证书任务ID
|
|||
|
|
IsShared bool `yaml:"isShared" json:"isShared"` // 是否为公用组件
|
|||
|
|
ServerName string `yaml:"serverName" json:"serverName"` // 证书使用的主机名,在请求TLS服务器时需要
|
|||
|
|
IsCA bool `yaml:"isCA" json:"isCA"` // 是否为CA证书
|
|||
|
|
|
|||
|
|
dnsNames []string
|
|||
|
|
cert *tls.Certificate
|
|||
|
|
timeBefore time.Time
|
|||
|
|
timeAfter time.Time
|
|||
|
|
issuer pkix.Name
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 获取新的SSL证书
|
|||
|
|
func NewSSLCertConfig(certFile string, keyFile string) *SSLCertConfig {
|
|||
|
|
return &SSLCertConfig{
|
|||
|
|
IsOn: true,
|
|||
|
|
Id: stringutil.Rand(16),
|
|||
|
|
CertFile: certFile,
|
|||
|
|
KeyFile: keyFile,
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 校验
|
|||
|
|
func (this *SSLCertConfig) Init() error {
|
|||
|
|
if this.IsShared {
|
|||
|
|
shared := this.FindShared()
|
|||
|
|
if shared == nil {
|
|||
|
|
return errors.New("the shared cert has been deleted")
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 拷贝之前需要保留的
|
|||
|
|
serverName := this.ServerName
|
|||
|
|
|
|||
|
|
// copy
|
|||
|
|
configutils.CopyStructObject(this, shared)
|
|||
|
|
this.ServerName = serverName
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
this.dnsNames = []string{}
|
|||
|
|
|
|||
|
|
if len(this.CertFile) == 0 {
|
|||
|
|
return errors.New("cert file should not be empty")
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 分析证书
|
|||
|
|
if this.IsCA { // CA证书
|
|||
|
|
data, err := ioutil.ReadFile(this.FullCertPath())
|
|||
|
|
if err != nil {
|
|||
|
|
return err
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
index := -1
|
|||
|
|
this.cert = &tls.Certificate{
|
|||
|
|
Certificate: [][]byte{},
|
|||
|
|
}
|
|||
|
|
for {
|
|||
|
|
index++
|
|||
|
|
|
|||
|
|
block, rest := pem.Decode(data)
|
|||
|
|
if block == nil {
|
|||
|
|
break
|
|||
|
|
}
|
|||
|
|
if len(rest) == 0 {
|
|||
|
|
break
|
|||
|
|
}
|
|||
|
|
this.cert.Certificate = append(this.cert.Certificate, block.Bytes)
|
|||
|
|
data = rest
|
|||
|
|
c, err := x509.ParseCertificate(block.Bytes)
|
|||
|
|
if err != nil {
|
|||
|
|
return err
|
|||
|
|
}
|
|||
|
|
if c == nil {
|
|||
|
|
return errors.New("no available certificates in file")
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
dnsNames := c.DNSNames
|
|||
|
|
if len(dnsNames) > 0 {
|
|||
|
|
for _, dnsName := range dnsNames {
|
|||
|
|
if !lists.ContainsString(this.dnsNames, dnsName) {
|
|||
|
|
this.dnsNames = append(this.dnsNames, dnsName)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if index == 0 {
|
|||
|
|
this.timeBefore = c.NotBefore
|
|||
|
|
this.timeAfter = c.NotAfter
|
|||
|
|
this.issuer = c.Issuer
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
} else { // 证书+私钥
|
|||
|
|
if len(this.KeyFile) == 0 {
|
|||
|
|
return errors.New("key file should not be empty")
|
|||
|
|
}
|
|||
|
|
cert, err := tls.LoadX509KeyPair(this.FullCertPath(), this.FullKeyPath())
|
|||
|
|
if err != nil {
|
|||
|
|
return errors.New("load certificate '" + this.CertFile + "', '" + this.KeyFile + "' failed:" + err.Error())
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
for index, data := range cert.Certificate {
|
|||
|
|
c, err := x509.ParseCertificate(data)
|
|||
|
|
if err != nil {
|
|||
|
|
continue
|
|||
|
|
}
|
|||
|
|
dnsNames := c.DNSNames
|
|||
|
|
if len(dnsNames) > 0 {
|
|||
|
|
for _, dnsName := range dnsNames {
|
|||
|
|
if !lists.ContainsString(this.dnsNames, dnsName) {
|
|||
|
|
this.dnsNames = append(this.dnsNames, dnsName)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if index == 0 {
|
|||
|
|
this.timeBefore = c.NotBefore
|
|||
|
|
this.timeAfter = c.NotAfter
|
|||
|
|
this.issuer = c.Issuer
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
this.cert = &cert
|
|||
|
|
}
|
|||
|
|
return nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 查找共享的证书
|
|||
|
|
func (this *SSLCertConfig) FindShared() *SSLCertConfig {
|
|||
|
|
if !this.IsShared {
|
|||
|
|
return nil
|
|||
|
|
}
|
|||
|
|
return SharedSSLCertList().FindCert(this.Id)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 证书文件路径
|
|||
|
|
func (this *SSLCertConfig) FullCertPath() string {
|
|||
|
|
if len(this.CertFile) == 0 {
|
|||
|
|
return ""
|
|||
|
|
}
|
|||
|
|
if !strings.ContainsAny(this.CertFile, "/\\") {
|
|||
|
|
return Tea.ConfigFile(this.CertFile)
|
|||
|
|
}
|
|||
|
|
return this.CertFile
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 密钥文件路径
|
|||
|
|
func (this *SSLCertConfig) FullKeyPath() string {
|
|||
|
|
if len(this.KeyFile) == 0 {
|
|||
|
|
return ""
|
|||
|
|
}
|
|||
|
|
if !strings.ContainsAny(this.KeyFile, "/\\") {
|
|||
|
|
return Tea.ConfigFile(this.KeyFile)
|
|||
|
|
}
|
|||
|
|
return this.KeyFile
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 校验是否匹配某个域名
|
|||
|
|
func (this *SSLCertConfig) MatchDomain(domain string) bool {
|
|||
|
|
if len(this.dnsNames) == 0 {
|
|||
|
|
return false
|
|||
|
|
}
|
|||
|
|
return configutils.MatchDomains(this.dnsNames, domain)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 证书中的域名
|
|||
|
|
func (this *SSLCertConfig) DNSNames() []string {
|
|||
|
|
return this.dnsNames
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 获取证书对象
|
|||
|
|
func (this *SSLCertConfig) CertObject() *tls.Certificate {
|
|||
|
|
return this.cert
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 开始时间
|
|||
|
|
func (this *SSLCertConfig) TimeBefore() time.Time {
|
|||
|
|
return this.timeBefore
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 结束时间
|
|||
|
|
func (this *SSLCertConfig) TimeAfter() time.Time {
|
|||
|
|
return this.timeAfter
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 发行信息
|
|||
|
|
func (this *SSLCertConfig) Issuer() pkix.Name {
|
|||
|
|
return this.issuer
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 删除文件
|
|||
|
|
func (this *SSLCertConfig) DeleteFiles() error {
|
|||
|
|
if this.IsLocal {
|
|||
|
|
return nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
var resultErr error = nil
|
|||
|
|
if len(this.CertFile) > 0 && !strings.ContainsAny(this.CertFile, "/\\") {
|
|||
|
|
err := files.NewFile(this.FullCertPath()).Delete()
|
|||
|
|
if err != nil {
|
|||
|
|
resultErr = err
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if len(this.KeyFile) > 0 && !strings.ContainsAny(this.KeyFile, "/\\") {
|
|||
|
|
err := files.NewFile(this.FullKeyPath()).Delete()
|
|||
|
|
if err != nil {
|
|||
|
|
resultErr = err
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return resultErr
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 读取证书文件
|
|||
|
|
func (this *SSLCertConfig) ReadCert() ([]byte, error) {
|
|||
|
|
if len(this.CertFile) == 0 {
|
|||
|
|
return nil, errors.New("cert file should not be empty")
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if this.IsLocal {
|
|||
|
|
return ioutil.ReadFile(this.CertFile)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return ioutil.ReadFile(Tea.ConfigFile(this.CertFile))
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 读取密钥文件
|
|||
|
|
func (this *SSLCertConfig) ReadKey() ([]byte, error) {
|
|||
|
|
if len(this.KeyFile) == 0 {
|
|||
|
|
return nil, errors.New("key file should not be empty")
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if this.IsLocal {
|
|||
|
|
return ioutil.ReadFile(this.KeyFile)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return ioutil.ReadFile(Tea.ConfigFile(this.KeyFile))
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 匹配关键词
|
|||
|
|
func (this *SSLCertConfig) MatchKeyword(keyword string) (matched bool, name string, tags []string) {
|
|||
|
|
if configutils.MatchKeyword(this.Description, keyword) {
|
|||
|
|
matched = true
|
|||
|
|
name = this.Description
|
|||
|
|
}
|
|||
|
|
return
|
|||
|
|
}
|