Files
2024-08-12 21:38:09 +08:00

225 lines
5.9 KiB
Go

package certificate
import (
"context"
"crypto/tls"
"crypto/x509"
"encoding/pem"
"errors"
"reflect"
"time"
"github.com/APIParkLab/APIPark/stores/certificate"
"github.com/eolinker/go-common/autowire"
"github.com/eolinker/go-common/utils"
"github.com/google/uuid"
)
var (
_ ICertificateService = (*imlCertificateService)(nil)
)
func init() {
autowire.Auto[ICertificateService](func() reflect.Value {
return reflect.ValueOf(new(imlCertificateService))
})
}
type ICertificateService interface {
Get(ctx context.Context, id string) (*Certificate, *File, error)
List(ctx context.Context, clusterId string) ([]*Certificate, error)
Save(ctx context.Context, id, clusterId string, key, cert []byte) (*Certificate, error)
Delete(ctx context.Context, id string) error
}
type imlCertificateService struct {
store certificate.ICertificateStore `autowired:""`
file certificate.ICertificateFileStore `autowired:""`
}
func (s *imlCertificateService) Delete(ctx context.Context, id string) error {
return s.store.Transaction(ctx, func(ctx context.Context) error {
i, err := s.store.DeleteWhere(ctx, map[string]interface{}{"uuid": id})
if err != nil {
return err
}
if i == 0 {
return nil
}
_, err = s.file.DeleteWhere(ctx, map[string]interface{}{"uuid": id})
if err != nil {
return err
}
return nil
})
}
func (s *imlCertificateService) Get(ctx context.Context, id string) (*Certificate, *File, error) {
ce, err := s.store.First(ctx, map[string]interface{}{"uuid": id})
if err != nil {
return nil, nil, err
}
fe, err := s.file.Get(ctx, ce.Id)
if err != nil {
return nil, nil, err
}
return &Certificate{
ID: ce.UUID,
Cluster: ce.Cluster,
UpdateTime: ce.UpdateTime,
Name: ce.Name,
Domains: ce.Domains,
NotAfter: ce.NotAfter,
NotBefore: ce.NotBefore,
Updater: ce.Updater,
}, &File{
ID: id,
Key: fe.Key,
Cert: fe.Cert,
}, nil
}
func (s *imlCertificateService) List(ctx context.Context, clusterId string) ([]*Certificate, error) {
list, err := s.store.List(ctx, map[string]interface{}{"cluster": clusterId})
if err != nil {
return nil, err
}
return utils.SliceToSlice(list, func(i *certificate.Certificate) *Certificate {
return &Certificate{
ID: i.UUID,
Name: i.Name,
Domains: i.Domains,
Cluster: i.Cluster,
NotAfter: i.NotAfter,
NotBefore: i.NotBefore,
Updater: i.Updater,
UpdateTime: i.UpdateTime,
}
}), nil
}
func (s *imlCertificateService) Save(ctx context.Context, id, clusterId string, key, cert []byte) (*Certificate, error) {
if id == "" {
id = uuid.NewString()
}
operator := utils.UserId(ctx)
certDERBlock, err := ParseCert(string(key), string(cert))
if err != nil {
return nil, err
}
dnsNames := certDERBlock.Leaf.DNSNames
if dnsNames == nil && certDERBlock.Leaf.IPAddresses != nil {
dnsNames = make([]string, 0, len(certDERBlock.Leaf.IPAddresses))
for _, ip := range certDERBlock.Leaf.IPAddresses {
dnsNames = append(dnsNames, ip.String())
}
}
if dnsNames == nil {
return nil, errors.New("证书中没有包含域名或者IP地址信息")
}
ce := &certificate.Certificate{
UUID: id,
Cluster: clusterId,
Name: certDERBlock.Leaf.Subject.CommonName,
Domains: dnsNames,
Updater: operator,
NotAfter: certDERBlock.Leaf.NotAfter,
NotBefore: certDERBlock.Leaf.NotBefore,
UpdateTime: time.Now(),
}
fe := &certificate.File{
UUID: id,
Key: key,
Cert: cert,
}
err = s.store.Transaction(ctx, func(ctx context.Context) error {
err := s.store.Save(ctx, ce)
if err != nil {
return err
}
fe.Id = ce.Id
return s.file.Save(ctx, fe)
})
if err != nil {
return nil, err
}
return &Certificate{
ID: ce.UUID,
Name: ce.Name,
Domains: ce.Domains,
Cluster: ce.Cluster,
NotAfter: ce.NotAfter,
NotBefore: ce.NotBefore,
Updater: ce.Updater,
UpdateTime: ce.UpdateTime,
}, nil
}
//func parseCert(crt []byte) (*x509.Certificate, error) {
//
// //获取下一个pem格式证书数据 -----BEGIN CERTIFICATE----- -----END CERTIFICATE-----
// certDERBlock, _ := pem.Decode(crt)
// if certDERBlock == nil {
// return nil, fmt.Errorf("pem.Decode failed")
// }
//
// //第一个叶子证书就是我们https中使用的证书
// x509Cert, err := x509.ParseCertificate(certDERBlock.Bytes)
// if err != nil {
//
// return nil, fmt.Errorf("x509.ParseCertificate failed:%w", err)
// }
// return x509Cert, nil
//}
func ParseCert(privateKey, pemValue string) (*tls.Certificate, error) {
var cert tls.Certificate
//获取下一个pem格式证书数据 -----BEGIN CERTIFICATE----- -----END CERTIFICATE-----
certDERBlock, restPEMBlock := pem.Decode([]byte(pemValue))
if certDERBlock == nil {
return nil, errors.New("证书解析失败")
}
//附加数字证书到返回
cert.Certificate = append(cert.Certificate, certDERBlock.Bytes)
//继续解析Certificate Chan,这里要明白证书链的概念
certDERBlockChain, _ := pem.Decode(restPEMBlock)
if certDERBlockChain != nil {
//追加证书链证书到返回
cert.Certificate = append(cert.Certificate, certDERBlockChain.Bytes)
}
//解码pem格式的私钥------BEGIN RSA PRIVATE KEY----- -----END RSA PRIVATE KEY-----
keyDERBlock, _ := pem.Decode([]byte(privateKey))
if keyDERBlock == nil {
return nil, errors.New("证书解析失败")
}
var key interface{}
var errParsePK error
if keyDERBlock.Type == "RSA PRIVATE KEY" {
//RSA PKCS1
key, errParsePK = x509.ParsePKCS1PrivateKey(keyDERBlock.Bytes)
} else if keyDERBlock.Type == "PRIVATE KEY" {
//pkcs8格式的私钥解析
key, errParsePK = x509.ParsePKCS8PrivateKey(keyDERBlock.Bytes)
}
if errParsePK != nil {
return nil, errors.New("证书解析失败")
} else {
cert.PrivateKey = key
}
//第一个叶子证书就是我们https中使用的证书
x509Cert, err := x509.ParseCertificate(certDERBlock.Bytes)
if err != nil {
return nil, err
}
cert.Leaf = x509Cert
return &cert, nil
}