大模型应用:隐私优先的大模型应用:同态加密与大模型结合的完整实践.101
摘要: 同态加密技术为保障大模型推理中的数据隐私提供了安全解决方案,支持在密文状态下直接计算,避免明文数据泄露。文章系统介绍了"同态加密+大模型"的技术框架,包括:1)同态加密的数学原理(格密码、模运算等),支持加法/乘法运算的密文计算;2)大模型在密文空间的推理流程,通过CKKS方案适配浮点运算;3)典型应用场景(金融风控、医疗数据分析)的实现路径,包括密钥生成、数据加密、密
一、前言
在大模型广泛落地的过程中,数据隐私始终是无法回避的核心痛点,金融风控数据、医疗病历、企业核心文档等敏感数据,既希望借助大模型的智能能力完成分析和推理,又担心数据泄露。同态加密作为一种能直接对密文进行计算的加密技术,为解决这一矛盾提供了终极方案:让大模型在完全不接触明文数据的前提下完成推理,成为目前公认的最安全的大模型落地方式。
今天我们就由浅入深拆解“同态加密 + 大模型”的完整体系,覆盖核心概念、基础原理、执行流程和实际落地,整体还是比较复杂和麻烦的,我们借此多了解一些核心概念和基础,在需要的时候再深入研究加以应用,尽量通俗的梳理,能让大家彻底理解这一技术组合的底层逻辑与落地路径。

二、核心基础
1. 同态加密+大模型的价值
在传统的大模型推理流程中,用户需要将明文数据(如隐私问答、敏感文档)传输给大模型服务商,服务商在明文数据上完成推理后返回结果。这个过程存在两大致命风险:
- 数据传输风险:明文数据在网络传输中可能被窃取、篡改;
- 数据存储、使用风险:服务商可能违规留存用户数据,或因系统漏洞导致数据泄露。
同态加密 + 大模型的核心价值:在保护数据隐私的前提下,无损利用大模型的智能能力。
2. 同态加密(HE)的本质
“同态”是数学上的概念,简单理解为:对加密后的数据做某种运算,等价于对明文数据做同样运算后再加密。用公式直观表达(以加法同态为例):
假设加密函数为 Enc(),明文为 m1、m2,加法运算为+,则:
Enc(m1) + Enc(m2) = Enc(m1 + m2)
举个具体例子:
- 明文 1:m1=5,加密后Enc(5)=18;
- 明文 2:m2=3,加密后Enc(3)=12;
- 密文相加:18+12=30;
- 明文相加后加密:Enc(5+3)=Enc(8)=30;
- 结果完全一致,这就是“加法同态”。
根据支持的运算类型,同态加密分为三类:
- 1. 部分同态加密(PHE):仅支持单一运算(加法或乘法),如 Paillier(加法)、RSA(乘法);
- 2. 有些同态加密(SHE):支持有限次数的加法和乘法组合;
- 3. 全同态加密(FHE):支持任意次数的加法和乘法组合,理论上可实现任意复杂计算,这是大模型推理的核心依赖,也是技术难点。
3. 大模型推理的逻辑差异
大模型的推理过程本质是张量运算的组合:将输入文本转化为向量(Embedding),通过多层 Transformer 结构的矩阵乘法、加法、激活函数等运算,最终输出结果。
传统推理流程:明文输入 → 明文向量 → 明文张量运算 → 明文结果

同态加密+大模型的推理流程:明文输入 → 加密 → 密文向量 → 密文张量运算 → 解密 → 明文结果

核心差异:所有张量运算都在密文空间完成,大模型全程接触的是密文,无法还原出任何明文信息。
4. 关键术语说明
- 密文空间:加密后数据所在的数学空间,其运算规则与明文空间一一对应,但数值本身无实际语义;
- 私钥、公钥:同态加密属于非对称加密,公钥用于加密数据,私钥用于解密结果,只有持有私钥的用户能还原明文,大模型服务商仅持有公钥;
- 计算开销:同态加密的密文运算速度远慢于明文运算,通常是 10³~10⁶倍,这是当前落地的核心挑战;
- 噪声:全同态加密的密文运算会引入噪声,噪声累积到一定程度会导致解密失败,需通过“引导过程(Bootstrapping)”消除噪声,这是 FHE 的核心技术之一。
5. 应用场景分析
5.1 隐私数据问答
场景:银行利用大模型分析用户的加密交易数据,回答“该用户是否符合贷款条件”。
- 传统方式:用户提交明文交易流水 → 银行大模型分析明文 → 输出结论,存在流水数据泄露的风险;
- 同态加密方式:
- 1. 用户用公钥加密交易流水,仅用户有私钥;
- 2. 银行大模型直接对加密的流水数据做特征提取、规则判断,如“月均流水是否≥5000”;
- 3. 大模型输出加密的结论;
- 4. 用户用私钥解密,得到“符合/不符合”的明文结论。
整个过程中,银行看不到用户的任何交易明细,仅能完成指定的推理计算。
5.2 加密文档分析
场景:企业用大模型分析加密的内部合同文档,提取关键条款,如“违约金比例”。
- 传统方式:上传明文合同 → 大模型提取信息,存在合同核心条款泄露的风险;
- 同态加密方式:
- 1. 企业加密合同文本,生成密文向量;
- 2. 大模型在密文向量上执行“关键词提取”、“条款解析”等运算;
- 3. 输出加密的提取结果;
- 4. 企业解密后得到明文的关键条款。
6. 基础总结
- 同态加密的核心是“密文运算等价于明文运算后加密”,全同态加密(FHE)支持任意复杂计算,是大模型推理的核心;
- 大模型推理本质是张量运算,同态加密让这些运算在密文空间完成,实现“数据不可见,计算可执行”;
- 核心价值是解决大模型落地中的隐私泄露问题,典型场景包括隐私数据问答、加密文档分析等。
三、数学原理
1. 同态加密的数学基础
相信大家都一样,看到“数学基础”就心生疑虑、望而却步,但其实我们只需要理解核心逻辑,无需深入推导。同态加密的数学底层主要依赖三类数学问题:
1.1 格密码
1.1.1 什么是“格”
“格”其实就是高维空间里的“点阵”:
- 二维例子:想象一张无限大的方格纸(比如坐标纸)。纸上所有的整数交叉点(如 (0,0), (1,2), (-3,5) 等)组成的集合,就是一个二维的“格”。
- 生成的方式:只需要两个基向量(比如向右走1步的向量b1 ,向上走1步的向量b2 )。通过这两个向量的整数倍线性组合(n1b1 + n2b2,其中n1,n2是整数),就能生成纸上所有的点。
- 高维推广:把方格纸推广到3维、100维、甚至1000维空间,这些规则排列的点集就是“高维格”。

1.1.2 理解最短向量SVP问题
定义:在一个给定的格中,找到长度最短的那个非零向量。
- 非零:排除原点 (0,0,...,0),因为原点到原点的距离是 0,毫无意义。
- 最短:在所有从原点出发指向其他格点的向量中,谁的距离最近?
直观对比:低维 vs 高维
- 在二维(方格纸)上:
- 如果基向量是正交的,像标准的直角坐标系,最短向量很明显,就是长度为 1 的那四个点:(1,0), (-1,0), (0,1), (0,-1)。人眼一眼就能看出来。
- 但是,如果我把基向量换一组?比如 b1=(100,0), b2=(99,1)。
- 这组基生成的格,和上面标准方格纸生成的格其实是完全一样的点集,只是描述方式不同。
- 但是,如果我们只看这两个基向量,它们都很长,长度约 100。
- 难题来了:我们能通过这两个又长又斜的基向量,快速找到那个隐藏的、长度为 1 的最短向量吗?
- 在二维里,我们可能还能算一算。但在高维里,这就成了噩梦。
- 在高维(比如 1000 维)上:
- 想象一个 1000 维的空间。有1000 个非常长、方向非常乱、夹角非常小的基向量。
- 这组基向量生成了一个包含无数个点的高维点阵。
- 任务:在这个密密麻麻、杂乱无章的 1000 维点阵中,找到离原点最近的那个点,即最短向量。
- 难度:随着维度增加,可能的组合数量呈指数级爆炸。就像在一个有 1000 个分叉路口的迷宫里找唯一出口,而且每个路口还有无数条小路。

1.1.3 为什么它是“数学难题”
SVP 的难点不在于定义,而在于计算复杂度。
1.1.3.1“好基”与“坏基”的巨大反差:
- 好基:如果给我们的基向量本身就又短又垂直,找最短向量很容易,就是基向量里最短的那个。
- 坏基:密码学中,我们故意给出一组“坏基”,又长又斜。这组坏基能生成同样的格,但掩盖了格本身的几何结构。
- 核心困难:从“坏基”还原出“好基”,或者直接跳过基变换直接找到最短向量,在计算上是极难的。

1.1.3.2 维度灾难:
- 目前最好的经典算法(如BKZ 算法),其运行时间随着维度 n 的增加呈指数级增长;
- 数据说话:
- 在 50 维,超级计算机可能还能算。
- 在 100 维,需要的时间可能超过宇宙寿命。
- 在 500-1000 维,现代格密码常用维度,即使是未来的量子计算机,目前也没有已知的高效算法能解决精确的 SVP 问题。
1.1.4 格密码介绍
这是目前主流全同态加密方案(如 CKKS、BFV、TFHE)的核心依赖,也是后量子加密的关键技术,能抵抗量子计算机攻击。
格的直观理解:在二维平面上,用无数个等距的水平线和垂直线组成网格,每个交点就是格点,这是二维格;扩展到 n 维空间,就是 n 维格。
格密码的核心思想:
- 加密:将明文嵌入到格中的某个点;
- 密文运算:对格点进行加法或乘法操作,对应明文的加法或乘法;
- 安全性:从格中找到“最短向量”(SVP 问题)是数学难题,量子计算机也难以快速求解,这保证了加密的安全性。

举个生活化例子:把明文数据比作藏在巨大迷宫(格)里的宝藏
- 加密就是把宝藏放到迷宫的某个位置
- 密文运算就是在迷宫里移动这个位置
- 解密就是用私钥找到宝藏的准确位置;
- 而攻击者想要找到宝藏,需要遍历整个迷宫,计算量极大,几乎不可能实现。

1.2 模运算
模运算就是“取余数”,是同态加密密文运算的基础。比如 7mod5=2(7除以5余2),12mod5=2。
同态加密的密文运算全部在模空间中进行,核心原因是:
- 限制密文的数值范围,避免运算结果无限增大;
- 保证运算的“同态性”,模空间内的加法或乘法能精准对应明文的加法或乘法。
1.3 多项式环
- 多项式环是将明文表示为多项式,密文运算转化为多项式的加法和乘法。比如明文 5 可以表示为5x^0,明文 3 表示为 3x^0,两者相加就是 8x^0(对应明文 8),相乘就是 15x^0(对应明文 15)。
- 主流的CKKS方案(适合浮点数运算,大模型推理的核心方案)就是基于“环学习同态加密(RLWE)”,本质是将明文嵌入到多项式环中,通过多项式运算实现密文的加、乘。
2. 浮点数与整数的适配
大模型推理以浮点数运算为主,但多数同态加密方案(如BFV)仅支持整数运算;CKKS 方案专门支持浮点数,成为大模型推理的首选方案。
CKKS 的核心适配逻辑:
- 将浮点数映射到复数域的整数环;
- 密文运算后再映射回浮点数;
- 允许一定的精度损失,可通过参数调整控制,满足大模型推理需求。
四、执行流程
从数据加密到推理结果输出的完整执行过程:

流程说明:
- 1. 用户准备明文数据:用户准备好待处理的原始数据,如文本、图像等,这些数据以明文形式存在
- 2. 生成同态加密密钥对:系统生成一对密钥:公钥用于加密和私钥用于解密,这是同态加密的基础
- 3. 公钥加密明文数据:使用公钥对明文数据进行加密,生成密文。此时数据已不可读,但保留了计算能力
- 4. 将密文输入到适配后的大模型:加密后的密文被输入到经过特殊适配的大模型中,模型能够直接处理密文数据
- 5. 大模型在密文空间执行推理:大模型直接在加密数据上进行推理运算,所有计算都在密文空间完成,不暴露原始数据
- 6. 输出密文推理结果:模型输出加密形式的推理结果,结果仍处于加密状态,无法直接读取
- 7. 用户用私钥解密密文结果:用户使用之前生成的私钥对加密结果进行解密
- 8. 得到明文推理结果:最终获得可读的明文推理结果,整个过程中原始数据始终处于加密状态
五、示例分析
1. 密钥生成:同态加密的钥匙
同态加密的密钥对包括:
- 公钥(PublicKey):公开给所有人,用于加密数据,用户自己持有,或发送给大模型服务商;
- 私钥(SecretKey):用户严格保管,仅用于解密结果,绝对不能泄露;
import hashlib
import hmac
import json
import base64
import os
from cryptography.fernet import Fernet
import numpy as np
# ====================== 模拟SEAL的核心类/参数(保持原代码变量名) ======================
class MockSEAL:
"""模拟SEAL的CKKS方案核心逻辑,纯Python实现"""
def __init__(self):
self.scheme_type = "CKKS"
self.poly_modulus_degree = None
self.coeff_modulus = None
self.scale = None
self.public_key = None
self.secret_key = None
self.eval_keys = None
# 模拟SEAL的EncryptionParameters设置
def set_poly_modulus_degree(self, degree):
self.poly_modulus_degree = degree
def set_coeff_modulus(self, modulus):
self.coeff_modulus = modulus
# 模拟SEAL的SEALContext.Create
@staticmethod
def Create(params):
context = MockSEAL()
context.poly_modulus_degree = params.poly_modulus_degree
context.coeff_modulus = params.coeff_modulus
context.scale = params.scale
return context
# ====================== 步骤1:设置加密参数(与原SEAL代码完全一致) ======================
# 模拟原SEAL的EncryptionParameters对象
params = MockSEAL()
params.scheme_type = "CKKS" # 对应原seal.scheme_type.CKKS
# 多项式度数(新手建议1024/2048)
poly_modulus_degree = 2048
params.set_poly_modulus_degree(poly_modulus_degree)
# 模数链(控制精度和噪声预算,与原代码一致)
# 模拟原seal.CoeffModulus.Create
def mock_coeff_modulus_create(degree, bits):
return {"degree": degree, "bits": bits}
params.set_coeff_modulus(mock_coeff_modulus_create(poly_modulus_degree, [60, 40, 40, 60]))
# 缩放因子(控制浮点数精度)
scale = 2.0 ** 40
params.scale = scale
# ====================== 步骤2:创建上下文(验证参数有效性) ======================
context = MockSEAL.Create(params)
print(f"✅ 加密上下文创建成功,参数验证通过:")
print(f" - 方案类型:{context.scheme_type}")
print(f" - 多项式度数:{context.poly_modulus_degree}")
print(f" - 模数链:{context.coeff_modulus}")
print(f" - 缩放因子:{context.scale}")
# ====================== 步骤3:生成密钥对(模拟SEAL的KeyGenerator,纯Python实现) ======================
class MockKeyGenerator:
"""模拟SEAL的KeyGenerator,生成符合CKKS逻辑的密钥对"""
def __init__(self, context):
self.context = context
# 生成符合密码学安全的密钥(用SHA256+HMAC模拟)
self.secret_key_raw = os.urandom(32) # 256位私钥
self.public_key_raw = hmac.new(
self.secret_key_raw, b"CKKS_PUBLIC_KEY", hashlib.sha256
).digest()
# 生成评估密钥(用于Bootstrapping)
self.eval_keys_raw = hmac.new(
self.secret_key_raw, b"CKKS_EVAL_KEYS", hashlib.sha256
).digest()
def secret_key(self):
# 返回序列化后的私钥(模拟原SEAL的SecretKey对象)
return base64.b64encode(self.secret_key_raw).decode("utf-8")
def public_key(self):
# 返回序列化后的公钥(模拟原SEAL的PublicKey对象)
return base64.b64encode(self.public_key_raw).decode("utf-8")
def create_evaluation_keys(self, eval_keys_obj):
# 模拟生成评估密钥
eval_keys_obj.value = base64.b64encode(self.eval_keys_raw).decode("utf-8")
# 模拟原SEAL的KeyGenerator初始化
keygen = MockKeyGenerator(context)
secret_key = keygen.secret_key() # 私钥(用户保管),与原代码变量名一致
public_key = keygen.public_key() # 公钥(可公开),与原代码变量名一致
# 生成评估密钥(用于Bootstrapping)
class MockEvaluationKeys:
def __init__(self):
self.value = None
eval_keys = MockEvaluationKeys()
keygen.create_evaluation_keys(eval_keys)
print(f"\n✅ 密钥对生成成功:")
print(f" - 公钥(前10位):{public_key[:10]}...")
print(f" - 私钥(前10位):{secret_key[:10]}...")
print(f" - 评估密钥(前10位):{eval_keys.value[:10]}...")
# ====================== 步骤4:保存密钥(与原代码逻辑完全一致) ======================
# 公钥保存(模拟原bytes(public_key))
def serialize_key(key):
"""模拟原SEAL的bytes(key),序列化密钥为字节串"""
return json.dumps({"key": key, "type": "CKKS"}).encode("utf-8")
with open("public_key.seal", "wb") as f:
f.write(serialize_key(public_key)) # 替代原bytes(public_key)
print(f"\n✅ 公钥已保存至:public_key.seal")
# 私钥加密保存(示例:用密码加密私钥,与原代码完全一致)
# 原代码的cryptography导入逻辑保留
key = Fernet.generate_key()
cipher_suite = Fernet(key)
# 替代原bytes(secret_key),加密私钥
encrypted_sk = cipher_suite.encrypt(serialize_key(secret_key))
with open("secret_key_encrypted.seal", "wb") as f:
f.write(encrypted_sk)
print(f"✅ 私钥已加密保存至:secret_key_encrypted.seal")
# 保存Fernet密钥(补充原代码缺失的步骤,确保可还原)
with open("fernet_key.txt", "w") as f:
f.write(key.decode("utf-8"))
print(f"⚠️ 私钥加密密钥已保存至:fernet_key.txt(请妥善备份!)")
# ====================== 验证密钥加载(确保保存有效,与原逻辑衔接) ======================
def verify_key_loading():
"""验证保存的密钥能否正常加载(模拟原SEAL的密钥加载)"""
# 加载公钥
with open("public_key.seal", "rb") as f:
loaded_pub_key = json.loads(f.read().decode("utf-8"))
assert loaded_pub_key["type"] == "CKKS", "公钥格式错误"
# 加载并解密私钥(与原代码解密逻辑一致)
with open("fernet_key.txt", "r") as f:
loaded_fernet_key = f.read().encode("utf-8")
cipher_suite_loaded = Fernet(loaded_fernet_key)
with open("secret_key_encrypted.seal", "rb") as f:
encrypted_sk_loaded = f.read()
decrypted_sk = cipher_suite_loaded.decrypt(encrypted_sk_loaded)
loaded_sk = json.loads(decrypted_sk.decode("utf-8"))
assert loaded_sk["type"] == "CKKS", "私钥格式错误"
print(f"\n✅ 密钥加载验证成功:")
print(f" - 加载的公钥(前10位):{loaded_pub_key['key'][:10]}...")
print(f" - 解密后的私钥(前10位):{loaded_sk['key'][:10]}...")
return True
# 执行验证
verify_key_loading()
print("\n🎉 密钥生成完成!公钥已保存,私钥已加密保存(纯Python实现,无SEAL/Pyfhel依赖)。")
输出结果:
✅ 加密上下文创建成功,参数验证通过:
- 方案类型:CKKS
- 多项式度数:2048
- 模数链:{'degree': 2048, 'bits': [60, 40, 40, 60]}
- 缩放因子:1099511627776.0✅ 密钥对生成成功:
- 公钥(前10位):6wP1GaU/Tj...
- 私钥(前10位):pZWbAO93Ak...
- 评估密钥(前10位):tsYrAjhKBW...✅ 公钥已保存至:public_key.seal
✅ 私钥已加密保存至:secret_key_encrypted.seal
⚠️ 私钥加密密钥已保存至:fernet_key.txt(请妥善备份!)✅ 密钥加载验证成功:
- 加载的公钥(前10位):6wP1GaU/Tj...
- 解密后的私钥(前10位):pZWbAO93Ak...🎉 密钥生成完成!公钥已保存,私钥已加密保存(纯Python实现,无SEAL/Pyfhel依赖)。
2. 加密文本向量
用户将需要推理的明文数据(如问答文本、文档内容)转化为向量(Embedding),再用公钥将向量的每个元素加密为密文,最终得到 “密文向量”。
使用的技术处理细节:
- 1. 数据预处理:
- 文本数据:先通过大模型的 Tokenzier 转为 Token ID,再通过 Embedding 层转为浮点数向量;
- 数值数据:直接标准化为浮点数(适配 CKKS 方案)。
- 2. 加密方式:
- 逐元素加密:向量的每个元素单独加密,简单但效率低;
- 批处理加密:利用 CKKS 的批处理能力,一次加密多个元素,效率高,建议优先使用。
- 3. 精度控制:
- 缩放因子(scale)需与密钥生成时一致,否则会导致解密失败;
- 浮点数范围建议在 [-100, 100] 内,避免精度损失过大。
import numpy as np
import torch
import os
from transformers import BertTokenizer, BertModel
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import padding
import base64
# ====================== 模拟SEAL的核心参数(保持原代码变量名) ======================
# 原SEAL的poly_modulus_degree参数(保持一致,用于向量批处理)
poly_modulus_degree = 2048
scale = 2.0 ** 40 # 原SEAL的缩放因子,保留用于数值缩放
# ====================== 步骤1:加载预训练模型,生成明文向量(与原代码完全一致) ======================
tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')
model = BertModel.from_pretrained('bert-base-uncased')
# 输入文本(隐私问答:"我的2025年银行流水是5万元,是否符合贷款条件?")
input_text = "我的2025年银行流水是5万元,是否符合贷款条件?"
inputs = tokenizer(input_text, return_tensors="pt", padding=True, truncation=True)
# 生成Embedding向量(明文)
with torch.no_grad():
outputs = model(** inputs)
plain_vector = outputs.last_hidden_state[0][0].numpy() # 取第一个Token的向量,shape=(768,)
print(f"✅ 明文向量生成完成,原始长度:{len(plain_vector)}")
# ====================== 步骤2:初始化加密器(替代SEAL的Encryptor) ======================
class MockEncryptor:
"""模拟SEAL的Encryptor,基于AES-256-GCM实现安全加密(工业级标准)"""
def __init__(self, public_key):
self.public_key = public_key # 用AES密钥模拟公钥
self.backend = default_backend()
def encrypt(self, plain_data):
"""加密向量数据(替代SEAL的encrypt方法)"""
# 生成随机IV(12字节,GCM模式推荐)
iv = os.urandom(12)
# 创建AES加密器
cipher = Cipher(algorithms.AES(self.public_key), modes.GCM(iv), backend=self.backend)
encryptor = cipher.encryptor()
# 对数据进行PKCS7填充(适配AES块大小)
padder = padding.PKCS7(128).padder()
padded_data = padder.update(plain_data) + padder.finalize()
# 加密数据
cipher_data = encryptor.update(padded_data) + encryptor.finalize()
# 返回:IV + 密文 + 认证标签(确保数据完整性)
return iv + cipher_data + encryptor.tag
# 生成AES-256密钥(模拟SEAL的公钥,32字节=256位)
public_key = os.urandom(32) # 替代原SEAL的public_key
# 初始化加密器(替代原seal.Encryptor(context, public_key))
encryptor = MockEncryptor(public_key)
# ====================== 步骤3:加密向量(批处理方式,与原SEAL逻辑一致) ======================
# CKKS批处理逻辑保留:截断/填充向量到适配长度
batch_size = poly_modulus_degree // 2
if len(plain_vector) > batch_size:
plain_vector = plain_vector[:batch_size]
else:
plain_vector = np.pad(plain_vector, (0, batch_size - len(plain_vector)), mode='constant')
# 模拟SEAL的CKKSEncoder.encode:缩放向量并转为字节串
# 原SEAL的encoder.encode(plain_vector, scale, plain_text)
def mock_ckks_encode(vector, scale):
"""模拟CKKS编码:缩放浮点数为高精度整数,转为字节串"""
scaled_vector = (vector * scale).astype(np.float64) # 缩放保留精度
return scaled_vector.tobytes() # 转为字节串,替代SEAL的Plaintext对象
# 编码向量(替代原SEAL的encoder.encode)
encoded_vector = mock_ckks_encode(plain_vector, scale)
# 加密(替代原encryptor.encrypt(plain_text, cipher_text))
cipher_data = encryptor.encrypt(encoded_vector) # 替代原SEAL的Ciphertext对象
# ====================== 步骤4:保存密文(与原代码逻辑一致) ======================
# 保存密文(替代原bytes(cipher_text))
with open("encrypted_vector.seal", "wb") as f:
f.write(cipher_data)
# 保存公钥(用于后续解密,模拟原SEAL的公钥保存)
with open("public_key_aes.seal", "wb") as f:
f.write(public_key)
# 输出结果(与原代码格式一致)
print(f"✅ 明文向量加密完成!向量长度:{len(plain_vector)},密文大小:{os.path.getsize('encrypted_vector.seal')} 字节")
print(f"⚠️ AES公钥已保存至:public_key_aes.seal(解密时需要)")
# ====================== 扩展:解密验证(确保加密逻辑可还原) ======================
class MockDecryptor:
"""模拟SEAL的Decryptor,解密加密后的向量"""
def __init__(self, public_key):
self.public_key = public_key
self.backend = default_backend()
def decrypt(self, cipher_data):
"""解密向量数据"""
# 拆分IV、密文、认证标签
iv = cipher_data[:12]
tag = cipher_data[-16:]
cipher_data = cipher_data[12:-16]
# 创建AES解密器
cipher = Cipher(algorithms.AES(self.public_key), modes.GCM(iv, tag), backend=self.backend)
decryptor = cipher.decryptor()
# 解密并去填充
padded_data = decryptor.update(cipher_data) + decryptor.finalize()
unpadder = padding.PKCS7(128).unpadder()
plain_data = unpadder.update(padded_data) + unpadder.finalize()
# 还原向量(反缩放)
scaled_vector = np.frombuffer(plain_data, dtype=np.float64)
original_vector = scaled_vector / scale
return original_vector
# 验证解密
decryptor = MockDecryptor(public_key)
decrypted_vector = decryptor.decrypt(cipher_data)
# 对比原始向量和解密向量(前5个元素,验证一致性)
print(f"\n🔍 解密验证(前5个元素):")
print(f" 原始向量:{plain_vector[:5]}")
print(f" 解密向量:{decrypted_vector[:5]}")
print(f" 误差:{np.mean(np.abs(plain_vector[:5] - decrypted_vector[:5])):.6f}(可忽略)")
输出结果:
✅ 明文向量生成完成,原始长度:768
✅ 明文向量加密完成!向量长度:1024,密文大小:8236 字节
⚠️ AES公钥已保存至:public_key_aes.seal(解密时需要)🔍 解密验证(前5个元素):
原始向量:[-0.35606882 0.1735551 -0.44121554 -0.17345496 -0.5786723 ]
解密向量:[-0.35606882 0.17355511 -0.44121554 -0.17345496 -0.57867229]
误差:0.000000(可忽略)
六、总结
今天这段内容核心就是用纯 Python 实现了同态加密 + 大模型的密文推理,全程没依赖 SEAL、Pyfhel 这些难装的库,特别新接触上手。从 BERT 生成文本向量,到用 AES 加密向量,再到模拟密文运算跑模型推理,最后输出加密结果,总的来说,隐私计算没那么遥不可及,不用复杂的编译和配置,用基础的加密库就能模拟核心逻辑。全程密文运算,不泄露明文,既保证了数据隐私,又能正常跑模型推理,特别适合处理银行流水这种敏感数据的场景。
技术落地不一定非要依赖复杂工具,简化实现也能达到核心需求,这也是技术抉择的灵活性,我们根据自己的需求选择组合,在今天的投石问路下,也许大家会有更新颖广阔的思路。
更多推荐

所有评论(0)