全部產品
Search
文件中心

Object Storage Service:Python用戶端加密

更新時間:Nov 09, 2024

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

免責聲明

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

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

使用情境

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

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

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

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

注意事項

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

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

  • 本文以OSS網域名稱建立OSSClient為例。如果您希望通過自訂網域名、STS等方式建立OSSClient,請參見初始化

背景資訊

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

加密方式

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

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

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

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

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

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

關於用戶端加密的更多資訊,請參見開發指南中的用戶端加密

V2版本用戶端加密(推薦)

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

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

  • 加密中繼資料

    參數

    描述

    是否必需

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

    加密後的密鑰。 經過RSA或KMS加密後再經過base64編碼的字串。

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

    隨機產生的加密資料的初始向量 。經過RSA或KMS加密後再經過base64編碼的字串。

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

    資料的密碼編譯演算法。

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

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

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

    內容加密金鑰(CEK)描述,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

    分區上傳檔案的總大小。

    否(分區上傳時必須指定)

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

    分區上傳中每個part的大小。

    否(分區上傳時必須指定)

  • 建立加密Bucket

    同普通上傳、下載等操作一樣,在使用用戶端加密上傳、下載檔案之前需要先初始化Bucket執行個體。通過Bucket執行個體的上傳、下載等介面進行檔案的上傳、下載操作。用戶端加密通過CryptoBucket這個類繼承了普通Bucket的介面,需要使用到用戶端加密時,只需要傳入相應的參數,同初始化Bucket執行個體一樣,初始化一個類似的CryptoBucket執行個體即可。

    • 初始化非對稱式加密主要金鑰方式(RSA)的Bucket

      重要

      使用RSA加密方式時,需要您自己管理加密的金鑰組,一旦丟失密鑰或者密鑰資料出現損壞,可能會導致資料無法解密,推薦使用KMS託管方式進行加密。如果由於業務情境一定要使用RSA加密方式,建議您做好密鑰資料的備份。

      初始化非對稱式加密主要金鑰方式(RSA)的Bucket範例程式碼如下:

      # -*- coding: utf-8 -*-
      import os
      import oss2
      from oss2.credentials import EnvironmentVariableCredentialsProvider
      from oss2.crypto import RsaProvider
      
      # 從環境變數中擷取訪問憑證。運行本程式碼範例之前,請確保已設定環境變數OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。
      auth = oss2.ProviderAuthV4(EnvironmentVariableCredentialsProvider())
      
      # 只使用解密功能。
      # key_pair = {'private_key': 'yourPrivateKey'}
      # 只使用加密功能。
      # key_pair = {'public_key': 'yourPublicKey'}
      # 同時使用加密和解密功能。
      key_pair = {'private_key': 'yourPrivateKey', 'public_key': 'yourPublicKey'}
      
      # 填寫Bucket所在地區對應的Endpoint。以華東1(杭州)為例,Endpoint填寫為https://oss-cn-hangzhou.aliyuncs.com。
      endpoint = "https://oss-cn-hangzhou.aliyuncs.com"
      # 填寫Endpoint對應的Region資訊,例如cn-hangzhou。注意,v4簽名下,必須填寫該參數
      region = "cn-hangzhou"
      
      # yourBucketName填寫儲存空間名稱。
      bucket = oss2.CryptoBucket(auth, endpoint, 'yourBucketName',
                                 crypto_provider=RsaProvider(key_pair), region=region)
      
    • 初始化KMS主要金鑰加密方式的Bucket

      初始化KMS主要金鑰加密方式的Bucket的範例程式碼如下:

      # -*- coding: utf-8 -*-
      import os
      import oss2
      from oss2.crypto import AliKMSProvider
      from oss2.credentials import EnvironmentVariableCredentialsProvider
      
      # 從環境變數中擷取訪問憑證。運行本程式碼範例之前,請確保已設定環境變數OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。
      auth = oss2.ProviderAuthV4(EnvironmentVariableCredentialsProvider())
      kms_provider = AliKMSProvider(auth, 'yourRegion', 'yourCMKID')
      
      # 填寫Bucket所在地區對應的Endpoint。以華東1(杭州)為例,Endpoint填寫為https://oss-cn-hangzhou.aliyuncs.com。
      endpoint = "https://oss-cn-hangzhou.aliyuncs.com"
      # 填寫Endpoint對應的Region資訊,例如cn-hangzhou。注意,v4簽名下,必須填寫該參數
      region = "cn-hangzhou"
      
      # yourBucketName填寫儲存空間名稱。
      bucket = oss2.CryptoBucket(auth, endpoint, 'yourBucketName', crypto_provider=kms_provider, region=region)
      
    • 使用和管理多個密鑰

      對於同一個bucket,您在上傳或者下載不同的資料時,可能會使用不同的密鑰進行加密。您可以給不同的密鑰配置不同的描述資訊,並將這些密鑰和描述資訊添加到bucket的加密資訊中,待您再解密資料時,SDK內部會根據加密資料的描述資訊自動匹配密鑰,從而實現資料無縫解密。範例程式碼如下:

      # -*- coding: utf-8 -*-
      import os
      import oss2
      from oss2.crypto import RsaProvider
      from oss2.credentials import EnvironmentVariableCredentialsProvider
      
      # 從環境變數中擷取訪問憑證。運行本程式碼範例之前,請確保已設定環境變數OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。
      auth = oss2.ProviderAuthV4(EnvironmentVariableCredentialsProvider())
      
      # 建立一個RSA金鑰組。
      key_pair_1 = {'private_key': 'yourPrivateKey_1', 'public_key': 'yourPublicKey_1'}
      mat_desc_1 = {'key1': 'value1'}
      
      # 建立另一個RSA金鑰組。
      key_pair_2 = {'private_key': 'yourPrivateKey_2', 'public_key': 'yourPublicKey_2'}
      mat_desc_2 = {'key2': 'value2'}
      
      provider = RsaProvider(key_pair=key_pair_2, mat_desc=mat_desc_2)
      # 將key_pair_1的描述資訊添加到provider。
      encryption_materials = oss2.EncryptionMaterials(mat_desc_1, key_pair=key_pair_1)
      provider.add_encryption_materials(encryption_materials)
      
      # 填寫Bucket所在地區對應的Endpoint。以華東1(杭州)為例,Endpoint填寫為https://oss-cn-hangzhou.aliyuncs.com。
      endpoint = "https://oss-cn-hangzhou.aliyuncs.com"
      # 填寫Endpoint對應的Region資訊,例如cn-hangzhou。注意,v4簽名下,必須填寫該參數
      region = "cn-hangzhou"
      
      # 使用provider初始化crypto_bucket,這樣可以使用crypto_bucket下載使用描述資訊為mat_desc_1的主要金鑰加密的對象資料。
      crypto_bucket = oss2.CryptoBucket(auth, endpoint, 'yourBucketName', crypto_provider=provider, region=region)
      
  • 普通上傳和下載檔案

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

    # -*- coding: utf-8 -*-
    import os
    import oss2
    from oss2.credentials import EnvironmentVariableCredentialsProvider
    from oss2.crypto import RsaProvider
    from oss2.cryptoimportAliKMSProvider
    
    # 從環境變數中擷取訪問憑證。運行本程式碼範例之前,請確保已設定環境變數OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。
    auth = oss2.ProviderAuthV4(EnvironmentVariableCredentialsProvider())
    
    kms_provider = AliKMSProvider(auth, 'yourRegion', 'yourCMKID')
    # 填寫Bucket所在地區對應的Endpoint。以華東1(杭州)為例,Endpoint填寫為https://oss-cn-hangzhou.aliyuncs.com。
    endpoint = "https://oss-cn-hangzhou.aliyuncs.com"
    # 填寫Endpoint對應的Region資訊,例如cn-hangzhou。注意,v4簽名下,必須填寫該參數
    region = "cn-hangzhou"
    bucket = oss2.CryptoBucket(auth, endpoint, 'yourBucketName', crypto_provider=kms_provider, region=region)
    
    key = 'motto.txt'
    content = b'a' * 1024 * 1024
    filename = 'download.txt'
    
    
    # 上傳檔案。
    bucket.put_object(key, content, headers={'content-length': str(1024 * 1024)})
    
    # 下載OSS檔案到本地記憶體。
    result = bucket.get_object(key)
    
    # 驗證擷取到的檔案內容跟上傳時的檔案內容是否一致。
    content_got = b''
    for chunk in result:
        content_got += chunk
    assert content_got == content
    
    # 下載OSS檔案到本地檔案。
    result = bucket.get_object_to_file(key, filename)
    
    # 驗證擷取到的檔案內容跟上傳時的檔案內容是否一致。
    with open(filename, 'rb') as fileobj:
        assert fileobj.read() == content
  • 分區上傳

    說明
    • 分區上傳檔案時,一旦上傳中斷(進程退出)後,加密分區上傳上下文可能會丟失。上傳中斷後如果要繼續上傳該檔案,則必須再次上傳整個檔案。

    • 推薦您直接使用OSS封裝好的斷點續傳上傳介面上傳大檔案,該介面已將加密分區上傳上下文儲存在使用者本地了,即使是上傳出現中斷,該上下文資訊也不會丟失。

    使用主要金鑰KMS分區上傳檔案範例程式碼如下:

    # -*- coding: utf-8 -*-
    import os
    import oss2
    from oss2.credentials import EnvironmentVariableCredentialsProvider
    from oss2.crypto import RsaProvider
    from oss2.cryptoimportAliKMSProvider
    
    # 從環境變數中擷取訪問憑證。運行本程式碼範例之前,請確保已設定環境變數OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。
    auth = oss2.ProviderAuthV4(EnvironmentVariableCredentialsProvider())
    
    kms_provider = AliKMSProvider(auth, 'yourRegion', 'yourCMKID')
    # 填寫Bucket所在地區對應的Endpoint。以華東1(杭州)為例,Endpoint填寫為https://oss-cn-hangzhou.aliyuncs.com。
    endpoint = "https://oss-cn-hangzhou.aliyuncs.com"
    # 填寫Endpoint對應的Region資訊,例如cn-hangzhou。注意,v4簽名下,必須填寫該參數
    region = "cn-hangzhou"
    bucket = oss2.CryptoBucket(auth, endpoint, 'yourBucketName', crypto_provider = kms_provider, region=region)
    
    """
    分區上傳
    """
    # 初始化上傳分區。
    part_a = b'a' * 1024 * 100
    part_b = b'b' * 1024 * 100
    part_c = b'c' * 1024 * 100
    multi_content = [part_a, part_b, part_c]
    
    parts = []
    data_size = 100 * 1024 * 3
    part_size = 100 * 1024
    multi_key = "test_crypto_multipart"
    
    # 初始化加密分區上傳上下文。
    context = models.MultipartUploadCryptoContext(data_size, part_size)
    res = bucket.init_multipart_upload(multi_key, upload_context=context)
    upload_id = res.upload_id
    
    # 分區上傳樣本中使用順序上傳,實際使用中為了加快上傳速度也可以支援多個線程並發上傳。
    for i in range(3):
        # context的值不允許修改,否則將導致資料上傳失敗。
        result = bucket.upload_part(multi_key, upload_id, i + 1, multi_content[i], upload_context=context)
        parts.append(oss2.models.PartInfo(i + 1, result.etag, size=part_size, part_crc=result.crc))
    
    # 完成分區上傳。
    result = bucket.complete_multipart_upload(multi_key, upload_id, parts)
    
    # 驗證擷取到的檔案內容跟上傳時的檔案內容是否一致。
    result = bucket.get_object(multi_key)
    content_got = b''
    for chunk in result:
        content_got += chunk
    assert content_got[0:102400] == part_a
    assert content_got[102400:204800] == part_b
    assert content_got[204800:307200] == part_c
  • 斷點續傳上傳

    使用主要金鑰RSA斷點續傳上傳檔案範例程式碼如下:

    # -*- coding: utf-8 -*-
    import os
    import oss2
    from oss2.credentials import EnvironmentVariableCredentialsProvider
    from  oss2.crypto import RsaProvider
    
    key = 'motto.txt'
    content = b'a' * 1024 * 1024 * 100
    file_name_put = 'upload.txt'
    
    # 將content的內容寫入檔案。
    with open(file_name_put, 'wb') as fileobj:
        fileobj.write(content)
    
    # 從環境變數中擷取訪問憑證。運行本程式碼範例之前,請確保已設定環境變數OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。
    auth = oss2.ProviderAuthV4(EnvironmentVariableCredentialsProvider())
    
    # 建立儲存空間,使用使用者自主管理(RSA)方式加密,此方式只支援檔案整體上傳、下載操作。
    # 如果您使用2.9.0及以上版本的SDK,不建議使用LocalRsaProvider初始化bucket。
    # bucket = oss2.CryptoBucket(auth,'yourEndpoint', 'yourBucketName', crypto_provider=LocalRsaProvider())
    
    # 只使用解密功能。
    # key_pair = {'private_key': 'yourPrivateKey'}
    # 只使用加密功能。
    # key_pair = {'public_key': 'yourPublicKey'}
    # 同時使用加密和解密功能。
    key_pair = {'private_key': 'yourPrivateKey', 'public_key': 'yourPublicKey'}
    
    # 填寫Bucket所在地區對應的Endpoint。以華東1(杭州)為例,Endpoint填寫為https://oss-cn-hangzhou.aliyuncs.com。
    endpoint = "https://oss-cn-hangzhou.aliyuncs.com"
    # 填寫Endpoint對應的Region資訊,例如cn-hangzhou。注意,v4簽名下,必須填寫該參數
    region = "cn-hangzhou"
    
    # 初始化bucket將upload_contexts_flag設定為True,調用upload_part介面不用傳入加密分區上傳上下文參數。
    bucket = oss2.CryptoBucket(auth, endpoint, 'yourBucketName',
                               crypto_provider=RsaProvider(key_pair), region=region)
    
    # 為示範方便,本樣本將multipart_threshold的值設定為10*1024*1024,預設值為10 MB。實際使用過程中可以根據使用情境靈活設定。
    # multipart_threshold表示檔案超過這個閾值就是用分區上傳方式上傳問題,如果檔案大小小於這個值,建議使用簡單的put_object介面上傳檔案。
    # part_size為使用分區上傳時分區的大小,預設值為10 MB。
    # num_threads為並發上傳線程的個數,預設值為1。
    oss2.resumable_upload(bucket, key, file_name_put, multipart_threshold=10 * 1024 * 1024, part_size=1024 * 1024, num_threads=3)
  • 斷點續傳下載

    使用主要金鑰KMS斷點續傳下載檔案範例程式碼如下:

    # -*- coding: utf-8 -*-
    import os
    import oss2
    from oss2.crypto import RsaProvider
    from oss2.cryptoimportAliKMSProvider
    from oss2.credentials import EnvironmentVariableCredentialsProvider
    
    # 從環境變數中擷取訪問憑證。運行本程式碼範例之前,請確保已設定環境變數OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。
    auth = oss2.ProviderAuthV4(EnvironmentVariableCredentialsProvider())
    
    key = 'motto.txt'
    content = b'a' * 1024 * 1024 * 100
    file_name_get = 'download.txt'
    
    kms_provider = AliKMSProvider(auth, 'yourRegion', 'yourCMKID')
    
    endpoint = "https://oss-cn-hangzhou.aliyuncs.com"
    # 填寫Endpoint對應的Region資訊,例如cn-hangzhou。注意,v4簽名下,必須填寫該參數
    region = "cn-hangzhou"
    bucket = oss2.CryptoBucket(auth, endpoint, 'yourBucketName', crypto_provider=kms_provider, region=region)
    
    
    # 斷點續傳下載。
    oss2.resumable_download(bucket, key, file_name_get, multiget_threshold=10 * 1024 * 1024, part_size=1024 * 1024, num_threads=3)
    
    # 驗證擷取到的檔案內容跟上傳時的檔案內容是否一致。
    with open(file_name_get, 'rb') as fileobj:
        assert fileobj.read() == content
    

V1版本用戶端加密(不推薦)

說明
  • V1版本的用戶端加密只支援通過PutObject介面上傳5 GB以下的檔案,不支援分區上傳、斷點續傳上傳和斷點續傳下載介面。

  • 通過用戶端加密介面上傳檔案後,不允許通過CopyObject等介面修改對象的加密中繼資料。如果修改了這些中繼資料,可能會導致資料無法解密。

  • V1版本的用戶端加密僅在Python SDK上支援,其它語言的SDK無法解密通過V1版本用戶端加密上傳的資料。

  • 自2.11.0版本後開始支援V2版本的用戶端加密,V2版本支援的功能更加完善,條件允許的情況下建議升級到新版本。

  • 加密中繼資料

    參數

    描述

    是否必需

    x-oss-meta-oss-crypto-key

    加密後的密鑰。 經過RSA加密後再經過base64編碼的字串。

    x-oss-meta-oss-crypto-start

    隨機產生的加密資料的初始值 。經過RSA加密後再經過base64編碼的字串。

    x-oss-meta-oss-cek-alg

    資料的密碼編譯演算法。

    x-oss-meta-oss-wrap-alg

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

    x-oss-meta-oss-matdesc

    內容加密金鑰(CEK)描述,JSON格式。暫未生效。

    x-oss-meta-unencrypted-content-length

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

    x-oss-meta-unencrypted-content-md5

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

  • 使用RSA方式上傳和下載檔案

    使用RSA方式上傳和下載檔案範例程式碼如下:

    # -*- coding: utf-8 -*-
    import os
    import oss2
    from oss2.credentials import EnvironmentVariableCredentialsProvider
    from oss2.crypto import LocalRsaProvider
    
    # 從環境變數中擷取訪問憑證。運行本程式碼範例之前,請確保已設定環境變數OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。
    auth = oss2.ProviderAuthV4(EnvironmentVariableCredentialsProvider())
    
    # 填寫Bucket所在地區對應的Endpoint。以華東1(杭州)為例,Endpoint填寫為https://oss-cn-hangzhou.aliyuncs.com。
    endpoint = "https://oss-cn-hangzhou.aliyuncs.com"
    # 填寫Endpoint對應的Region資訊,例如cn-hangzhou。注意,v4簽名下,必須填寫該參數
    region = "cn-hangzhou"
    # 建立儲存空間,使用使用者自主管理(RSA)方式加密,此方式只支援檔案整體上傳下載操作。
    bucket = oss2.CryptoBucket(auth, endpoint, 'yourBucketName', crypto_provider=LocalRsaProvider(), region=region)
    
    key = 'motto.txt'
    content = b'a' * 1024 * 1024
    filename = 'download.txt'
    
    
    # 上傳檔案。
    bucket.put_object(key, content, headers={'content-length': str(1024 * 1024)})
    
    # 下載OSS檔案到本地記憶體。
    result = bucket.get_object(key)
    
    # 驗證擷取到的檔案內容跟上傳時的檔案內容是否一致。
    content_got = b''
    for chunk in result:
        content_got += chunk
    assert content_got == content
    
    # 下載OSS檔案到本地檔案。
    result = bucket.get_object_to_file(key, filename)
    
    # 驗證擷取到的檔案內容跟上傳時的檔案內容是否一致。
    with open(filename, 'rb') as fileobj:
        assert fileobj.read() == content

相關文檔

關於用戶端加密的完整範例程式碼,請參見GitHub樣本