使用Parquet Modular Encryption读取加密Parquet文件时遇空指针错误
问题分析与解决方案
问题原因
当设置parquet.encryption.key.material.store.internally=false时,Parquet文件不会内嵌密钥材料(加密后的Data Encryption Keys,DEK),而是依赖密钥管理服务(KMS)获取解密所需的信息。但你使用的InMemoryKMS是仅用于本地测试的mock组件,它的密钥材料只存储在当前JVM的内存中:
- 如果写入和读取是两个独立的Spark进程,读取进程的
InMemoryKMS中没有写入时生成的密钥材料,就会导致解密时找不到密钥,触发NullPointerException。 - 当
store.internally=true时,密钥材料直接存储在Parquet文件内部,读取时无需依赖KMS的内存数据,因此读写操作正常。
解决方案
根据你的使用场景,提供以下三种可行方案:
方案1:单进程测试场景(适用于本地验证)
确保写入和读取代码在同一个Spark上下文(同一JVM进程)中执行,这样InMemoryKMS的内存数据可以共享,读取时能直接获取到对应的密钥材料。
完整代码示例:
public class ParquetEncryptionTest { public static void main(String[] args) { SparkSession spark = SparkSession.builder() .appName("ParquetEncryptionTest") .master("local[*]") .getOrCreate(); // 配置加密参数 spark.sparkContext().hadoopConfiguration().set("parquet.crypto.factory.class", "org.apache.parquet.crypto.keytools.PropertiesDrivenCryptoFactory"); spark.sparkContext().hadoopConfiguration().set("parquet.encryption.key.material.store.internally", "false"); spark.sparkContext().hadoopConfiguration().set("parquet.encryption.kms.client.class", "org.apache.parquet.crypto.keytools.mocks.InMemoryKMS"); spark.sparkContext().hadoopConfiguration().set("parquet.encryption.key.list", "keyA:AAECAwQFBgcICQoLDA0ODw==,keyB:AAECAAECAAECAAECAAECAA==,keyC:AAECAwPFBgcICQoLDA0ODw==,keyD:AAECAwPFdgcICQoLDA0ODw=="); // 写入加密Parquet String csvPath = "/home/tuantq2/Downloads/stat/perstat/stats_data_read_csv9.csv"; Dataset<Row> csvData = spark.read() .format("csv") .option("header", "true") .option("inferSchema", "true") .load(csvPath); csvData.write() .option("parquet.encryption.column.keys", "keyA:team") .option("parquet.encryption.footer.key", "keyB") .mode(SaveMode.Overwrite) .parquet("hdfs://127.0.0.1:9000/data/tuantq2/parquet_sec/teampath.parquet.encrypted"); // 读取加密Parquet(同一进程内执行) Dataset<Row> df = spark.read() .parquet("hdfs://127.0.0.1:9000/data/tuantq2/parquet_sec/teampath.parquet.encrypted/") .select("team"); df.show(); spark.stop(); } }
方案2:生产/跨进程场景(推荐)
替换InMemoryKMS为真实的KMS服务(如AWS KMS、Azure Key Vault、HashiCorp Vault等)。这些服务会持久化存储密钥材料,跨进程读取时可以通过官方KMS客户端获取解密所需的信息,同时提供安全的密钥生命周期管理。
需要根据所选KMS的文档配置对应的客户端参数(如访问凭证、服务端点等),并确保Spark作业具备访问KMS的权限。
方案3:保持密钥材料内嵌(最简兼容方案)
如果不需要将密钥材料存储在外部,继续使用parquet.encryption.key.material.store.internally=true即可。这种方式是Parquet加密的默认推荐模式之一,兼容绝大多数读写场景,无需额外依赖外部服务。
内容的提问来源于stack exchange,提问作者truong quoc tuan
相关产品推荐
相关产品推荐

