Skip to content

Instantly share code, notes, and snippets.

@masonbedard
Last active May 29, 2020 14:33
Show Gist options
  • Save masonbedard/4d4e21ed94ecccc8ab16158cbb763e8e to your computer and use it in GitHub Desktop.
Save masonbedard/4d4e21ed94ecccc8ab16158cbb763e8e to your computer and use it in GitHub Desktop.
Making an iOS exported public key compatible with Android (270 bytes -> 294 bytes)
A public key that is contained in the iOS keychain can be exported via something like SecKeyCopyExternalRepresentation(secKey, cferrorPtr).
But the returned CFData does not contain the OID and ASN.1 headers that Android/Java is expecting when it wants to import a public key.
The CFData is originally 270 bytes of data and needs 24 bytes of header to make it compatible.
A solution for this in Objective-C resides at: <https://blog.wingsofhermes.org/?p=42>.
I needed it in Swift so I translated it. It has not been tested extensively but I have successfully imported the resulting public key data into an Android app.
// implementation
func encodeLength(buf: inout [UInt8], length: size_t) -> size_t {
if length < 128 {
buf[0] = UInt8(length)
return 1
}
let i = (length / 256) + 1
var l = length
buf[0] = UInt8(i + 0x80)
for j in 0..<i {
buf[i-j] = UInt8(l & 0xFF)
l = l >> 8
}
return i + 1
}
func getInteropablePublicKey(publicKeyData: Data) -> Data {
let oid: [UInt8] = [
0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00
]
let bitStringEncLength: Int
if publicKeyData.count + 1 < 128 {
bitStringEncLength = 1
} else {
bitStringEncLength = ((publicKeyData.count + 1) / 256) + 2
}
var encKey = [UInt8]()
var buffer = [UInt8](repeating: 0x00, count: 14)
var builder = [UInt8]()
builder += [0x30]
let i = oid.count + 2 + bitStringEncLength + publicKeyData.count
var j = encodeLength(buf: &buffer, length: i)
builder += buffer[0..<j]
encKey += builder
encKey += oid
builder = [UInt8]()
builder += [0x03]
j = encodeLength(buf: &buffer, length: publicKeyData.count + 1)
builder += buffer[0..<j]
builder += [0x00]
encKey += builder
encKey += publicKeyData
return Data(bytes: encKey, count: encKey.count)
}
// usage
func exportPublicKey() -> String? {
let publicKey: SecKey = getPublicKey() // grab the public key either from KeyChain or from an external source passed into SecKeyCreateWithData
var error: Unmanaged<CFError>?
guard let externalPublicKey = SecKeyCopyExternalRepresentation(publicKey, &error) else {
if let error = error {
print(error)
}
return nil
}
let interoperablePublicKeyData = getInteropablePublicKey(publicKeyData: externalPublicKey as Data)
return interoperablePublicKeyData.base64EncodedString()
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment