Hi,
I would like to create a JWT to interact with Apple's Webservices.
As the first step, I have a private key file, I have loaded it as Data.
I would like to create an instance of Private Key instance (not sure what is the correct struct to use, tried HMAC, P256.Signing.PrivateKey etc)
Every time an exception is thrown.
Any help and direction would greatly help. Many thanks.
let privateKeyPath = URL(fileURLWithPath: "some valid path")
let privateKeyData = try Data(contentsOf: privateKeyPath, options: .alwaysMapped)
let message = "abcd"
let messageData = message.data(using: .utf8)!
do {
//Option1:
//This throws: Error: incorrectParameterSize
let signature1 = try P256.Signing.ECDSASignature(rawRepresentation: privateKeyData)
//Option2:
let key2 = SymmetricKey(data: privateKeyData)
//This throws: Error: incorrectKeySize
var encryptedContent2 = try AES.GCM.seal(messageData, using: key2).combined
}
catch {
print("Error: \(error)")
}
The trick here is to understand the various representations of an EC key. When you create an API key for Apple web service (like the App Store Connect API), you get back a
.p8
file, which a PEM-encoded PKCS#8 private key:
% cat AuthKey_94H2XDKFA3.p8
-----BEGIN PRIVATE KEY-----
MIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQguInKjBKrr3SeyhHU
2Da4KNGZT42zclJJPYVEAggFZB6gCgYIKoZIzj0DAQehRANCAATNu4bTLE0IEWqk
0zhOrhvf4+zl1G8DDc85zXk8LuZ0+pNUEPIQQZJ4V32HclXzML70ym/wiVUkuLaE
iZxKCLUa
-----END PRIVATE KEY-----
IMPORTANT This is a real key generated using App Store Connect, but it has been revoked.
Now extract the Base64 data into a file
key.b64
and use
base64
to decode it:
% base64 -D < key.b64 > key.asn1
You can then dump the key using
dumpasn1
:
1 % dumpasn1 -p key.asn1
2 SEQUENCE {
3 INTEGER 0
4 SEQUENCE {
5 OBJECT IDENTIFIER ecPublicKey (1 2 840 10045 2 1)
6 OBJECT IDENTIFIER prime256v1 (1 2 840 10045 3 1 7)
7 }
8 OCTET STRING, encapsulates {
9 SEQUENCE {
10 INTEGER 1
11 OCTET STRING
12 B8 89 CA 8C 12 AB AF 74 9E CA 11 D4 D8 36 B8 28
13 D1 99 4F 8D B3 72 52 49 3D 85 44 02 08 05 64 1E
14 [0] {
15 OBJECT IDENTIFIER prime256v1 (1 2 840 10045 3 1 7)
16 }
17 [1] {
18 BIT STRING
19 04 CD BB 86 D3 2C 4D 08 11 6A A4 D3 38 4E AE 1B
20 DF E3 EC E5 D4 6F 03 0D CF 39 CD 79 3C 2E E6 74
21 FA 93 54 10 F2 10 41 92 78 57 7D 87 72 55 F3 30
22 BE F4 CA 6F F0 89 55 24 B8 B6 84 89 9C 4A 08 B5
23 1A
24 }
25 }
26 }
27 }
The format of this key is complex, but you should pay attention to the following:
Line 5 tells you that it’s an elliptic curve (EC) key.
Line 6 tells you the specific type of EC, namely prime 256 random 1 (
showsdumpasn1
, which RFC 5440 lists as a legacy name forprime256v1
).secp256r1
Lines 12…13 represent the key’s K value. This is essentially the private key bytes.
Lines 19…23 represent the key’s X || Y value, prefixed by an 04. This is essentially the public key bytes.
Apple CryptoKey represents a prime 256 random 1 key as the
P256
type. The public and private keys in that type have two properties,
rawRepresentation
and
x963Representation
[1]. If you successfully imported the above mentioned key and printed those properties, here’s what you get:
public raw: cdbb86d32c4d08116aa4d3384eae1bdfe3ece5d46f030dcf39cd793c2ee674fa935410f210419278577d877255f330bef4ca6ff0895524b8b684899c4a08b51a
public x9.63: 04cdbb86d32c4d08116aa4d3384eae1bdfe3ece5d46f030dcf39cd793c2ee674fa935410f210419278577d877255f330bef4ca6ff0895524b8b684899c4a08b51a
private raw: b889ca8c12abaf749eca11d4d836b828d1994f8db37252493d8544020805641e
private x9.63: 04cdbb86d32c4d08116aa4d3384eae1bdfe3ece5d46f030dcf39cd793c2ee674fa935410f210419278577d877255f330bef4ca6ff0895524b8b684899c4a08b51ab889ca8c12abaf749eca11d4d836b828d1994f8db37252493d8544020805641e
So, here’s what’s going on here:
The
for the public key is just X || Y.rawRepresentation
The
for the public key is 04 || X || Y.x963Representation
The
for the private key is just K.rawRepresentation
The
for the private key is 04 || X || Y || K.x963Representation
You can use the corresponding initialisers to create a key. For example, to create a key from the K value above:
let keyK = Data([
0xB8, 0x89, 0xCA, 0x8C, 0x12, 0xAB, 0xAF, 0x74, 0x9E, 0xCA, 0x11, 0xD4, 0xD8, 0x36, 0xB8, 0x28,
0xD1, 0x99, 0x4F, 0x8D, 0xB3, 0x72, 0x52, 0x49, 0x3D, 0x85, 0x44, 0x02, 0x08, 0x05, 0x64, 0x1E,
])
let privateKey = try P256.Signing.PrivateKey(rawRepresentation: keyK)
Share and Enjoy
—
Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware
let myEmail = "eskimo" + "1" + "@apple.com"
[1] There’s also
compactRepresentation
, but it returns
nil
.