Parquet 模組加密

如果您的資料是以直欄格式儲存,則可使用 Parquet 模組化加密,在寫入 Parquet 檔時加密機密直欄,並在讀取加密檔時解密這些直欄。 在直欄層次加密資料,可讓您決定要加密的直欄以及控制直欄存取權的方式。

除了確保隱私之外,Parquet 模組化加密還會保護儲存資料的完整性。 系統會偵測對檔案內容進行的任何竄改,並觸發讀取器端異常狀況。

主要特性包括:

  1. Parquet 模組化加密及解密是在 Spark 叢集上執行。 因此,機密資料及加密金鑰對儲存體不可見。

  2. 標準 Parquet 特性(如編碼、壓縮、直欄式投射及述詞推入)在 Parquet 模組化加密格式的檔案中會繼續照常運作。

  3. 您可以選擇在 Parquet 規格中定義的兩個加密演算法之一。 這兩個演算法皆支援直欄加密,但是:

    • 預設演算法 AES-GCM 可全面防止竄改 Parquet 檔中的資料及 meta 資料部分。
    • 替代演算法 AES-GCM-CTR 支援對 Parquet 檔的局部完整性保護。 僅會防止竄改 meta 資料部分(而非資料部分)。 相較於 AES-GCM 演算法,此演算法的優點是具有較低的傳輸量額外負擔。
  4. 您可以選擇要加密的直欄。 其他直欄將不會加密,從而減少傳輸量額外負擔。

  5. 不同的直欄可以使用不同的金鑰來加密。

  6. 依預設,將會加密主要 Parquet meta 資料模組(檔案標底),以隱藏檔案綱目及機密直欄清單。 不過,您可以選擇不加密檔案標底,以便讓舊式讀取器 (例如尚未支援 Parquet 模組化加密的其他 Spark 發行套件) 能夠讀取已加密檔案中未加密的直欄。

  7. 加密金鑰可透過下列兩種方式之一進行管理:

    • 直接透過您的應用程式。 請參閱由應用程式執行的金鑰管理
    • 透過金鑰管理系統 (KMS),此系統會產生、儲存和毀損 Spark 服務使用的加密金鑰。 這些金鑰永不離開 KMS 伺服器,因此對於包括 Spark 服務在內的其他元件都不可見。 請參閱透過 KMS 進行金鑰管理

    附註:只有主要加密金鑰 (MEK) 需要由應用程式或 KMS 管理。

    對於每個機密直欄,您必須指定要用於加密的主要金鑰。 此外,必須為每個加密檔(資料框架)的標底指定主要金鑰。 依預設,標底金鑰將用於標底加密。 但是,如果您選擇純文字標底模式,則不會加密標底,且金鑰僅會用於標底的完整性驗證。

    加密參數可以透過標準 Spark Hadoop 配置傳遞,例如透過在應用程式 SparkContext的 Hadoop 配置中設定配置值:

    sc.hadoopConfiguration.set("<parameter name>" , "<parameter value>")
    

    或者,您可以透過 write 選項來傳遞參數值:

    <data frame name>.write
    .option("<parameter name>" , "<parameter value>")
    .parquet("<write path>")
    

使用 Parquet 模組化加密來執行

Parquet 模組化加密僅適用於在 IBM Analytics Engine 服務實例中執行的 Spark Notebook。 在 Spark 模組化環境內執行的 Notebook 中不支援 Parquet 加密。

若要啟用 Parquet 模組化加密,請設定下列 Spark 類別路徑內容,以指向實作 Parquet 模組化加密的 Parquet Jar 檔,以及金鑰管理 Jar 檔:

  1. 導覽至 Ambari > Spark> 配置-> 自訂 spark2-default

  2. 新增下列兩個參數,以明確指向 JAR 檔的位置。 請確保編輯路徑以使用叢集上 Jar 檔的實際版本。

    spark.driver.extraClassPath=/home/common/lib/parquetEncryption/ibm-parquet-kms-<latestversion>-jar-with-dependencies.jar:/home/common/lib/parquetEncryption/parquet-format-<latestversion>.jar:/home/common/lib/parquetEncryption/parquet-hadoop-<latestversion>.jar
    
    spark.executor.extraClassPath=/home/common/lib/parquetEncryption/ibm-parquet-<latestversion>-jar-with-dependencies.jar:/home/common/lib/parquetEncryption/parquet-format-<latestversion>.jar:/home/common/lib/parquetEncryption/parquet-hadoop-<latestversion>.jar
    

必要的參數

寫入加密資料需要下列參數:

  • 要加密的具有主要加密金鑰的直欄清單:

    parameter name: "encryption.column.keys"
    parameter value: "<master key ID>:<column>,<column>;<master key ID>:<column>,.."
    
  • 標底金鑰:

    parameter name: "encryption.footer.key"
    parameter value: "<master key ID>"
    

    例如:

    dataFrame.write
    .option("encryption.footer.key" , "k1")
    .option("encryption.column.keys" , "k2:SSN,Address;k3:CreditCard")
    .parquet("<path to encrypted files>")
    
    重要事項:

    如果既未設定 encryption.column.keys 參數,也未設定 encryption.footer.key 參數,則不會加密檔案。 如果僅設定其中一個參數,則會擲出異常狀況,因為這些參數對於加密檔案是必要的。

選用參數

在寫入加密資料時,可以使用下列選用參數:

  • 加密演算法 AES-GCM-CTR

    依預設,Parquet 模組化加密會使用 AES-GCM 演算法,該演算法可全面防止竄改 Parquet 檔中的資料及 meta 資料。 但是,由於 Spark 2.3.0 在 Java 8 上執行,而 Java 8 不支援 CPU 硬體中的 AES 加速(僅在 Java 9 中新增了此支援),因此資料完整性驗證的額外負擔在某些狀況下可能會影響工作量傳輸量。

    為了彌補此不足,您可以關閉資料完整性驗證支援,並使用替代演算法 AES-GCM-CTR 來寫入加密檔,該演算法僅會驗證 meta 資料部分的完整性,而不驗證資料部分的完整性,因此相較於 AES-GCM 演算法,它具有較低的傳輸量額外負擔。

    parameter name: "encryption.algorithm"
    parameter value: "AES_GCM_CTR_V1"
    
  • 適用於舊式讀取器的純文字標底模式

    依預設,將會加密主要 Parquet meta 資料模組(檔案標底),以隱藏檔案綱目及機密直欄清單。 不過,您可以決定不加密檔案標底,以啟用其他 Spark 及 Parquet 讀取器 (尚未支援 Parquet 模組化加密) 來讀取已加密檔案中未加密的直欄。 若要關閉標底加密,請設定下列參數:

    parameter name: "encryption.plaintext.footer"
    parameter value: "true"
    
    重要事項:

    encryption.footer.key 參數也必須在純文字標底模式中指定。 雖然標底未加密,但會使用金鑰來簽署標底內容,這表示新讀取器可以驗證其完整性。 舊式讀取器不受新增標底簽章的影響。

用法範例

Python 的下列範例程式碼 Snippet 顯示如何建立資料訊框、寫入已加密的 parquet 檔案,以及從已加密的 parquet 檔案讀取。

  • Python:寫入加密資料:

    from pyspark.sql import Row
    
    squaresDF = spark.createDataFrame(
        sc.parallelize(range(1, 6))
        .map(lambda i: Row(int_column=i, square_int_column=i ** 2)))
    
    sc._jsc.hadoopConfiguration().set("encryption.key.list",
        "key1: AAECAwQFBgcICQoLDA0ODw==, key2: AAECAAECAAECAAECAAECAA==")
    sc._jsc.hadoopConfiguration().set("encryption.column.keys",
        "key1:square_int_column")
    sc._jsc.hadoopConfiguration().set("encryption.footer.key", "key2")
    
    encryptedParquetPath = "squares.parquet.encrypted"
    squaresDF.write.parquet(encryptedParquetPath)
    
  • Python:讀取加密資料:

    sc._jsc.hadoopConfiguration().set("encryption.key.list",
        "key1: AAECAwQFBgcICQoLDA0ODw==, key2: AAECAAECAAECAAECAAECAA==")
    
    encryptedParquetPath = "squares.parquet.encrypted"
    parquetFile = spark.read.parquet(encryptedParquetPath)
    parquetFile.show()
    

Python 工作檔案 InMemoryKMS.py 的內容如下:

from pyspark.sql import SparkSession
from pyspark import SparkContext
from pyspark.sql import Row

if __name__ == "__main__":
    spark = SparkSession \
        .builder \
        .appName("InMemoryKMS") \
        .getOrCreate()
    sc = spark.sparkContext
    ##KMS operation
    print("Setup InMemoryKMS")
    hconf = sc._jsc.hadoopConfiguration()
    encryptedParquetFullName = "testparquet.encrypted"
    print("Write Encrypted Parquet file")
    hconf.set("encryption.key.list", "key1: AAECAwQFBgcICQoLDA0ODw==, key2: AAECAAECAAECAAECAAECAA==")
    btDF = spark.createDataFrame(sc.parallelize(range(1, 6)).map(lambda i: Row(ssn=i,  value=i ** 2)))
    btDF.write.mode("overwrite").option("encryption.column.keys", "key1:ssn").option("encryption.footer.key", "key2").parquet(encryptedParquetFullName)
    print("Read Encrypted Parquet file")
    encrDataDF = spark.read.parquet(encryptedParquetFullName)
    encrDataDF.createOrReplaceTempView("bloodtests")
    queryResult = spark.sql("SELECT ssn, value FROM bloodtests")
    queryResult.show(10)
    sc.stop()
    spark.stop()

加密金鑰處理的內容

寫入 Parquet 檔時,會為每個加密直欄及標底產生隨機資料加密金鑰 (DEK)。 這些金鑰可用來加密 Parquet 檔中的資料及 meta 資料模組。

然後,資料加密金鑰會使用金鑰加密金鑰 (KEK) 進行加密,該金鑰也是在 Spark/Parquet 內針對每個主要金鑰而產生。 金鑰加密金鑰會在本端使用主要加密金鑰 (MEK) 進行加密。

已加密的資料加密金鑰及金鑰加密金鑰,會與主要金鑰身分一起儲存在 Parquet 檔 meta 資料中。 每個金鑰加密金鑰皆具有唯一身分(在本端產生為安全隨機 16 位元組值),該身分也會儲存在檔案 meta 資料中。

讀取 Parquet 檔時,會從檔案 meta 資料中擷取主要加密金鑰 (MEK) 的 ID、已加密的金鑰加密金鑰 (KEK) 及其 ID,以及已加密的資料加密金鑰 (DEK)。

金鑰加密金鑰會在本端使用主要加密金鑰進行解密。 然後,會在本端使用金鑰加密金鑰 (KEK) 來解密資料加密金鑰 (DEK)。

進一步瞭解