如何使用DUKPT加密在读卡器中生成密文?

时间:2013-06-28 10:25:44

标签: encryption cryptography 3des

对于

`BDK = "0123456789ABCDEFFEDCBA9876543210"` `KSN = "FFFF9876543210E00008"` 

生成的密文低于

"C25C1D1197D31CAA87285D59A892047426D9182EC11353C051ADD6D0F072A6CB3436560B3071FC1FD11D9F7E74886742D9BEE0CFD1EA1064C213BB55278B2F12"`

我找到了here。我知道这个密文是基于BDK和KSN但是如何生成这个128长度的密文?它涉及的步骤或用于此的算法是什么?有人可以通过简单的步骤解释。我发现很难理解我用google搜索时得到的文件。

4 个答案:

答案 0 :(得分:14)

关于 DUKPT ,对 Wiki 有一些解释。如果这还不够,这里有一些简短的解释。

引用http://www.maravis.com/library/derived-unique-key-per-transaction-dukpt/

  

什么是DUKPT?

     

每个事务的派生唯一密钥(DUKPT)是密钥管理方案。它使用一次加密密钥,该密钥是从加密的实体(或设备)共享的秘密主密钥和解密数据的实体(或设备)共享的。   为何选择DUKPT?   任何加密算法都只能与其密钥一样安全。如果用于使用算法加密数据的密钥不安全,则最强的算法是无用的。这就像用最大和最强的锁来锁门,但是如果你把钥匙藏在门垫下面,锁本身就没用了。当我们谈论加密时,我们还需要记住,数据必须在另一端解密。   通常,任何加密方案中最薄弱的环节是加密和解密方之间的密钥共享。 DUKPT试图确保双方都可以加密和解密数据,而无需传递加密/解密密钥。   VISA发布的“加密最佳实践”文档还建议使用DUKPT以符合PCI DSS。

     

DUKPT如何运作

     

DUKPT使用为每个事务生成的一个时间密钥,然后丢弃。优点是,如果其中一个密钥被泄露,则只有一个事务会受到损害。使用DUKPT,始发(例如,引脚输入设备或PED)和接收(处理器,网关等)各方共享密钥。此密钥实际上不用于加密。相反,从该主密钥导出的另一个一次性密钥用于加密和解密数据。请务必注意,主密钥不应从派生的一次性密钥中恢复。   为了解密数据,接收端必须知道使用哪个主密钥来生成一次性密钥。这意味着接收端必须存储和跟踪每个设备的主密钥。对于支持大量设备的人来说,这可能是很多工作。处理这个问题需要更好的方法。   这就是它在现实生活中的工作方式:接收器有一个称为Base Derivation Key(BDK)的主密钥。 BDK应该是秘密的,永远不会与任何人共享。该密钥用于生成称为初始引脚加密密钥(IPEK)的密钥。由此生成一组名为Future Keys的密钥,并丢弃IPEK。每个Future密钥都由设备制造商嵌入到PED中,与之共享这些密钥。这个额外的推导步骤意味着接收器不必跟踪进入PED的每个键。它们可以在需要时重新生成。

     

enter image description here

     

接收方与PED制造商共享Future密钥,PED制造商将一个密钥嵌入到每个PED中。如果其中一个密钥被泄露,则可以使用从BDK派生的新Future密钥重新生成PED,因为BDK仍然是安全的。

     

加密和解密

     

当需要将数据从PED发送到接收方时,该设备中的Future密钥用于生成一次性密钥,然后该密钥与加密算法一起用于加密数据。然后将该数据与密钥序列号(KSN)一起发送到接收器,密钥序列号由设备ID和设备事务计数器组成。   enter image description here

     

基于KSN,接收器然后生成IPEK,并从中生成设备使用的Future Key,然后生成用于加密数据的实际密钥。使用此密钥,接收方将能够解密数据。

Source

答案 1 :(得分:5)

首先,让我引用您链接的完整源代码,并且您只提供了3行...

require 'bundler/setup'
require 'test/unit'
require 'dukpt'

class DUKPT::DecrypterTest < Test::Unit::TestCase

      def test_decrypt_track_data
        bdk = "0123456789ABCDEFFEDCBA9876543210"
        ksn = "FFFF9876543210E00008"
        ciphertext = "C25C1D1197D31CAA87285D59A892047426D9182EC11353C051ADD6D0F072A6CB3436560B3071FC1FD11D9F7E74886742D9BEE0CFD1EA1064C213BB55278B2F12"
        plaintext = "%B5452300551227189^HOGAN/PAUL ^08043210000000725000000?\x00\x00\x00\x00"

        decrypter = DUKPT::Decrypter.new(bdk, "cbc")
        assert_equal plaintext, decrypter.decrypt(ciphertext, ksn)
      end
end

现在,你问的是“密文”是如何创建的......

嗯,首先我们知道它是基于“明文”,它在代码中用于验证解密是否有效。

明文是0填充的 - 它适合通过使用此DecrypterTest TestCase验证解密而正在测试的加密。

让我们看一下编码代码......

我在https://github.com/Shopify/dukpt/blob/master/lib/dukpt/encryption.rb找到了相关的加密代码。

由于DecrypterTEst使用“cbc”,显然加密使用:

 @cipher_type_des = "des-cbc"
 @cipher_type_tdes = "des-ede-cbc"

加密代码更多一点,以下解决了我们对答案的追求:

ciphertext = des_encrypt(...

这表明我们确实在查看DES加密的结果。

现在,DES的块大小为64位。这是(64/8 =)8字节二进制,或 - 因为“密文”是字节的十六进制编码文本表示 - 16个字符十六进制。

“密文”是128个十六进制字符长,这意味着它保存(128个十六进制字符/ 16个十六进制字符=)8个DES块,每个64位加密信息。

在一个简单的答案中解决所有问题:

当查看“ciphertext”时,您正在查看(8块)DES加密数据,这些数据使用人类可读的十六进制(2个十六进制字符= 1个字节)表示法表示而不是DES加密会产生的原始二进制字节。

至于“重新创建”密文所涉及的步骤,我倾向于告诉您只需使用 ruby​​ 项目的相关部分, 。只需要看一下源代码。 https://github.com/Shopify/dukpt/blob/master/lib/dukpt/encryption.rb中的文件几乎解释了这一切,我非常确定您需要的所有功能都可以在项目的GitHub存储库中找到。或者,您可以尝试自己重新创建它 - 使用您选择的首选编程语言。您只需要处理两件事:DES加密/解密和bin-to-hex / hex-to-bin翻译。

答案 2 :(得分:2)

由于这是关于此问题的第一个主题之一,我想我会分享我如何编码密文。这是我第一次使用Ruby,它专门用于处理DUKPT

首先,我必须获得ipek和pek(与解密相同)方法。然后解压缩明文字符串。将解压缩的字符串转换为72字节数组(如果我的术语不正确,请再次原谅我。)

我在dukpt gem作者示例中注意到他使用了以下纯文本字符串

  

“%B5452300551227189 ^ HOGAN / PAUL ^ 08043210000000725000000?\ x00 \ x00 \ x00 \ x00”

我觉得这个字符串不正确,因为名字后面不应该有空格(AFAIK)..所以它应该是

  

“%B5452300551227189 ^ HOGAN / PAUL ^ 08043210000000725000000?\ X00 \ X00 \ X00 \ X00”

总而言之,这是我最终可以加密字符串然后使用DUKPT解密它的解决方案

class Encrypt
include DUKPT::Encryption
attr_reader :bdk

def initialize(bdk, mode=nil)
  @bdk = bdk
  self.cipher_mode = mode.nil? ? 'cbc' : mode
end

def encrypt(plaintext, ksn)
  ipek = derive_IPEK(bdk, ksn)
  pek = derive_PEK(ipek, ksn)
  message =  plaintext.unpack("H*").first
  message = hex_string_from_unpacked(message, 72)
  encrypted_cryptogram = triple_des_encrypt(pek,message).upcase
  encrypted_cryptogram
end
def hex_string_from_unpacked val, bytes
  val.ljust(bytes * 2, "0")
end

  

boomedukpt FFFF9876543210E00008“%B5452300551227189 ^ HOGAN / PAUL ^ 08043210000000725000000?”

(我的ruby gem,KSN和纯文本字符串)

  

2542353435323330303535313232373138395e484f47414e2f5041554c5e30383034333231303030303030303732353030303030303f000000000000000000000000000000000000

(我的ruby gem在调用hex_string_from_unpacked后执行unpacked字符串)

  

C25C1D1197D31CAA87285D59A892047426D9182EC11353C0B82D407291CED53DA14FB107DC0AAB9974DB6E5943735BFFE7D72062708FB389E65A38C444432A6421B7F7EDD559AF11

(我的红宝石宝石做了加密字符串)

  

%B5452300551227189 ^ HOGAN / PAUL ^ 08043210000000725000000?

(我的ruby gem在dukpt gem上调用解密后执行put)

答案 3 :(得分:0)

看看这个:https://github.com/sgbj/Dukpt.NET,我处于类似的情况,我想知道如何在终端有自己的函数调用时在终端上实现dukpt,这会调用INIT和KSN来创建第一个密钥,所以我唯一的问题是确保INIT键在终端上以与上面提到的repo代码相同的方式生成,这很简单,使用ossl加密库,3des与ebc并应用适当的掩码。