From 9006d4787c5e7a67cd8b32132aef7836da41712f Mon Sep 17 00:00:00 2001 From: DahuK Date: Thu, 24 Oct 2024 19:52:53 +0800 Subject: [PATCH] refine to support kms self generated keys Signed-off-by: dahu.kdh --- .../plugin.go | 50 ++++--- internal/sm/secret_manager.go | 127 +++++++++++++++++- 2 files changed, 146 insertions(+), 31 deletions(-) diff --git a/cmd/notation-alibabacloud-secret-manager/plugin.go b/cmd/notation-alibabacloud-secret-manager/plugin.go index 0ad3cf0..d4678c1 100644 --- a/cmd/notation-alibabacloud-secret-manager/plugin.go +++ b/cmd/notation-alibabacloud-secret-manager/plugin.go @@ -98,7 +98,6 @@ func (p *AlibabaCloudSecretManagerPlugin) DescribeKey(_ context.Context, req *pl } func (p *AlibabaCloudSecretManagerPlugin) GenerateSignature(_ context.Context, req *plugin.GenerateSignatureRequest) (*plugin.GenerateSignatureResponse, error) { - messageType := "RAW" signRequest := &dkms.SignRequest{ KeyId: tea.String(req.KeyID), @@ -108,6 +107,8 @@ func (p *AlibabaCloudSecretManagerPlugin) GenerateSignature(_ context.Context, r runtimeOptions := &dedicatedkmsopenapiutil.RuntimeOptions{ IgnoreSSL: tea.Bool(true), } + + rawCertChain := make([][]byte, 0) //set instance ca from file caFilePath := sm.GetKMSCAFile() if caFilePath != "" { @@ -143,6 +144,7 @@ func (p *AlibabaCloudSecretManagerPlugin) GenerateSignature(_ context.Context, r log.Logger.Infof("sign response is %s", signResponse.String()) var certChain []*x509.Certificate if caCertsPath, ok := req.PluginConfig[CaCerts]; ok { + //for imported key caCertPEMBlock, err := os.ReadFile(caCertsPath) if err != nil { log.Logger.Errorf("Failed to read ca_certs from %s, err %v", caCertsPath, err) @@ -153,32 +155,26 @@ func (p *AlibabaCloudSecretManagerPlugin) GenerateSignature(_ context.Context, r log.Logger.Errorf("Failed to parse ca_certs from %s, err %v", caCertsPath, err) return nil, err } + // build raw cert chain + for _, cert := range certChain { + rawCertChain = append(rawCertChain, cert.Raw) + } } else { - //getPublicKeyRequest := &dkms.GetPublicKeyRequest{ - // KeyId: tea.String(req.KeyID), - //} - //publicKeyResponse, err := p.DedicatedClient.GetPublicKeyWithOptions(getPublicKeyRequest, runtimeOptions) - //if err != nil { - // log.Logger.Errorf("Failed to get public key from KMS service, err %v", err) - // return nil, err - //} - //log.Logger.Infof("public key response is %s", tea.StringValue(publicKeyResponse.PublicKey)) - // - //certChain, err := sm.ParseCertificates(tea.StringValue(publicKeyResponse.PublicKey)) - //if err != nil { - // log.Logger.Errorf("Failed to parse public key response, err %v", err) - // return nil, err - //} - - log.Logger.Errorf("Can not find ca_certs in plugin-config") - return nil, errors.New("No ca_certs parameter given in plugin-config") + //for kms self generated key + pub, err := sm.GetPublicKey(p.DedicatedClient, req.KeyID) + if err != nil { + log.Logger.Errorf("Failed to get the public key from the given kms key %s, err %v", req.KeyID, err) + return nil, err + } + //get cert data based on the given key id + certData, err := sm.GetCertDataFromKey(p.DedicatedClient, pub, req.KeyID) + if err != nil { + log.Logger.Errorf("Failed to parse ca_certs from %s, err %v", caCertsPath, err) + return nil, err + } + rawCertChain = append(rawCertChain, certData) } - // build raw cert chain - rawCertChain := make([][]byte, 0, len(certChain)) - for _, cert := range certChain { - rawCertChain = append(rawCertChain, cert.Raw) - } return &plugin.GenerateSignatureResponse{ KeyID: req.KeyID, Signature: signResponse.Signature, @@ -217,10 +213,10 @@ func (p *AlibabaCloudSecretManagerPlugin) VerifySignature(_ context.Context, req func (p *AlibabaCloudSecretManagerPlugin) GetMetadata(_ context.Context, _ *plugin.GetMetadataRequest) (*plugin.GetMetadataResponse, error) { return &plugin.GetMetadataResponse{ SupportedContractVersions: []string{plugin.ContractVersion}, - Name: "notation-alibabacloud.secretmanager.plugin", + Name: "alibabacloud.secretmanager.plugin", Description: "Alibaba Cloud Secret Manager signer plugin for Notation", - URL: "https://github.com/AliyunContainerService/notation-alibabacloud-secret-manager", - Version: "0.1.0", + URL: "https://example.com/notation/plugin", + Version: "0.0.1", Capabilities: []plugin.Capability{ plugin.CapabilitySignatureGenerator, plugin.CapabilityTrustedIdentityVerifier}, diff --git a/internal/sm/secret_manager.go b/internal/sm/secret_manager.go index ee89411..79983ac 100644 --- a/internal/sm/secret_manager.go +++ b/internal/sm/secret_manager.go @@ -1,14 +1,22 @@ package sm import ( + "crypto" + "crypto/rand" + "crypto/rsa" "crypto/x509" + "crypto/x509/pkix" "encoding/pem" "errors" "fmt" + "github.com/AliyunContainerService/notation-alibabacloud-secret-manager/internal/log" "github.com/alibabacloud-go/tea/tea" dedicatedkmsopenapi "github.com/aliyun/alibabacloud-dkms-gcs-go-sdk/openapi" dedicatedkmssdk "github.com/aliyun/alibabacloud-dkms-gcs-go-sdk/sdk" "github.com/notaryproject/notation-plugin-framework-go/plugin" + "io" + "math/big" + "time" ) const ( @@ -16,16 +24,127 @@ const ( KMS_RSA_3072 = "RSA_3072" KMS_RSA_4096 = "RSA_4096" KMS_EC_P256 = "EC_P256" + //sign algorithm supported by KMS + KMS_ALG_RSA_PSS_SHA_256 = "RSA_PSS_SHA_256" + KMS_ALG_RSA_PKCS1_SHA_256 = "RSA_PKCS1_SHA_256" + + NOTATION_CN = "notation" ) +type KmsPrivateKeySigner struct { + client *dedicatedkmssdk.Client + publicKey crypto.PublicKey + keyId string + algorithm string +} + +func (ks *KmsPrivateKeySigner) Public() crypto.PublicKey { + return ks.publicKey +} + +func (ks *KmsPrivateKeySigner) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) (signature []byte, err error) { + request := &dedicatedkmssdk.SignRequest{ + KeyId: tea.String(ks.keyId), + Message: digest, + MessageType: tea.String("DIGEST"), + Algorithm: tea.String(ks.algorithm), + } + resp, err := ks.client.Sign(request) + if err != nil { + return nil, err + } + return resp.Signature, nil +} + +func genSerialNum() (*big.Int, error) { + serialNumLimit := new(big.Int).Lsh(big.NewInt(1), 128) + serialNum, err := rand.Int(rand.Reader, serialNumLimit) + if err != nil { + return nil, fmt.Errorf("serial number generation failure (%v)", err) + } + return serialNum, nil +} + +func GetCertDataFromKey(dkmsClient *dedicatedkmssdk.Client, pub *rsa.PublicKey, keyId string) ([]byte, error) { + //init csr subject + subject := pkix.Name{ + Country: []string{"CN"}, + Organization: []string{"AlibabaCloud"}, + OrganizationalUnit: []string{"Ack"}, + CommonName: NOTATION_CN, + } + + //Create kms service signer object + priv := &KmsPrivateKeySigner{ + client: dkmsClient, //kms client + keyId: keyId, //kms instance asymmetric key Id + publicKey: pub, //kms instance asymmetric public key + algorithm: KMS_ALG_RSA_PSS_SHA_256, //kms instance signing algorithm, RSA_PKCS1_SHA_256 is not conform with notation specification + } + + serialNum, err := genSerialNum() + if err != nil { + log.Logger.Errorf("Failed to generate serail number, err %v", err) + return nil, err + } + + // Create a new certificate template + template := x509.Certificate{ + SerialNumber: serialNum, + Subject: subject, + NotBefore: time.Now(), + NotAfter: time.Now().Add(365 * 24 * time.Hour), // Valid for 1 year + IsCA: false, + KeyUsage: x509.KeyUsageDigitalSignature, + SignatureAlgorithm: x509.SHA256WithRSAPSS, //only support RSA_PSS_SHA_256 here + } + + // Create the certificate + certBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, pub, priv) + if err != nil { + log.Logger.Errorf("Failed to generate certificate from key %s, err %v", keyId, err) + return nil, err + } + return certBytes, nil +} + +func GetPublicKey(client *dedicatedkmssdk.Client, keyId string) (*rsa.PublicKey, error) { + request := &dedicatedkmssdk.GetPublicKeyRequest{ + KeyId: tea.String(keyId), + } + response, err := client.GetPublicKey(request) + if err != nil { + return nil, err + } + block, _ := pem.Decode([]byte(*response.PublicKey)) + if block == nil || block.Type != "PUBLIC KEY" { + return nil, errors.New("failed to decode public key") + } + pub, err := x509.ParsePKIXPublicKey(block.Bytes) + if err != nil { + return nil, err + } + //return rsa public key + rsaPub, ok := pub.(*rsa.PublicKey) + if !ok { + return nil, errors.New(fmt.Sprintf("unsupport public key type %T", pub)) + } + + return rsaPub, nil +} + func GetDkmsClientByClientKeyFile(clientKeyPath, password, endpoint string) (*dedicatedkmssdk.Client, error) { + // 创建DKMS Client配置 config := &dedicatedkmsopenapi.Config{ - Protocol: tea.String("https"), + Protocol: tea.String("https"), + // 请替换为您在KMS应用管理获取的ClientKey文件的路径 ClientKeyFile: tea.String(clientKeyPath), - Password: tea.String(password), - Endpoint: tea.String(endpoint), + // 请替换为您在KMS应用管理创建ClientKey时输入的加密口令 + Password: tea.String(password), + // 请替换为您实际的专属KMS实例服务地址(不包括协议头https://) + Endpoint: tea.String(endpoint), } - // Init DKMS client + // 创建DKMS Client对象 client, err := dedicatedkmssdk.NewClient(config) if err != nil { return nil, err