Last active
May 29, 2020 14:33
-
-
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)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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