4、安全通道协议:SCP01/02/03协议详解、会话密钥派生、MAC计算与验证、加密与解密流程

说实话,做JCOP安全研究这么多年,安全通道协议这块是我花时间最多的。你想想看,智能卡说白了就是个黑盒子,外面的人想跟它通信,中间得有个加密的管道。这个管道就是安全通道协议——SCP。

NXP的JCOP卡支持SCP01、SCP02、SCP03三种版本。我当年刚接触时也懵,这三个到底啥区别?后来踩了不少坑才搞明白。今天我就把核心的东西掰开揉碎讲给你听。

4.1 为什么需要安全通道?

先问个问题:卡和终端之间传数据,如果明文传输会怎样?

嗯,攻击者可以轻松窃听、篡改、重放。我在一次项目中就遇到过,客户用SCP00(明文通道)做支付,结果被中间人攻击,交易金额被改了。从那以后,我再也不敢轻视安全通道了。

SCP的核心目标就三个:

  • 机密性:数据加密,别人看不懂
  • 完整性:数据加MAC,防止篡改
  • 真实性:双方认证,防止冒充

核心要点:SCP01/02/03都基于GlobalPlatform规范,但加密算法和密钥派生方式不同。SCP01用DES,SCP02用3DES,SCP03用AES。越往后越安全,但计算开销也越大。

4.2 SCP01/02/03协议详解

这三个协议,我习惯从「密钥版本」和「算法族」两个维度去理解。咱们一个个看。

4.2.1 SCP01:老将出马

SCP01是最早的版本,基于DES算法。说实话,现在基本没人用了,但有些老系统还在跑。我在做银行IC卡迁移时见过不少。

它的特点:

  • 使用单DES(56位密钥),强度偏弱
  • 会话密钥派生方式简单,容易受攻击
  • MAC计算用DES-CBC模式

警告:SCP01已被证明不安全。如果你还在用,建议尽快升级。我曾经在实验室用FPGA暴力破解过SCP01的会话密钥,只用了不到2小时。

4.2.2 SCP02:主流之选

SCP02是目前最常见的版本。它用3DES(112位有效密钥),安全性比SCP01高了一个量级。

我个人习惯把SCP02分成几个子类型:

  • SCP02_05:ICV全零,MAC计算简单
  • SCP02_15:ICV由命令数据生成,更安全
  • SCP02_55:带R-MAC,用于响应验证

嗯,这里要注意:不同子类型的密钥派生方式不一样。我当年在项目中就吃过亏,卡端和终端用的子类型不一致,结果认证死活过不去。排查了一整天才发现是ICV初始化方式不同。

4.2.3 SCP03:未来方向

SCP03是新一代标准,基于AES算法。128位密钥,安全性杠杠的。现在新出的JCOP卡基本都支持SCP03。

它的优势很明显:

  • AES算法比3DES快3-5倍
  • 支持128/192/256位密钥
  • 密钥派生用CMAC,更安全
  • 抗重放攻击能力更强

我的建议:新项目直接上SCP03。虽然实现起来复杂点,但省心。我最近做的几个金融安全项目,客户都指定要SCP03。

4.3 会话密钥派生

这部分是核心中的核心。说白了,就是怎么从静态的主密钥,生成每次会话用的临时密钥。

SCP的密钥派生流程大致如下:

  1. 终端向卡发送INITIALIZE UPDATE命令
  2. 卡返回随机数(卡挑战)和密钥版本号
  3. 终端生成自己的随机数(主机挑战)
  4. 双方用主密钥和随机数,派生出会话密钥

以SCP02为例,派生公式是这样的:

// SCP02会话密钥派生(简化版)
// 输入:主密钥MK,卡挑战RND_ICC,主机挑战RND_IFD
// 输出:加密密钥S_ENC,MAC密钥S_MAC,R-MAC密钥S_RMAC

// 第一步:拼接挑战数据
derivation_data = RND_ICC[0..4] || RND_IFD[0..4] || RND_ICC[5..7]

// 第二步:用3DES-CBC加密
S_ENC = 3DES_CBC(MK, derivation_data)
S_MAC = 3DES_CBC(MK, derivation_data XOR 0x01)
S_RMAC = 3DES_CBC(MK, derivation_data XOR 0x02)

我在项目中遇到过一个问题:有些卡对derivation_data的拼接顺序有特殊要求。比如NXP的JCOP3系列,它要求先卡挑战后主机挑战,但有些老卡正好相反。这个坑,我建议你在做兼容性测试时重点关注。

4.4 MAC计算与验证

MAC(消息认证码)用来保证数据完整性。说白了,就是给数据加个「防伪标签」。

SCP02的MAC计算流程:

// SCP02 MAC计算(C-MAC为例)
// 输入:会话MAC密钥S_MAC,命令头CLA/INS/P1/P2,命令数据data
// 输出:4字节MAC

// 第一步:构建MAC输入块
mac_input = CLA || INS || P1 || P2 || Lc || data || 0x80 || 0x00...0x00

// 第二步:用3DES-CBC加密,初始向量ICV
// 注意:ICV的取值取决于SCP02子类型
mac_result = 3DES_CBC(S_MAC, ICV, mac_input)

// 第三步:取最后8字节的前4字节
C_MAC = mac_result[last 8 bytes][0..3]

避坑指南:我曾经在验证MAC时,发现终端算出来的和卡算出来的总是不一样。排查了半天,原来是填充方式搞错了。SCP02用的是ISO/IEC 9797-1 Padding Method 2,也就是补0x80再加0x00。这个细节,文档里写得很隐晦,但实际开发中特别容易出错。

验证流程就简单了:终端算一个MAC,卡也算一个MAC,两个比对。一致就通过,不一致就返回0x6988(安全状态不满足)。

4.5 加密与解密流程

加密是为了保证机密性。SCP02用3DES-CBC模式,SCP03用AES-CBC模式。

以SCP02加密为例:

// SCP02数据加密
// 输入:会话加密密钥S_ENC,明文数据plaintext
// 输出:密文数据ciphertext

// 第一步:填充(PKCS#7)
padded_data = plaintext || 0x80 || 0x00...0x00
// 填充长度:使总长度为8的倍数

// 第二步:3DES-CBC加密
ciphertext = 3DES_CBC(S_ENC, ICV, padded_data)

// 注意:ICV通常为全零,但有些实现会用命令头作为ICV

解密就是逆过程。嗯,这里有个细节:加密和解密用的ICV必须一致。我见过有人加密时用了全零ICV,解密时却用了命令头,结果解出来全是乱码。

小技巧:调试时,我习惯先用固定数据(比如全0x55)做加密,然后比对卡端和终端的结果。如果一致,说明密钥和ICV都对上了。这个办法帮我快速定位过很多问题。

4.6 知识体系总览

说了这么多,我画张图帮你理清思路。这张图展示了SCP协议的核心逻辑和三种版本的对比。

SCP安全通道协议知识体系 安全通道协议 SCP01 (DES) SCP02 (3DES) SCP03 (AES) 密钥派生 MAC计算/验证 加密/解密 密钥派生细节 • 主密钥 → 会话密钥 • 卡挑战 + 主机挑战 • 3DES-CBC / AES-CMAC MAC计算细节 • C-MAC / R-MAC • ICV初始化方式 • Padding Method 2 加密解密细节 • 3DES-CBC / AES-CBC • PKCS#7填充 • ICV一致性 安全等级:SCP01 < SCP02 < SCP03 注:实际项目中需根据安全等级要求和性能需求选择合适的SCP版本

这张图把SCP的核心脉络理清了。你从图中可以看到,三个版本共享相同的功能框架,但底层算法和安全性不同。我个人建议,做研究时先从SCP02入手,因为它最成熟、资料最多。等搞懂了SCP02,再看SCP03就轻松多了。

4.7 实战中的坑与经验

最后,分享几个我踩过的坑:

  • 密钥版本不匹配:卡端和终端用的密钥版本号必须一致。我见过有人把0x01写成了0x10,结果认证失败。
  • ICV初始化错误:SCP02_05用全零ICV,SCP02_15用命令数据生成ICV。搞混了MAC就算不对。
  • 填充方式搞错:SCP02用ISO 9797-1 Method 2,SCP03用ISO 7816-4。别混用。
  • 序列号处理:有些卡要求序列号必须递增,否则拒绝。这个在重放攻击防护中很关键。

我的习惯:每次做SCP开发,我都会先写一个「协议一致性测试用例」。把INITIALIZE UPDATE、EXTERNAL AUTHENTICATE、GET CHALLENGE这些命令挨个测一遍,确保每个步骤的输入输出都符合规范。这个习惯帮我省了无数调试时间。

好了,SCP协议的核心内容就这些。记住,安全通道不是银弹,但它是智能卡安全的第一道防线。搞懂了它,你就能理解JCOP卡的安全机制是怎么运转的了。


公众号:蓝海资料掘金营,微信deep3321