Skip to content

Commit

Permalink
[feature] support aes-128 & aes-256 encryption (#668)
Browse files Browse the repository at this point in the history
* support aes-128 & aes-256 encryption
  • Loading branch information
robynron authored Nov 8, 2023
1 parent 6930f82 commit 9b9ff77
Show file tree
Hide file tree
Showing 25 changed files with 1,528 additions and 90 deletions.
90 changes: 61 additions & 29 deletions clients/config_client/config_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,13 @@ import (
"sync"
"time"

"github.com/aliyun/alibaba-cloud-sdk-go/services/kms"
"github.com/alibabacloud-go/tea/tea"
dkms_api "github.com/aliyun/alibabacloud-dkms-gcs-go-sdk/openapi"
"github.com/nacos-group/nacos-sdk-go/v2/clients/cache"
"github.com/nacos-group/nacos-sdk-go/v2/clients/nacos_client"
"github.com/nacos-group/nacos-sdk-go/v2/common/constant"
nacos_inner_encryption "github.com/nacos-group/nacos-sdk-go/v2/common/encryption"
"github.com/nacos-group/nacos-sdk-go/v2/common/filter"
"github.com/nacos-group/nacos-sdk-go/v2/common/logger"
"github.com/nacos-group/nacos-sdk-go/v2/common/monitor"
"github.com/nacos-group/nacos-sdk-go/v2/common/nacos_error"
Expand All @@ -49,7 +52,7 @@ type ConfigClient struct {
ctx context.Context
cancel context.CancelFunc
nacos_client.INacosClient
kmsClient *kms.Client
kmsClient *nacos_inner_encryption.KmsClient
localConfigs []vo.ConfigParam
mutex sync.Mutex
configProxy IConfigProxy
Expand All @@ -66,6 +69,7 @@ type cacheData struct {
group string
content string
contentType string
encryptedDataKey string
tenant string
cacheDataListener *cacheDataListener
md5 string
Expand All @@ -84,12 +88,18 @@ func (cacheData *cacheData) executeListener() {
cacheData.cacheDataListener.lastMd5 = cacheData.md5
cacheData.configClient.cacheMap.Set(util.GetConfigCacheKey(cacheData.dataId, cacheData.group, cacheData.tenant), *cacheData)

decryptedContent, err := cacheData.configClient.decrypt(cacheData.dataId, cacheData.content)
if err != nil {
logger.Errorf("decrypt content fail ,dataId=%s,group=%s,tenant=%s,err:%+v ", cacheData.dataId,
param := &vo.ConfigParam{
DataId: cacheData.dataId,
Content: cacheData.content,
EncryptedDataKey: cacheData.encryptedDataKey,
UsageType: vo.ResponseType,
}
if err := filter.GetDefaultConfigFilterChainManager().DoFilters(param); err != nil {
logger.Errorf("do filters failed ,dataId=%s,group=%s,tenant=%s,err:%+v ", cacheData.dataId,
cacheData.group, cacheData.tenant, err)
return
}
decryptedContent := param.Content
go cacheData.cacheDataListener.listener(cacheData.tenant, cacheData.group, cacheData.dataId, decryptedContent)
}

Expand Down Expand Up @@ -121,7 +131,15 @@ func NewConfigClient(nc nacos_client.INacosClient) (*ConfigClient, error) {
}

if clientConfig.OpenKMS {
kmsClient, err := kms.NewClientWithAccessKey(clientConfig.RegionId, clientConfig.AccessKey, clientConfig.SecretKey)
var kmsClient *nacos_inner_encryption.KmsClient
switch clientConfig.KMSVersion {
case constant.KMSv1, constant.DEFAULT_KMS_VERSION:
kmsClient, err = initKmsV1Client(clientConfig)
case constant.KMSv3:
kmsClient, err = initKmsV3Client(clientConfig)
default:
err = fmt.Errorf("init kms client failed. unknown kms version:%s\n", clientConfig.KMSVersion)
}
if err != nil {
return nil, err
}
Expand All @@ -144,48 +162,57 @@ func initLogger(clientConfig constant.ClientConfig) error {
return logger.InitLogger(logger.BuildLoggerConfig(clientConfig))
}

func initKmsV1Client(clientConfig constant.ClientConfig) (*nacos_inner_encryption.KmsClient, error) {
return nacos_inner_encryption.InitDefaultKmsV1ClientWithAccessKey(clientConfig.RegionId, clientConfig.AccessKey, clientConfig.SecretKey)
}

func initKmsV3Client(clientConfig constant.ClientConfig) (*nacos_inner_encryption.KmsClient, error) {
return nacos_inner_encryption.InitDefaultKmsV3ClientWithConfig(&dkms_api.Config{
Protocol: tea.String("https"),
Endpoint: tea.String(clientConfig.KMSv3Config.Endpoint),
ClientKeyContent: tea.String(clientConfig.KMSv3Config.ClientKeyContent),
Password: tea.String(clientConfig.KMSv3Config.Password),
}, clientConfig.KMSv3Config.CaContent)
}

func (client *ConfigClient) GetConfig(param vo.ConfigParam) (content string, err error) {
content, err = client.getConfigInner(param)
content, err = client.getConfigInner(&param)
if err != nil {
return "", err
}
return client.decrypt(param.DataId, content)
param.UsageType = vo.ResponseType
if err = filter.GetDefaultConfigFilterChainManager().DoFilters(&param); err != nil {
return "", err
}
content = param.Content
return content, nil
}

func (client *ConfigClient) decrypt(dataId, content string) (string, error) {
var plainContent string
var err error
if client.kmsClient != nil && strings.HasPrefix(dataId, "cipher-") {
request := kms.CreateDecryptRequest()
request.Method = "POST"
request.Scheme = "https"
request.AcceptFormat = "json"
request.CiphertextBlob = content
response, err := client.kmsClient.Decrypt(request)
plainContent, err = client.kmsClient.Decrypt(content)
if err != nil {
return "", fmt.Errorf("kms decrypt failed: %v", err)
}
content = response.Plaintext
}
return content, nil
return plainContent, nil
}

func (client *ConfigClient) encrypt(dataId, content string) (string, error) {
func (client *ConfigClient) encrypt(dataId, content, kmsKeyId string) (string, error) {
var cipherContent string
var err error
if client.kmsClient != nil && strings.HasPrefix(dataId, "cipher-") {
request := kms.CreateEncryptRequest()
request.Method = "POST"
request.Scheme = "https"
request.AcceptFormat = "json"
request.KeyId = "alias/acs/mse" // use default key
request.Plaintext = content
response, err := client.kmsClient.Encrypt(request)
cipherContent, err = client.kmsClient.Encrypt(content, kmsKeyId)
if err != nil {
return "", fmt.Errorf("kms encrypt failed: %v", err)
}
content = response.CiphertextBlob
}
return content, nil
return cipherContent, nil
}

func (client *ConfigClient) getConfigInner(param vo.ConfigParam) (content string, err error) {
func (client *ConfigClient) getConfigInner(param *vo.ConfigParam) (content string, err error) {
if len(param.DataId) <= 0 {
err = errors.New("[client.GetConfig] param.dataId can not be empty")
return "", err
Expand Down Expand Up @@ -220,6 +247,8 @@ func (client *ConfigClient) getConfigInner(param vo.ConfigParam) (content string
logger.Warnf("read config from cache success, dataId=%s, group=%s, namespaceId=%s", param.DataId, param.Group, clientConfig.NamespaceId)
return cacheContent, nil
}
param.EncryptedDataKey = response.EncryptedDataKey
param.Content = response.Content
return response.Content, nil
}

Expand All @@ -236,8 +265,10 @@ func (client *ConfigClient) PublishConfig(param vo.ConfigParam) (published bool,
if len(param.Group) <= 0 {
param.Group = constant.DEFAULT_GROUP
}
if param.Content, err = client.encrypt(param.DataId, param.Content); err != nil {
return

param.UsageType = vo.RequestType
if err = filter.GetDefaultConfigFilterChainManager().DoFilters(&param); err != nil {
return false, err
}

clientConfig, _ := client.GetClientConfig()
Expand Down Expand Up @@ -480,6 +511,7 @@ func (client *ConfigClient) refreshContentAndCheck(cacheData cacheData, notify b
}
cacheData.content = configQueryResponse.Content
cacheData.contentType = configQueryResponse.ContentType
cacheData.encryptedDataKey = configQueryResponse.EncryptedDataKey
if notify {
logger.Infof("[config_rpc_client] [data-received] dataId=%s, group=%s, tenant=%s, md5=%s, content=%s, type=%s",
cacheData.dataId, cacheData.group, cacheData.tenant, cacheData.md5,
Expand Down
13 changes: 13 additions & 0 deletions common/constant/client_config_options.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,19 @@ func WithOpenKMS(openKMS bool) ClientOption {
}
}

// WithOpenKMS ...
func WithKMSVersion(kmsVersion KMSVersion) ClientOption {
return func(config *ClientConfig) {
config.KMSVersion = kmsVersion
}
}

func WithKMSv3Config(kmsv3Config *KMSv3Config) ClientOption {
return func(config *ClientConfig) {
config.KMSv3Config = kmsv3Config
}
}

// WithCacheDir ...
func WithCacheDir(cacheDir string) ClientOption {
return func(config *ClientConfig) {
Expand Down
11 changes: 10 additions & 1 deletion common/constant/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,9 @@ type ClientConfig struct {
RegionId string // the regionId for kms
AccessKey string // the AccessKey for kms
SecretKey string // the SecretKey for kms
OpenKMS bool // it's to open kms,default is false. https://help.aliyun.com/product/28933.html
OpenKMS bool // it's to open kms, default is false. https://help.aliyun.com/product/28933.html
KMSVersion KMSVersion // kms client version. https://help.aliyun.com/document_detail/380927.html
KMSv3Config *KMSv3Config //KMSv3 configuration. https://help.aliyun.com/document_detail/601596.html
CacheDir string // the directory for persist nacos service info,default value is current path
DisableUseSnapShot bool // It's a switch, default is false, means that when get remote config fail, use local cache file instead
UpdateThreadNum int // the number of goroutine for update nacos service info,default value is 20
Expand Down Expand Up @@ -98,3 +100,10 @@ type TLSConfig struct {
KeyFile string // server use when verifying client certificates
ServerNameOverride string // serverNameOverride is for testing only
}

type KMSv3Config struct {
ClientKeyContent string
Password string
Endpoint string
CaContent string
}
9 changes: 9 additions & 0 deletions common/constant/const.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,14 @@ package constant

import "time"

type KMSVersion string

const (
KMSv1 KMSVersion = "KMSv1"
KMSv3 KMSVersion = "KMSv3"
DEFAULT_KMS_VERSION KMSVersion = "" //to fit original version
UNKNOWN_KMS_VERSION KMSVersion = "UNKNOWN_KMS_VERSION"
)
const (
KEY_USERNAME = "username"
KEY_PASSWORD = "password"
Expand Down Expand Up @@ -99,4 +107,5 @@ const (
GRPC = "grpc"
FAILOVER_FILE_SUFFIX = "_failover"
RpcPortOffset = 1000
MSE_KMSv1_DEFAULT_KEY_ID = "alias/acs/mse"
)
72 changes: 72 additions & 0 deletions common/encoding/encryption.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*
* Copyright 1999-2020 Alibaba Group Holding 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 encoding

import (
"encoding/base64"
"unicode/utf8"
)

func DecodeString2Utf8Bytes(data string) []byte {
resBytes := make([]byte, 0, 8)
if len(data) == 0 {
return resBytes
}
bytesLen := 0
runes := []rune(data)
for _, r := range runes {
bytesLen += utf8.RuneLen(r)
}
resBytes = make([]byte, bytesLen)
pos := 0
for _, r := range runes {
pos += utf8.EncodeRune(resBytes[pos:], r)
}
return resBytes
}

func EncodeUtf8Bytes2String(bytes []byte) string {
if len(bytes) == 0 {
return ""
}
var startPos, endPos int
resRunes := make([]rune, 0, 8)
for endPos <= len(bytes) {
if utf8.FullRune(bytes[startPos:endPos]) {
decodedRune, _ := utf8.DecodeRune(bytes[startPos:endPos])
resRunes = append(resRunes, decodedRune)
startPos = endPos
}
endPos++
}
return string(resRunes)
}

func DecodeBase64(bytes []byte) ([]byte, error) {
dst := make([]byte, base64.StdEncoding.DecodedLen(len(bytes)))
n, err := base64.StdEncoding.Decode(dst, bytes)
if err != nil {
return nil, err
}
return dst[:n], nil
}

func EncodeBase64(bytes []byte) ([]byte, error) {
dst := make([]byte, base64.StdEncoding.EncodedLen(len(bytes)))
base64.StdEncoding.Encode(dst, bytes)
return dst[:], nil
}
Loading

0 comments on commit 9b9ff77

Please sign in to comment.