ARTICLE DETAIL

资讯详情

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

地铁AFC密钥管理:票卡密钥从发卡到清分的全链路分析

地铁AFC密钥管理:票卡密钥从发卡到清分的全链路分析 Mifare Classic(M1)卡的密钥被逆向之后,一张复制卡就能刷进闸机——这是 AFC 行业十年来反复讲的一课。M1 的密钥躺在卡里,可以被读出、可以被仿造,代价是整个票务体系的信用归零。今天的做法是 CPU 卡 国密算法 密钥分散,把每张卡一把独立的钥匙这件事从算法层面固定下来:就算一张卡被拆开读,也撬不动别的卡。这篇文章把地铁 AFC 的密钥链路讲清楚:从根密钥怎么躺在加密机里,到发卡机怎么给每张票卡分散出一把独立的钥匙,再到进出站扣费、清分结算,最后落进清分数据库。整条链上,密钥是唯一从头贯到尾的东西。学习地图先把 AFC 系统放平,密钥链路在这张图里的位置就清楚了:票卡(CPU卡) ── 终端(闸机/TVM/BOM) ── 线路中心 LC ── 清分中心 ACC ── 结算 ↑ 发卡机注入密钥 ↑ 交易上传 ↑ 多线路数据汇集 └────────────── 密钥体系贯穿全程 ──────────────┘票卡:单程票、储值票、纪念票,现在的标准形态是 13.56MHz CPU 卡(ISO/IEC 14443-A),国密 SM4终端:闸机(AGM)、自动售票机(TVM)、人工窗口(BOM),负责验票、扣费、写交易线路中心(LC):汇总本线交易,与清分中心对接清分中心(ACC):多条线路的交易数据汇到这里,按协议比例清分票款、跨线结算为什么值得研究这条链:①M1 破解是现成的教训,密钥体系设计错了,一张复制卡就能打穿整条线路;②密评(GB/T 39786-2021)的应用和数据层面,票务数据完整性与密钥管理是必查项;③《交通运输领域数据安全管理办法》对运营数据的采集、存储、清分提出了明确的安全要求。公路一侧的 ETC(不停车收费)密钥体系与 AFC 同源——同样是多级分散密钥 发卡注入,只是载体从地铁票卡换成了 OBU/RSU 车载单元和路侧设备。看懂 AFC 这条链,ETC 的车道密钥注入、密钥分散那一套也就通了。本系列围绕地铁 AFC 密钥,计划拆成几篇:本篇讲密钥链路全貌,后续讲终端密钥的注入细节、清分数据交换的防篡改。环节一:密钥体系的根——根密钥只躺在加密机里AFC 密钥体系是典型的多级分散结构,不是一张卡一把钥匙放在一个大库里,而是一棵树:根密钥 MK(只在 HSM 里,永不明文导出) ├── 线路母密钥(按线路派生) │ ├── 乘车密钥(每张卡分散出独立钥匙) │ └── 扣费密钥 └── 清分签名密钥(数据交换验签)根密钥是整棵树的源头,它存在的唯一地方是硬件密码机。为什么必须硬件?因为根密钥一旦以明文形式出现在任何一台服务器上,攻击者拿到它就等于拿到了整棵树——所有线路、所有卡都可以被派生。硬件密码机的意义在于:根密钥从生成到使用,都发生在加密机内部,外部只有调用,没有读取。安当 HSM 的服务器密码机(符合 GM/T 0030-2014)在这一层的职责,就是托管根密钥并对外提供签名、加密服务,私钥永不出硬件。这一层还有一个容易被忽略的细节:根密钥的备份与恢复。加密机坏了的场景是真实存在的,所以根密钥必须有受控的备份机制(加密备份、多人分持恢复),备份时不许直接导出明文,而是以加密分片的形式离开硬件。密评现场,评审会直接问你的根密钥备份在哪、谁有权限恢复,答不上来就是一个不符合项。环节二:发卡密钥分散——每张卡唯一的钥匙是怎么算出来的这是整条链的核心机制,值得贴代码。发卡机初始化一张票卡时,并不会把根密钥写进卡里(那样卡丢了整棵树就漏了),而是用根密钥分散出一把该卡专用的工作密钥,只把工作密钥注入卡内。分散的原理很简单:拿根密钥对卡号 分散因子做一次 SM4 加密,输出就是这把卡的工作密钥。packagemainimport(encoding/hexfmtgithub.com/tjfoc/gmsm/sm4)// 密钥分散:用根密钥对卡号分散因子做一次 SM4 加密,得到该卡独立的工作密钥funcderiveCardKey(root[]byte,cardNo[]byte,factorbyte)([]byte,error){block,err:sm4.NewCipher(root)// SM4 分组固定 16 字节iferr!nil{returnnil,err}in:make([]byte,16)copy(in,cardNo[:4])// 卡号取前 4 字节作为分散输入的一部分in[4]factor// 分散因子:0x00 乘车密钥 / 0x01 扣费密钥 ...out:make([]byte,16)block.Encrypt(out,in)// 加密输出即分散密钥returnout,nil}funcmain(){root,_:hex.DecodeString(0123456789ABCDEF0123456789ABCDEF)key,_:deriveCardKey(root,[]byte{0x30,0x03,0x05,0x88},0x00)fmt.Printf(card ride key: %X\n,key)}拆开逐行看:sm4.NewCipher(root):SM4 是国密对称算法,分组固定 16 字节。根密钥作为主密钥传入。in[4] factor:分散因子是防止乘车密钥和扣费密钥算出同一个值的关键——同一个卡号,不同因子派生出的密钥不同,用途也就分开,一个场景泄了不影响另一个。block.Encrypt(out, in):一次 SM4 加密,输入是卡号相关的 16 字节,输出就是这张卡独立的工作密钥。这个机制解决的正是 M1 当年的死穴。M1 卡的流密码 CRYPTO1 在 2008 年被逆向之后,密钥可以直接从卡内被读出,攻击者拿到密钥就能写一张一模一样的复制卡。M1 的问题不在用了对称密码,而在密钥存在卡里、且所有卡共用同一批密钥——读出一张,等于读出了全部。CPU 卡 分散密钥的改法,把共钥变成一卡一钥:卡内只存分散密钥,不存根密钥;攻击者拆开一张卡,最多拿到这一把卡的工作密钥,用它去派生根密钥计算上不可行(SM4 128 位密钥空间,无可行捷径),更撬不动别人的卡。这就是M1 教训 → CPU 卡的技术答案。发卡机在这个环节扮演的角色是执行者,而密钥的管理权不应该在发卡机手里。正确的做法是:发卡机只管把分散密钥注入卡内,根密钥和分散的编排都在密钥管理平台,发卡机通过接口取用,拿不到根密钥明文。安当 KSP 在这一层的职责,是把根密钥、线路母密钥、密钥版本、轮换周期全部管起来,密钥版本递增、旧版归档,发卡机按版本取用。版本切换还有一个绕不开的工程问题:在途票卡——旧版本密钥发行的票卡还在乘客手里,换版必须留出兼容窗口:新卡用新版本,在途旧卡按旧版本继续可用、到期前逐步回收;KSP 的版本管理正是给这个窗口兜底,旧版本归档但不清除,发卡、验票、清分各自按版本取用,窗口期一过再彻底下线。环节三:交易链路——闸机怎么用卡内密钥干活密钥注入之后,真正的高频场景来了:乘客进站、出站,闸机要和票卡完成一次认证和扣费。进站:闸机读取卡号,用卡内分散密钥做一次 MAC 运算,验证票卡合法性,写入进站信息;出站:闸机根据进出站记录算票价,用扣费密钥做扣费运算,更新卡内余额,同时生成一条交易记录;上传:交易记录带着票卡号、进出站、金额、设备号,定时上传到线路中心 LC。进站还有一步关键动作经常被忽略:挑战-应答。闸机发一个随机数,票卡用卡内工作密钥对随机数做加密运算应答,闸机验证应答,同时卡内交易计数器递增。这一步防的是 M1 时代的另一类攻击——重放:截获一次认证报文原样重发,就能假装刷卡成功。随机数让每次认证的报文都不一样,旧报文重放必被识破;计数器让同一张卡的交易序号单调递增,重放旧序号直接判失败。这条链上密钥的作用是认证与完整性:让闸机能确认这是一张真卡、这笔扣费没被改过。而交易数据本身,在从终端到 LC 的传输中需要加密和签名——这对应密评网络和通信层面的要求。终端设备数量大、分布广,密钥的分发要能做到按设备按批次管理,谁在用哪把钥、多久换,都要可追溯。这也是为什么密钥管理平台要留接口给终端对接,而不是让每个终端厂商各管一摊。环节四:清分链路——多线路数据汇到 ACC,谁也别想改多线路运营的城市,票务数据最终汇到清分中心 ACC。乘客跨线换乘,一张卡刷了两条线,票价怎么分——这靠清分模型,但数据本身的真实性和防篡改靠密码。清分链路的关键动作:数据汇集:各线路 LC 把交易数据加密上传到 ACC;对账:ACC 与各线路核对交易量、金额;结算:按协议比例把票款分给各线路公司。这一层最容易出问题的是账务数据被改:一条交易记录金额被篡改、一条记录被删除,直接影响钱。所以清分数据交换必须做签名——各线路上传时对交易汇总做数字签名,ACC 验签后才入账。逻辑可以用 SM2 一次说清:// 线路 LC 上传交易汇总前,对汇总做 SM2 签名funcsignSummary(priv*sm2.PrivateKey,summary[]byte)([]byte,error){// gmsm 按 GB/T 32918 内部计算 ZA‖M 摘要,默认用户标识 1234567812345678returnsm2.Sign(priv,summary,[]byte(1234567812345678))}// ACC 侧验签,任何一条记录被改,验签必失败funcverifySummary(pub*sm2.PublicKey,summary[]byte,sig[]byte)bool{returnsm2.Verify(pub,summary,[]byte(1234567812345678),sig)}这段的逻辑就两件事:线路侧只拿得到签名私钥的调用权(私钥存在加密机里,线路侧通过签名服务接口请求,拿不到明文);ACC 侧拿公钥验签。汇总数据哪怕改一个字节,验签失败,这条线就不会入账——这就是清分防篡改里可当场证明的那一步。生产环境里 userID 要按线路约定(代码里是 gmsm 默认值),不同线路用不同用户标识,防止跨线路签名被重放。签名密钥同样走密钥体系,证书与签名密钥由 CAS-KSS(CA 证书系统)签发管理,KSP 统一密钥生命周期,HSM 提供签名算力。环节五:存储层——清分库和计费库,密文才落盘清分链路最后落进数据库,这才是数据安全最容易失守的环节:数据库文件被人拷走、被拖库、被勒索加密,应用层完全感知不到。存储层的保护跟前面几个环节逻辑不一样——它不在应用里做,而是在驱动层做。安当 TDE 透明加密的原理是:应用照常读写,数据库驱动在数据写盘前加密、读盘后解密,对应用和开发完全透明,不需要改一行代码。清分库、计费库这类结构化数据,插入自动加密、读取自动解密,性能影响约 3%。产品通过 GM/T 0028 密码模块安全二级认证,内置国密 SM4(GB/T 32907-2016),密钥由 KSP 统一管理、HSM 保护。防勒索则是在另一条线上兜底。AFC 清分中心是典型的勒索目标——数据在结算窗口期丢了,票款清算直接停摆。安当 RDM 的做法是进程白名单 透明加密 实时审计三层:非白名单进程根本碰不到数据库文件;就算勒索程序进来了,想对已加密的数据二次加密也被拒绝;异常访问实时告警。这把数据被偷走和数据被锁死两条路都堵上。但要讲清楚边界:TDE 和白名单防的是静态文件与进程层的攻击;如果攻击者通过数据库服务本身拖数据(SQL 注入、合法账号批量导出),服务进程看到的正是明文,TDE 和 RDM 都拦不住——那需要访问控制、脱敏、审计(数据库加密网关那层)兜底。加密解决的是文件被拷走,不解决账号被滥用。回到现实:安当组件在这条链上的位置把上面五个环节的密钥角色对到安当产品上,就是一张完整的图:环节密钥角色安当组件根密钥信任根,只存加密机HSM(服务器密码机,GM/T 0030-2014)密钥分散与生命周期派生、版本、轮换、审计KSP(三级密钥体系,密钥版本递增)清分签名证书数据交换验签CAS-KSS(证书签发) KSP(密钥管理)清分/计费数据库落盘加密TDE(SM4 透明加密,性能影响≈3%)防勒索兜底进程白名单 防二次加密RDM(三层主动防护)注意一个共性:这条链上没有任何一个环节依赖发卡机或应用服务器保存根密钥明文。根在硬件,派生在平台,应用只通过接口取用——这是密评和《交通运输领域数据安全管理办法》都在反复强调的结构。验收:当场怎么证明链路是通的密钥分散验证:拿一个卡号 分散因子,用根密钥独立重算一遍分散密钥,与卡内取出的工作密钥比对,一致才说明发卡链路是通的(上面那段 Go 代码 5 分钟跑完)。密钥版本可查:在 KSP 里查根密钥与母密钥的版本、轮换记录、操作人、时间戳,确认日志带数字签名不可改。根密钥不可导出:确认 HSM 的密钥导出权限为 0,任何调用都无法拿到根密钥明文。落盘密文:对清分库执行一次查询,应用视角看到的是明文;再用strings /var/lib/mysql/xxx.ibd | grep 卡号看库文件,应找不到明文卡号。但只查表空间文件不够——redo log、undo log、doublewrite buffer 和逻辑备份文件里也可能残留明文副本,这几个文件要一并检查或同样启用加密,否则等于还有明文出口。防二次加密:模拟一次异常进程对数据库文件写操作,确认被 RDM 拦截并产生告警。系列导航上一篇:《CBTC信号系统安全:一文讲透车载ATP认证与信号系统访问控制》——信号系统那条线;下一篇:《铁路CTC调度系统安全实战:调度员双因素认证与等保三级合规落地》;本篇定位是AFC 密钥链路全貌地图:每环节的机制细节会单独展开——防重放的挑战-应答、在途卡版本切换、ETC 的密钥注入(OBU/RSU 侧,与 AFC 同源)都将在后续各篇拆透;本系列主线:轨道交通密码安全(信号 → AFC → 调度),同一套密钥逻辑在不同子系统里的落地。上面的分散、验签代码都拿本地跑一遍,五分钟就能验证篡改必失败。收藏 关注,票卡线接着拆。文章作者:安当加密-焱垚
返回列表