【问题标题】:How ciphertext was generated in card reader using DUKPT encryption?如何使用 DUKPT 加密在读卡器中生成密文?
【发布时间】:2013-06-28 10:25:43
【问题描述】:

对于

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

生成的密文如下

"C25C1D1197D31CAA87285D59A892047426D9182EC11353C051ADD6D0F072A6CB3436560B3071FC1FD11D9F7E74886742D9BEE0CFD1EA1064C213BB55278B2F12"`

我找到了here。我知道这个密文是基于 BDK 和 KSN 的,但是这个 128 长度的密文是如何生成的呢?它涉及哪些步骤或用于此的算法?有人可以用简单的步骤解释一下。我发现很难理解我在谷歌上获得的文件。

【问题讨论】:

  • 你知道读卡器使用的BDK和KSN吗?
  • 您的问题到底是什么?如果您想知道 DUKPT 是如何工作的,您肯定可以查看相关规范吗?您能否更具体地说明“RTFM”响应无法解决的您需要什么?
  • 嗨@PaulG,@DuncanJones 是的,我知道我的读卡器中使用的 BDK 和 KSN。但我是这个项目的新手。我完全不知道如何在读卡器中加密数据以及如何在刷卡时生成结果密文?是的,他们使用了 DUKPT 技术。
  • @PaulG,@DuncanJones 我编辑了问题你能提供任何链接或解释所涉及的步骤吗?

标签: encryption cryptography 3des


【解决方案1】:

关于DUKPTWiki上给出了一些解释。如果这还不够,这里有一些简短的解释。

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

什么是 DUKPT?

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

DUKPT 的工作原理

DUKPT 使用一次性密钥,为每笔交易生成然后丢弃。这样做的好处是,如果其中一个密钥被泄露,那么只有一个事务会被泄露。使用 DUKPT,发起方(例如,PIN 输入设备或 PED)和接收方(处理器、网关等)共享一个密钥。此密钥实际上并未用于加密。相反,从该主密钥派生的另一个一次性密钥用于加密和解密数据。重要的是要注意,主密钥不应从派生的一次性密钥中恢复。 要解密数据,接收端必须知道使用哪个主密钥来生成一次性密钥。这意味着接收端必须存储和跟踪每个设备的主密钥。对于支持很多设备的人来说,这可能是很多工作。需要更好的方法来处理这个问题。 这就是它在现实生活中的工作方式:接收者有一个称为基本派生密钥 (BDK) 的主密钥。 BDK 应该是保密的,永远不会与任何人共享。此密钥用于生成称为初始 Pin 加密密钥 (IPEK) 的密钥。由此生成一组称为 Future Keys 的密钥并丢弃 IPEK。每个 Future 密钥都由设备制造商嵌入到 PED 中,并与他们共享这些密钥。这个额外的推导步骤意味着接收器不必跟踪进入 PED 的每个密钥。它们可以在需要时重新生成。

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

加解密

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

基于 KSN,接收方然后生成 IPEK,并从中生成设备使用的未来密钥,然后生成用于加密数据的实际密钥。有了这个密钥,接收方就可以解密数据了。

Source

【讨论】:

    【解决方案2】:

    首先,让我引用您链接的完整源代码,其中您只提供了 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 字节二进制,或者 - 因为 "ciphertext" 是字节的十六进制编码文本表示 - 16 字符十六进制。

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

    简单回答一下:

    查看“密文”时,您正在查看(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 转换。

    【讨论】:

      【解决方案3】:

      由于这是与此相关的第一个主题之一,我想我会分享我如何能够对密文进行编码。这是我第一次使用 Ruby,它专门用于 DUKPT

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

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

      "%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 P>

      (我的 ruby​​ gem 在调用 hex_string_from_unpacked 后对解压后的字符串执行 put)

      C25C1D1197D31CAA87285D59A892047426D9182EC11353C0B82D407291CED53DA14FB107DC0AAB9974DB6E5943735BFFE7D72062708FB389E65A38C444432A59AF11B7FED1

      (我的 ruby​​ gem 对加密字符串进行了 put)

      %B5452300551227189^HOGAN/PAUL^08043210000000725000000?

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

      【讨论】:

        【解决方案4】:

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-07-29
          • 2012-10-05
          • 2011-08-09
          • 2014-03-03
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多