diff --git a/pkg/cert/certapi.go b/pkg/cert/certapi.go index 21304f16dcbd7cd9fe3facb28c3e2ef0dd58f737..4c7c642f8e166e675431640f419f4f8041b1f03c 100644 --- a/pkg/cert/certapi.go +++ b/pkg/cert/certapi.go @@ -23,6 +23,18 @@ import ( "time" ) +type CertInterface interface { + // Cert returns the certificate. + Cert() []byte +} + +// CertKeyInterface contains a private key and the associated cert. +type CertKeyInterface interface { + CertInterface + // Key returns the private key. + Key() []byte +} + type CertificateGenerator interface { GenerateCACertificate() error GenerateSignedCertificate(commonName string) error diff --git a/pkg/cert/certs.go b/pkg/cert/certs.go deleted file mode 100644 index fe1b4b448eb078872c5edca15422caf44147c839..0000000000000000000000000000000000000000 --- a/pkg/cert/certs.go +++ /dev/null @@ -1,76 +0,0 @@ -/* -Copyright 2023 KylinSoft Co., Ltd. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package cert - -import ( - "crypto/rand" - "crypto/rsa" - "crypto/x509" - "crypto/x509/pkix" - "math/big" - "time" - - "github.com/sirupsen/logrus" -) - -// 使用CA证书和私钥生成组件证书和私钥 -func (cm *CertificateManager) GenerateComponentCertificate(componentName string) error { - - // 创建组件的公钥和私钥 - var err error - cm.ComponentKey, err = rsa.GenerateKey(rand.Reader, 2048) - if err != nil { - logrus.Errorf("Failed to generate %s privatekey: %v", componentName, err) - return err - } - componentPublicKey := &cm.ComponentKey.PublicKey - - // 生成一个介于 0 和 2^128 - 1 之间的随机序列号,并将结果存储在 serialNumber 变量中 - serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) - serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) - if err != nil { - logrus.Errorf("Failed to generate %s serialNumber: %v", componentName, err) - return err - } - now := time.Now() - - // 组件证书模板 - componentTemplate := &x509.Certificate{ - SerialNumber: serialNumber, - Subject: pkix.Name{Organization: []string{"NKD"}, CommonName: componentName}, - NotBefore: now, - NotAfter: time.Now().AddDate(0, 0, cm.ValidDays), // 有效期为1年 - KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, - ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth}, - } - - // 使用CA证书和私钥生成组件证书 - componentCertBytes, err := x509.CreateCertificate(rand.Reader, componentTemplate, cm.CACert, componentPublicKey, cm.CAKey) - if err != nil { - logrus.Errorf("Failed to create %s Certificate(caCertBytes): %v", componentName, err) - return err - } - - cm.ComponentCert, err = x509.ParseCertificate(componentCertBytes) - if err != nil { - logrus.Errorf("Failed to parse %s Certificate: %v", componentName, err) - return err - } - - logrus.Infof("Successfully generate %s certificate", componentName) - return nil -} diff --git a/pkg/cert/signedcerts.go b/pkg/cert/signedcerts.go new file mode 100644 index 0000000000000000000000000000000000000000..66d8a4482f64f6aa3b2a874a75b1e902f1ca63b4 --- /dev/null +++ b/pkg/cert/signedcerts.go @@ -0,0 +1,130 @@ +/* +Copyright 2023 KylinSoft Co., Ltd. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package cert + +import ( + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "math/big" + "time" + + "github.com/pkg/errors" + "github.com/sirupsen/logrus" +) + +func SignedCertificate( + cfg *CertConfig, + csr *x509.CertificateRequest, + key *rsa.PrivateKey, + caCert *x509.Certificate, + caKey *rsa.PrivateKey, +) (*x509.Certificate, error) { + serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) + serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) + if err != nil { + logrus.Errorf("Failed to generate serialNumber: %v", err) + return nil, err + } + certTemplate := x509.Certificate{ + BasicConstraintsValid: true, + IsCA: cfg.IsCA, + DNSNames: csr.DNSNames, + ExtKeyUsage: cfg.ExtKeyUsages, + IPAddresses: csr.IPAddresses, + KeyUsage: cfg.KeyUsages, + NotAfter: time.Now().Add(cfg.Validity), + NotBefore: caCert.NotBefore, + SerialNumber: serialNumber, + Subject: csr.Subject, + } + certBytes, err := x509.CreateCertificate(rand.Reader, &certTemplate, caCert, key.Public(), caKey) + if err != nil { + return nil, errors.Wrap(err, "failed to create x509 certificate") + } + return x509.ParseCertificate(certBytes) +} + +func GenerateSignedCertificate(caKey *rsa.PrivateKey, caCert *x509.Certificate, + cfg *CertConfig) (*rsa.PrivateKey, *x509.Certificate, error) { + key, err := PrivateKey() + if err != nil { + return nil, nil, errors.Wrap(err, "failed to generate private key") + } + // create a CSR + csrTmpl := x509.CertificateRequest{Subject: cfg.Subject, DNSNames: cfg.DNSNames, IPAddresses: cfg.IPAddresses} + csrBytes, err := x509.CreateCertificateRequest(rand.Reader, &csrTmpl, key) + if err != nil { + return nil, nil, errors.Wrap(err, "failed to create certificate request") + } + csr, err := x509.ParseCertificateRequest(csrBytes) + if err != nil { + logrus.Debugf("Failed to parse x509 certificate request: %s", err) + return nil, nil, errors.Wrap(err, "error parsing x509 certificate request") + } + + // create a cert + cert, err := SignedCertificate(cfg, csr, key, caCert, caKey) + if err != nil { + logrus.Debugf("Failed to create a signed certificate: %s", err) + return nil, nil, errors.Wrap(err, "failed to create a signed certificate") + } + return key, cert, nil + +} + +type SignedCertKey struct { + CertKey +} + +func (c *SignedCertKey) Generate( + cfg *CertConfig, + parentCA CertKeyInterface, + filename string, +) error { + var key *rsa.PrivateKey + var crt *x509.Certificate + var err error + + caKey, err := PemToPrivateKey(parentCA.Key()) + if err != nil { + logrus.Debugf("Failed to parse RSA private key: %s", err) + return errors.Wrap(err, "failed to parse rsa private key") + } + + caCert, err := PemToCertificate(parentCA.Cert()) + if err != nil { + logrus.Debugf("Failed to parse x509 certificate: %s", err) + return errors.Wrap(err, "failed to parse x509 certificate") + } + + key, crt, err = GenerateSignedCertificate(caKey, caCert, cfg) + if err != nil { + logrus.Debugf("Failed to generate signed cert/key pair: %s", err) + return errors.Wrap(err, "failed to generate signed cert/key pair") + } + + c.KeyRaw = PrivateKeyToPem(key) + c.CertRaw = CertToPem(crt) + + err = c.SaveCertificateToFile(filename) + if err != nil { + logrus.Errorf("Faile to save %s: %v", filename, err) + } + + return nil +} diff --git a/pkg/cert/tools.go b/pkg/cert/tools.go index 21a8a7c6b0ba2794c03fd2dc373170ac53ff0ac4..4d10d39b52edad1d5e7237e43eda1b2a12c3b235 100644 --- a/pkg/cert/tools.go +++ b/pkg/cert/tools.go @@ -49,6 +49,14 @@ func PrivateKeyToPem(key *rsa.PrivateKey) []byte { return keyinPem } +func PemToPrivateKey(data []byte) (*rsa.PrivateKey, error) { + block, _ := pem.Decode(data) + if block == nil { + return nil, errors.Errorf("could not find a PEM block in the private key") + } + return x509.ParsePKCS1PrivateKey(block.Bytes) +} + // CACertPEM 返回证书的PEM格式字节切片 func CertToPem(cert *x509.Certificate) []byte { certInPem := pem.EncodeToMemory( @@ -60,6 +68,14 @@ func CertToPem(cert *x509.Certificate) []byte { return certInPem } +func PemToCertificate(data []byte) (*x509.Certificate, error) { + block, _ := pem.Decode(data) + if block == nil { + return nil, errors.Errorf("could not find a PEM block in the certificate") + } + return x509.ParseCertificate(block.Bytes) +} + // SaveCertificateToFile 将证书保存到文件 func (c *CertKey) SaveCertificateToFile(filename string) error { err := ioutil.WriteFile(c.SavePath+"/"+filename, c.CertRaw, 0644)