爸【本地Docker部署开源低代码开发神器Appsmith与远程访问在线使用】G

式羌形驹在当代的密码学工程中,有一个非常主流的建议:“GCM 是现代加密的首选,应该优先考虑它,而不是像 CBC 这样的传统模式。” 这个建议在绝大多数情况下都很有道理。AES-GCM (Galois/Counter Mode) 凭借其卓越的性能、并行处理能力以及内置的认证加密 (AEAD) 特性,确实能提供远超 CBC (Cipher Block Chaining) 的机密性与完整性保障。

然而,作为在真实世界中构建软件的工程师,我们深知技术选型并非简单的“非黑即白”。在某些特定的、带有约束条件的场景下,我们是否真的只能选择 GCM?会不会存在一些“灰色地带”,让看似“过时”的 CBC 反而成为更务实、更巧妙的解决方案?

在我开发开源项目 Sdcb.Chats 的过程中,就遇到了这样一个有趣的场景。这段经历让我深刻体会到,真正的工程决策,是在深刻理解原理之后,基于具体需求所做的权衡与取舍(Trade-off)。本文将结合这段实践,深入探讨 GCM 和 CBC 之间那些不常被提及的选择考量。

GCM 的光环:为何它被誉为黄金标准?

在深入探讨“特例”之前,我们必须先充分肯定 GCM 的普适优势。简单回顾一下,GCM 之所以强大,主要在于:

认证加密 (AEAD):这是 GCM 最核心的优势。它在加密数据(提供机密性)的同时,会生成一个认证标签(Authentication Tag)。这个标签能保证数据在传输过程中未被篡改(提供完整性)。任何对密文的修改都会导致标签验证失败,解密操作会直接抛出异常,从根本上杜绝了篡改风险,也让“填充预言攻击”等针对 CBC 的攻击方式成为历史。

高性能:GCM 的核心是 CTR (Counter) 模式,其加密过程可以被高度并行化。在支持 AES-NI 指令集的现代 CPU 上,GCM 的吞吐量通常远超需要串行加密的 CBC 模式。

无需填充:作为一种流加密模式,GCM 不需要对明文进行填充(Padding),可以直接处理任意长度的数据,代码实现更简洁,也避免了与填充相关的潜在安全问题。

总而言之,当你需要为一个新系统设计通用的、安全的网络通信协议或数据存储加密时,请毫不犹豫地选择 AES-GCM。

现实的骨感:当 GCM 的要求与需求冲突

在 Sdcb.Chats 项目中,我遇到了一个需求:将数据库中的自增 int 或 long 类型的 ID,在 API 和前端 URL 中展示为一个看起来随机、无规律的标识符,以防止信息泄露(如系统规模)和恶意猜测。同时,这个标识符最好能保持统一、简洁的格式。

这看似简单的需求,却让 GCM 的两个核心要求显得格外“碍事”。

冲突一:固定的 IV/Nonce 与 GCM 的“灾难性”后果

为了保证前端逻辑的稳定性(例如,基于 ID 的缓存和状态管理),我需要一个确定性的加密:对于同一个输入的整数 ID,加密后的字符串结果必须永远相同。

这意味着,我不能在每次加密时都使用随机生成的 Nonce (Number used once)。我必须为每种加密目的(如 ChatId, MessageId)使用一个固定的初始向量(IV),或者说,一个固定的 Nonce。

这对于 GCM 来说是绝对禁止的操作。GCM 的安全性基石在于,对于同一个密钥,Nonce 绝不能重复使用。一旦你用相同的密钥和 Nonce 加密了不同的明文(哪怕明文之间只有微小的差异,比如连续的整数 ID 1, 2, 3...),攻击者就可以通过简单的计算破解出密钥流,进而恢复所有明文。

让我们用代码直观地看一下后果。假设我们使用固定的 Nonce 来加密连续的整数:

using System.Security.Cryptography;

using System.Text;

// 假设我们为某个加密目的,固定使用一个 Nonce

byte[] key = RandomNumberGenerator.GetBytes(16);

byte[] fixedNonce = RandomNumberGenerator.GetBytes(12);

Console.WriteLine($"Key: {Convert.ToHexString(key)}");

Console.WriteLine($"Fixed Nonce: {Convert.ToHexString(fixedNonce)}\n");

using AesGcm aesGcm = new AesGcm(key, tagSizeInBytes: 16);

for (int id = 1; id <= 5; id++)

{

byte[] plaintext = BitConverter.GetBytes(id);

byte[] ciphertext = new byte[plaintext.Length];

byte[] tag = new byte[16];

// 每次都使用相同的 Nonce!这是非常危险的!

aesGcm.Encrypt(fixedNonce, plaintext, ciphertext, tag);

Console.WriteLine($"ID: {id}, Plaintext: {Convert.ToHexString(plaintext)}");

Console.WriteLine($"Ciphertext: {Convert.ToHexString(ciphertext)}");

Console.WriteLine();

}

输出结果可能如下:

Key: DE66C08C3C22D646422DD28D9E539912

Fixed Nonce: 23B5E92983623712E943B6DB

ID: 1, Plaintext: 01000000

Ciphertext: 75749629

ID: 2, Plaintext: 02000000

Ciphertext: 76749629

ID: 3, Plaintext: 03000000

Ciphertext: 77749629

ID: 4, Plaintext: 04000000

Ciphertext: 70749629

ID: 5, Plaintext: 05000000

Ciphertext: 71749629

请仔细观察 Ciphertext!虽然输入的 Plaintext 只有第一个字节在变,但输出的 Ciphertext 也呈现出极其明显的规律性(只有第一个字节在变化)。这完全违背了加密的初衷,攻击者可以轻易地利用这种模式。

那么,CBC 在这种场景下表现如何呢?

CBC 模式虽然也建议每次使用随机的 IV,但即使 IV 固定,其“链式”的内在结构也提供了更好的扩散性。每个明文块都会与前一个密文块进行异或,这使得即使输入数据有规律,输出的密文块也会显得非常混乱。

byte[] key = RandomNumberGenerator.GetBytes(16);

Console.WriteLine($"Key: {Convert.ToHexString(key)}");

using (Aes aes = Aes.Create())

{

aes.Key = key;

aes.Mode = CipherMode.CBC;

aes.Padding = PaddingMode.PKCS7;

// 使用固定的 IV

aes.IV = new byte[16];

Console.WriteLine("\n--- Testing CBC with Fixed IV ---\n");

for (int id = 1; id <= 5; id++)

{

byte[] plaintext = BitConverter.GetBytes(id);

byte[] ciphertext = aes.EncryptCbc(plaintext, aes.IV);

Console.WriteLine($"ID: {id}, Plaintext: {Convert.ToHexString(plaintext)}");

// CBC + PKCS7 padding on a 4-byte input results in a 16-byte output

Console.WriteLine($"Ciphertext: {Convert.ToHexString(ciphertext)}");

Console.WriteLine();

}

}

CBC 的输出结果:

Key: 2255D210C5397DB4454C73DC190DE821

--- Testing CBC with Fixed IV ---

ID: 1, Plaintext: 01000000

Ciphertext: 595F8EFF602FD258C59BE8F0D94D57ED

ID: 2, Plaintext: 02000000

Ciphertext: B9D180464306DF29EE58EEB2086C2C54

ID: 3, Plaintext: 03000000

Ciphertext: 0332B6765638FF5AEA3D64755AA150B9

ID: 4, Plaintext: 04000000

Ciphertext: C8A67BC6F9E5C479EE77B54ADA5BF553

ID: 5, Plaintext: 05000000

Ciphertext: 42C69ABACAB18B35B2A3A8837EB4C17C

看到了吗?尽管输入 ID 是连续的,并且 IV 是固定的,但输出的密文看起来完全是随机和无规律的,成功地隐藏了原始数据的模式。

结论一:在必须使用固定 IV/Nonce 的确定性加密场景下,CBC 的安全性表现远优于 GCM。

冲突二:输出长度的限制与 GCM 的“累赘”

我的另一个需求,是将加密后的 ID 能够方便地表示为一个 Guid。一个标准的 Guid 是一个 16 字节(128位)的数据结构。

这给 GCM 带来了第二个无法解决的问题。GCM 的输出负载必然包含三部分:Nonce、认证标签 (Tag) 和密文。

让我们算一笔账。即使我们加密一个仅 4 字节的 int ID:

密文:4 字节

Nonce:通常至少 12 字节

Tag:通常至少 12 字节(推荐 16 字节)

总长度 = 4 + 12 + 12 = 28 字节。这个长度远远超过了 Guid 所能容纳的 16 字节。我们无法在不破坏 GCM 安全模型的前提下,将它的输出“塞”进一个 Guid 里。

而这,恰恰是 AES-CBC 的“高光时刻”。

AES 本身是一个块加密算法,其块大小固定为 16 字节。当我们使用 CBC 模式配合 PKCS7 填充来加密一个小于 16 字节的数据(比如一个 4 字节的 int 或 8 字节的 long)时,算法会自动将其填充到 16 字节,然后进行加密,最终输出的密文恰好就是 16 字节!

这简直是为 Guid 量身定做的!

byte[] key = RandomNumberGenerator.GetBytes(16);

Console.WriteLine($"Key: {Convert.ToHexString(key)}");

int idToEncrypt = 12345;

byte[] idBytes = BitConverter.GetBytes(idToEncrypt);

byte[] encryptedBytes;

using (Aes aes = System.Security.Cryptography.Aes.Create())

{

aes.Key = key;

aes.IV = new byte[16]; // 固定 IV

aes.Mode = CipherMode.CBC;

aes.Padding = PaddingMode.PKCS7;

encryptedBytes = aes.EncryptCbc(idBytes, aes.IV);

}

Console.WriteLine($"Input is {idBytes.Length} bytes.");

Console.WriteLine($"Encrypted output is {encryptedBytes.Length} bytes.");

// 完美转换为 Guid

Guid finalGuid = new Guid(encryptedBytes);

Console.WriteLine($"Final Guid: {finalGuid}");

输出:

Key: 4B8D859D12AFE340018562C8F70258D5

Input is 4 bytes.

Encrypted output is 16 bytes.

Final Guid: 84a873bb-6bb1-01b1-216c-1fba73400fda

结论二:当需要将加密结果限制在固定长度(特别是 16 字节以适配 Guid)时,AES-CBC 是一个完美且自然的选择,而 GCM 则完全不适用。

安全性的再思考:我们放弃了什么?

选择 CBC,意味着我们放弃了 GCM 提供的内置完整性验证。攻击者理论上可以篡改我们生成的 Guid。

但这在我的场景下是可接受的风险,原因如下:

低碰撞概率:篡改后的 16 字节数据,在解密后,需要恰好能解析为一个有效的、存在于数据库中的整数 ID。这个概率极低。

应用层验证:即使碰巧解密出了一个有效的 ID,后续的业务逻辑和权限验证层(例如,验证当前用户是否有权访问该 ChatId)会成为第二道、也是更坚固的防线。

风险收益不对等:我们场景的核心目标是防止信息泄露和批量扫描,而不是保护像银行交易那样的高价值数据免于定点攻击。为了这个目标,牺牲 GCM 的完整性保护,换取确定性加密和固定的 Guid 输出格式,是一个非常划算的买卖。

总结: 务实主义胜于教条主义

通过 Sdcb.Chats 项目的这次实践,我想分享的核心观点是:

AES-GCM 依然是现代加密的首选和黄金标准。 对于绝大多数需要同时保证机密性和完整性的新应用,你应该毫不犹豫地选择它。

然而,技术世界没有“银弹”。我们不应将“最佳实践”奉为不可违背的教条。

在遇到特殊约束条件时——例如需要确定性加密(固定 IV/Nonce)或对输出长度有严格限制(如适配 Guid)——我们应该深入思考,并勇敢地选择更适合当前场景的工具。

在这种情况下,古老的 AES-CBC 模式,在充分理解其安全边界并做好应用层风险规避的前提下,可以焕发出新的生命力,成为一个更优雅、更务实的解决方案。

作为工程师,我们的价值不仅在于知道“什么是最好的”,更在于知道“在何种情况下,什么是最合适的”。

AI 代码审查Review工具 是一个旨在自动化代码审查流程的工具。它通过集成版本控制系统(如 GitHub 和 GitLab)的 Webhook,利用大型语言模型(LLM)对代码变更进行分析,并将审查意见反馈到相应的 Pull Request 或 Merge Request 中。此外,它还支持将审查结果通知到企业微信等通讯工具。 一个基于 LLM 的自动化代码审查助手。通过 GitHub/GitLab Webhook 监听 PR/MR 变更,调用 AI 分析代码,并将审查意见自动评论到 PR/MR,同时支持多种通知渠道。 主要功能 多平台支持: 集成 GitHub 和 GitLab Webhook,监听 Pull Request / Merge Request 事件。 智能审查模式: 详细审查 (/github_webhook, /gitlab_webhook): AI 对每个变更文件进行分析,旨在找出具体问题。审查意见会以结构化的形式(例如,定位到特定代码行、问题分类、严重程度、分析和建议)逐条评论到 PR/MR。AI 模型会输出 JSON 格式的分析结果,系统再将其转换为多条独立的评论。 通用审查 (/github_webhook_general, /gitlab_webhook_general): AI 对每个变更文件进行整体性分析,并为每个文件生成一个 Markdown 格式的总结性评论。 自动化流程: 自动将 AI 审查意见(详细模式下为多条,通用模式下为每个文件一条)发布到 PR/MR。 在所有文件审查完毕后,自动在 PR/MR 中发布一条总结性评论。 即便 AI 未发现任何值得报告的问题,也会发布相应的友好提示和总结评论。 异步处理审查任务,快速响应 Webhook。 通过 Redis 防止对同一 Commit 的重复审查。 灵活配置: 通过环境变量设置基
【直流微电网】径向直流微电网的状态空间建模线性化:一种耦合DC-DC变换器状态空间平均模型的方法 (Matlab代码实现)内容概要:本文介绍了径向直流微电网的状态空间建模线性化方法,重点提出了一种基于耦合DC-DC变换器的状态空间平均模型的建模策略。该方法通过数学建模手段对直流微电网系统进行精确的状态空间描述,并对其进行线性化处理,以便于系统稳定性分析控制器设计。文中结合Matlab代码实现,展示了建模仿真过程,有助于研究人员理解和复现相关技术,推动直流微电网系统的动态性能研究工程应用。; 适合人群:具备电力电子、电力系统或自动化等相关背景,熟悉Matlab/Simulink仿真工具,从事新能源、微电网或智能电网研究的研究生、科研人员及工程技术人员。; 使用场景及目标:①掌握直流微电网的动态建模方法;②学习DC-DC变换器在耦合条件下的状态空间平均建模技巧;③实现系统的线性化分析并支持后续控制器设计(如电压稳定控制、功率分配等);④为科研论文撰写、项目仿真验证提供技术支持代码参考。; 阅读建议:建议读者结合Matlab代码逐步实践建模流程,重点关注状态变量选取、平均化处理和线性化推导过程,同时可扩展应用于更复杂的直流微电网拓扑结构中,提升系统分析设计能力。
内容概要:本文介绍了基于物PINN驱动的三维声波波动方程求解(Matlab代码实现)理信息神经网络(PINN)求解三维声波波动方程的Matlab代码实现方法,展示了如何利用PINN技术在无需大量标注数据的情况下,结合物理定律约束进行偏微分方程的数值求解。该方法将神经网络物理方程深度融合,适用于复杂波动问题的建模仿真,并提供了完整的Matlab实现方案,便于科研人员理解和复现。此外,文档还列举了多个相关科研方向和技术服务内容,涵盖智能优化算法、机器学习、信号处理、电力系统等多个领域,突出其在科研仿真中的广泛应用价值。; 适合人群:具备一定数学建模基础和Matlab编程能力的研究生、科研人员及工程技术人员,尤其适合从事计算物理、声学仿真、偏微分方程数值解等相关领域的研究人员; 使用场景及目标:①学习并掌握PINN在求解三维声波波动方程中的应用原理实现方式;②拓展至其他物理系统的建模仿真,如电磁场、热传导、流体力学等问题;③为科研项目提供可复用的代码框架和技术支持参考; 阅读建议:建议读者结合文中提供的网盘资源下载完整代码,按照目录顺序逐步学习,重点关注PINN网络结构设计、损失函数构建及物理边界条件的嵌入方法,同时可借鉴其他案例提升综合仿真能力。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值