全部產品
Search
文件中心

Key Management Service:使用KMS信封加密在本地加密和解密資料

更新時間:Jul 06, 2024

阿里雲使用者在雲上部署IT資產,需要對敏感性資料進行加密保護。如果被加密的資料對象較大,則可以通過KMS的密碼運算API線上產生資料密鑰,用離線資料密鑰在本地加密大量資料。這類加密模式叫作信封加密。

背景資訊

典型的情境包括(但不限於):

  • 對業務資料檔案的加密

  • 對全磁碟資料加密

本文以加密本地檔案為例,介紹如何使用KMS實現對資料的信封加密,以及如何解密被信封加密的資料。

加密和解密的原理

使用KMS建立一個主要金鑰,使用主要金鑰產生一個資料密鑰,再使用資料密鑰在本地加解密資料。這種情境適用於大量資料的加解密。具體架構如下所示:

  • 信封加密信封加密操作流程如下:

    1. 通過KMS控制台,或者調用CreateKey,建立一個使用者主要金鑰。

    2. 調用GenerateDataKey建立一個資料密鑰。KMS會返回一個明文的資料密鑰和一個密文的資料密鑰。

    3. 使用明文的資料祕密金鑰加密檔案,產生密文檔案,然後銷毀記憶體中的清除金鑰。

    4. 使用者將密文資料密鑰和密文檔案一同儲存到持久化存放裝置或服務中。

  • 信封解密信封解密操作流程如下:

    1. 從本地檔案中讀取密文資料密鑰。

    2. 調用KMS服務的Decrypt,將加密過的密鑰解密為清除金鑰。

    3. 用清除金鑰為本機資料解密,再銷毀記憶體中的清除金鑰。

加密和解密的API

您可以調用以下KMS API,在本地對資料進行加解密。

API名稱

說明

CreateKey

建立使用者主要金鑰(CMK)。

CreateAlias

為指定使用者主要金鑰建立一個別名。

GenerateDataKey

線上產生資料密鑰,用指定CMK加密資料密鑰後,返回資料密鑰的密文和明文。

Decrypt

解密KMS直接加密的資料(包括GenerateDataKey產生的資料密鑰的密文),不需要指定CMK。

加密和解密本地檔案

說明

阿里雲帳號AccessKey擁有所有OpenAPI的存取權限,建議您使用RAM使用者進行API訪問或日常營運。強烈建議不要把AccessKey ID和AccessKey Secret儲存到工程代碼裡,否則可能導致AccessKey泄露,威脅您帳號下所有資源的安全。

本樣本以將AccessKey配置在環境變數ALIBABA_CLOUD_ACCESS_KEY_ID和ALIBABA_CLOUD_ACCESS_KEY_SECRET的方式來實現身分識別驗證為例。

您可以通過阿里雲CLI建立使用者主要金鑰,加密和解密本地檔案。

  1. 調用CreateKey,建立使用者主要金鑰。

    aliyun kms CreateKey

    預期輸出:

    {
      "KeyMetadata": {
        "CreationDate": "2019-04-08T07:45:54Z",
        "Description": "",
        "KeyId": "1234abcd-12ab-34cd-56ef-12345678****",
        "KeyState": "Enabled",
        "KeyUsage": "ENCRYPT/DECRYPT",
        "DeleteDate": "",
        "Creator": "151266687691****",
        "Arn": "acs:kms:cn-hangzhou:151266687691****:key/1234abcd-12ab-34cd-56ef-12345678****",
        "Origin": "Aliyun_KMS",
        "MaterialExpireTime": ""
      },
      "RequestId": "2a37b168-9fa0-4d71-aba4-2077dd9e80df"
    }
  2. (可選)給主要金鑰添加別名。

    別名是使用者主要金鑰的可選標識。如果使用者不建立別名,也可以直接使用密鑰的ID。

    aliyun kms CreateAlias --AliasName alias/Apollo/WorkKey --KeyId 1234abcd-12ab-34cd-56ef-12345678****
    說明

    其中,Apollo/WorkKey表示Apollo專案中的工作密鑰(當前被用於加密的密鑰)。您可以在後續範例程式碼中使用別名(alias/Apollo/WorkKey)調用加密API。

  3. 加密本地檔案。

    範例程式碼中:

    • 使用者主要金鑰:別名為alias/Apollo/WorkKey

    • 明文資料檔案:./data/sales.csv。

    • 輸出的密文資料檔案:./data/sales.csv.cipher

    #!/usr/bin/env python
    #coding=utf-8
    
    import json
    import base64
    
    from Crypto.Cipher import AES
    
    from aliyunsdkcore import client
    from aliyunsdkkms.request.v20160120 import GenerateDataKeyRequest
    
    def KmsGenerateDataKey(client, key_alias):
        request = GenerateDataKeyRequest.GenerateDataKeyRequest()
        request.set_accept_format('JSON')
        request.set_KeyId(key_alias)
        request.set_NumberOfBytes(32)
        response = json.loads(client.do_action(request))
    
        datakey_encrypted = response["CiphertextBlob"]
        datakey_plaintext = response["Plaintext"]
        return (datakey_plaintext, datakey_encrypted)
    
    def ReadTextFile(in_file):
      file = open(in_file, 'r')
      content = file.read()
      file.close()
      return content
    
    def WriteTextFile(out_file, lines):
      file = open(out_file, 'w')
      for ln in lines:
        file.write(ln)
        file.write('\n')
      file.close()
    
    # Out file format (text)
    # Line 1: b64 encoded data key
    # Line 2: b64 encoded IV
    # Line 3: b64 encoded ciphertext
    # Line 4: b64 encoded authentication tag
    def LocalEncrypt(datakey_plaintext, datakey_encrypted, in_file, out_file):
      data_key_binary = base64.b64decode(datakey_plaintext)
      cipher = AES.new(data_key_binary, AES.MODE_EAX)
    
      in_content = ReadTextFile(in_file)
      ciphertext, tag = cipher.encrypt_and_digest(in_content)
    
      lines = [datakey_encrypted, base64.b64encode(cipher.nonce), base64.b64encode(ciphertext), base64.b64encode(tag)];
      WriteTextFile(out_file, lines)
    
    clt = client.AcsClient(os.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID"),os.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET"),'Region-Id')
    
    key_alias = 'alias/Apollo/WorkKey'
    
    in_file = './data/sales.csv'
    out_file = './data/sales.csv.cipher'
    
    # Generate Data Key
    datakey = KmsGenerateDataKey(clt, key_alias)
    
    # Locally Encrypt the sales record
    LocalEncrypt(datakey[0], datakey[1], in_file, out_file)
  4. 解密本地檔案。

    範例程式碼中:

    • 密文資料檔案:./data/sales.csv.cipher

    • 輸出的明文資料檔案:./data/decrypted_sales.csv

    #!/usr/bin/env python
    #coding=utf-8
    
    import json
    import base64
    
    from Crypto.Cipher import AES
    
    from aliyunsdkcore import client
    from aliyunsdkkms.request.v20160120 import DecryptRequest
    
    def KmsDecrypt(client, ciphertext):
      request = DecryptRequest.DecryptRequest()
      request.set_accept_format('JSON')
      request.set_CiphertextBlob(ciphertext)
      response = json.loads(client.do_action(request))
      return response.get("Plaintext")
    
    def ReadTextFile(in_file):
      file = open(in_file, 'r')
      lines = []
      for ln in file:
        lines.append(ln)
      file.close()
      return lines
    
    def WriteTextFile(out_file, content):
      file = open(out_file, 'w')
      file.write(content)
      file.close()
    
    def LocalDecrypt(datakey, iv, ciphertext, tag, out_file):
      cipher = AES.new(datakey, AES.MODE_EAX, iv)
      data = cipher.decrypt_and_verify(ciphertext, tag).decode('utf-8')
      WriteTextFile(out_file, data)
    
    clt = client.AcsClient(os.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID"),os.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET"),'Region-Id')
    
    in_file = './data/sales.csv.cipher'
    out_file = './data/decrypted_sales.csv'
    
    # Read encrypted file
    in_lines = ReadTextFile(in_file)
    
    # Decrypt data key
    datakey = KmsDecrypt(clt, in_lines[0])
    
    # Locally decrypt the sales record
    LocalDecrypt(
      base64.b64decode(datakey),
      base64.b64decode(in_lines[1]), # IV
      base64.b64decode(in_lines[2]), # Ciphertext
      base64.b64decode(in_lines[3]), # Authentication tag
      out_file
      )