文章目录
- 一、DUKPT流程简析
一、DUKPT流程简析
接着上一篇文章DUKPT讲
依旧引用上图,我们单看POS和Acquirer Host这两个结点之间(其它结点之间的处理,基本类似):
Acquirer在布放POS到商场时,已经提前给这台POS的安全芯片中,灌输了BDK,并:生成了EC=0的Initial PEK:Initial PEK =PEK_Derive(BDK, KSN with EC=0),然后销毁了终端中的BDK(保证BDK的安全性)。当然,BDK在Acquirer Host中肯的是有保存的。一般来说Acquirer Host都是使用硬件加密机(HSM/SafeNet)保存密钥的,基本不会存在密钥泄漏的风险。
重头戏来啦:小明在POS上进行刷卡交易,此时POS做了哪些事情呢?
1、 Current KSN = IKSN and EC++【即,Current KSN中的EC = 1】
2、 Current PEK = PEK_Derive(Initial PEK, Current KSN)
3、 Encrypted PIN = T-DES(Opr=Encrypt, Current PEK, Clear PIN)
至此,Encrypted PIN已经得出来啦!POS开开心心的把Encrypted PIN放到交易报文里面,送给Acquirer Host咯。
等等!POS只送出一个Encrypted PIN,真的够了吗?
想一下,Acquirer Host收到交易报文之后,需要解密Encrypted PIN的哦!怎么解密?Clear PIN = T-DES(Opr=Decrypt, Current PEK, Encrypted PIN)
【淡定,这个步骤是在HSM里面做的,只有HSM/SafaNet知道clear PIN,Acquirer Host的软件是不知道的哦】。
问题又来了,Acquirer Host大叫:我怎么知道Current PEK(IPEK)是神马?我只知道BDK啊!
Acquirer Host真是个笨蛋!人家POS都知道怎么算Current PEK,你咋不知道?同样的步骤算一下呗:
1、 Initial PEK = PEK_Derive(BDK, KSN with EC=0)
2、 Current PEK = PEK_Derive(Initial PEK, Current KSN)
Acquirer Host再次大叫:我说了我只知道BDK啊!看看上面两个步骤的参数,我还需要Current KSN,才能算出Current PEK呢!
好吧,POS不得不承认自己的错误:不好意思刚刚只在交易报文里面塞进了Encrypted PIN,忘了把Current EC塞进去啦!
于是,POS不得不屁颠屁颠的把Encrypted PIN和Current KSN都塞进交易报文,送给Acquirer Host
于是,Acquirer Host收到报文后,开开心心的解密成功,可以进行后续交易处理啦。
于是,我们总结一下上述流程:
初始化:Acquirer Host和POS交互相同的BDK,且POS中销毁BDK,仅保存由BDK分散出来的Initial PEK
发生交易时,POS的处理:再重复一遍,不就是上面的这些步骤么
1、 Current KSN = IKSN and EC++
2、Current PEK = PEK_Derive(Initial PEK, Current KSN)
3、Encrypted PIN = T-DES(Opr=Encrypt, Current PEK, Clear PIN)
4、把Current KSN和Encrypted PIN放到交易报文里面,发送给Acquirer Host
收到交易时,Acquirer Host的处理:
1、 Initial PEK = PEK_Derive(BDK, KSN with EC=0)
2、Current PEK = PEK_Derive(Initial PEK, Current KSN)
3、Clear PIN = T-DES(Opr=Decrypt, Current PEK, Encrypted PIN)
4、后续交易处理
呼…说这么多,刚解释完DUKPT的第一轮计算方式… 想想如果是你来设计这个算法,接下来该怎么处理EC=2、EC=3…EC=n的情况呢?
我们回头看一下上文中提到的”发生交易时,POS的处理“:
1、 Current KSN = IKSN and EC++
2、Current PEK = PEK_Derive(Initial PEK, Current KSN)
3、Encrypted PIN = T-DES(Opr=Encrypt, Current PEK, Clear PIN)
4、把Current KSN和Encrypted PIN放到交易报文里面,发送给Acquirer Host
想一下,在这个追求用户体验的年代,刷卡交易的速度一定是评估刷卡体验的重要指标。
那么,为了提高速度,怎么简化一下上述流程呢?抛开算法层面的优化,很自然的一个想法:把能提起做的事情,先提前做起来!
哪些步骤可以提前做?看看上述流程中每个步骤的入参,好像只有Clear PIN是持卡人在交易发生时输入的,其它参数不都是POS内部的数据么!
这是个好消息!这样的话,我们可以在初始化POS(想想上文提到的“初始化”流程)时,就把EC=2、EC=3…EC=n时的每个EC对应的(Current Key)i 计算出来,提前保存到POS中,交易发生时,只需要EC++并取出相应的(Current Key)i ,就万事大吉啦!
呃,好像有哪里不对的样子……哦哦哦,EC=2、EC=3…EC=n,这个n到底是多少啊?要是有个几十上百万,POS中哪有那么大的空间给你存(Current Key)i呢!要知道,POS的硬件就那么一块小板子,每一个字节都是很宝贵的哦!
该死的,又是时间和空间互换的问题,那就找个平衡点咯!于是乎,ANSI果断的找到了这个平衡点:POS中预先存储的Current Key(插一句,这些Key都是后续交易中,EC++之后才用到的Key,是不是应该叫做Future Key啊?)数量,是21个!
【为啥是21?我咋知道!或许是这个数字比较吉利?不过想想EC的长度也是21个bit,好像发现了点儿神马哦…】
于是,POS中出现了21个寄存器,叫做Future Key Register,在POS初始化时,它们被依次填充了EC=1 … EC=21的Future Key
于是,我们更新一下POS初始化的流程:
1、 Acquirer给POS灌输BDK
2、Initial PEK = PEK_Derive(BDK, KSN with EC=0)
3、
(Future Key)1 = PEK_Derive(Initial PEK, KSN with EC=1) -> (Future Key Register)1
(Future Key)2 = PEK_Derive(Initial PEK, KSN with EC=2) -> (Future Key Register)2
……
(Future Key)21 = PEK_Derive(Initial PEK, KSN with EC=21) -> (Future Key Register)21
4、 为了保证密钥安全性,别忘了销毁BDK和Initial PEK!
ok啦,POS机曰:万事大吉,坐等刷卡!
那么,21个Future Key用完了咋办?当然后面接着生成啦。这里就有2个新问题了:
比较明显的问题:什么时机去生成和更新EC=22、EC=23…的Future Key?
既然POS中只有21个寄存器,那当然得物尽其用啦!反正EC又不会重复使用(EC++操作是有上限的哦,后面会提到),EC=1的Future Key用过一次之后,就木有用啦!
那就在POS的第一笔交易处理完成后,把(Future Key Register)1中保存的Key,更新成(Future Key)22吧!这样既不影响交易处理效率,又可以充分利用空间,多好啊!
比较隐晦的问题:(Future Key)22是怎么生成的呢?
最直接的想法,跟(Future Key)1采用同样的方式呗:
(Future Key)22 = PEK_Derive(Initial PEK, KSN with EC=22) -> (Future Key Register)1
Stop!POS曰:我去哪儿找Initial PEK啊?之前初始化的第4步里面,已经把这玩意儿销毁了哎!
呃,有点儿麻烦了,是之前不应该销毁Initial PEK吗?可是如果不销毁,后续所有的Key,都由Initial PEK分散得到的话,那这个Initial PEK可是个相当重要、相当机密的东东啦!
嘿,换个思路如何?我现在是要生成(Future Key)22,而它要占用的地盘儿(寄存器),正是(Future Key Register)1,那我干脆就用(Future Key)1来分散得到(Future Key)22吧!
于是: (Future Key)22 = PEK_Derive( (Future Key)1 , KSN with EC=22) -> (Future Key Register)1
ok,总算是把(Future Key)22搞定了,接下来就好说啦,以此类推呗:
(Future Key)23 = PEK_Derive( (Future Key)2 , KSN with EC=23) -> (Future Key Register)2
(Future Key)24 = PEK_Derive( (Future Key)3 , KSN with EC=24) -> (Future Key Register)3
……
(Future Key)42 = PEK_Derive( (Future Key)21 , KSN with EC=42) -> (Future Key Register)21
哦,又是21,好巧哦,又是一个轮回啊!接下来咋办?
(Future Key)43 = PEK_Derive( (Future Key)? , KSN with EC=43) -> (Future Key Register)?
好办啊!跟(Future Key)22一样处理呗:
(Future Key)43 = PEK_Derive( (Future Key)22 , KSN with EC=43) -> (Future Key Register)1
……
其实前文已经将DUKPT算法解释的差不多了,需要进一步说明的,就是Future Key的计算了。其实之前已经推理了一大堆了,我就直接把结果贴出来吧:
EC共有21个bit,每个bit可能的取值为“0”或“1”,那么如此多的EC,可以形成一棵树状结构:
说明一下,这棵树的组成是这样的:
层次:树的每一层,EC中包含的
bit为“1”
的数量是固定的;即
第0层的EC中,包含0个bit的“1”
第1层的EC中,包含1个bit的“1”
第2层的EC中,包含2个bit的“1”
…
子结点:每个结点的子结点,均保留本结点的EC中所有为“1”的bit,并将最右侧的“1”后面的“0”,依次替换为“1”;例如
EC=(1000)2的结点,有(1100)2、(1010)2和(1001)2这3个子节点
EC=(1100)2的结点,有(1110)2和(1101)2这2个子结点
深度:为保证算法效率,DUKPT规定,上面那棵树的
最大深度为10
,即
有效的EC中,包含“1”的bit个数 <= 10
计算过程中遇到无效的EC,直接跳过
这样的话,有效的EC个数为100多万个
Future Key:与EC是对应的
每个EC,对应一个Future Key
子结点EC的Future Key,是被父结点EC的Future Key加密的
即,上面的那棵树,就是一棵Future Key计算体系的树
好啦,DUKPT基本上就是这样了
哦对了,再补充一句: 上述流程只讲解到了Future Key,但实际用于数据(如PIN、MAC等)加密的,其实不是直接拿Future Key来用的,而是Future Key xor 分散向量得到的工作密钥
PIN密钥的分散向量:00000000000000FF00000000000000FF
MAC密钥的分散向量:000000000000FF00000000000000FF00
【其它的分散向量好像还有,但没有在ANSI x9.24规范(2004版)中提及】
话说看到评论才发现,全文对于PEK_DERIVE的步骤,没有进行明确说明, 不多说,直接上图吧
简要说明:
黄色底色的2个框框,是PEK_DERIVE的2个入参;绿色底色的框框,是PEK_DERIVE的结果
图中的“Set Bit”(右边KSN的黄色框框 下面的步骤),是一个很简单的步骤:
取KSN中最右边的21bit的EC(例如,01100000…1000,共21bit)
得到EC中最右侧的bit “1”(01100000…1 000)
将EC其余bit置为0,即获得Set Bit的结果(00000000…1 000)