首页 > 解决方案 > 如何访问 SecKeyCreateRandomKey() 生成的公钥中的字节?

问题描述

我正在尝试使用 AES-GCM 在 iOS 应用程序和 ESP 微控制器之间发送加密消息。但在我这样做之前,我需要在两个设备之间执行密钥交换,以便它们都有一个共享的秘密。所以我正在研究用于生成私钥/公钥对的 iOS 方法,并且有几种方法。但是有一个问题,iOS 生成的密钥与 iOS 之外的任何东西都不容易兼容(据我所知),这意味着我必须介入并对密钥进行一些修改。我注意到生成的密钥总是abdc1234在每次生成密钥时都abcd不会更改和更改。1234所以我假设这abcd只是 iOS 使用的东西1234是实际的关键。至少,这就是在 Android 中发生的事情。因此,为了检验我的假设,我试图获取在 iOS 中生成的公钥的原始字节,以便我可以切断abcd并将实际密钥发送1234到 ESP。问题是我找不到如何访问密钥包含的字节。这是我到目前为止尝试过的...

let tag = "".data(using: .utf8)!
let attributes: [String: Any] = [
   kSecAttrType as String: kSecAttrKeyTypeECSECPrimeRandom,
   kSecAttrKeySizeInBits as String: 256,
   kSecPrivateKeyAttrs as String: [
     kSecAttrIsPermanent as String: true,
     kSecAttrApplicationTag as String: tag
   ]
]
let privateKey = SecKeyCreateRandomKey(attributes as CFDictionary, &error)
let publicKey = SecKeyCopyPublicKey(privateKey!)
print("\(publicKey!)")
var address = Unmanaged.passUnretained(publicKey!).toOpaque()
do{
   withUnsafeBytes(of: &address) { bytes in
      for byte in bytes{
      print(byte)
      }
   }
}

所以第一个打印语句输出以下内容

<SecKeyRef curve type: kSecECCurveSecp256r1, algorithm id: 3, key type: ECPublicKey, version: 4, block size: 256, bits, y: ..., x: ..., addr: 0x...>

到目前为止,这很好,我想要的钥匙在我想要的曲线上,钥匙是正确的长度等等。但是现在下一个打印输出以下

144
70
215
9
1
0
0
0

就是这样。显然,要打印的最后 8 件事不是关键,它太短了。所以是的,我如何提取公钥的 x 和 y 值。我可以看到它已打印,所以必须有一种方法可以访问它,但是是的,我到处搜索,没有骰子。我认为我可以“砍掉”生成密钥的“iOS”部分的理论可能甚至不正确,但问题是如果无法将字节发送到 ESP,我什至无法对其进行测试。我希望有一种更简单的方法来实现 iOS 应用程序和 ESP 之间的密钥交换,但目前,这是我知道的唯一方法。哦,是的,我通过蓝牙发送密钥字节,我能够通过蓝牙将 iOS 应用程序连接到 ESP,这就是我尝试进行密钥交换的方式。我知道蓝牙是经过技术加密的,但我只想进行密钥交换,然后使用 AES-GCM 进一步加密蓝牙消息。所以,请。如果您知道访问密钥字节的方法,请分享!

更新:

我使用此代码为 ECDH 密钥交换生成密钥对:

let attributes: [String: Any] = [
   kSecAttrType as String: kSecAttrKeyTypeECSECPrimeRandom,
   kSecAttrKeySizeInBits as String: 256,
   kSecPrivateKeysAttrs as String: [
      kSecAttrIsPermanent as String: true
   ]
]

var error: Unmanaged<CFError>?
privateKey = SecKeyCreateRandomKey(attributes as CFDictionary, &error)!
publicKey = SecKeyCopyPublicKey(privateKey!)!
let pubKeyExternRep = SecKeyCopyExternalRepresentation(publicKey!, &error)

print("\(publicKeyExternRep!)")

输出以下内容:

{length = 65, bytes = 0x048820d8 0482e62f 7abac673 02d8a68e ... 6e0117684
ff455540 }

我试图将0x04部分中的所有内容bytes放在字符数组中,以便我可以通过蓝牙以一系列数据包的形式发送它。因此,我必须获取0x04所有 128 个字符之后的所有内容,并将其保存在一个字节数组中。最后,如果我打印字节数组的内容,它应该只是说8820d80482e62f ... 6e0117684ff455540.

我尝试了以下方法:

//^^^ previous code block that generates the key pair ^^^

let length = CFDataGetLength(pubKeyExternRep!)
let range = CFRange.init(location: 0, length: length)
let uint8Pointer = UnsafeMutablePointer<UInt8>.allocate(capacity:length)
CFDataGetBytes(pubKeyExternRep!, range, uint8Pointer)
print("\(uint8Pointer.pointee)")

这将输出以下内容:

4

这似乎很有希望,因为它与键的前 4 个匹配,0x04但随后我通过切换向上移动指针location: 5,它打印出一个随机的两位数字,该数字与任何有用的东西不匹配,即68或类似的东西。

我也试过这个:

^^^ previous code block that generates the key pair ^^^

let keyData = pubKeyExternRep as Data?
let dataString = keyData?.base64EncodedString()
var byteArray: [UInt8] = []
byteArray = Array(dataString!.utf8)
print("\(byteArray)")

现在这让我非常接近我需要的东西,它是一个字节数组,我可以对它执行操作并连接东西等等,如果我打印它输出这个:

[66, 79, 76, ..., 119, 81, 61]

问题不仅在于数组的成员与 128 字节中的任何一个都不匹配,pubKeyExternRep而且它也比我需要的 128 字节短。它是 64 字节。这实际上是我在尝试过的两种方法(CFDataGetBytes 方法和 String to Array 方法)中注意到的,无论我得到什么,它从来都不是 128 字节,它总是 64。

0x04之后我只需要所有 128 个字节print(publicKeyExternRep!)

更新2:找到解决方案,它不是标记的,但在技术上确实回答了这个问题。下班后会发布解决方案!

标签: iosencryptionbyteextractswift5

解决方案


只需使用SecKeyCopyExternalRepresentation(_:_:). 编码格式顺便说一下不是通常所说的X9.63,它是一个扁平的未压缩公钥点,没有命名曲线的参数。


推荐阅读