Skip to content

Commit

Permalink
refine to support kms self generated keys
Browse files Browse the repository at this point in the history
Signed-off-by: dahu.kdh <[email protected]>
  • Loading branch information
DahuK committed Oct 24, 2024
1 parent 3bed917 commit 9006d47
Show file tree
Hide file tree
Showing 2 changed files with 146 additions and 31 deletions.
50 changes: 23 additions & 27 deletions cmd/notation-alibabacloud-secret-manager/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand All @@ -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 != "" {
Expand Down Expand Up @@ -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)
Expand All @@ -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,
Expand Down Expand Up @@ -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},
Expand Down
127 changes: 123 additions & 4 deletions internal/sm/secret_manager.go
Original file line number Diff line number Diff line change
@@ -1,31 +1,150 @@
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 (
KMS_RSA_2048 = "RSA_2048"
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
Expand Down

0 comments on commit 9006d47

Please sign in to comment.