一种立即可用的软件离线许可解决方案
引言
背景
兄弟是做图片视频后期处理相关工作的,前段时间请我喝酒,谈到他面临的一个问题:他需要将一批图片提供给用户作为预览,但担心直接分享给用户会导致他们不仅不下单,还将这些图片转手卖出。他知道我是搞计算机的,问我有没有什么技术能够限制一下。
我花费了几个晚上和两个周末的时间,最终完成了一个解决方案。他说一旦将图片发送给用户后,再请我喝一次酒以表示感谢。
后来,我意识到他的需求可能是一个典型的问题,并且我的解决方案可能在许多类似场景中都适用,因此我决定将这个经验整理出来与大家分享。
目标与意义
这里我将对需求进行拆解,以便更好地讨论解决方案。
兄弟的需求其实可以用一句话概括:只有申请预览的用户能够查看图片内容。当时我兴致勃勃地认为这个需求似乎很容易实现,例如可以对图片进行加密处理。
但是在拆解后,发现要处理的环节其实非常多。考虑一个bad guy在拿到这些文件后可能想要将这些图片再次分发的场景。
尽管对图片进行加密保护可以防止图片泛滥到任何看图工具上查看,但这并不能实现我的目标。为了更好地实现图片和查看器的保护,需要采取更全面的措施。
在用户正常查看图片(E)之前需要经历几个时间点:用户获取图片和查看器的时间点(A),用户获取授权激活码的时间点(B),进行激活授权操作的时间点©,以及激活码失效的时间点(D)。如果不采取任何措施,用户在A点之后仍然可能再次分发图片。
基于上述思路,需要对图片文件的保护进行转变,将其变为对软件使用的授权保护。用户在获取图片和查看器后,需要使用作者提供的授权码进行授权,才能使用查看器。然而,授权码存在一个问题,就是用户仍然可以同时再次分发图片、查看器软件和授权码,导致授权失去控制。
这里需要确保授权码的失效机制,以防止授权码再次分发给其他用户后仍然有效。需要对授权进行管理,做到能够验证授权码的有效性并在授权码失效后及时停止对查看器的使用权限。
需要特别强调的是,由于成本限制,我无法选择搭建一个在线服务器来进行验证和授权功能。所有这些需求必须在没有服务器支持的情况下满足。
因此,为了实现这一立即可用的软件离线许可解决方案,我需要:
- 设计一个授权管理机制,以确保用户在A点之后需要获取授权激活码(B)来使用查看器。
- 确保授权管理机制能够验证授权码的有效性,并设置授权码的失效时间点(D),在授权码失效后,未授权查看器无法授权。
- 采用特定的加密和解密算法,确保图片在查看器中仅能被有效授权的用户查看,从而防止未经授权的再次分发。
到此,大的思路就确定了。
软件概述
用户查看图片过程如下:
用户侧
- 拿到看图应用程序和图片文件
- 用户线下获取激活文件
- 首次运行程序,弹出"要求激活"提示
- 用户根据提示选择激活文件,完成激活授权过程
应用授权是一次性的,一旦激活完成,用户可以直接使用应用程序。
发布者侧
在这里,发布者是我的兄弟,我为他提供了一个授权文件生成工具。每当有新的用户申请查看图片时,应用所有者可以使用生成工具为用户生成激活文件。
整个过程的原则是保证"不是申请预览的用户不能查看图片内容",同时,保证要求用户参与的操作次数越少越好。
实现思路
上图展示了图片查看器的内部工作流程,分为用户侧和发布者侧两部分内容。发布者侧涉及橘色1、2两个问题,分别为图片加密和生成授权文件。用户侧涉及图片查看器运行过程中的授权检查、授权激活以及图片展示。
下面详细说明这几个问题是如何解决的。
几个主要问题的解决
图片加密
为了保护图片内容,我采用了AES对称加密算法,使用CBC模式和256位密钥。在加密过程中,随机生成了256位加密密钥和初始化向量。由于处理的图片数量较多(近1000张),且图片大小较大(约5~8MB),我选择只加密图片中的200个字节,这样既保证了加解密效率,又能满足限制普通浏览器查看图片的需求。
授权文件中的内容
授权文件以JSON格式存储信息,主要包括授权类型、授权文件过期时间、用户ID以及一个附加信息字段。授权文件过期时间被设定为生成授权文件的两天之内。此设计旨在限制用户将授权文件再次分发。虽然在这段时间内仍存在再分发的时间窗口,但已经显著降低了未授权传播的风险。
授权文件签名
使用非对称密钥(一对公钥和私钥)进行授权文件签名。首先,对授权文件中的明文数据计算SHA-256哈希值,得到一个唯一且固定长度的摘要,用于表示原始数据的"指纹"。接着,使用私钥对摘要进行加密,生成数字签名。将生成的数字签名附加到授权文件的明文数据上,形成签名数据。
授权文件验证
授权文件的验证过程主要包括两个步骤:验证签名的正确性和检查授权时间是否在有效期内。验证者使用签名者的公钥对授权文件中的数字签名进行解密,得到原始的摘要。同时,验证者再次对授权文件中的明文数据计算SHA-256哈希值,然后将这两个摘要进行比对。如果两个摘要相同,说明签名数据没有被篡改,签名有效;如果摘要不同,则表示签名数据已被篡改或者签名无效。
签名有效,则进一步验证授权时间是否在有效期内。为获取准确的时间,我们提供两种方式:一是使用阿里的 “ntp.aliyun.com” 时间服务器,另一种是获取系统时间。时间服务器作为获取时间的主要方式,而系统时间则作为备选。通过这样的验证过程,我们保证了授权文件的完整性和有效性。
绑定硬件
在用户获得授权后,会将从系统中获得的硬件信息写到用户的文件系统和注册表。在这之后,用户每次使用图片查看器的时候,就会检查文件系统和注册表,判断图片查看器是否已经被授权。这里要注意两点:1. 选择一定存在的硬件信息:在裸机可以获得的硬盘序列号,在hyper-v中获得的时候可能为空;2. 选择不会改变的硬件信息,仍以硬盘举例:如果获取当前计算机的硬盘信息,要考虑用户插上和移除移动硬盘的场景,这两种情况是不同的。我选择了处理器ID和主板序列号作为待绑定的硬件信息。
图片解密及显示
图片解密过程和图片加密过程使用相同的密钥和初始化向量,针对图片中加密的200字节进行解密。因为每张图片比较大,整个图片的加载过程采用事件触发多线程加载。随着用户拉动滚动条,动态加载解密后的图片内容。
其他问题
存在的问题及可能的改进
-
授权文件中的过期时间
在目前的解决方案中,授权文件中的过期时间用于限制授权文件再次分发的时间窗口。然而,可以进一步加强授权文件的安全性,采用用户侧工具获取用户唯一标识(UID)后,将其传递给发行者。这样,生成的授权文件将真正绑定到特定用户,确保授权的唯一性和准确性,避免被其他用户使用。 -
算法选择与保密性
老板在给我们的安全讲座中提到过,"一旦密码算法的详细信息被暴露,依靠对密码算法本身进行保密来确保机密性的密码系统也就土崩瓦解了。"但考虑到我的这个软件受众范围和目标,我还是在软件中使用了简单的保密算法。比如在对签名数据进行base64操作的时候,这个base64算法就是我自己实现了一个变种版本,目的就是为用户直接看出授权文件的内容设置个小障碍。 -
密钥随软件携带
加解密过程中最重要的一个问题就是密钥如何存储管理,这是被保护内容是否安全的关键。限于成本的考虑,也就选择当前的方案了。如有必要,可以考虑将密钥存储在独立的安全硬件模块(如HSM)中,确保密钥在硬件层面得到保护,防止泄露和篡改。 -
对硬件信息的保护
用户成功被授权后,会获取计算机的硬件信息并处理之后写到系统中,用于之后的授权检查使用。这个在写文件和注册表的时候,可以使用操作系统提供的权限控制机制,限制对关键信息的访问权限。 -
兼容性问题
前面只是列出了几个通用问题,在实际开发中,考虑到不同用户对图片集的不同需求,以及同一用户可能要求扩大图片集的情况,需要处理兼容性问题。当前内置白名单来限制图片查看器的使用,后续在解决方案中可以考虑增加动态加载图片集的功能,通过在线资源更新的方式,来满足不同用户的需求,从而避免版本不兼容的问题。
通过以上措施,就实现了一种立即可用的软件离线许可解决方案,为保护图片和软件的许可权提供了一种简单有效的方式。在这个过程中,我不仅解决了兄弟的问题,也为类似场景的解决方案提供了一种参考。当然,如果真有人破解了这个工具,那说明这些图片和工具还是挺有价值的呢!