20231427田泽航第二周预习报告

1.AI对学习内容的总结

使用AI工具:DEEPSEEK
以下是对其内容的总结:


Head First C第8章:静态与动态库

核心思想: 代码复用与模块化。将常用代码编译成库文件,供多个程序共享,避免重复编译,便于更新和维护。
静态库:含义:静态库在程序编译链接时,会将库中用到的代码完整地复制到最终的可执行文件中。一旦链接完成,可执行文件就不再需要原来的静态库文件了。文件扩展名:Linux/Unix: .a (Archive,归档文件)

Windows: .lib (Library)
优点:
独立性/无依赖:生成的可执行文件是自包含的。它不依赖于运行环境上是否安装了某个特定版本的库文件。只要操作系统兼容,它就能运行。部署非常简单,直接拷贝可执行文件即可。

性能略高:因为所有代码都在一个可执行文件里,函数调用在加载时就已经确定地址,没有运行时加载和动态链接的开销,理论上启动和运行速度会快一点点。
缺点:
可执行文件体积大:如果多个程序都使用了同一个静态库,那么每个程序的内部都会包含一份该库代码的完整拷贝,导致磁盘空间浪费。

内存占用可能更高:如果系统同时运行多个使用了相同静态库的程序,那么该库的代码会在内存中存在多份副本。

更新维护困难:如果静态库发现了安全漏洞或需要更新功能,你必须重新编译整个程序,并重新分发整个新的可执行文件。用户需要重新安装整个程序。
动态库:
优点:
节省磁盘空间:多个程序可以共享同一个动态库文件,磁盘上只需保存一份。

节省内存:当多个程序使用同一个动态库时,操作系统会智能地安排内存,使得该库的代码在内存中通常只有一份副本,被所有程序共享。

更新方便:更新库非常方便。如果修复了动态库的一个bug,你只需要替换掉旧的库文件(.so或.dll),所有依赖它的程序在下次启动时就会自动使用新版本。无需重新编译程序本身。

缺点:
依赖管理复杂(“DLL Hell”):可执行文件不能独立运行,它强烈依赖于系统上是否存在正确版本的动态库。如果目标机器上没有安装所需的库,或者版本不兼容,程序将无法启动。这就是著名的“DLL Hell”问题。

性能轻微损耗:程序启动时需要加载和链接动态库,这会带来微小的性能开销。

部署稍复杂:分发程序时,你需要确保动态库也一并打包分发,并正确安装到系统能够找到的路径下。

《Windows C/C++加密解密实战》第3章:对称密码算法

核心概念: 加密和解密使用相同密钥。速度快,适合加密大量数据。分为流密码和分组密码。
流密码:逐位或逐字节加密。典型代表RC4算法,密钥流与明文异或。实现简单速度快,但密钥管理不当易被攻破。
分组密码:将明文分块处理。核心是工作模式(ECB, CBC, CFB, OFB, CTR) 和短块处理
DES/3DES算法: DES密钥56位,已不安全。3DES使用三个密钥执行“加密-解密-加密”以增强安全性,兼容DES。

《Windows C/C++加密解密实战》第5章:密码学常见编码格式

Base64编码: 将二进制数据转换为64个可打印ASCII字符(A-Z, a-z, 0-9, +, /)。每3字节(24位)二进制数据转换为4个6位Base64字符(不足3字节需填充=)。用于在仅支持文本的环境中传输存储二进制数据(如Email、HTTP)。
PEM格式: 一种用于存储和发送证书、密钥等密码学对象的文本文件格式。通常包含Base64编码的数据和首尾的标签(如 -----BEGIN PRIVATE KEY----- 和 -----END PRIVATE KEY-----)。
ASN.1 (Abstract Syntax Notation One): 一种描述数据结构与编码的接口描述语言,用于定义复杂的、与平台无关的数据结构。
DER (Distinguished Encoding Rules): ASN.1的一种编码规则,提供唯一的数据二进制表示,常用于编码证书和密钥。是BER的子集,但保证编码结果唯一。
重点 5.3.7 ASN.1/DER编码: 深入理解ASN.1语法(如SEQUENCE, INTEGER)和DER的TLV(Tag-Length-Value)编码格式,是解析和生成X.509证书、PKCS#8密钥等标准格式的基础。
总结:
Base64 解决了二进制数据在文本媒介中生存的问题。
PEM 通过添加标签,让Base64编码的数据变得可识别和管理。
ASN.1 是密码学领域的“通用语言”,定义了数据的抽象结构。
DER 是这种通用语言最严格的“书写规范”,确保了编码结果的唯一性和确定性,是数字信任链条的基石。

教材学习内容总结

2.对AI总结的反思与补充

AI总结的局限性:

第一部分:静态与动态库的反思与补充

已准确抓住了静态库的“独立”与动态库的“共享”这一核心矛盾。这本质上是空间、部署便利性与性能、管理复杂度之间的权衡,但缺乏对知识内在逻辑和实际意义的深入剖析。

补充与深化:

动态库的版本管理:这是解决“DLL Hell”的关键。Linux 使用 soname 机制(如 libcrypto.so.1.1),允许同时安装多个版本库。链接时程序指定依赖的具体版本,平衡了更新需求和稳定性。Windows 后来通过 Side-by-Side Assembly 机制也试图解决此问题。

显式运行时链接:除了编译时链接,程序还可以在运行时动态加载动态库(如 Linux 的 dlopen(), Windows 的 LoadLibrary())。这提供了极大灵活性,插件系统就是典型应用。程序可以判断环境后再决定加载哪个库。

静态库的“链接时优化”优势:因为编译器在链接时能看到所有静态库的代码,它可以进行跨模块的优化(如内联函数),这可能会生成比动态链接更高效的代码

第二部分:对称密码算法的反思与补充

AI总结的局限性:
你清晰地区分了流密码和分组密码的概念,并指出了DES/3DES的历史地位和现状。理解分组密码的工作模式是应用它们的关键,但对“为何这么做”以及“过程中的核心痛点”解释不足。

补充与深化:

分组密码工作模式的深入比较:

ECB 的致命缺陷:不仅是安全性差,它还会泄露数据的模式。一张图片用ECB加密后,密文可能仍能看出轮廓。绝对避免使用ECB。

CBC 的局限性:虽然安全,但加密过程无法并行化,因为每一块都依赖于前一块的密文。解密过程可以并行。

CTR 模式的现代性:它将分组密码转换为流密码来使用。生成一个密钥流,然后与明文异或。它支持加解密并行化,且不需要填充,是当前推荐的模式之一(与GCM等AEAD模式相关)。

AES 算法的关键地位:虽然你的总结中提到,但值得强调:AES 是取代DES/3DES的现代标准。它支持128、192、256位密钥和128位分组,高效且安全,是当前对称加密的绝对主力。SM4是中国的商用密码标准,设计与AES类似,在国家关键领域使用。

认证加密模式:现代应用不仅需要保密性,还需要完整性和真实性(确保数据未被篡改)。GCM 等模式在提供保密性的同时,会生成一个认证标签,是TLS 1.3等协议的首选。

第三部分:密码学编码格式的反思与补充
反思: 对Base64, PEM, ASN.1, DER四者关系的总结非常精辟,尤其是“通用语言”和“书写规范”的比喻,准确抓住了本质。

补充:

TLV 编码详解:这是理解DER的基石。

Tag:不仅标识类型,还区分类别(通用、应用、上下文特定、私有)。SEQUENCE 的Tag是 0x10(结构化的 0x20 | 通用类 0x00)。

Length:有两种形式。短形式(长度 < 128)用1字节;长形式用多个字节表示长度。

这种结构使得解析器可以“盲解析”,即使不知道具体数据结构,也能根据TLV跳过未知字段,保证了很好的向前/向后兼容性。

PKCS# 标准系列:这些是建立在ASN.1/DER之上的具体应用。

PKCS#7/CMS:定义加密消息的语法(如数字信封)。

PKCS#8:定义私钥信息的语法。这就是PEM标签 BEGIN PRIVATE KEY 内部数据的结构。

PKCS#12(.pfx/.p12文件):一种同时打包私钥、证书链的格式,通常使用密码加密。它不使用PEM,而是自己的二进制格式。

X.509 证书的本质:一个X.509证书就是一个用ASN.1/DER定义和编码的数据结构,核心包含持有者信息、颁发者信息、有效期、公钥以及颁发者的数字签名。解析证书就是解析这个ASN.1结构并验证签名的过程。

3.学习思维导图

第二周

4.基于ai的学习

关于分组密码的CBC工作模式原理
我: 我正在学习分组密码的CBC工作模式,请你以苏格拉底的方式对我进行提问,一次一个问题。

AI: 很高兴与你进行这样的探索。让我们开始吧。首先,我们回顾一下,一个基本的分组密码(比如AES)在独立处理一个明文块时,它最主要的特性是什么?

我: 嗯,它会把一个固定长度的明文块(比如128位),通过一个密钥,加密成一个相同长度的密文块。关键是,如果密钥和明文块不变,加密结果总是相同的。

AI: 非常准确。那么,请你想一想,如果直接用这种方式加密一段很长的、包含重复模式的信息(比如一张纯色图片),会暴露出什么安全问题?

我: 啊,这就是ECB模式的问题!相同的明文块会生成相同的密文块,明文中重复的模式会在密文中原形毕露,这会泄露信息。

AI: 很好,你指出了核心缺陷。那么,为了消除这种模式泄露,让相同的明文块在不同位置加密出不同的密文,CBC模式最关键的思路是什么?

我: 我认为是引入“随机性”或者说“上下文”。不能让一个块的加密过程是孤立的,应该让它受到前面内容的影响。

AI: 精辟的概括。那么,CBC模式是通过什么具体的机制,让当前明文块的加密过程受到“前面内容”的影响呢?

我: 它使用了一个叫“初始化向量”的随机数作为起点,然后让每一个明文块在加密之前,先与前一个密文块进行异或操作。

AI: 完全正确。你能更详细地描述一下这个“链式”反应是如何一步步进行的吗?比如从第一个块开始。

我: 好的。首先,第一个明文块不会直接加密,而是先与一个随机的IV进行异或。然后,将异或后的结果用密钥加密,得到第一个密文块。接下来,第二个明文块会先与第一个密文块进行异或,然后再用密钥加密,得到第二个密文块。后续的每一个块都遵循这个规则:密文_n = 加密(密钥, 明文_n XOR 密文_n-1)。

AI: 非常清晰的描述!这个机制确实像一条链子,环环相扣。基于这个精妙的链式结构,我还有一个问题:在解密端,当收到密文和IV后,它是如何逆向操作,还原出明文的呢?

我: 解密过程其实很巧妙。它也是链式的,但顺序不同。对于第一个密文块,先用密钥解密它,得到一个中间值,然后将这个中间值与IV进行异或,就得到了第一个明文块。对于第二个密文块,同样是先解密,然后将解密后的结果与第一个密文块(注意,是前一个密文块本身,而不是解密后的内容)进行异或,得到第二个明文块。

AI: 非常好!你注意到了解密时的一个关键点:异或的对象是前一个密文块,而这个密文块是接收方已知的。这引出了最后一个问题:这个特性使得CBC模式的加密和解密过程在“并行性”上有什么不同?

我: 我明白了!因为加密时必须等待前一个块的密文出来,才能进行下一个块的异或和加密,所以加密过程是串行的,无法并行。但解密时,由于异或操作需要的是前一个密文块(这些数据已经全部收到了),所以每个密文块都可以先独立进行解密操作,最后再一起异或。因此,解密过程是可以并行化的。

AI: 完美的总结!通过这一问一答,你已经清晰地阐述了CBC模式如何通过链式反馈消除模式、加解密的详细步骤以及其并行特性。你对CBC原理的理解非常扎实。

关于Base64编码的规则与用途

我: 我想更深入地理解Base64编码,请你用苏格拉底的方式向我提问。

AI: 很高兴与你探讨这个话题。让我们从最根本的需求开始。在计算机中,数据最底层的表示形式是什么?

我: 是二进制,由0和1组成的比特流。

AI: 正确。那么,当我们需要在只设计用于传输文本数据的媒介(比如早期的电子邮件系统)上传输这些二进制数据时,你认为会遇到什么根本性的问题?

我: 嗯,二进制数据中可能包含很多不可打印的字符,比如控制字符。这些字符在文本传输系统中可能会被错误地解释,比如被当作传输结束的标志,从而导致数据损坏。

AI: 很好的洞察。那么,要解决这个问题,核心思路是什么?

我: 核心思路应该是找到一种方法,将二进制的数据“翻译”成一种只由安全、可打印的文本字符构成的形式。

AI: 正是如此。Base64就是这种思路的一种经典实现。现在请思考,如果我们想用文本字符来表示二进制数据,一个直接的挑战是什么?计算机是如何表示一个文本字符的?

我: 计算机用ASCII码这样的编码来表示字符,通常是一个字节(8位)代表一个字符。而二进制数据本身也是字节流。所以挑战在于,如何用一种“冗余”的方式,用更多的文本字符来忠实地表示原始的字节序列。

AI: 非常棒的切入点。既然一个字节是8位,而我们需要用64个特定的安全字符来充当“新字母表”,那么,每个这样的字符理论上能代表多少位二进制信息?

我: 2的6次方是64。所以,每个Base64字符可以精确地代表6位二进制信息。

AI: 逻辑严谨。那么,现在有一个关键的转换问题:原始的二进制数据是以8位为单位的,而我们的“新字母表”是以6位为单位的。如何解决这个单位不匹配的问题?

我: 这就需要将8位字节流重新分组为6位的单元。具体来说,就是取3个原始字节(一共24位),然后将这24位平均分成4个6位的单元。

AI: 非常准确!3×8 = 4×6 = 24。这个等式是Base64的核心。那么,接下来的步骤是什么?这4个6位的单元如何变成可打印字符?

我: 每个6位的单元都有一个值(0到63)。Base64编码定义了一个包含64个字符的索引表,比如0对应‘A’,1对应‘B’……63对应‘/’。我们只需根据每个6位单元的值,从表中找到对应的字符,按顺序排列起来就行了。

AI: 完美。但是,如果原始数据的字节数不是3的倍数,这个“3字节变4字符”的完美转换就无法完成。Base64是如何处理这个剩余问题的?

我: 这时就需要填充。如果最后只剩2个字节(16位),我们会补上1个字节的0,凑成18位,然后分成3个6位的单元。但因为这3个单元只对应2个原始字节,所以编码输出的4个字符中,最后1个字符要用特殊的‘=’填充符来代替。同样,如果最后只剩1个字节,就补2个字节的0,凑成12位,分成2个6位单元,输出2个字符,最后2个字符都用‘=’填充。

AI: 非常清晰的解释!现在,让我们思考它的用途。基于我们讨论的这些规则,你认为Base64编码的主要优点和代价分别是什么?

我: 主要优点是它可靠地将任何二进制数据转换为纯文本,确保了在文本系统间传输的兼容性和安全性。代价是它增加了数据的大小,因为每3个字节会变成4个字符,数据体积会增加大约33%。

AI: 非常精彩的总结!通过这一系列的问答,你已经从问题根源、核心原理、具体规则到优缺点,完整地推导并阐述了Base64编码。你对它的理解已经非常深刻了

5.学习实践过程遇到的问题与解决方式

问题一:Base64编码末尾的填充符 = 到底是不是必须的?在实际应用中我们有时看到没有 = 的Base64字符串,这是怎么回事?

回答:为什么需要填充? 解码器的工作原理是“4个Base64字符解码为3个字节”。如果收到的字符串长度不是4的倍数,解码器就无法确定原始数据在末尾补了多少个0字节。填充符 = 就像一个占位符,明确告诉解码器:“这个位置是空的,对应的原始数据不存在”。例如:

TWFu (长度4) -> 解码为3字节。

TWFuTQ== (长度8,但最后两个是 =) -> 解码器知道最后两个字符是填充,所以只解码前6个字符,得到 TWFuTQ 对应的4个字节,然后忽略最后填充所代表的0字节,最终得到原始的第4和第5个字节。

为什么有时看不到 = ? 你观察得很对!在许多现代Web应用(如URL、数据URL、JWT令牌)中,填充符 = 经常被省略。这主要是出于美观或避免冲突的考虑(因为 = 在URL中有特殊含义)。这是一种“变种”,通常被称为 “URL-Safe Base64” 或 “无填充Base64”。

没有 = 如何解码? 在这种情况下,解码器不能依赖“长度是4的倍数”这个规则。它需要先计算解码后的字节数,公式为:floor(encoded_string_length * 6 / 8)。然后直接处理编码字符串,忽略末尾可能不完整的部分。大多数现代解码库都具备处理有无填充两种格式的能力。

结论: 填充符在标准定义中是必须的,确保了编码的规范性和解码的可靠性。但在特定的应用场景中,为了便利性,可以省略填充符,但这要求编解码双方有共同的约定。

问题二:既然Base64编码会让数据体积增大大约33%,那为什么我们在网络上传输图片等二进制数据时,还经常把它转换成Base64格式嵌入(比如在CSS或HTML中)?这不是反而降低效率了吗?

回答:是的,这会降低传输效率。我们之所以这么做,是用牺牲传输效率来换取部署和管理的简化,在某些场景下,后者的收益大于前者的成本。

详细解释:
这种技术被称为 “数据URL”(Data URL),格式类似:data:image/png;base64,<Base64编码数据>。

这样做的主要优点有:

减少HTTP请求次数:这是最重要的原因。在Web页面中,每加载一个外部资源(如图片、字体),浏览器都需要向服务器发起一次新的HTTP请求。而HTTP请求的建立、等待响应是有开销的。如果页面有几十个小图标,就会产生几十次请求,可能成为性能瓶颈。将小图片转换成Base64嵌入HTML或CSS文件中,意味着浏览器只需要加载一个主文件(HTML或CSS)就获得了所有资源,极大地减少了HTTP请求数。对于大量微小资源,这个好处可以抵消数据体积增大带来的负面影响。

简化部署和缓存:所有资源都包含在一个文件里,部署时无需担心图片路径错误、缺失等问题。缓存策略也只需针对一个文件设置,更简单。

实现自包含:生成的HTML/CSS文件是一个完整的、独立的单元,可以离线查看或轻松地复制粘贴到别处,而不会丢失图片等资源。

那么,什么时候不应该使用呢?

大文件:对于大图片或文件,体积增大的33%会变成巨大的开销,远超过减少HTTP请求带来的收益。

无法利用浏览器缓存:外部资源被浏览器缓存后,再次访问或跳转页面时无需重复下载。而嵌入的Base64数据每次都会随着HTML/CSS文件一起加载,无法被独立缓存。

结论: 这是一个典型的“空间换时间”或“传输效率换管理便利”的权衡。最佳实践是:仅对非常小的(通常小于10KB)、需要频繁使用的、不经常改变的静态资源(如1x1像素的占位图、小图标SVG)使用Base64嵌入。对于大尺寸图片,始终使用外部链接方式。

posted @ 2025-09-21 22:36  田泽航  阅读(11)  评论(0)    收藏  举报