mirror of
https://github.com/TeaOSLab/EdgeCommon.git
synced 2025-12-28 21:46:37 +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
|
||
}
|