All Products
Search
Document Center

Object Storage Service:Client-side encryption (Go SDK V2)

Last Updated:Aug 02, 2025

OSS client-side encryption lets you encrypt data locally before you upload it to OSS. This ensures that only authorized key holders can decrypt the data and enhances data security during transmission and storage.

Precautions

  • The sample code in this topic uses the China (Hangzhou) region ID, cn-hangzhou, and the public endpoint by default. If you want to access OSS from other Alibaba Cloud services in the same region, use the internal endpoint. For more information about the mappings between OSS regions and endpoints, see Regions and endpoints.

  • The examples in this topic read access credentials from environment variables. For more information about how to configure access credentials, see Configure access credentials.

  • When you use the client-side encryption feature, you are responsible for the integrity of the master key.

  • When you copy or migrate encrypted data, you are responsible for the integrity of the encryption metadata.

Method definition

Go SDK V2 supports the following methods to use a master key:

  • Use a user-managed master key (RSA)

    The SDK provides a default implementation of RSA. You must provide the public key and private key of the master key to the SDK as parameters.

  • Use a custom master key

    If the RSA master key method does not meet your requirements, you can implement custom encryption and decryption behavior for the master key. This topic uses Alibaba Cloud KMS 3.0 as an example to show how to customize the encryption and decryption of the master key.

Using these two encryption methods can effectively protect client data from leakage. Even if the encrypted data is compromised, it cannot be decrypted to reveal the raw data.

Important

For more information about the principles of OSS client-side encryption, see Client-side encryption.

To use client-side encryption, you must first instantiate an encryption client and then call the client's interfaces to perform operations. Your objects are automatically encrypted and decrypted as part of the request.

type EncryptionClient struct {
  ...
}

func NewEncryptionClient(c *Client, masterCipher crypto.MasterCipher, optFns ...func(*EncryptionClientOptions)) (eclient *EncryptionClient, err error)

Request parameters

Parameter

Type

Description

c

*Client

A non-encrypted client instance

masterCipher

crypto.MasterCipher

The master key instance, which is used to encrypt and decrypt data keys.

optFns

...func(*EncryptionClientOptions)

(Optional) The configuration options for the encryption client.

The following table describes the EncryptionClientOptions options.

Parameter

Type

Description

MasterCiphers

[]crypto.MasterCipher

The master key instance group, which is used to decrypt data keys.

Return values

Return value

Type

Description

eclient

*EncryptionClient

The encryption client instance. This parameter is valid only when err is nil.

err

error

The status of creating the encryption client. If the operation fails, err is not nil.

The following table lists the EncryptionClient interfaces.

Basic interface

Description

GetObjectMeta

Obtains some metadata of an object.

HeadObject

Obtains some metadata of an object.

GetObject

Downloads an object and automatically decrypts it.

PutObject

Uploads an object and automatically encrypts it.

InitiateMultipartUpload

Initializes a multipart upload event and a sharding encryption context (EncryptionMultiPartContext).

UploadPart

Initializes a multipart upload event. You can call this interface to upload part data and automatically encrypt the data. When you call this interface, you must set the sharding encryption context.

CompleteMultipartUpload

After all part data is uploaded, you can call this interface to merge the parts into a file.

AbortMultipartUpload

Cancels a multipart upload event and deletes the corresponding part data.

ListParts

Lists all successfully uploaded parts that belong to a specified upload event.

Advanced interface

Description

NewDownloader

Creates a download manager instance.

NewUploader

Creates an upload manager instance.

OpenFile

Creates a ReadOnlyFile instance.

Helper interface

Description

Unwrap

Obtains a non-encrypted client instance. You can use this instance to access other basic interfaces.

Use an RSA master key

Use an RSA master key to perform simple upload and download of an object

The following sample code shows how to use an RSA master key to perform a simple upload and download of an object:

package main

import (
	"context"
	"flag"
	"log"
	"strings"

	"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss"
	"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss/credentials"
	"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss/crypto"
)

// Global variables
var (
	region     string // The storage region.
	bucketName string // The bucket name.
	objectName string // The object name.
)

// The init function is used to initialize command-line parameters.
func init() {
	flag.StringVar(&region, "region", "", "The region in which the bucket is located.")
	flag.StringVar(&bucketName, "bucket", "", "The name of the bucket.")
	flag.StringVar(&objectName, "object", "", "The name of the object.")
}

func main() {
	// Parse command-line parameters.
	flag.Parse()

	// Check whether the bucket name is empty.
	if len(bucketName) == 0 {
		flag.PrintDefaults()
		log.Fatalf("invalid parameters, bucket name required")
	}

	// Check whether the region is empty.
	if len(region) == 0 {
		flag.PrintDefaults()
		log.Fatalf("invalid parameters, region required")
	}

	// Check whether the object name is empty.
	if len(objectName) == 0 {
		flag.PrintDefaults()
		log.Fatalf("invalid parameters, object name required")
	}

	// Load the default configurations and set the credential provider and region.
	cfg := oss.LoadDefaultConfig().
		WithCredentialsProvider(credentials.NewEnvironmentVariableCredentialsProvider()).
		WithRegion(region)

	// Create an OSS client.
	client := oss.NewClient(cfg)

	// Create a description for the master key. The description cannot be modified after it is created. The master key description corresponds to the master key.
	// If all objects use the same master key, the master key description can be empty, but you cannot change the master key later.
	// If the master key description is empty, you cannot determine which master key is used for decryption.
	// We recommend that you configure a description for each master key and save the mapping between the master key and the description on the client.
	materialDesc := make(map[string]string)
	materialDesc["desc"] = "your master encrypt key material describe information"

	// Create an encryption client that contains only the master key.
	// If no download operation is performed, you can leave the private key empty. That is, set it to "".
	mc, err := crypto.CreateMasterRsa(materialDesc, "yourRsaPublicKey", "yourRsaPrivateKey")
	if err != nil {
		log.Fatalf("failed to create master rsa %v", err)

	}

	// Create an encryption client.
	eclient, err := oss.NewEncryptionClient(client, mc)
	if err != nil {
		log.Fatalf("failed to create encryption client %v", err)
	}

	// Create a simple upload request.
	putObjRequest := &oss.PutObjectRequest{
		Bucket: oss.Ptr(bucketName),
		Key:    oss.Ptr(objectName),
		Body:   strings.NewReader("hi, simple put object"),
	}

	// Use the encryption client to upload the object.
	putObjRequestResult, err := eclient.PutObject(context.TODO(), putObjRequest)
	if err != nil {
		log.Fatalf("failed to put object with encryption client %v", err)
	}
	log.Printf("put object with encryption client result:%#v\n", putObjRequestResult)

	// Create a simple download request.
	getObjRequest := &oss.GetObjectRequest{
		Bucket: oss.Ptr(bucketName),
		Key:    oss.Ptr(objectName),
	}

	// Use the encryption client to download the object.
	getObjRequestResult, err := eclient.GetObject(context.TODO(), getObjRequest)
	if err != nil {
		log.Fatalf("failed to put object with encryption client %v", err)
	}
	log.Printf("put object with encryption client result:%#v\n", getObjRequestResult)

}

Use an RSA master key to perform a multipart upload of an object

The following sample code shows how to use an RSA master key to perform a multipart upload of an object:

package main

import (
	"bufio"
	"context"
	"flag"
	"io"
	"log"
	"math/rand"
	"sort"
	"strings"
	"sync"
	"time"

	"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss"
	"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss/credentials"
	"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss/crypto"
)

// Global variables
var (
	region     string                                                                     // The storage region.
	bucketName string                                                                     // The bucket name.
	objectName string                                                                     // The object name.
	letters    = []rune("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") // The character set used to generate random strings.
)

// The init function is used to initialize command-line parameters.
func init() {
	flag.StringVar(&region, "region", "", "The region in which the bucket is located.")
	flag.StringVar(&bucketName, "bucket", "", "The name of the bucket.")
	flag.StringVar(&objectName, "object", "", "The name of the object.")
}

func main() {
	// Parse command-line parameters.
	flag.Parse()

	// Check whether the bucket name is empty.
	if len(bucketName) == 0 {
		flag.PrintDefaults()
		log.Fatalf("invalid parameters, bucket name required")
	}

	// Check whether the region is empty.
	if len(region) == 0 {
		flag.PrintDefaults()
		log.Fatalf("invalid parameters, region required")
	}

	// Check whether the object name is empty.
	if len(objectName) == 0 {
		flag.PrintDefaults()
		log.Fatalf("invalid parameters, object name required")
	}

	// Load the default configurations and set the credential provider and region.
	cfg := oss.LoadDefaultConfig().
		WithCredentialsProvider(credentials.NewEnvironmentVariableCredentialsProvider()).
		WithRegion(region)

	// Create an OSS client.
	client := oss.NewClient(cfg)

	// Create a description for the master key. The description cannot be modified after it is created. The master key description corresponds to the master key.
	// If all objects use the same master key, the master key description can be empty, but you cannot change the master key later.
	// If the master key description is empty, you cannot determine which master key is used for decryption.
	// We recommend that you configure a description for each master key and save the mapping between the master key and the description on the client.
	materialDesc := make(map[string]string)
	materialDesc["desc"] = "your master encrypt key material describe information"

	// Create an encryption client that contains only the master key.
	// If no download operation is performed, you can leave the private key empty. That is, set it to "".
	mc, err := crypto.CreateMasterRsa(materialDesc, "yourRsaPublicKey", "yourRsaPrivateKey")
	if err != nil {
		log.Fatalf("failed to create master rsa %v", err)

	}

	// Create an encryption client.
	eclient, err := oss.NewEncryptionClient(client, mc)
	if err != nil {
		log.Fatalf("failed to create encryption client %v", err)
	}

	// Create a request to initialize a multipart upload.
	initRequest := &oss.InitiateMultipartUploadRequest{
		Bucket: oss.Ptr(bucketName),
		Key:    oss.Ptr(objectName),
	}
	initResult, err := eclient.InitiateMultipartUpload(context.TODO(), initRequest)
	if err != nil {
		log.Fatalf("failed to initiate multi part upload %v", err)
	}

	var wg sync.WaitGroup
	var parts oss.UploadParts
	count := 3
	body := randStr(400000)
	reader := strings.NewReader(body)
	bufReader := bufio.NewReader(reader)
	content, _ := io.ReadAll(bufReader)
	partSize := len(body) / count
	var mu sync.Mutex

	for i := 0; i < count; i++ {
		wg.Add(1)
		go func(partNumber int, partSize int, i int) {
			defer wg.Done()
			partRequest := &oss.UploadPartRequest{
				Bucket:              oss.Ptr(bucketName),                                             // The bucket name.
				Key:                 oss.Ptr(objectName),                                             // The object name.
				PartNumber:          int32(partNumber),                                               // The part number.
				UploadId:            oss.Ptr(*initResult.UploadId),                                   // The upload ID.
				Body:                strings.NewReader(string(content[i*partSize : (i+1)*partSize])), // The part content.
				CSEMultiPartContext: initResult.CSEMultiPartContext,                                  // The multipart context.
			}
			partResult, err := eclient.UploadPart(context.TODO(), partRequest)
			if err != nil {
				log.Fatalf("failed to upload part %d: %v", partNumber, err)
			}
			part := oss.UploadPart{
				PartNumber: partRequest.PartNumber, // The part number.
				ETag:       partResult.ETag,        // The ETag.
			}
			mu.Lock()
			parts = append(parts, part)
			mu.Unlock()
		}(i+1, partSize, i)
	}
	wg.Wait()
	sort.Sort(parts)

	request := &oss.CompleteMultipartUploadRequest{
		Bucket:   oss.Ptr(bucketName),           // The bucket name.
		Key:      oss.Ptr(objectName),           // The object name.
		UploadId: oss.Ptr(*initResult.UploadId), // The upload ID.
		CompleteMultipartUpload: &oss.CompleteMultipartUpload{
			Parts: parts, // The list of uploaded parts.
		},
	}
	result, err := eclient.CompleteMultipartUpload(context.TODO(), request)
	if err != nil {
		log.Fatalf("failed to complete multipart upload %v", err)
	}
	log.Printf("complete multipart upload result:%#v\n", result)
}

// Generate a random string.
func randStr(n int) string {
	b := make([]rune, n)
	randMarker := rand.New(rand.NewSource(time.Now().UnixNano()))
	for i := range b {
		b[i] = letters[randMarker.Intn(len(letters))]
	}
	return string(b)
}

Use a custom master key

Use a custom master key to perform a simple upload and download of an object

The SDK provides a default implementation of RSA. If this method does not meet your requirements, you can implement custom encryption and decryption behavior for the master key. The following sample code uses Alibaba Cloud KMS 3.0 as an example to show how to customize the master key to perform a simple upload and download of an object.

package main

import (
	"context"
	"encoding/base64"
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"log"
	"strings"

	kms "github.com/aliyun/alibaba-cloud-sdk-go/services/kms"
	kmssdk "github.com/aliyun/alibabacloud-dkms-transfer-go-sdk/sdk"
	"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss"
	"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss/credentials"
	osscrypto "github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss/crypto"
)

// CreateMasterAliKms3 creates a master key interface implemented by Alibaba Cloud KMS 3.0.
// matDesc is converted to a JSON string.
func CreateMasterAliKms3(matDesc map[string]string, kmsID string, kmsClient *kmssdk.KmsTransferClient) (osscrypto.MasterCipher, error) {
	var masterCipher MasterAliKms3Cipher
	if kmsID == "" || kmsClient == nil {
		return masterCipher, fmt.Errorf("kmsID is empty or kmsClient is nil")
	}

	var jsonDesc string
	if len(matDesc) > 0 {
		b, err := json.Marshal(matDesc)
		if err != nil {
			return masterCipher, err
		}
		jsonDesc = string(b)
	}

	masterCipher.MatDesc = jsonDesc
	masterCipher.KmsID = kmsID
	masterCipher.KmsClient = kmsClient
	return masterCipher, nil
}

// The Alibaba Cloud KMS master key interface.
type MasterAliKms3Cipher struct {
	MatDesc   string                    // The key description.
	KmsID     string                    // The KMS key ID.
	KmsClient *kmssdk.KmsTransferClient // The KMS client.
}

// Obtain the wrapping algorithm of the master key.
func (mrc MasterAliKms3Cipher) GetWrapAlgorithm() string {
	return "KMS/ALICLOUD"
}

// Obtain the description of the master key.
func (mkms MasterAliKms3Cipher) GetMatDesc() string {
	return mkms.MatDesc
}

// Use Alibaba Cloud KMS to encrypt data. This is mainly used to encrypt the symmetric key and IV of the object.
func (mkms MasterAliKms3Cipher) Encrypt(plainData []byte) ([]byte, error) {
	base64Plain := base64.StdEncoding.EncodeToString(plainData)
	request := kms.CreateEncryptRequest()
	request.RpcRequest.Scheme = "https"
	request.RpcRequest.Method = "POST"
	request.RpcRequest.AcceptFormat = "json"

	request.KeyId = mkms.KmsID
	request.Plaintext = base64Plain

	response, err := mkms.KmsClient.Encrypt(request)
	if err != nil {
		return nil, err
	}
	return base64.StdEncoding.DecodeString(response.CiphertextBlob)
}

// Use Alibaba Cloud KMS to decrypt data. This is mainly used to decrypt the symmetric key and IV of the object.
func (mkms MasterAliKms3Cipher) Decrypt(cryptoData []byte) ([]byte, error) {
	base64Crypto := base64.StdEncoding.EncodeToString(cryptoData)
	request := kms.CreateDecryptRequest()
	request.RpcRequest.Scheme = "https"
	request.RpcRequest.Method = "POST"
	request.RpcRequest.AcceptFormat = "json"
	request.CiphertextBlob = string(base64Crypto)
	response, err := mkms.KmsClient.Decrypt(request)
	if err != nil {
		return nil, err
	}
	return base64.StdEncoding.DecodeString(response.Plaintext)
}

var (
	region     string // The storage region.
	bucketName string // The bucket name.
	objectName string // The object name.
)

// The init function is used to initialize command-line parameters.
func init() {
	flag.StringVar(&region, "region", "", "The region in which the bucket is located.")
	flag.StringVar(&bucketName, "bucket", "", "The name of the bucket.")
	flag.StringVar(&objectName, "object", "", "The name of the object.")
}

func main() {
	// Parse command-line parameters.
	flag.Parse()

	// Check whether the storage region is empty.
	if len(region) == 0 {
		flag.PrintDefaults()
		log.Fatalf("invalid parameters, region required")
	}

	// Check whether the bucket name is empty.
	if len(bucketName) == 0 {
		flag.PrintDefaults()
		log.Fatalf("invalid parameters, bucket name required")
	}

	// Check whether the object name is empty.
	if len(objectName) == 0 {
		flag.PrintDefaults()
		log.Fatalf("invalid parameters, object name required")
	}

	// Load the default configurations and set the credential provider and region.
	cfg := oss.LoadDefaultConfig().
		WithCredentialsProvider(credentials.NewEnvironmentVariableCredentialsProvider()).
		WithRegion(region)

	// Create an OSS client.
	client := oss.NewClient(cfg)

	// Create a KMS client.
	kmsRegion := "cn-hangzhou"                // The KMS region.
	kmsAccessKeyId := "access key id"         // The KMS AccessKey ID.
	kmsAccessKeySecret := "access key secret" // The KMS AccessKey secret.
	kmsKeyId := "kms id"                      // The KMS key ID.

	kmsClient, err := kmssdk.NewClientWithAccessKey(kmsRegion, kmsAccessKeyId, kmsAccessKeySecret, nil)
	if err != nil {
		log.Fatalf("failed to create kms sdk client %v", err)
	}

	// Create a key description.
	materialDesc := make(map[string]string)
	materialDesc["desc"] = "your kms encrypt key material describe information"

	// Create a master key instance.
	masterKmsCipher, err := CreateMasterAliKms3(materialDesc, kmsKeyId, kmsClient)
	if err != nil {
		log.Fatalf("failed to create master AliKms3 %v", err)
	}

	// Create an encryption client.
	eclient, err := oss.NewEncryptionClient(client, masterKmsCipher)
	if err != nil {
		log.Fatalf("failed to create encryption client %v", err)
	}

	// Create a request to upload an object.
	request := &oss.PutObjectRequest{
		Bucket: oss.Ptr(bucketName),         // The bucket name.
		Key:    oss.Ptr(objectName),         // The object name.
		Body:   strings.NewReader("hi kms"), // The data to upload.
	}

	// Upload the object.
	result, err := eclient.PutObject(context.TODO(), request)
	if err != nil {
		log.Fatalf("failed to put object with encryption client %v", err)
	}
	log.Printf("put object with encryption client result:%#v\n", result)

	// Create a request to download an object.
	getRequest := &oss.GetObjectRequest{
		Bucket: oss.Ptr(bucketName), // The bucket name.
		Key:    oss.Ptr(objectName), // The object name.
	}

	// Download the object.
	getResult, err := eclient.GetObject(context.TODO(), getRequest)
	if err != nil {
		log.Fatalf("failed to get object with encryption client %v", err)
	}
	defer getResult.Body.Close()

	// Read the downloaded data.
	data, err := io.ReadAll(getResult.Body)
	if err != nil {
		log.Fatalf("failed to read all %v", err)
	}
	log.Printf("get object data:%s\n", data)
}

References

  • For more information about the principles of OSS client-side encryption, see Client-side encryption.

  • For more information about the Go SDK user guide for client-side encryption, see User Guide.

  • For the complete sample code for performing a simple upload and download of an object using an RSA master key, see GitHub sample.

  • For the complete sample code for performing a simple upload and download of an object using a KMS master key, see GitHub sample.