Cryptokit - How to get PEM representation of Curve25519 Public Key

Hi,

How do I generate the pem representation of a curve25519 public key? I can generate the key using :
Code Block
     let privateKey = Curve25519.KeyAgreement.PrivateKey()
let publicKey = privateKey.publicKey
print(publicKey.rawRepresentation.base64EncodedString())


This prints a string like this :

Code Block
GyQfzi3bLfpDpzi8e9j6lovX15EZY1t1fQQcnJlURxI=


But the expected strings are more like :

Code Block
-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEApxUNh3jHlNSAWE7fadipsh9AjXv6439VY3EWEC5kbgY=\n-----END PUBLIC KEY-----


Even if I add the
" -----BEGIN PUBLIC KEY-----"
and
"-----END PUBLIC KEY-----"
tags, it still doesn't process the key.

So what format is exactly the base64 encoded string of the raw format of curve25519 public key? And how do I generate the public key pem format?

The requirement is for Swift iOS.

Accepted Reply

As I’ve discussed on your other thread, the rawRepresentation of a Curve25519 key is simply the 32-byte raw key. For example, this:

Code Block
let privateKey = Curve25519.KeyAgreement.PrivateKey()
let publicKey = privateKey.publicKey
let rawKeyBytes = publicKey.rawRepresentation
debugPrint(rawKeyBytes as NSData)


printed this:

Code Block
<eeaef887 dec4db7c d5b1022f c504185c e28ac34f a55daf54 45f1739b a97dfd30>


The PEM you’re trying to create needs three levels of wrapping:
  1. You first have to wrap the raw key in an ASN.1 SubjectPublicKeyInfo structure.

  2. You then have to Base64 encode that.

  3. Finally, you add the header and trailer strings.

I’m presuming that you can handle steps 2 and 3, so I’m going to focus on step 1.

The correct way to render and parse ASN.1 is with an ASN.1 library. Unfortunately, iOS does not have an API for this, and the third-party libraries out there are quite hard to integrate.

In this case, however, you can get away with prepending a fixed header on to your 32-byte raw key data. To continue the above example,

Code Block
let prefix = Data([0x30, 0x2A, 0x30, 0x05, 0x06, 0x03, 0x2B, 0x65, 0x6E, 0x03, 0x21, 0x00])
let subjectPublicKeyInfo = prefix + rawKeyBytes


If you put those bytes into a file and then dump the ASN.1, you’ll see this:

Code Block
% dumpasn1 tmp.asn1
0 42: SEQUENCE {
2 5: SEQUENCE {
4 3: OBJECT IDENTIFIER curveX25519 (1 3 101 110)
: }
9 33: BIT STRING
: EE AE F8 87 DE C4 DB 7C D5 B1 02 2F C5 04 18 5C
: E2 8A C3 4F A5 5D AF 54 45 F1 73 9B A9 7D FD 30
: }
0 warnings, 0 errors.


which should look very familiar.

IMPORTANT This only works because you know you’re working with a Curve 25519 key and that key’s raw bytes is of a fixed length.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Replies

As I’ve discussed on your other thread, the rawRepresentation of a Curve25519 key is simply the 32-byte raw key. For example, this:

Code Block
let privateKey = Curve25519.KeyAgreement.PrivateKey()
let publicKey = privateKey.publicKey
let rawKeyBytes = publicKey.rawRepresentation
debugPrint(rawKeyBytes as NSData)


printed this:

Code Block
<eeaef887 dec4db7c d5b1022f c504185c e28ac34f a55daf54 45f1739b a97dfd30>


The PEM you’re trying to create needs three levels of wrapping:
  1. You first have to wrap the raw key in an ASN.1 SubjectPublicKeyInfo structure.

  2. You then have to Base64 encode that.

  3. Finally, you add the header and trailer strings.

I’m presuming that you can handle steps 2 and 3, so I’m going to focus on step 1.

The correct way to render and parse ASN.1 is with an ASN.1 library. Unfortunately, iOS does not have an API for this, and the third-party libraries out there are quite hard to integrate.

In this case, however, you can get away with prepending a fixed header on to your 32-byte raw key data. To continue the above example,

Code Block
let prefix = Data([0x30, 0x2A, 0x30, 0x05, 0x06, 0x03, 0x2B, 0x65, 0x6E, 0x03, 0x21, 0x00])
let subjectPublicKeyInfo = prefix + rawKeyBytes


If you put those bytes into a file and then dump the ASN.1, you’ll see this:

Code Block
% dumpasn1 tmp.asn1
0 42: SEQUENCE {
2 5: SEQUENCE {
4 3: OBJECT IDENTIFIER curveX25519 (1 3 101 110)
: }
9 33: BIT STRING
: EE AE F8 87 DE C4 DB 7C D5 B1 02 2F C5 04 18 5C
: E2 8A C3 4F A5 5D AF 54 45 F1 73 9B A9 7D FD 30
: }
0 warnings, 0 errors.


which should look very familiar.

IMPORTANT This only works because you know you’re working with a Curve 25519 key and that key’s raw bytes is of a fixed length.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"
Wow!!! That's just super awesome.. base64encoded text, and then remove the

Hey Eskimo, will the reverse work ?

Like I can convert the pem to input for curve25519 for creating public key?
Hard luck :(

I tried this method but the online pem parser - (8gwifi.org/PemParserFunctions.jsp) and server are returning an error :

Code Block
java.lang.Exception: Error Performing Parsing java.lang.Exception: org.bouncycastle.openssl.PEMException: unable to convert key pair: no such algorithm: 1.3.101.110 for provider BC





Hi Eskimo,

Somehow, the suggested prefix is not working with the server. The server is expecting something similar to this (I got this from the hex of the given server public key's data)

Code Block
Data([0x30, 0x82, 0x01, 0x31, 0x30, 0x81, 0xea, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x30, 0x81, 0xde, 0x02, 0x01, 0x01, 0x30, 0x2b, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x01, 0x01, 0x02, 0x20, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xed, 0x30, 0x44, 0x04, 0x20, 0x2a, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x98, 0x49, 0x14, 0xa1, 0x44, 0x04, 0x20, 0x7b, 0x42, 0x5e, 0xd0, 0x97, 0xb4, 0x25, 0xed, 0x09, 0x7b, 0x42, 0x5e, 0xd0, 0x97, 0xb4, 0x25, 0xed, 0x09, 0x7b, 0x42, 0x5e, 0xd0, 0x97, 0xb4, 0x26, 0x0b, 0x5e, 0x9c, 0x77, 0x10, 0xc8, 0x64, 0x04, 0x41, 0x04, 0x2a, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xad, 0x24, 0x5a, 0x20, 0xae, 0x19, 0xa1, 0xb8, 0xa0, 0x86, 0xb4, 0xe0, 0x1e, 0xdd, 0x2c, 0x77, 0x48, 0xd1, 0x4c, 0x92, 0x3d, 0x4d, 0x7e, 0x6d, 0x7c, 0x61, 0xb2, 0x29, 0xe9, 0xc5, 0xa2, 0x7e, 0xce, 0xd3, 0xd9, 0x02, 0x20, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0xde, 0xf9, 0xde, 0xa2, 0xf7, 0x9c, 0xd6, 0x58, 0x12, 0x63, 0x1a, 0x5c, 0xf5, 0xd3, 0xed, 0x02, 0x01, 0x08, 0x03, 0x42, 0x00])

If I add this prefix to the raw bytes, and then base64 encode it, I get something like this:

Code Block
MIIBMTCB6gYHKoZIzj0CATCB3gIBATArBgcqhkjOPQEBAiB/////////////////////////////////////////7TBEBCAqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqYSRShRAQge0Je0Je0Je0Je0Je0Je0Je0Je0Je0Je0JgtenHcQyGQEQQQqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq0kWiCuGaG4oIa04B7dLHdI0UySPU1+bXxhsinpxaJ+ztPZAiAQAAAAAAAAAAAAAAAAAAAAFN753qL3nNZYEmMaXPXT7QIBCANCALG4aNI0Dxlyo1FqE+EUPCzIiSIYcchBQSjdzazH7eI6


which is somewhat similar to the expected string.

So I'm guessing I need to find out what THIS particular header corresponds to
(client confirms that this is curve25519 key )

Any help with this header? :)
As of now, the said reply works for me, but the server is still not accepting it. We're waiting for talks from them to finalise on the format. Thank you Eskimo :)

Hi, did you find a solution to this issue @annathomas?

The server was generating using a different library, hence what Eskimo was telling right. Though we were sending the correct keys, server was expecting something else. The server was accepting requests based on a Java library, so it was working on Android. In the end we switched to using Tink library by Google for iOS

  • Hi @annathomas, I am suffering from the exact same issue, can you please provide the link to the library that was able to get that key generated for you? I tried looking into "Tink by Google", but, there seems to be no live codebase for iOS. Your help would be really appreciated. Thanks.

Add a Comment