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 }