去提问»
提问背景 Question:
原来,公钥和私钥是一对,一个负责生成,一个负责验证,分别生成和验证签名。那么签名不能伪造的话,这个签名中应该含有授权的信息并解密成明文吧,比如过期时间等信息。

理清授权迷思:数字签名与明文的关系

发布时间:2026-08-20 14:18 👁️ 阅读:1

你的理解已经非常接近核心了,但这里有一个关键的概念偏差需要厘清:

数字签名本身并不包含、也不需要被“解密”成明文。

在离线授权(License)的实际应用中,标准的做法是**“明文数据 + 数字签名”**成对提供。让我们用最通俗的逻辑来看看它们是如何协同工作的。


1. 离线授权书(License)的真实结构

一份合法的授权文件(比如 license.lic),在底层其实由两部分组成。我们通常会用 JSON 格式来表达:

{
  "data": {
    "userId": "USR-9527",
    "ip": "192.168.1.100",
    "version": "enterprise",
    "expireAt": "2026-12-31"
  },
  "signature": "MEQCID3Y5k8...[这是一串由私钥生成的超长签名字符串]..."
}
  • data(明文):这就是你说的授权信息(域名、IP、时间、版本)。它是完全公开、透明、可读的。
  • signature(数字签名):这是官方利用私钥,对上面整段 data 进行哈希并加密后生成的“防伪水印”。

2. 客户端(软件)在离线状态下如何校验?

当用户启动你编写的软件时,程序在后台只做三件事:

第一步:验证真伪(核心)

软件读取 license.lic,拿到 data(明文)signature(签名)。然后,软件调用内置的公钥去校验:

校验逻辑:这个 signature 是不是由“官方私钥”针对这个 data 签署的?

  • 通过:说明这份 data 确实是官方颁发的,且中途没有被任何人篡改过
  • 不通过:说明要么签名是假的,要么明文被篡改了,直接拒绝运行。

第二步:读取规则

一旦签名验证通过,软件就百分之百信任 data 里的内容。此时,程序在内存中读取:

  • “哦,有效期到 2026-12-31。”
  • “当前系统时间是 2025 年,没有过期,允许运行。”
  • “授权 IP 是 192.168.1.100,对比一下当前网卡 IP,一致,放行。”

3. 为什么用户无法伪造和篡改?

假设有一个爱钻空子的用户,他用记事本打开了 license.lic

  1. 他把过期时间 2026-12-31 改成了 2099-12-31
  2. 他保存文件并启动软件。
  3. 软件读取了修改后的 data,并用公钥去验证那个旧的 signature
  4. 结果:验证失败! 因为修改了哪怕一个字符,原来的签名就失效了。
  5. 如果他想自己生成一个能匹配 2099-12-31 的新签名,他就必须拥有官方的私钥。但他没有,所以他永远无法伪造出一个能通过公钥验证的签名。

4. 如果我不想让用户看到明文怎么办?

虽然用户“看得见改不了”,但有时你可能觉得明文里的 userIdip 暴露出来不太美观,或者不想让他们知道具体的授权细节。此时你可以采取辅助手段

  1. 最简单的混淆 (Base64)
    把整个 JSON 转化为 Base64 编码。这不属于加密,只是让普通人肉眼看不懂,程序读取时先解码一下。
  2. 对称加密 (AES)
    你可以用一个固定的密钥(硬编码在程序里)把 data 加密。程序启动时先用 AES 解密出明文,再用 RSA 公钥验签。但通常对于授权来说,防篡改(RSA签名)的意义远大于防看(加密)。

总结

  • 私钥:放在你家服务器上,负责给“授权数据”盖章(签名)
  • 公钥:编译在你的软件里,负责验章(验签)
  • 授权书:包含“谁都能看、但谁也别想改”的明文数据,以及证明这串数据是官方发放的签名。你的程序离线运行时,只要验证签名通过,就可以放心地读取明文数据来控制软件行为。