全部產品
Search
文件中心

Object Storage Service:用戶端加密(Go SDK V1)

更新時間:Nov 28, 2025

OSS用戶端加密是在資料上傳至OSS之前,由使用者在本地對資料進行加密處理,確保只有密鑰持有人才能解密資料,增強資料在傳輸和預存程序中的安全性。

免責聲明

  • 使用用戶端加密功能時,您需要對主要金鑰的完整性和正確性負責。因您維護不當導致主要金鑰用錯或丟失,從而導致加密資料無法解密所引起的一切損失和後果均由您自行承擔。

  • 在對加密資料進行複製或者遷移時,您需要對加密中繼資料的完整性和正確性負責。因您維護不當導致加密中繼資料出錯或丟失,從而導致加密資料無法解密所引起的一切損失和後果均由您自行承擔。

使用情境

  • 高度敏感性資料:對於包含極高敏感度資訊的資料,如個人識別資訊(PII)、金融交易記錄、醫學健康資料等,使用者可能希望在資料離開本地環境之前就對其進行加密處理,確保即使資料在傳輸過程中被截獲,未經處理資料仍能得到有效保護。

  • 合規要求:某些行業和法規(例如HIPAA、GDPR等)要求對儲存在第三方平台上的資料進行嚴格的加密控制,用戶端加密能夠滿足這些合規性要求,因為密鑰由使用者自己管理,不通過網路傳遞,也不由雲端服務商直接掌握。

  • 更強的自主控制權:企業或者開發人員可能希望對加密過程有完全的控制權,包括選擇密碼編譯演算法、管理和輪換密鑰。通過用戶端加密,可以實現這一目標,確保只有合法授權的使用者才能解密和訪問資料。

  • 跨地區資料移轉安全性:在將資料從一個地區遷移到另一個地區的過程中,使用用戶端加密可以在資料移轉前後保持資料始終處於加密狀態,增強了資料在公網傳輸的安全性。

注意事項

  • 本文以華東1(杭州)外網Endpoint為例。如果您希望通過與OSS同地區的其他阿里雲產品訪問OSS,請使用內網Endpoint。關於OSS支援的Region與Endpoint的對應關係,請參見地區和Endpoint

  • 本文以從環境變數讀取存取憑證為例。如何配置訪問憑證,請參見配置訪問憑證(Go SDK V1)

  • 本文以OSS網域名稱建立OSSClient為例。如果您希望通過自訂網域名、STS等方式建立OSSClient,請參見配置用戶端(Go SDK V1)

背景資訊

使用用戶端加密時,會為每個Object產生一個隨機資料加密金鑰,用該隨機資料加密金鑰明文對Object的資料進行對稱式加密。主要金鑰用於產生隨機的資料加密金鑰,加密後的內容會作為Object的meta資訊儲存在服務端。解密時先用主要金鑰將加密後的隨機密鑰解密出來,再用解密出來的隨機資料加密金鑰明文解密Object的資料。主要金鑰只參與用戶端本地計算,不會在網路上進行傳輸或儲存在服務端,以保證主要金鑰的資料安全。

重要
  • 用戶端加密支援分區上傳超過5 GB的檔案。在使用分區方式上傳檔案時,需要指定上傳檔案的總大小和分區大小, 除了最後一個分區外,每個分區的大小要一致,且分區大小目前必須是16的整數倍。

  • 調用用戶端加密上傳檔案後,加密中繼資料會被保護,無法通過CopyObject修改Object meta資訊。

加密方式

對於主要金鑰的使用,目前支援如下兩種方式:

  • 使用KMS託管使用者主要金鑰

    當使用KMS託管使用者主要金鑰用於用戶端資料加密時,需要將KMS使用者主要金鑰ID(即CMK ID)傳遞給SDK。

  • 使用使用者自主管理的主要金鑰(RSA)

    主要金鑰資訊由使用者提供,需要使用者將主要金鑰的公開金鑰、私密金鑰資訊作為參數傳遞給SDK。

使用以上兩種加密方式能夠有效地避免資料泄漏,保護用戶端資料安全。即使資料泄漏,其他人也無法解密得到未經處理資料。

加密中繼資料

參數

描述

是否必需

x-oss-meta-client-side-encryption-key

加密後的密鑰。 經過主要金鑰加密後再經過base64編碼的字串。

x-oss-meta-client-side-encryption-start

隨機產生的用於加密資料的初始值 。經過主要金鑰加密後再經過base64編碼的字串。

x-oss-meta-client-side-encryption-cek-alg

資料的密碼編譯演算法。

x-oss-meta-client-side-encryption-wrap-alg

資料密鑰的密碼編譯演算法。

x-oss-meta-client-side-encryption-matdesc

主要金鑰的描述資訊。JSON格式。

警告

強烈建議為每個主要金鑰都配置描述資訊,並儲存好主要金鑰和描述資訊之間的對應關係。否則不支援更換主要金鑰進行加密。

x-oss-meta-client-side-encryption-unencrypted-content-length

加密前的資料長度。如未指定content-length則不產生該參數。

x-oss-meta-client-side-encryption-unencrypted-content-md5

加密前資料的MD5。如未指定MD5,則不產生該參數。

x-oss-meta-client-side-encryption-data-size

若加密Multipart檔案需要在init_multipart時傳入整個大檔案的總大小。

是(分區上傳)

x-oss-meta-client-side-encryption-part-size

若加密Multipart檔案需要在init_multipart時傳入分區大小。

說明

目前分區大小必須是16的整數倍。

是(分區上傳)

範例程式碼

使用主要金鑰RSA普通上傳和下載Object

使用主要金鑰RSA普通上傳和下載Object範例程式碼如下:

package main

import (
	"bytes"
	"io"
	"log"

	"github.com/aliyun/aliyun-oss-go-sdk/oss"
	osscrypto "github.com/aliyun/aliyun-oss-go-sdk/oss/crypto"
)

func main() {
	// 從環境變數中擷取訪問憑證。運行本程式碼範例之前,請確保已設定環境變數OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。
	provider, err := oss.NewEnvironmentVariableCredentialsProvider()
	if err != nil {
		log.Fatalf("Error creating credentials provider: %v", err)
	}

	// 建立OSSClient執行個體。
	// yourEndpoint填寫Bucket對應的Endpoint,以華東1(杭州)為例,填寫為https://oss-cn-hangzhou.aliyuncs.com。其它Region請按實際情況填寫。
	// yourRegion填寫Bucket所在地區,以華東1(杭州)為例,填寫為cn-hangzhou。其它Region請按實際情況填寫。
	clientOptions := []oss.ClientOption{oss.SetCredentialsProvider(&provider)}
	clientOptions = append(clientOptions, oss.Region("yourRegion"))
	// 設定簽名版本
	clientOptions = append(clientOptions, oss.AuthVersion(oss.AuthV4))
	client, err := oss.New("yourEndpoint", "", "", clientOptions...)
	if err != nil {
		log.Fatalf("Error creating OSS client: %v", err)
	}

	// 建立一個主要金鑰的描述資訊,建立後不允許修改。主要金鑰描述資訊和主要金鑰一一對應。
	// 如果所有的Object都使用相同的主要金鑰,主要金鑰描述資訊可以為空白,但後續不支援更換主要金鑰。
	// 如果主要金鑰描述資訊為空白,解密時無法判斷使用的是哪個主要金鑰。
	// 強烈建議為每個主要金鑰都配置主要金鑰描述資訊(json字串),由用戶端儲存主要金鑰和描述資訊之間的對應關係(服務端不儲存兩者之間的對應關係)。

	// 由主要金鑰描述資訊(json字串)轉換的map。
	materialDesc := map[string]string{
		"desc": "your master encrypt key material describe information",
	}

	// 根據主要金鑰描述資訊建立一個主要金鑰對象。
	// yourRsaPublicKey填寫您自主管理的主要金鑰公開金鑰資訊,yourRsaPrivateKey填寫您自主管理的主要金鑰私密金鑰資訊。
	masterRsaCipher, err := osscrypto.CreateMasterRsa(materialDesc, "yourRsaPublicKey", "yourRsaPrivateKey")
	if err != nil {
		log.Fatalf("Error creating master RSA cipher: %v", err)
	}

	// 根據主要金鑰對象建立一個用於加密的介面,使用aes ctr模式加密。
	contentProvider := osscrypto.CreateAesCtrCipher(masterRsaCipher)

	// 擷取一個用於用戶端加密的已建立Bucket。
	// 用戶端加密Bucket和普通Bucket具有相似的用法。
	cryptoBucket, err := osscrypto.GetCryptoBucket(client, "yourBucketName", contentProvider)
	if err != nil {
		log.Fatalf("Error getting crypto bucket: %v", err)
	}

	// PutObject時自動加密。
	err = cryptoBucket.PutObject("yourObjectName", bytes.NewReader([]byte("yourObjectValueByteArrary")))
	if err != nil {
		log.Fatalf("Error putting object: %v", err)
	}

	// GetObject時自動解密。
	body, err := cryptoBucket.GetObject("yourObjectName")
	if err != nil {
		log.Fatalf("Error getting object: %v", err)
	}
	defer body.Close()

	data, err := io.ReadAll(body)
	if err != nil {
		log.Fatalf("Error reading object data: %v", err)
	}
	log.Printf("Data: %s", string(data))
}

使用主要金鑰RSA分區上傳Object

使用主要金鑰RSA分區上傳Object範例程式碼如下:

package main

import (
	"log"
	"os"

	"github.com/aliyun/aliyun-oss-go-sdk/oss"
	osscrypto "github.com/aliyun/aliyun-oss-go-sdk/oss/crypto"
)

func main() {
	// 從環境變數中擷取訪問憑證。運行本程式碼範例之前,請確保已設定環境變數OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。
	provider, err := oss.NewEnvironmentVariableCredentialsProvider()
	if err != nil {
		log.Fatalf("Error creating credentials provider: %v", err)
	}

	// 建立OSSClient執行個體。
	// yourEndpoint填寫Bucket對應的Endpoint,以華東1(杭州)為例,填寫為https://oss-cn-hangzhou.aliyuncs.com。其它Region請按實際情況填寫。
	// yourRegion填寫Bucket所在地區,以華東1(杭州)為例,填寫為cn-hangzhou。其它Region請按實際情況填寫。
	clientOptions := []oss.ClientOption{oss.SetCredentialsProvider(&provider)}
	clientOptions = append(clientOptions, oss.Region("yourRegion"))
	// 設定簽名版本
	clientOptions = append(clientOptions, oss.AuthVersion(oss.AuthV4))
	client, err := oss.New("yourEndpoint", "", "", clientOptions...)
	if err != nil {
		log.Fatalf("Error creating OSS client: %v", err)
	}

	// 建立一個主要金鑰的描述資訊,建立後不允許修改。主要金鑰描述資訊和主要金鑰一一對應。
	// 如果所有的Object都使用相同的主要金鑰,主要金鑰描述資訊可以為空白,但後續不支援更換主要金鑰。
	// 如果主要金鑰描述資訊為空白,解密時無法判斷使用的是哪個主要金鑰。
	// 強烈建議為每個主要金鑰都配置主要金鑰描述資訊(json字串),由用戶端儲存主要金鑰和描述資訊之間的對應關係(服務端不儲存兩者之間的對應關係)。

	// 由主要金鑰描述資訊(json字串)轉換的map。
	materialDesc := map[string]string{
		"desc": "your master encrypt key material describe information",
	}

	// 根據主要金鑰描述資訊建立一個主要金鑰對象。
	// yourRsaPublicKey填寫您自主管理的主要金鑰公開金鑰資訊,yourRsaPrivateKey填寫您自主管理的主要金鑰私密金鑰資訊。
	masterRsaCipher, err := osscrypto.CreateMasterRsa(materialDesc, "yourRsaPublicKey", "yourRsaPrivateKey")
	if err != nil {
		log.Fatalf("Error creating master RSA cipher: %v", err)
	}

	// 根據主要金鑰對象建立一個用於加密的介面,使用aes ctr模式加密。
	contentProvider := osscrypto.CreateAesCtrCipher(masterRsaCipher)

	// 擷取一個用於用戶端加密的已建立Bucket。
	// 用戶端加密Bucket和普通Bucket具有相似的用法。
	cryptoBucket, err := osscrypto.GetCryptoBucket(client, "yourBucketName", contentProvider)
	if err != nil {
		log.Fatalf("Error getting crypto bucket: %v", err)
	}

	fileName := "yourLocalFilePath"
	fileInfo, err := os.Stat(fileName)
	if err != nil {
		log.Fatalf("Error getting file info: %v", err)
	}
	fileSize := fileInfo.Size()

	// 加密上下文資訊。
	var cryptoContext osscrypto.PartCryptoContext
	cryptoContext.DataSize = fileSize

	// 期望的分區數,實際分區數以後續計算出來的為準。
	expectPartCount := int64(10)

	// 目前aes ctr加密分區大小需16個位元組對齊。
	cryptoContext.PartSize = (fileSize / expectPartCount / 16) * 16

	// 初始化分區上傳。
	imur, err := cryptoBucket.InitiateMultipartUpload("yourObjectName", &cryptoContext)
	if err != nil {
		log.Fatalf("Error initiating multipart upload: %v", err)
	}

	// 分割檔案。
	chunks, err := oss.SplitFileByPartSize(fileName, cryptoContext.PartSize)
	if err != nil {
		log.Fatalf("Error splitting file: %v", err)
	}

	var partsUpload []oss.UploadPart
	for _, chunk := range chunks {
		part, err := cryptoBucket.UploadPartFromFile(imur, fileName, chunk.Offset, chunk.Size, int(chunk.Number), cryptoContext)
		if err != nil {
			log.Fatalf("Error uploading part: %v", err)
		}
		partsUpload = append(partsUpload, part)
	}

	// 完成分區上傳。
	_, err = cryptoBucket.CompleteMultipartUpload(imur, partsUpload)
	if err != nil {
		log.Fatalf("Error completing multipart upload: %v", err)
	}

	log.Println("Multipart upload completed successfully")
}

解密使用主要金鑰為不同的RSA加密的Object

解密使用主要金鑰為不同的RSA加密的Object範例程式碼如下:

package main

import (
	"bytes"
	"io"
	"log"

	"github.com/aliyun/aliyun-oss-go-sdk/oss"
	osscrypto "github.com/aliyun/aliyun-oss-go-sdk/oss/crypto"
)

// 根據主要金鑰描述資訊查詢主要金鑰。如果需要解密不同的主要金鑰加密的Object,需要提供此介面。
type MockRsaManager struct{}

func (mg *MockRsaManager) GetMasterKey(matDesc map[string]string) ([]string, error) {
	keyList := []string{"yourRsaPublicKey", "yourRsaPrivateKey"}
	return keyList, nil
}

// 解密不同主要金鑰加密的Object。
func main() {
	// 從環境變數中擷取訪問憑證。運行本程式碼範例之前,請確保已設定環境變數OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。
	provider, err := oss.NewEnvironmentVariableCredentialsProvider()
	if err != nil {
		log.Fatalf("Error creating credentials provider: %v", err)
	}

	// 建立OSSClient執行個體。
	// yourEndpoint填寫Bucket對應的Endpoint,以華東1(杭州)為例,填寫為https://oss-cn-hangzhou.aliyuncs.com。其它Region請按實際情況填寫。
	// yourRegion填寫Bucket所在地區,以華東1(杭州)為例,填寫為cn-hangzhou。其它Region請按實際情況填寫。
	clientOptions := []oss.ClientOption{oss.SetCredentialsProvider(&provider)}
	clientOptions = append(clientOptions, oss.Region("yourRegion"))
	// 設定簽名版本
	clientOptions = append(clientOptions, oss.AuthVersion(oss.AuthV4))
	client, err := oss.New("yourEndpoint", "", "", clientOptions...)
	if err != nil {
		log.Fatalf("Error creating OSS client: %v", err)
	}

	// 建立一個主要金鑰的描述資訊,建立後不允許修改。主要金鑰描述資訊和主要金鑰一一對應。
	// 如果所有的Object都使用相同的主要金鑰,主要金鑰描述資訊可以為空白,但後續不支援更換主要金鑰。
	// 如果主要金鑰描述資訊為空白,解密時無法判斷使用的是哪個主要金鑰。
	// 強烈建議為每個主要金鑰都配置主要金鑰描述資訊(json字串),由用戶端儲存主要金鑰和描述資訊之間的對應關係(服務端不儲存兩者之間的對應關係)。

	// 由主要金鑰描述資訊(json字串)轉換的map。
	materialDesc := map[string]string{
		"desc": "your master encrypt key material describe information",
	}

	// 根據主要金鑰描述資訊建立一個主要金鑰對象。
	// yourRsaPublicKey填寫您自主管理的主要金鑰公開金鑰資訊,yourRsaPrivateKey填寫您自主管理的主要金鑰私密金鑰資訊。
	masterRsaCipher, err := osscrypto.CreateMasterRsa(materialDesc, "yourRsaPublicKey", "yourRsaPrivateKey")
	if err != nil {
		log.Fatalf("Error creating master RSA cipher: %v", err)
	}

	// 根據主要金鑰對象建立一個用於加密的介面,使用aes ctr模式加密。
	contentProvider := osscrypto.CreateAesCtrCipher(masterRsaCipher)

	// 如果需要解密不同主要金鑰加密的Object,需要提供此介面。
	var mockRsaManager MockRsaManager
	var options []osscrypto.CryptoBucketOption
	options = append(options, osscrypto.SetMasterCipherManager(&mockRsaManager))

	// 擷取一個用於用戶端加密的已建立Bucket。
	// 用戶端加密Bucket和普通Bucket具有相似的用法。
	cryptoBucket, err := osscrypto.GetCryptoBucket(client, "yourBucketName", contentProvider, options...)
	if err != nil {
		log.Fatalf("Error getting crypto bucket: %v", err)
	}

	// PutObject時自動加密。
	err = cryptoBucket.PutObject("yourObjectName", bytes.NewReader([]byte("yourObjectValueByteArrary")))
	if err != nil {
		log.Fatalf("Error putting object: %v", err)
	}

	// GetObject時自動解密。
	body, err := cryptoBucket.GetObject("otherObjectNameEncryptedWithOtherRsa")
	if err != nil {
		log.Fatalf("Error getting object: %v", err)
	}
	defer body.Close()

	data, err := io.ReadAll(body)
	if err != nil {
		log.Fatalf("Error reading object data: %v", err)
	}
	log.Printf("Data: %s", string(data))
}

使用主要金鑰KMS普通上傳和下載Object

使用主要金鑰KMS普通上傳和下載Object範例程式碼如下:

package main

import (
	"bytes"
	"io"
	"log"

	"github.com/aliyun/alibaba-cloud-sdk-go/services/kms"
	"github.com/aliyun/aliyun-oss-go-sdk/oss"
	crypto "github.com/aliyun/aliyun-oss-go-sdk/oss/crypto"
)

func main() {
	// 從環境變數中擷取訪問憑證。運行本程式碼範例之前,請確保已設定環境變數OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。
	provider, err := oss.NewEnvironmentVariableCredentialsProvider()
	if err != nil {
		log.Fatalf("Error creating credentials provider: %v", err)
	}

	// 建立OSSClient執行個體。
	// yourEndpoint填寫Bucket對應的Endpoint,以華東1(杭州)為例,填寫為https://oss-cn-hangzhou.aliyuncs.com。其它Region請按實際情況填寫。
	// yourRegion填寫Bucket所在地區,以華東1(杭州)為例,填寫為cn-hangzhou。其它Region請按實際情況填寫。
	clientOptions := []oss.ClientOption{oss.SetCredentialsProvider(&provider)}
	clientOptions = append(clientOptions, oss.Region("yourRegion"))
	// 設定簽名版本
	clientOptions = append(clientOptions, oss.AuthVersion(oss.AuthV4))
	client, err := oss.New("yourEndpoint", "", "", clientOptions...)
	if err != nil {
		log.Fatalf("Error creating OSS client: %v", err)
	}

	// 建立KMS Client執行個體。
	kmsClient, err := kms.NewClientWithAccessKey("yourKmsRegion", "yourKmsAccessKeyId", "yourKmsAccessKeySecret")
	if err != nil {
		log.Fatalf("Error creating KMS client: %v", err)
	}

	// 建立一個主要金鑰的描述資訊,建立後不允許修改。主要金鑰描述資訊和主要金鑰一一對應。
	// 如果所有的Object都使用相同的主要金鑰,主要金鑰描述資訊可以為空白,但後續不支援更換主要金鑰。
	// 如果主要金鑰描述資訊為空白,解密時無法判斷使用的是哪個主要金鑰。
	// 強烈建議為每個主要金鑰都配置主要金鑰描述資訊(json字串),由用戶端儲存主要金鑰和描述資訊之間的對應關係(服務端不儲存兩者之間的對應關係)。

	// 由主要金鑰描述資訊(json字串)轉換的map。
	materialDesc := map[string]string{
		"desc": "your kms encrypt key material describe information",
	}

	// 根據主要金鑰描述資訊建立一個主要金鑰對象。
	// yourKmsId填寫KMS使用者主要金鑰ID,即CMK ID。
	masterkmsCipher, err := crypto.CreateMasterAliKms(materialDesc, "yourKmsId", kmsClient)
	if err != nil {
		log.Fatalf("Error creating master KMS cipher: %v", err)
	}

	// 根據主要金鑰對象建立一個用於加密的介面,使用aes ctr模式加密。
	contentProvider := crypto.CreateAesCtrCipher(masterkmsCipher)

	// 擷取一個用於用戶端加密的已建立Bucket。
	// 用戶端加密Bucket和普通Bucket具有相似的用法。
	cryptoBucket, err := crypto.GetCryptoBucket(client, "yourBucketName", contentProvider)
	if err != nil {
		log.Fatalf("Error getting crypto bucket: %v", err)
	}

	// PutObject時自動加密。
	err = cryptoBucket.PutObject("yourObjectName", bytes.NewReader([]byte("yourObjectValueByteArrary")))
	if err != nil {
		log.Fatalf("Error putting object: %v", err)
	}

	// GetObject時自動解密。
	body, err := cryptoBucket.GetObject("yourObjectName")
	if err != nil {
		log.Fatalf("Error getting object: %v", err)
	}
	defer body.Close()

	data, err := io.ReadAll(body)
	if err != nil {
		log.Fatalf("Error reading object data: %v", err)
	}
	log.Printf("Data: %s", string(data))
}

相關文檔