软件授权系统实现基础探讨

2025 年 01 月 20 日

基本目标

一个软件授权系统包含很多内容,但归结到其本质,就是要回答这个问题:

是否允许受保护的软件运行?

应用类型

很明显,由于受保护的软件类型不同,控制手段自然不同。简化一下,大概是以下几种类型:

  • Web 应用:这类软件是在服务端以及浏览器中运行的,其可用性完全受控于服务端的实现。举例来说,服务端可以通过用户名密码确认用户身份,用 Session 验证和 JWT 等方式确认在软件运行过程中确认用户权限。因此这类软件的授权系统是集成于服务端内部的。不在本文讨论之列。
  • 客户端应用:这里指的是需要联网才能正常使用的软件。这类软件与 Web 应用实际很相似。软件运行过程中,如果需要确认下一步动作是否可以继续执行,那么在执行之前可以与服务器通信,确认后再继续执行。因此这类软件的授权系统的核心部分也是在服务端内部。但要注意客户端的联网请求部分需要加以保护,避免被轻易破解以跳过某些确认请求。
  • 离线应用:即无需联网就能正常使用的软件。这类软件需要通过某种方式在本地环境中确认是否可以执行。这类软件应该是软件授权系统服务的主要目标。

再换个角度表述,把授权想象成给软件加锁的过程。

  • 对于 Web 应用,代码都在服务端(包括纯前端代码),因此锁完全受服务端控制,授权系统是跟服务端深度整合的,无法独立。
  • 对于客户端应用,服务端提供的是一个开锁接口,客户端从这个接口拿到必须的数据后,进行下一步动作。但是:
    • 相对于离线应用的方案,此方法实际非常僵化,与业务逻辑的关联也非常紧密。
    • 即使是客户端应用,也可以使用跟离线应用相同的方式进行保护。
  • 对于离线应用:
    • 服务端提供的是一个经过签名的锁。可以简单的认为,别人无法提供,也无法修改这样的锁。
    • 受保护的应用通过内置的专属钥匙,可以打开这个锁,并根据其内容,确认自己是否可以进行下一步动作。

技术基础

为了保证达到保护离线应用的目的,需要让服务端生成的签名锁(即一个经过签名的文件)不可篡改,或者说篡改后可以被识别。

这依赖于现代密码学中的非对称加密。

简单说一下非对称加密。密钥是一对,分为私钥和公钥。如同名称所暗示的,私钥不可公开,公钥可以公开。私钥持有者可以用私钥对一段数据的 hash 值进行加密,称为签名。公钥持有者可以验证签名,也就是可以确认数据是由私钥持有者提供,且未经篡改。

参考 https 原理,密钥可以使用信任链传递。因此,为了减少密钥的暴露机会,最终的签名可以由经过授权的模块使用自己的中间密钥完成,主系统只需要保留一个根密钥不被外部访问,并使用根密钥为中间密钥签名即可。这样即使中间密钥泄露,还可以通过黑名单禁用泄露的中间密钥进行一些补救。

整个系统的核心都在于对密钥的保护。因此也可以考虑为这部分增加复杂度,比如使用独立加密硬件保存密钥,或使用 KMS 服务。

受保护的数据格式可以通过抽象得到一个通用格式,因此对数据的校验和解析,这部分可以独立出来。

校验之后的行为属于业务逻辑,是无法固化的。因此,受保护的应用必然需要二次开发。换言之,考虑业务逻辑,软件授权系统通常无法做成 turnkey 方案。

受保护的应用,需要将公钥集成在自己的应用中,这样就可以保证拿到的数据一定是来自于私钥持有者(通常也就是自己)。为防止简单的二进制搜索,保存的公钥需要考虑混淆或加密处理。为了简化开发,受保护应用需要集成授权控制系统所提供的客户端库。这个库不应该作为动态库提供,以避免攻击者可以轻松替换这个库来完成攻击。这里只能提供静态库。

授权系统

按照上面的做法,一个授权系统最少需要包含:

  • 提供授权的模块(至少二选一):
    • 授权服务器,可以进行签名文件的构建和分发。
    • 授权应用,可以进行签名文件的构建。
  • 申请和检验授权的模块(可以合并):
    • 包含检验授权相关函数的静态库。
    • 包含申请授权相关函数的静态库。
  • 密钥(需要考虑对 dev/staging/release 版本进行分级处理)。
  • 其他的辅助调试工具。

以及需要二次开发的受保护应用(在授权系统范围以外)。

总结

以上是关于一个软件授权系统的一些思考。我正在尝试实现一个尽可能通用的软件授权系统,如果有兴趣或需求,可以进一步沟通交流。

Top