Reputation: 195
I am trying to send encrypted messages using AES-GCM between an iOS app and an ESP microcontroller. But before I do that, I need to perform a key exchange between the two devices so that they both have a shared secret. So I am looking into iOS methods for generating private/public key pairs and there are several. However there is one problem, the keys generated by iOS aren't readily compatible with anything outside of iOS (as far as I understand) which means I have to jump in and make some modifications to the keys. I noticed the keys that are generated are always abdc1234
where abcd
never changes and 1234
changes every time a key is generated. So I am assuming that abcd
is just something iOS uses and 1234
is the actual key. At least, that's what happens in Android. So to test my hypothesis, I'm trying to get the raw bytes of the public key that was generated in iOS so I can cut off the abcd
and send the actual key 1234
to the ESP. The problem is I can't find how to access the bytes that the key contains. Here's what I tried so far...
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)
}
}
}
So the first print statement outputs the following
<SecKeyRef curve type: kSecECCurveSecp256r1, algorithm id: 3, key type: ECPublicKey, version: 4, block size: 256, bits, y: ..., x: ..., addr: 0x...>
Which so far so good, the key that I want is on the curve that I want and the key is the correct length and such. But now the next print outputs the following
144
70
215
9
1
0
0
0
That's it. Clearly, the last 8 things to be printed are not the key, it's too short. So yeah, how do I extract the x and y value of the public key. I can see it printed so there must be a way to access it but yeah I have searched everywhere and no dice. My theory that I can "chop" off the "iOS" part of the generated key might not even be correct but the thing is I can't even test it without being able to send the bytes to the ESP. I hope there is an easier way to achieve the key exchange between iOS app and ESP but for now, this is the only way I know how. Oh yeah, I send the key bytes over bluetooth, I was able to connect the iOS app to the ESP via bluetooth and that's how I'm trying to make the key exchange. I know bluetooth is techncially encrypted but I just want to do the key exchange and then encrypt the bluetooth messages further by using AES-GCM. So please. If you know of a way to access the key bytes, please share!
UPDATE:
I use this code to generate key pair for ECDH key exchange:
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!)")
Which outputs the following:
{length = 65, bytes = 0x048820d8 0482e62f 7abac673 02d8a68e ... 6e0117684
ff455540 }
I am trying to get everything after the 0x04
in the bytes
section to be in a character array so that I can send it over bluetooth in a sequence of packets. So it is imperative that I obtain everything after the 0x04
, all 128 characters, and have it in a byte array. In the end, if I print the contents of the byte array, it should just say 8820d80482e62f ... 6e0117684ff455540
.
I have tried the following:
//^^^ 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)")
This outputs the following:
4
Which seems promising since it matches the first 4 of the key, the 0x04
but then I move up the pointer by switching location: 5
and it prints a random two digit number that doesn't match with anything useful, i.e. 68
or something like that.
I also tried this:
^^^ 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)")
Now this gets me extremely close to what I need, it's a byte array and I can perform operations on it and concatenate stuff and such, if I print it outputs this:
[66, 79, 76, ..., 119, 81, 61]
The problem is not only do the members of the array not match any of the 128 bytes in the pubKeyExternRep
but its also shorter than the 128 bytes I need. It's 64 bytes. This is actually something I been noticing for both methods I tried (the CFDataGetBytes method and the String to Array method), whatever it is I get, it's never 128 bytes, it's always 64.
I just need all 128 bytes after the 0x04
when I print(publicKeyExternRep!)
UPDATE 2: Found solution, it's not the one that's marked but that did technically answer the question. Will post solution after work!
Upvotes: 1
Views: 2148
Reputation: 93968
Just use SecKeyCopyExternalRepresentation(_:_:)
. Encoded format is not as what is commonly referred to as X9.63 by the way, it is a flat uncompressed public key point without the parameters of the named curve.
Upvotes: 2