# 5 分钟给你的软件接入卡密验证(Python / Lua / C 三语言实操) 5 分钟给你的软件接入卡密验证(Python / Lua / C 三语言实操)关键词:卡密验证、网络验证、软件授权接入、HMAC 签名、Python/Lua/C SDK、设备绑定上一篇讲了网络验证系统怎么选,这篇直接上手:从零开始,5 分钟让你的软件跑通卡密激活 心跳保活。三种语言的完整可运行代码,复制就能用。用的是 Whisper 的开放 API,SDK 全部开源:https://github.com/whisperSean/whisper-sdk。API 是标准 RESTful JSON,换成别的服务或自建后端,签名逻辑照抄即可。一、准备工作(1 分钟)注册账号,创建一个软件实例。记下两个东西:instance_id—— 软件实例 ID,公开的。secret_key—— 签名密钥,绝对不能硬编码进客户端明文(后面讲怎么处理)。就这两个,拿到就能开始。二、先理解协议,再看代码只有两个接口:接口方法是否签名作用/api/v1/activatePOST✅ 需要激活/验证卡密,返回授权信息/api/v1/heartbeatPOST❌ 不需要心跳保活,判断用户是否在线激活接口的请求体{card_secret:用户输入的卡密,instance_id:你的软件实例 ID,hwid:设备硬件指纹}签名怎么算(核心,必须搞懂)激活接口需要在请求头带三个字段:x-timestamp、x-nonce、x-signature。签名算法:签名材料 POST\n/api/v1/activate\n{timestamp}\n{nonce}\n{sha256(body)} x-signature HMAC-SHA256(secret_key, 签名材料) # 十六进制小写五行拼接,用\n连接。**注意:body 必须是发出去的那一份字节——先序列化成字节、算它的 sha256、再原样发出去。**如果算 hash 的 body 和实际发送的 body 不一致(比如重新序列化导致空格/顺序变了),服务端验签必然失败。这是接入时最常见的坑。成功响应{code:0,data:{lua_url:https://.../main.lua?sign...,version:2.1.0,checksum:sha256:...,update_policy:force}}code ! 0就是失败,对照错误码表处理。三、Python 接入(最快)只依赖requests,标准库自带hmac/hashlib。importhashlib,hmac,json,time,uuid,requestsclassWhisperClient:def__init__(self,base_url,instance_id,secret_key,timeout10.0):self.base_urlbase_url.rstrip(/)self.instance_idinstance_id self.secret_keysecret_key self.timeouttimeoutdef_sign(self,method,path,body:bytes)-dict:tsstr(int(time.time()))nonceuuid.uuid4().hexbody_hashhashlib.sha256(body).hexdigest()message\n.join([method.upper(),path,ts,nonce,body_hash])sighmac.new(self.secret_key.encode(),message.encode(),hashlib.sha256).hexdigest()return{x-timestamp:ts,x-nonce:nonce,x-signature:sig}defactivate(self,card_secret,hwid)-dict:path/api/v1/activate# 关键:先序列化成 body 字节,算 hash 和发送用的是同一份bodyjson.dumps({card_secret:card_secret,instance_id:self.instance_id,hwid:hwid,}).encode()headers{Content-Type:application/json,**self._sign(POST,path,body)}resprequests.post(self.base_urlpath,databody,headersheaders,timeoutself.timeout)resultresp.json()ifresult.get(code)!0:raiseRuntimeError(f[{result.get(code)}]{result.get(msg)})returnresult[data]调用:clientWhisperClient(https://commu.fun,你的instance_id,你的secret_key)try:dataclient.activate(USER-CARD-KEY,get_hwid())print(授权成功,脚本版本:,data[version])exceptRuntimeErrorase:print(激活失败:,e)注意databody而不是jsonpayload—— 用json会让 requests 重新序列化,和你算 hash 的字节可能对不上。必须发同一份body。四、Lua 接入(游戏脚本 / FiveM / GMod 常用)Lua 没有内置 crypto,很多教程用os.execute/io.popen调openssl命令行算签名 ——这是个大坑:目标机器没装 openssl CLI 时,io.popen会静默返回空串,签名变空,服务端直接回 403,而且报错完全没提示,排查半天找不到原因。更别说 FiveM / GMod 这类沙箱环境根本禁用io.popen,这条路直接走不通。正确做法是用纯 Lua 实现 SHA-256 / HMAC-SHA256,零外部依赖。Whisper 的 Lua SDK 已经内置了这套实现(纯算术模拟 32 位运算,兼容 Lua 5.1~5.4 LuaJIT,能跑在禁用 io.popen 的沙箱里),而且模块加载时会用标准测试向量自检,算法出错直接报错,不会静默产生错误签名。直接用它:-- 完整实现见仓库 lua/whisper_sdk.lua(含纯 Lua SHA-256/HMAC)localWhisperClientrequire(whisper_sdk)localclientWhisperClient.new(https://commu.fun,你的instance_id,你的secret_key)-- 激活:返回 data(table), code(number), msg(string)localdata,code,msgclient:activate(card_secret,hwid)ifcode0thenprint(授权成功,脚本版本:,data.version)elseprint(激活失败:,code,msg)end签名逻辑和 Python 版完全一致(五要素拼接 HMAC-SHA256),SDK 内部已处理好sha256(body)用的就是实际发送的 body 字节。游戏引擎适配:引擎通常自带 HTTP,把 SDK 里的socket.http换成引擎 API 即可,crypto 部分(纯 Lua)可以原样复用:FiveM 用PerformHttpRequestGarry’s Mod 用HTTP({...})Roblox 用HttpService:RequestAsync五、C 接入(原生 EXE / DLL)依赖 libcurl OpenSSL,适合嵌进你的可执行文件或驱动。核心签名部分:// 伪代码示意,完整实现见仓库 c/whisper_sdk.ccharbody[512];snprintf(body,sizeof(body),{\card_secret\:\%s\,\instance_id\:\%s\,\hwid\:\%s\},card_secret,instance_id,hwid);charbody_hash[65];sha256_hex((unsignedchar*)body,strlen(body),body_hash);// OpenSSL SHA256charmessage[1024];snprintf(message,sizeof(message),POST\n/api/v1/activate\n%ld\n%s\n%s,(long)time(NULL),nonce,body_hash);charsignature[65];hmac_sha256_hex(secret_key,message,signature);// OpenSSL HMAC// curl 设置三个头 x-timestamp / x-nonce / x-signature,POST body编译:# Linux / macOSgcc-oexample example.c whisper_sdk.c-lcurl-lssl-lcrypto# Windows (MSVC)cl example.c whisper_sdk.c /Icurl/include/Iopenssl/includelibcurl.lib libssl.lib libcrypto.lib完整代码(含内置 JSON 解析,不用 cJSON)在仓库c/目录。六、硬件指纹(HWID)怎么采别只用 MAC(虚拟机、换网卡就变)。推荐多因子混合:importhashlib,subprocess,uuiddefget_hwid():factors[str(uuid.getnode())]# MACforcmdin([wmic,baseboard,get,serialnumber],# 主板序列号[wmic,cpu,get,processorid]):# CPU IDtry:rsubprocess.run(cmd,capture_outputTrue,textTrue)factors.append(r.stdout.strip())exceptException:passreturnhashlib.sha256(|.join(factors).encode()).hexdigest()[:32]七、心跳保活激活成功后,后台线程每 5 分钟调一次 heartbeat,服务端据此判断用户在线。心跳不需要签名:defheartbeat_loop(client,card_key,hwid,interval300):importthreading,timedef_loop():whileTrue:time.sleep(interval)try:requests.post(client.base_url/api/v1/heartbeat,json{card_key:card_key,hwid:hwid},timeout10)exceptException:pass# 网络波动容错,别直接踢人threading.Thread(target_loop,daemonTrue).start()建议配 1 小时离线容错:网络抖动或服务端短暂维护,不要立刻把已激活用户踢下线。八、错误码速查Code含义处理0成功正常使用1无效卡密提示重新输入2卡密不属于该实例检查 instance_id4卡密已过期引导续费5卡密已封禁联系客服6已绑定其他设备引导解绑403签名无效 / 重放 / IP 黑名单检查密钥和时钟同步429频率超限降低调用频率遇到 403 先查三件事:secret_key 对不对、本机时间和服务器差没差超过 300 秒、算 hash 的 body 和实际发送的 body 是不是同一份。九成的 403 是这三个之一。九、关于 secret_key 的安全secret_key是签名密钥,硬编码进客户端明文 等于没有,逆向一抓就拿到了。几种处理:服务端代理:客户端不持有 key,验证请求走你自己的中间服务器签名转发。最安全。加壳保护:如果必须放客户端,用加壳 代码变异把 key 和签名逻辑藏起来,提高逆向成本。动态下发:首次用一个临时凭证换取会话密钥。Whisper 的加壳器就是干第三件事的 —— AES-256-GCM 整体加密 VMProtect 代码变异,把验证逻辑和密钥藏进虚拟机字节码。这部分下一篇《卡密验证的防重放与防破解设计》细讲。三种语言都跑通后,你的软件就有了付费才能用的能力。官网(注册即用,7 天全功能免费试用):https://commu.funSDK 开源仓库(先看代码再接):https://github.com/whisperSean/whisper-sdk有接入问题欢迎评论区问,或去仓库提 Issue.