首页 / 资讯中心 / 文章详情

公钥私钥、数字签名与数字证书:核心原理和工程实践

公钥私钥、数字签名与数字证书:核心原理和工程实践 ★ FEATURED ARTICLE
简介围绕公钥、私钥、数字签名与数字证书展开的密码学总结文档面向需要快速建立密码学知识框架的开发者与安全初学者帮助厘清单钥与双钥体系的差异、密钥用途和信任链问题。包内为1个docx文件约310KB篇幅紧凑适合通读后作为概念速查与复习提纲。已有627人学习。内容从单钥加密的密钥保存风险讲起延伸到双钥加密中公钥与私钥的一一对应、由公钥难以推导私钥等特性并提及DES与RSA两类典型算法随后结合Hash摘要、私钥加密摘要、公钥验签说明数字签名的生成与验证流程再通过证书中心对公钥及相关信息的签名解释数字证书如何防止公钥伪造最后落到HTTPS中服务器私钥加密、客户端校验证书并交换加密信息的应用场景。文中还借助Bob、Susan与Doug的通信案例串联签名、篡改校验和中间人风险便于把抽象概念对应到具体流程。1. 从一把钥匙到两把钥匙公钥和私钥到底在解决什么麻烦想象你家门口有个投信口谁都能往里塞纸条但只有拿着钥匙的人才能把信箱打开取信。这个“谁都能投、只有你能取”的结构是理解公钥和私钥最省力的入口。密码学里的公钥、私钥、数字签名、数字证书说到底都在回答同一件事在一个谁都能发消息、谁都能冒充别人的环境里怎么确认某条消息确实出自某人而且中途一个字都没被改过。这事听着抽象其实天天发生——浏览器地址栏那把锁、软件更新包旁边的校验值、手机上的支付确认背后跑的都是这几样东西。分不清谁管保密、谁管身份看技术文档时就只能靠玄学记忆。先从更老的问题讲起为什么只用一把钥匙的加密方式用着用着就不够用了。2. 单钥加密一把钥匙的老办法卡在哪一步人类最早的加密思路朴素得可爱把信息搅乱让外人看不懂收信人再用相反的手法搅回来。加密和解密共用同一套规则、同一把钥匙这就是单钥加密也叫对称加密。它统治了密码学几千年从手写的替换表到机械转轮思路一脉相承。问题不在于它不够聪明而在于它有一个绕不过去的物理约束钥匙得先送到对方手里。2.1 加密和解密共用一把钥匙到底怎么运作最直观的例子是字母位移把每个字母往后挪三位A 变成 DB 变成 E。收信人往前挪三位就还原了。“挪三位”这个规则就是钥匙谁拿到它谁就能加密也能解密。用代码看结构会更清楚。下面这段用异或做了一个最小演示重点不在强度而在“同一把钥匙干两件事”这个特征# 单钥加密的最小演示同一个 key 既负责加密也负责解密 def encrypt(plain, key): return .join(chr(ord(ch) ^ key) for ch in plain) def decrypt(cipher, key): return .join(chr(ord(ch) ^ key) for ch in cipher) key 0b1011 # 一把钥匙收发双方必须持有完全相同的一份 msg hello c encrypt(msg, key) print(c, decrypt(c, key)) # 输出密文再把 hello 还原出来这里的关键参数只有一个key收发双方必须完全一致。异或有个数学性质同一个数作用两次会回到原值所以加密函数和解密函数可以写成同一段逻辑。这恰好把单钥加密的优缺点同时暴露了——实现简单、速度快但钥匙一旦落到第三方手里对方既能解密你发出去的东西也能伪造一条用同样钥匙加密的消息你根本分不出来。2.2 真正难的不是算法是把钥匙安全送到对方手里假设你要和十个人分别通信而且每段对话的内容都不该被其他人看到。你跟每个人用的钥匙必须不同否则任何一个人都能读别人的信。于是十个人两两之间都要有一把独立钥匙总数是 45 把。人数再涨数字会难看得让人不想管通信人数两两配对需要的密钥数51010455012251004950增长是平方级的。这还只是保管成本更麻烦的是“第一次怎么给”。你要么当面交接要么邮寄要么在电话里念一遍——而这几种通道都可能被监听。也就是说单钥加密把全部安全性押在了一件事上钥匙在传递过程中绝对没被人看过。这个前提在熟人小圈子里勉强成立一旦放到开放网络里就成了先有鸡还是先有蛋的死循环。在实际工程里密钥管理往往比算法本身更容易出问题。算法是公开的、经过反复检验的反而是钥匙的生成、分发、轮换、销毁这一整套流程容易被图省事的人做成黑匣子等到出事才发现谁也说不清钥匙到底流经过几台机器。2.3 从转轮密码机到 DES单钥加密的高光与天花板上世纪前半叶机械转轮式加密机把单钥加密推到了巅峰。它的密钥不是一串字符而是转子的初始排列和接线方式每天换一次设置等于每天分发一次密钥。这套流程在当年极其有效因为分发靠的是专门的信使和纸质密码本物理隔离本身就是一道防线。到了 1970 年代DES 成为通行的单钥加密标准密钥 56 位、分组 64 位。在那个年代的算力条件下这个长度相当扎实。但几十年后穷举 2 的 56 次方对成规模的机器来说已经不算什么难事于是有了更长密钥的变体和后来新的分组算法。不过要看清一点这些改进解决的是“算法被暴力破解”的问题没有解决“钥匙怎么送出去”的问题。只要钥匙还得靠一条可能被监听的通道传递单钥加密的天花板就一直存在。2.4 单钥加密没有被淘汰它换了个位置继续当主力很多人学完双钥体系会产生一个错觉单钥加密过时了公钥私钥才是现代方案。事实正好相反。今天你打开一个加密网页真正用来加密网页正文的仍然是对称加密因为它快快几个数量级。公钥体系做的事情是“协商出那把临时的对称钥匙”协商完成之后双方的通信就交给对称加密去跑。这种搭配叫混合加密用慢但不怕被听的公钥机制去解决“钥匙怎么安全产生”的问题用快但需要共享钥匙的对称机制去解决“大量数据怎么加密”的问题。两条腿走路各干各擅长的事。理解了这层分工后面看到“公钥用来加密信息、私钥用来数字签名”这句话才不会误以为单钥加密已经退场。3. 双钥加密公钥和私钥凭什么能配对双钥加密要解决的问题很具体能不能让通信双方在不事先共享秘密的前提下安全地把信息传过去答案是把一把钥匙拆成两把一把公开、一把私藏并且让这两把钥匙在数学上绑定在用途上分工。听起来像变魔术但它的规则其实只有四条把这四条吃透后面所有应用都是它们的组合。3.1 四条规则配对、唯一、互开、推不出来第一公钥和私钥一一对应。有一把公钥就必然存在唯一一把与它配对的私钥反过来也一样。它们不是随便两个数而是在生成时一起算出来的。第二每一对公钥私钥都和其他对所有不同。没有两对钥匙会撞车这保证了“用某人的公钥能验证的签名只能来自那把对应的私钥”。第三用公钥可以解开私钥处理过的信息反过来也成立。注意这句话描述的是数学上的可逆关系不代表这两种方向在实际使用中地位相同这点后面会专门讲。第四生成一对钥匙相对容易但从公钥反推私钥极其困难困难到在现实时间里可以认为不可能。这条是整个体系的命根子前三条只是结构第四条才是安全性来源。一个常被用到的类比是挂锁你把一把可以随便复制的开口挂锁寄给朋友朋友把信放进盒子、扣上锁、寄回来全程他都不需要你的钥匙而只有你手里的那把能开。公钥就是那把可以到处发的锁私钥是唯一能开的钥匙。3.2 一个方向容易反方向难到不可能第四条约等于在说存在一种运算正着算很轻松倒着算几乎不可能。这类函数被称作单向函数如果还额外存在一个“秘密捷径”能让反算变容易就叫单向陷门函数那个捷径就是私钥。最通俗的类比是调油漆。把两种颜料按某个比例混在一起几秒钟的事可如果只给你一桶混好的颜料让你精确还原出原来的配比基本没戏。而如果你事先知道配方验证起来又很容易。这里的“配方”就相当于陷门知道它的人能反推不知道的人只能干瞪眼。要提醒一句任何类比都只是帮助建立直觉不能当证明用。油漆混色是连续的物理过程而密码学里的单向函数建立在离散数学上两者的“难”不是一回事。真正让体系站得住脚的是经过长期公开检验的数学难题而不是某个巧妙的比喻。3.3 RSA 的玩具版e、d、n 三个数怎么配合RSA 是最常被提起的双钥算法1970 年代提出思路建立在“两个大素数相乘容易、把乘积拆回两个素数很难”这件事上。用一组小到不能再小的数字可以把三个核心参数的关系跑一遍# 玩具级 RSA只用来理解 e、d、n 的关系密钥小到几秒就能被拆开 p, q 61, 53 # 两个素数实际应用中会是几百位的大数 n p * q # 模数公开 phi (p - 1) * (q - 1) # 欧拉函数值只在生成密钥时私下计算 e 17 # 公钥指数需与 phi 互质 d pow(e, -1, phi) # 私钥指数e 关于 phi 的模逆 m 65 # 明文这里取单个字符的编码m 必须小于 n c pow(m, e, n) # 公钥加密只用 (e, n) print(c, pow(c, d, n)) # 私钥解密只用 (d, n)结果应还原为 65三个参数的职责很清楚n和e一起构成公钥可以公开d是私钥绝不能外传p、q和phi是生成过程中的中间量用完就该销毁因为知道phi就能反算出d。代码里用pow(e, -1, phi)求模逆这是 Python 内置的整数运算换成真实规模的大数同样成立。必须强调这段代码只能拿来理解关系。真实场景里n通常是 2048 位以上而且实际实现还要处理填充、编码、侧信道防护等一大堆细节手写一遍几乎必然翻车。密码学有一条血泪经验——不要自己发明加密实现用经过审计的库。3.4 两个方向都能算但只有一种用法有意义四条规则里说“公钥能解私钥处理过的信息反过来也成立”这句话容易被误读成“两种方向随便用”。实际上两个方向的意义完全不对等用收件人的公钥加密只有收件人的私钥能解这条路上公钥是公开的但解开需要私钥所以能保密。反过来用发件人的私钥处理任何人都能用发件人的公钥解开——既然谁都能解它就一点保密性都没有但它证明了一件事这段内容确实由持有那把私钥的人处理过。所以公钥体系的两种用法各司其职公钥负责加密信息私钥负责数字签名。前者解决“别人看不见”后者解决“别人赖不掉”。把这两件事记牢后面讨论证书和 HTTPS 时就不会乱。4. 数字签名怎么证明这条消息真的是你发的签名要解决两个问题消息是不是你发的以及消息有没有被人动过。这两件事必须同时成立才算数——只证明来源内容被改了一个数字照样出大事只证明内容没变却不知道是谁发的同样没有意义。数字签名用一套组合拳把两者绑在一起先给消息取一枚指纹再对指纹做只有你能做的运算。4.1 先给消息取指纹Hash 摘要Hash 函数把任意长度的输入压成固定长度的一串值通常叫摘要。它有四个性质值得记住长度固定不管原文是一句话还是一部电影输出都是同样位数同样的输入永远得到同样的输出改一个字符输出面目全非这叫雪崩效应从摘要反推原文不可行这叫单向性。此外还要求抗碰撞——很难找到两条不同的消息产生同一个摘要。import hashlib # 原文的摘要输出固定为 64 个十六进制字符对应 256 位 d1 hashlib.sha256(btransfer 100 to A).hexdigest() # 只把收款方改一个字符摘要立刻面目全非 d2 hashlib.sha256(btransfer 100 to B).hexdigest() print(d1) print(d2) # 两串值看不出任何相似性比对结果必然是“不一致”sha256指的是摘要算法hexdigest()把结果转成十六进制字符串方便展示和比对。注意摘要不是加密加密有钥匙、可以还原摘要没有钥匙、不能还原。你只能拿两份摘要对比判断原文是否一致永远无法从摘要里把原文抠出来。4.2 私钥签、公钥验签名流程走一遍设 A 有一对钥匙公钥早就给了朋友私钥只在自己手里。A 要给 B 发一封带签名的信A 写完正文后对正文做一次 Hash得到摘要。然后用私钥对这份摘要做运算结果就是签名。签名附在正文后面一起发出去。B 收到后先用 A 的公钥处理签名拿回摘要再自己对收到的正文做一次 Hash得到第二份摘要两份一比对一致就通过。这两步分别证明了不同的东西不能混着说。用公钥能解出东西说明这份签名确实由对应的私钥产生过——而私钥只有 A 有于是来源确定。两份摘要一致说明 B 收到的正文和 A 当时签的那份一字不差中途没人改过。任何一处对不上验证就该判失败。实际工程里验签失败的原因五花八门正文在传输中被追加了换行、编码从 UTF-8 变成 GBK、签名做了 Base64 编码又被多解了一次。这些都属于实现层面的坑不是算法不灵。调试时最有效的做法是先把“送进 Hash 的字节”打印出来逐字节比对而不是盯着算法怀疑人生。4.3 签名不是加密方向反了目的也不同初学者最容易把签名和加密当成同一件事的两种叫法。它们确实共用同一套数学关系但目的、方向、可见性全都相反维度公钥加密私钥签名解决的问题内容不被第三方看到来源可信、内容未被修改用谁的钥匙收件人的公钥发件人的私钥谁能打开只有收件人所有拿到发件人公钥的人内容是否隐藏是否正文通常是明文最要紧的一条是最后一行签名不提供保密性。你签过名的消息全世界都能读懂只是没人能冒充你重签一份。真要既保密又防伪做法是先签名再加密或者先加密再签名两层叠起来用而不是指望一层包办两件事。4.4 换掉公钥的人签名体系唯一的软肋设想第三方 C 想冒充 A 给 B 发消息。算法本身没有破绽他打不开 A 的私钥也伪造不出签名。但 C 可以换个思路他找机会把 B 手上那把“以为属于 A 的公钥”换成自己的公钥。B 并没有察觉在他看来A 的公钥还是那把公钥。于是 C 用自己的私钥做签名B 用那把被换过的公钥去验一路绿灯。所有数学运算都正确无误结论却完全错了——因为验证的前提“这把公钥属于 A”本身是假的。这个场景点出了密码学的边界数学只能保证钥匙和签名之间的对应关系保证不了“这把钥匙和人之间的绑定关系”。算法管不了身份认定身份认定得靠一套社会性机制那就是数字证书。4.5 数字证书与 HTTPS把公钥交给第三方担保证书的思路很直接找一个大家都信的第三方让它用自己的私钥对“A 的公钥 A 的身份信息”签个名。这份带签名的数据就是数字证书。任何人拿到证书用第三方的公钥去验签就能确认“这把公钥确实登记在 A 名下而且没被改过”。这个第三方就是证书颁发机构通常简称 CA。一张证书里大致装着这些内容主体是谁、主体的公钥、有效期、颁发者是谁、颁发者的签名。验签时浏览器会依次检查几件事——颁发者是不是在自己信任的名单里、证书里的域名跟你正在访问的域名是否一致、证书有没有过期。任何一项不对就会弹出警告。前两种警告刻意分开因为含义完全不同域名不一致意味着证书可能被冒用颁发者不可信意味着这条信任链根本立不住。HTTPS 的握手过程就是这套机制的实战。客户端发起请求服务器把自己的证书连同响应一起发回浏览器拿内置的受信任根证书列表去验证这张证书验证通过后浏览器用证书里的服务器公钥加密一段临时密钥发给服务器服务器用自己的私钥解开双方从此用这把临时密钥做对称加密跑完整个会话。注意最后一句真正加密网页内容的还是对称加密公钥体系只负责“安全地把钥匙递过去”。5. 易混概念辨析公钥、私钥、签名、证书最容易搞混的几处这几个词经常一起出现于是很多人把它们当成同一件事的不同说法。实际上它们分属不同层次有的是数学工具有的是使用方式有的是社会机制。下面五组混淆出现频率最高每一组都值得停下来掰开看。5.1 误区一公钥和私钥可以随便互换着用易混点在于规则里明确写了“公钥能解私钥处理的信息反之也成立”于是有人推论两把钥匙地位对等随便挑一把公开、另一把藏起来就行。之所以会混是因为这句话描述的是数学上的对称性而实际使用中还叠加了“哪一把公开”这个社会约定。一旦你把本该公开的那把藏起来、把本该藏起来的那把发出去整个安全模型就崩了。区分方法很简单看这把钥匙是不是公开的。公开的那把叫公钥只用于加密和验签藏起来的那把叫私钥只用于解密和签名。方向搞反的后果非常具体——用私钥加密等于把秘密广播出去因为所有人都能用公钥解开用公钥签名等于谁都能伪造因为公钥人人都有。5.2 误区二有了数字签名就不需要数字证书易混点在于签名已经能证明“这条消息确实由某把私钥的主人发出、且没被改过”看起来安全性已经闭环了证书像是多余的一层。之所以会混是因为签名验证里藏了一个没被说出口的前提你手上那把公钥确实是对方的。一旦公钥在传递过程中被调包签名验证会一路通过结论却完全错误。签名保证的是“钥匙与内容”的对应证书保证的是“钥匙与人”的对应两者管的不是同一段。区分时可以这样判断如果你和对方是当面交换公钥的理论上不需要证书只要公钥是从网络上拿到的就必须有一条可信链路来证明它的归属。这也是为什么浏览器对证书链的检查那么严格——它检查的不是算法而是那把公钥的来历。5.3 误区三数字证书就是加密过的文件等同于网站的身份证易混点在于证书确实是一段看起来像乱码的数据也确实由 CA 用私钥处理过很容易被理解成“加密后的凭证”。之所以会混是因为“证书”这个词在生活里指向身份证、资格证天然带着“证明身份”的联想而它在技术上的作用确实也包含身份信息只是机制完全不同。证书不是把身份信息藏起来恰恰相反它把身份信息和公钥明明白白写在里面只是加了一个 CA 的签名让这些内容无法被篡改。任何人拿到证书都能读出里面的域名和公钥保密性在这里根本不参与。区分方法是问一句这张证书是给谁看的答案是给所有人看的。它的价值在于“可公开验证”不在于“不可读”。把证书理解成一张盖了公章、内容全公开的登记表比理解成加密文件准确得多。5.4 误区四Hash 是一种加密易混点在于Hash 和加密都表现为“把明文变成一串看不懂的东西”输出看起来都是乱码日常交流里也常有人说“这个密码是加密存储的”实际上多数场景用的是 Hash。之所以会混是因为两者在输入端的行为相似在输出端的目的完全不同。加密是可逆的有钥匙就能还原Hash 是不可逆的没有“钥匙”这个概念谁也还原不出来。系统存你的登录口令时通常存的是 Hash 值验证时把你输入的口令再 Hash 一遍做比对——它根本不需要知道你原来的口令是什么。区分时抓一个特征如果系统能把你丢失的密码“找回来”并发给你那它多半用了可逆加密这本身就是个安全隐患正规做法是只提供“重置密码”因为它自己也不知道原密码。5.5 误区五地址栏有锁就说明这个网站可信易混点在于浏览器把 HTTPS 的视觉提示做得很像“安全认证”于是用户把“连接加密”直接理解成“网站靠谱”。之所以会混是因为证书确实校验了域名归属也确实保证了传输过程不被窃听和篡改这些都属于安全范畴。但它校验的只有一件事你连的这个域名其证书是合法的。至于这个网站是正经做生意还是搞诈骗证书体系一无所知也管不着。区分时要分清两层锁代表“这条通道是加密的、域名没被冒充”不代表“对面的人值得信任”。钓鱼站点同样可以申请到合法证书照样显示小锁。判断网站可信度还得看域名拼写、业务常识和来源渠道这部分没有任何密码学机制能替你完成。6. 什么时候该用、什么时候别用了解了机制落到判断上就三步这件事需要保密还是需要防伪钥匙该由谁持有公钥的归属由谁背书。三个问题答完方案基本就定了。需要保密、数据量大、双方已经共享了密钥用对称加密快且稳需要向不特定的人证明来源用私钥签名公钥要从开放网络传递就必须配证书链三者都不需要只是想让数据完整一点防止传输出错一个校验值就够了别上重装备。6.1 该用的场景跨网络传输敏感数据、需要向第三方证明“这份文件是我发布的”、软件更新包的完整性校验、接口调用方身份确认这些都是公钥体系的正当用武之地。判断标准是有没有一个“互不信任的对方”——只要通信双方不能假定对方天然可信对称加密的密钥分发就会卡住这时候公钥机制才有价值。反过来如果通信双方本来就在同一个受控环境里、密钥能通过配置中心统一下发硬套一套证书体系只会让运维复杂度陡增。6.2 不该用、容易翻车的地方最常见的误用是拿公钥体系去加密大批量数据。它的运算速度比对称加密慢几个数量级正确姿势是只用它协商或包裹一把临时对称密钥。第二常见的误用是自己实现签名验签填充方式、编码格式、时间比较这些细节里埋着大量坑用成熟的库能省下大把时间。第三是忽略有效期和吊销证书过期那天系统会集体罢工这类事故几乎每年都在不同团队重演。6.3 一段能照着跑的验证流程真要在命令行里确认一次签名下面几条命令覆盖了全流程# 1. 生成一对 2048 位密钥私钥留在本地公钥可以对外分发 openssl genrsa -out private.pem 2048 openssl rsa -in private.pem -pubout -out public.pem # 2. 用私钥对文件签名内部先做 SHA-256 摘要再对摘要做私钥运算 openssl dgst -sha256 -sign private.pem -out msg.sig msg.txt # 3. 对方用公钥验签摘要对得上才算通过 openssl dgst -sha256 -verify public.pem -signature msg.sig msg.txt三个参数值得留意-sha256指定摘要算法签名和验签两端必须一致换成别的算法就对不上-sign与-verify决定了用私钥还是公钥-signature后面跟的是签名文件而不是原文。验签输出Verified OK才是通过任何其他提示都应当按失败处理不要因为“看起来没报错”就放过。我第一次在生产环境排查签名问题时盯着算法看了整整半天最后发现是传输环节给文本文件补了一个换行符导致两边 Hash 的字节流根本不同。从那以后我每次接签名相关的活都强制先把待签名的字节流落盘、比对长度和十六进制头尾再谈算法。密码学里那些看似唬人的失败十有八九出在这种不起眼的地方。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站