Files
EdgeCommon/pkg/serverconfigs/sslconfigs/ssl_cert.go

272 lines
6.1 KiB
Go
Raw Normal View History

2020-09-13 19:27:47 +08:00
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
}