一、前言

        在大模型广泛落地的过程中,数据隐私始终是无法回避的核心痛点,金融风控数据、医疗病历、企业核心文档等敏感数据,既希望借助大模型的智能能力完成分析和推理,又担心数据泄露。同态加密作为一种能直接对密文进行计算的加密技术,为解决这一矛盾提供了终极方案:让大模型在完全不接触明文数据的前提下完成推理,成为目前公认的最安全的大模型落地方式。

        今天我们就由浅入深拆解“同态加密 + 大模型”的完整体系,覆盖核心概念、基础原理、执行流程和实际落地,整体还是比较复杂和麻烦的,我们借此多了解一些核心概念和基础,在需要的时候再深入研究加以应用,尽量通俗的梳理,能让大家彻底理解这一技术组合的底层逻辑与落地路径。

二、核心基础

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 加密向量,再到模拟密文运算跑模型推理,最后输出加密结果,总的来说,隐私计算没那么遥不可及,不用复杂的编译和配置,用基础的加密库就能模拟核心逻辑。全程密文运算,不泄露明文,既保证了数据隐私,又能正常跑模型推理,特别适合处理银行流水这种敏感数据的场景。

        技术落地不一定非要依赖复杂工具,简化实现也能达到核心需求,这也是技术抉择的灵活性,我们根据自己的需求选择组合,在今天的投石问路下,也许大家会有更新颖广阔的思路。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐