【问题标题】:Equivalent of CBCBlockCipherMac from bouncycastle for swift相当于来自 bouncycastle 的 CBCBlockCipherMac 用于 swift
【发布时间】:2019-09-20 13:36:05
【问题描述】:

感谢 bouncycastle 的图书馆,我需要为 iOS (swift) 重新实现为 Android 应用 (kotlin) 完成的加密操作。 kotlin 代码是:

val mac = "9D3391051A4E774B".hexStringToByteArray()
val macKey = "89D7B23D500D492FA01DC53B44864AB8".hexStringToByteArray()
val cipheredData = "E77A914D5C94A04B6D8E10BA7A56A015AC2C40167F867A97B6349F29F3100D6D".hexStringToByteArray()

var macBlock = CBCBlockCipherMac(AESEngine(), ISO7816d4Padding())
macBlock.init(KeyParameter(macKey))
macBlock.update(cipheredData, 0, cipheredData.size)
var output = ByteArray(8)
macBlock.doFinal(output, 0)

if(output.toHex() == mac.toHex()) {
    print("equals !!")
} else {
    print("not equals : ${output.toHex()}")
}

此代码有效,从输出中找到的 mac 与原始 'mac' 属性相同。

我尝试使用 swift Library CryptoSwift 和这段代码:

let mac = Data(hex: "9D3391051A4E774B")
let macKey = Data(hex: "89D7B23D500D492FA01DC53B44864AB8")
let cipheredData = Data(hex: "E77A914D5C94A04B6D8E10BA7A56A015AC2C40167F867A97B6349F29F3100D6D")

do {
    var output = try CBCMAC(key: macKey.bytes).authenticate(cipheredData.bytes)
    checkOutput(mac: mac, output: output)
} catch {
    debugPrint("Exception \(error)")
}

但这不起作用。 CryptoSwift 的 CBCMAC 类背后的算法与 bouncycastle 的 CBCBlockCipherMac 的想法不同。

我也尝试使用苹果的 CommonCrypto 库,但没有 CBCMAC 身份验证,只有 HMAC。我没有找到任何方法可以轻松地为 iOS 平台进行 CBC-MAC 身份验证。

【问题讨论】:

  • LOL,看起来CBCMAC实际上实现了CMAC。如果您甚至不知道正确的名称,如何创建加密库?
  • CBC-MAC 只是使用全零 IV 进行 CBC 加密,丢弃中间结果并仅使用位填充保留最后一个块。这应该不是那么难实现。
  • 我已经对此发表了评论并“支持”your github issue
  • 哦,为了好玩,我用 Java 实现了 CBC-MAC,得到的结果是 16 字节结果中最左边的 8 字节确实是 9D3391051A4E774B 所以你的 Android / Kotlin / Bouncy Castle 代码返回正确的结果。错误的是 CryptoSwift 库,这很容易通过查看源代码来确认。
  • 谢谢,如果我创建真正的 CBC-MAC swift 类,我会注意到这个主题。

标签: ios swift cryptography bouncycastle cbc-mac


【解决方案1】:

我找到了一个解决方案,在 CryptoSwift 类中开发真正的 CBC-MAC 加密:

public func authenticate(_ cipheredBytes: Array<UInt8>, padding: Padding, blockSize: Int) throws -> Array<UInt8> {
    var inBytes = cipheredBytes
    bitPadding(to: &inBytes, blockSize: blockSize)
    let blocks = inBytes.chunked(into: blockSize)

    var lastBlockEncryptionResult : [UInt8] = CBCMAC.Zero
    try blocks.forEach { (block) in
        let aes = try AES(key: Array(key), blockMode: CBC(iv: lastBlockEncryptionResult), padding: padding)
        lastBlockEncryptionResult = try aes.encrypt(block)
    }

    return lastBlockEncryptionResult
}

用我的初始参数调用它给出了答案:

9d3391051a4e774b7572fb9bca51dc51

所以前 8 位是好的。

【讨论】:

  • 第一个(最左边的)字节是 CBC-MAC 的正确字节,配置的输出大小为 64 位,是的。正如我的 cmets 和 GitHub 问题中所指出的,您当然 可以 输出 16 个字节,这通常被认为是默认配置选项(哪个更安全是另一个讨论)。跨度>
  • 哦,您可能希望在验证期间使用计算结果的时间常数比较。如果您正确实现了该功能,然后踏入下一个可能的陷阱,那就有点可惜了:) 至于您的参数,对于 CBC-MAC,只有一种类型的填充,对于 AES,块大小是预先知道的(它是128 位 / 16 字节),因此您应该只在框架需要时使用这些参数。
  • 这就是我在拉取请求中所做的 :) github.com/krzyzanowskim/CryptoSwift/pull/749 再次感谢您的帮助
猜你喜欢
  • 1970-01-01
  • 2017-02-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-08-08
  • 2020-09-11
  • 1970-01-01
相关资源
最近更新 更多