ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

公钥密码学:RSA、ECC与TLS场景落地全解析

公钥密码学:RSA、ECC与TLS场景落地全解析 最近好几个同事不约而同来问我公钥密码学PKCPublic Key Cryptography的问题有人搞不清私钥签名和公钥加密的区别有人被RSA和ECC的密钥长度绕晕还有人明明拿到了证书却不知道TLS握手到底在磨叽什么。这些问题我这些年都遇到过也踩过不少坑。这篇博文就是把我脑内关于公钥密码学的知识重新梳理一遍把概念讲清楚把算法讲明白把场景讲落地力争让不同基础的读者都能从中拿走点东西。这篇文章适合这么几类人写接口、做鉴权、管服务器的工程师想搞懂HTTPS小锁图标背后原理的前端和产品同学以及所有被“公钥”“私钥”“数字签名”这些词折磨过的人。我不会堆论文公式但会把关键参数和推导逻辑说透不会只谈理论还会给出能直接复制的命令和配置。先说结论公钥密码学本质上解决的是“两个互不信任、也没有提前见过面的人怎么在不安全的信道上安全地通信”这个问题。它靠的是数学上的不对称性——加密容易、解密难但持有特定秘密信息时又可以轻松解密。顺着这条线往下走你会发现所有看似复杂的算法和协议其实都是围绕这个核心在展开。1. PKC到底解决了什么问题从对称加密的困境说起1.1 对称加密的硬伤密钥怎么送过去在接触公钥密码学之前人类使用的加密方式几乎都是对称加密。对称加密的特点是加密用一把钥匙解密也用同一把钥匙。比如AES速度快、硬件支持好、安全性经过全球密码学家的反复验证到今天依然是数据加密的主力。但对称加密有个绕不开的难题既然通信双方要使用同一把钥匙那这把钥匙该如何安全地传给对方想象一个场景你要给异地的同事发一份加密合同用AES加密后对方确实解不开。于是你把密钥也通过邮件发过去。可是邮件内容可能被截获如果加密文件和密钥走同一条不安全信道那截获者连文件带钥匙一起拿到加密形同虚设。这被称为“密钥分发难题”。在多人场景下更严重n个人组成的小组每两人之间都要有一对独立的会话密钥总密钥数量是n(n-1)/2人员越多密钥管理越失控。你可能想到建立中心密钥服务器但服务器本身成为了最脆弱的目标一旦被攻破全局通信都暴露。1.2 非对称加密的核心直觉把“锁”和“钥匙”拆开公钥密码学打破了这个僵局。它的核心思想是不要用同一把钥匙干所有事而是把“加密能力”和“解密能力”分离。你可以把公钥理解成一个公开的挂锁任何人都能拿到把私钥理解成唯一能开这把锁的钥匙只有你自己保管。别人想给你发消息就用你的公钥把消息“锁”进一个箱子箱子寄给你后只有你能用私钥打开。即使挂锁和箱子都被截获没有私钥的人也无法开箱。这样整个通信过程不再需要提前分享秘密。接收方把“锁”公开发出去发送方用“锁”加密接收方用“钥匙”解密。1976年Diffie和Hellman在论文《New Directions in Cryptography》中首次提出了这个构想1978年RSA算法将其真正落地自此打开了现代密码学的大门。1.3 别搞混公钥加密和数字签名是两码事刚接触PKC的人几乎都会混淆两个方向公钥加密私钥解密以及私钥签名公钥验签。这其实是公钥密码学的两种互补用途加密公钥加密、私钥解密目的是保密性。谁都能给你发密文只有你能看。签名私钥签名、公钥验签目的是完整性和身份认证。只有你能产生有效签名谁都能验证这确实出自你手。可以这样记加密是“锁箱子的锁”签名是“防篡改的印章”。锁和印章完全是两种工具虽然都基于同一对密钥对但使用方向相反。后面讲TLS和GPG时你会看到实际协议里这两种用途经常同时出现。2. 公钥密码学的数学地基单向函数与陷门2.1 什么叫“单向函数”容易下去、难上来的山坡公钥密码学建立在“单向函数”之上。单向函数是指正向计算极其容易逆向计算却极其困难。最经典的例子是大整数乘法。给你两个大素数p和q把它们乘起来得到npq用计算机一眨眼的功夫就能完成。但反过来给你一个n让你找出是哪两个素数相乘难度会随位数指数级上升。到今天人类能分解的最大“RSA挑战数”也只有约829位RSA-2502020年分解成功而这个分解动用了数百台高性能机器和数月时间。但仅仅有单向函数还不够。如果只有单向函数连合法的接收方也无法逆推密文那加密就没有意义了。所以公钥密码学还需要一个关键部件陷门。陷门就像单向山坡上一部只有特定人知道位置的秘密电梯正常人只能从坡顶滑到底拥有陷门的人却可以瞬间回到坡顶。在RSA里这个陷门就是私钥指数d或者等价地n的因数分解。在ECC里陷门就是私钥标量。2.2 三大困难问题现代密码学的“信任基石”不同公钥密码算法依赖的是不同的数学困难问题。目前主流的有三个大整数分解问题IFP给定npq分解出p和q。RSA依赖它。离散对数问题DLP给定素数p、生成元g和g^x mod p反推x。Diffie-Hellman密钥交换和DSA签名依赖它。椭圆曲线离散对数问题ECDLP椭圆曲线上的点构成一个群已知基点G和倍数点dG反推d。ECC依赖它。需要注意ECDLP目前没有已知的亚指数级经典攻击算法所以同样的安全强度下ECC可以使用远比RSA短的密钥。这也是为什么近些年大家都在从RSA向ECC迁移。多说一句量子计算Shor算法可以在理论上高效解决大整数分解和离散对数问题所以RSA和ECC在未来会受到量子计算机的威胁。这也是后量子密码学PQC升温的原因。不过目前实用化的量子计算机离威胁现有密钥长度还有很远的距离现阶段不必恐慌但新系统架构上最好留出算法替换的余地。2.3 安全强度对照别被“密钥越长越安全”带偏“安全强度”这个概念粗略理解就是破解所需计算量等价于破解多少位对称密钥。NIST SP 800-57给出了一套行业公认的对照关系对称加密参考RSA/DH/DSAECC椭圆曲线等价安全强度说明AES-128RSA-3072P-256secp256r1约128位安全强度当前主流默认AES-192RSA-7680P-384约192位安全强度高保密等级AES-256RSA-15360P-521约256位安全强度极端场景表格看起来可能让人惊讶RSA要3072位才能对标AES-128而ECC只需要256位。原因就是ECDLP比大整数分解和一般离散对数更难啃。所以新项目优先选ECC系算法兼容老系统时才不得不保留RSA。至于网上还能看到的RSA-1024早在2010年前后就被证明可以实用化分解现在必须视为不安全。3. 核心算法拆解RSA、Diffie-Hellman、ECC与签名家族3.1 RSA从密钥生成到加解密RSA是目前寿命最长、知名度最高的公钥密码算法。它的密钥生成步骤如下随机选两个不相等的大素数p和q位数足够当前建议各至少1024位即RSA总长2048位以上。计算n p×qn的位数就是RSA密钥长度。计算欧拉函数φ(n) (p-1)×(q-1)。选择一个与φ(n)互质的公钥指数e通常直接取655370x10001。计算私钥指数d满足d ≡ e⁻¹ mod φ(n)即e×d mod φ(n) 1。公钥是(n, e)私钥是(n, d)。加密时计算c m^e mod n解密时计算m c^d mod n。乘法、取模、指数运算就这么简单。为什么公钥指数选65537因为它的二进制是10000000000000001只有两个1做模幂运算时计算量极小而且它是个费马素数和很多φ(n)都互质久经考验几乎成为行业默认。但教科书式的裸RSA直接对原始消息做模幂不能直接用于生产环境原因有两个其一消息m必须小于n没法加密任意长度的数据其二裸RSA有很强的代数结构攻击者可以通过已知明文、选择密文等方式展开攻击。所以实际使用RSA时必须先对消息做填充常用的有RSA-OAEP推荐和PKCS#1 v1.5兼容场景。补充一下处理超长消息的正确姿势是“混合加密”用RSA加密随机会话密钥再用AES加密真正的数据这也是TLS干的事。3.2 Diffie-Hellman不传秘密也能商量出共享秘密Diffie-HellmanDH解决的是另一个问题两个人完全不事先约定能不能在公开信道里各自算出同一个共享秘密而且旁观者算不出来DH协议的简化流程是这样的双方公开约定一个大素数p和一个模p的原根g这些可以公开。Alice 生成随机数a计算A g^a mod p把A发给Bob。Bob 生成随机数b计算B g^b mod p把B发给Alice。Alice 收到B后计算共享密钥s B^a mod p。Bob 收到A后计算共享密钥s A^b mod p。因为(g^b)^a和(g^a)^b都等于g^(ab) mod p所以双方得到一致的s。旁观者只看到p、g、A、B想反推a或b就要解离散对数问题这在参数足够大时是做不到的。但DH有个致命前提它不验证对方的身份。中间的窃听者完全可以同时冒充Alice和Bob分别与双方建立共享密钥这就是“中间人攻击”。解决思路是给DH参数加上认证用数字签名签名自己的DH公钥或者将DH公钥嵌入到CA签发的证书里。现代TLS大量使用ECDHE那个E就是Ephemeral临时意思是每次握手都临时生成DH密钥对用完后即弃。这样即使服务器的长期私钥哪天泄露历史会话也无法被回溯解密这就是前向保密。TLS 1.3直接把静态RSA密钥交换移除了只保留ECDHE等具备前向保密的协商方式原因就在这里。3.3 ECC与Ed25519短密钥、高性能的现代选择椭圆曲线密码学ECC是当前最性感的公钥密码方向。椭圆曲线的方程形如y² x³ ax b mod p曲线上的点之间定义了一种“加法”运算连续做k次加法就相当于“倍点”运算kG。已知基点G和倍数点Q dG反推d就是ECDLP困难问题。这里不展开具体的点运算和群论推导你只需要记住一个直观画面在椭圆曲线上从一个点“翻折相加”得到下一个点反复操作几百次毫无压力但让你从最后那个点反推“到底加了多少次”就难如登天。这个不对称性正是ECC安全性的来源。实操中你会遇到两类曲线的选择NIST P-256secp256r1浏览器和TLS生态兼容性最好大量HTTPS证书使用。虽然NIST曲线参数来历有过一些讨论争议但主流信任度依然很高。Curve25519 / Ed25519这是Daniel J. Bernstein设计的现代曲线在协议设计上明显更干净常数时间实现更容易几乎没有历史上那些边角陷阱。X25519用于密钥交换Ed25519用于数字签名是目前新系统里我最推荐的一对。Ed25519签名有个特别好的设计签名过程是确定性的私钥直接参与生成随机标量不需要额外随机数源。这意味着它天生免疫一类“随机数重用”导致私钥泄露的灾难性问题这一点后面我还会再细讲。3.4 数字签名算法DSA、ECDSA与EdDSA数字签名解决的是“你怎么证明一条消息确实是你发出的而且没有被篡改”。通用流程是签名方对消息计算哈希比如SHA-256用私钥对哈希值做签名运算输出签名。验证方用公钥对签名做验证运算同时自己计算消息哈希比对结果。DSA是纯离散对数方案ECDSA是在椭圆曲线上实现的DSA变体二者都需要签名方提供一个随机数k。这个k一旦重用私钥就能被解出来后面我会讲这类实际事故。EdDSAEd25519则是完全不同的签名框架签名确定性、速度快、实现安全性高是我给所有新项目的默认签名方案。签名为什么非要先做哈希直接对消息签名不行吗可以但代价很大消息长了签名运算就慢而且不同长度消息会导致签名长度不固定、结构复杂。哈希把任意长度消息压缩成固定长度摘要再用摘要做签名既快又规范。这里的隐忧是哈希算法本身的安全强度必须匹配签名算法比如还在使用的SHA-1只有约80位安全强度用来配合ECC-256签名就是明显短板所以现在一律要求SHA-2或SHA-3族。4. 场景落地从TLS握手机制到日常运维命令4.1 HTTPS里面到底发生了什么TLS握手全流程拆解HTTPS之所以安全表面上是那个小锁图标底层是TLS协议在每次会话开始前做了一系列PKC操作。以TLS 1.2的完整握手为例简化流程如下ClientHello和ServerHello客户端和服务器协商协议版本、加密套件CipherSuite、随机数等。服务器发送数字证书链证书中包含服务器的公钥以及CA对证书的签名。客户端验证证书链证书是否由可信CA签发、域名是否匹配、有效期是否正常、是否被吊销。密钥交换现代套件中双方进行ECDHE握手临时生成ECC密钥对协商出一个只有双方知道的会话密钥。客户端发送Finished消息此后所有应用数据都用这个会话密钥配合AES等对称加密算法传输。这个流程最容易被忽略的一点是非对称加密只出现在第2步到第4步的“握手”阶段真正的网页数据传输阶段全部是AES这类对称加密。原因很朴素RSA-2048的模幂运算比AES-128慢好几个数量级拿它加密整条HTTP请求体性能和CPU都扛不住。所以PKC的角色是“安全地分发一把临时钥匙”对称加密负责“高效地使用这把钥匙”。实际排查HTTPS问题时我常用的命令是openssl s_client -connect example.com:443 -showcerts -servername example.com这条命令会输出服务器实际发送的证书链、套件协商结果和握手信息。如果证书链不完整输出里会明显缺中间证书且客户端报错。4.2 SSH免密登录密钥对的实际用法SSH是PKC在运维场景里最日常的应用。生成密钥对的命令非常简单ssh-keygen -t ed25519 -C your_emailexample.com如果是需要兼容很老的路由器、交换机等设备再考虑用RSAssh-keygen -t rsa -b 4096 -C your_emailexample.com然后把公钥放到服务器上。最简单的方式是用自带的工具ssh-copy-id userserver_ip它会自动把本机的公钥追加到服务器~/.ssh/authorized_keys文件里。手动方式也行核心步骤就是把公钥内容粘贴到authorized_keys中然后设置权限chmod 700 ~/.ssh chmod 600 ~/.ssh/authorized_keys权限这步必须做。SSHD对密钥相关文件的权限极其敏感如果authorized_keys或用户目录权限过宽服务端会直接拒绝使用密钥认证并回退到密码登录甚至拒绝连接。服务器端配置里确认以下两项PubkeyAuthentication yes PasswordAuthentication no关掉密码登录后SSH的安全等级会大幅提升暴力破解基本可以直接免疫。我给自己的服务器就是这么配置的密钥用passphrase保护私钥文件即使被拷走没有口令也白搭这个习惯建议每个人都养成。4.3 GPG签名与文件加密不只网站需要非对称密码HTTPS和SSH之外GPGGNU Privacy Guard是普通人也能直接上手的PKC工具。它的典型用途包括软件发布方给校验和文件做签名、个人之间加密传输文件、Git提交签名、邮件加密。生成密钥对gpg --full-generate-key按提示选择RSA 3072位或ECC/Ed25519设置有效期和身份信息。导出公钥给合作方gpg --export --armor --output public.asc加密文件给特定接收方gpg --encrypt --recipient someoneexample.com -o secret.gpg secret.pdf接收方解密gpg --decrypt secret.gpg secret.pdf给文件签名和验证签名gpg --detach-sign --armor -o checksums.txt.asc checksums.txt gpg --verify checksums.txt.asc checksums.txt这里最容易被忽视的是“吊销证书”。GPG密钥一旦泄露或丢失你需要在公钥服务器上宣告撤销否则别人会一直信任一把可能已落入他人之手的公钥。生成密钥时就建议顺手做一份吊销证书并离线保存这和给证书配置自动续期一样属于“平时不显眼、出事救命”的操作。4.4 密钥生命周期生成、存储、撤销与轮换密钥不是生成一次就能躺平不管。一个完整的生命周期管理至少包含四件事生成优先Ed25519/ECDSARSA用于兼容。随机数来源必须可靠服务器生成密钥时尽量用硬件熵源。存储私钥的存储等级决定风险等级。个人用本地文件加权限加passphrase足够团队或公司用建议上HSM或云KMS把私钥从普通服务器文件系统里隔离出来。撤销Web证书依赖CA的CRL/OCSP机制密钥泄露后立刻吊销。GPG密钥则需要生成吊销证书并发布。轮换Lets Encrypt证书默认90天有效期配合acme.sh或certbot可以全自动续期实际上强制了高频轮换。企业证书建议不超过一年同时要保留旧密钥的解密能力一段时间防止突然轮换导致存量数据无法读取。个人经验主密钥尽量留在离线机器或硬件令牌上服务器上只放用于日常签名的子密钥。YubiKey这类硬件令牌可以在签名时要求物理触碰私钥永远不落盘这套方案我用下来觉得是性价比最高的安全提升。5. 容易踩的坑与排查技巧实录5.1 私钥保管的“三个不要”我处理过的安全事故里有相当一部分不是算法被攻破而是私钥保管出了低级问题。三条铁律不要明文存储私钥文件至少用passphrase加密环境变量和配置文件里不出现私钥原文。不要放进代码仓库无论是git、SVN还是内部文档库私钥一旦进入版本历史就相当于公开了。就算你后来删掉文件历史commit里还有。不要发给任何人包括自称“技术支持”的人、配合排查问题的云厂商、甚至同事。正规服务商不会索要你的私钥。还有一个特别常见的低级错误想给别人发公钥结果手一滑发成了私钥。发布前用ssh-keygen -lf或openssl pkey -pubout检查一下文件头确保是PUBLIC KEY而不是PRIVATE KEY。5.2 随机数重用为什么是灾难ECDSA的k与私钥危机ECDSA签名时签名方会生成一个临时随机数k。如果同一个私钥对两条不同消息签名时用了同一个k那么攻击者只需要两个签名方程就能消掉未知数k直接解出私钥d。这不是理论问题行业里有过不止一次真实事故2010年索尼PlayStation 3的ECDSA签名私钥被公开提取根源就是签名随机数固定不变。区块链领域也出现过因钱包实现缺陷重用随机数导致私钥泄露、资产被转走的案例。应对手段很直接一是签名代码里永远不要自己“随手”生成随机数用RFC 6979规定的确定性k推导或者升级到Ed25519这种从设计上就确定性的签名算法二是用成熟的密码学库OpenSSL、libsodium、Bouncy Castle等不要自己实现签名算法。密码学里“不做自己发明创造”是一条救命原则。5.3 参数选择速查不同场景该用哪个算法场景推荐方案备注Web服务器证书ECDSA P-256 / RSA 3072TLS 1.3下优先ECDHE证书本身用ECC更省带宽SSHEd25519老设备再考虑RSA 4096文件加密与签名GPG默认RSA 3072或ECC不同GPG版本默认略有差异代码签名RSA 3072/4096或ECDSA P-256按目标系统和审计要求选内部高密级通信X25519密钥交换 Ed25519签名现代方案实现干净杜绝使用RSA 1024、SHA-1、DSA固定参数均已不满足当前安全要求这条表来自我日常配置服务器和签发证书的实践不是唯一答案但照着选基本不会出事。特别提醒不要让“已有系统用了RSA-1024”成为继续使用它的理由老系统的迁移成本远低于数据泄露的代价。5.4 现场排查实录一次HTTPS证书链报错的完整分析说一个我实际处理过的排障。某天安卓客户端突然访问接口报certificate_unknown而同一站点在PC浏览器里一切正常。排查路径如下第一步用openssl s_client -connect api.example.com:443 -showcerts -servername api.example.com查看真实证书链发现服务器只发送了叶子证书没有发送中间证书。第二步确认问题根源Nginx的ssl_certificate配置只指向了cert.pem没有使用包含完整链的fullchain.pem。PC浏览器往往能通过AI补全或本地缓存补齐中间证书但手机上的原生客户端没有这个能力校验直接失败。第三步修正配置ssl_certificate /etc/letsencrypt/live/example/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/example/privkey.pem;然后nginx -t systemctl reload nginx问题立刻消失。这类问题的排查要点是先分清是证书到期、证书链不完整、域名不匹配还是系统时间不对。检查系统时间就一个命令timedatectl status证书链校验依赖UTC时间服务器时钟漂移几分钟新证书就会被判定为“尚未生效”。把这些步骤走一遍绝大多数HTTPS访问异常都能定位。我自己这几年总结下来的检查清单其实就几条把公钥想象成地址把私钥想象成钥匙任何让你交出私钥的操作直接拒绝新系统优先选Ed25519/X25519这对现代曲线老系统尽快从RSA-1024和SHA-1里迁移出来服务器证书一定要开自动续期并且每次都确认fullchain配置正确然后定期用openssl命令给线上站点做个体检。密码学最大的风险从不是数学不够难而是人在使用细节上的疏忽把流程变得简单可靠比什么都管用。
返回列表