How to Encode an Unmanaged<Seckey> to Base64 to Send to Another Server

How to encode SecKey to base64 string using swift

To convert a string to its base64 representation, you need to take a trip through Data.

let s = "string to encode as base64"
let d = s.data(using: .utf8)
let b64 = base64EncodedString()

Swift 3 export SecKey to String

Export Key (iOS 10 only)

var error:Unmanaged<CFError>?
if let cfdata = SecKeyCopyExternalRepresentation(publicKey!, &error) {
let data:Data = cfdata as Data
let b64Key = data.base64EncodedString()
}

See https://stackoverflow.com/a/30662270/5276890 and https://stackoverflow.com/a/27935528/5276890 for longer ways which probably support iOS < 10.

Reimport Key

guard let data2 = Data.init(base64Encoded: b64Key) else {
return
}

let keyDict:[NSObject:NSObject] = [
kSecAttrKeyType: kSecAttrKeyTypeRSA,
kSecAttrKeyClass: kSecAttrKeyClassPublic,
kSecAttrKeySizeInBits: NSNumber(value: 512),
kSecReturnPersistentRef: true as NSObject
]

guard let publicKey = SecKeyCreateWithData(data2 as CFData, keyDict as CFDictionary, nil) else {
return
}

Note: This generates a base64 key and not a certificate. A lot of code samples online deal with how to generate a public key from a certificate using SecCertificateCreateWithData

Also: 512 bit is fast to generate but worthless. Pick a longer and secure value once you're satisfied with the results.

I got valid results back when importing the key I generated and exported, so I assume it works, but I did not try to encrypt and decrypt with it.

How do I export a public key SecKey that was generated using SecKeyGeneratePair to be used on a server?

SecItemCopyMatching is for you:

var dataPtr:Unmanaged<AnyObject>?
let query: [String:AnyObject] = [
kSecClass: kSecClassKey,
kSecAttrApplicationTag: "com.example.site.public",
kSecReturnData: kCFBooleanTrue
]
let qResult = SecItemCopyMatching(query, &dataPtr)

// error handling with `qResult` ...

let publicKeyData = dataPtr!.takeRetainedValue() as NSData

// convert to Base64 string
let base64PublicKey = publicKeyData.base64EncodedStringWithOptions(nil)

Swift 4:

var dataPtr:CFTypeRef?
let query: [String: Any] = [
kSecClass as String: kSecClassKey,
kSecAttrApplicationTag as String: "com.example.site.public",
kSecReturnData as String: true
]

let qResult = SecItemCopyMatching(query as CFDictionary, &dataPtr)

// error handling with `qResult` ...

let data = dataPtr as! Data
let base64PublicKey = data.base64EncodedString()

Note that the size of the data is 270, not the same as block size of the key. See this question on the crypto.stackexchange.com.

Getting a string representation of a SecKey

The normal way to encode a binary blob, like a certificate or RSA key as a string is to use base64 encoding. You can convert a Data to base64 quite easily with the function base64EncodedString(options:). i.e.

let myString = myData.base64EncodedString()

whether that is exactly what you need for this application is hard to tell because your question doesn't give much context.

Looking at your screen shot, as well as the base64 encoded string, you need a header and footer. Most of the apparently random letters in the data structure are the base64 string (the JSON conversion has encoded the line feeds with \n and something else has doubled up the backslashes). Your last step is therefore to prepend the string with -----BEGIN PUBLIC KEY----- and a new line, ad append a new line and -----END PUBLIC KEY-----

One more thing: you can get the original data back from the base64 string quite easily with Data.init?((base64Encoded base64String:,options:). i.e.

guard let myDataCopy = Data(base64Encoded: myString)
else { fatalError("the string was not really base64") }

Seckey from public key string from server in Swift


For mac:

let pubKey = "-----BEGIN PUBLIC KEY-----MIICIjANBgAgK.......InbFk1FkucQqruMyUCAwEAAQ==-----END PUBLIC KEY-----"
let pubKeyData = pubKey.dataUsingEncoding(NSASCIIStringEncoding)
var error: Unmanaged<CFErrorRef>?
let secKey = SecKeyCreateFromData(NSDictionary(), pubKeyData!, &error)

Where pubKey is a string representation of your public key.
If you don't know your public key, you can infer it from your private key with the following command:

openssl rsa -in server.key -pubout  > mykey.pub

Where server.key is the file containing -----BEGIN RSA PRIVATE KEY-----
as the first line.

For iOS:

It's a bit more complicate.
You need a der file. It's a binary representation of your certificate.
If you need to convert an existing certificate, you can do so with the following command:

 openssl x509 -outform der -in file.crt|pem -out mycert.der

The .crt or .pem file contains -----BEGIN CERTIFICATE----- as the first line.

Put the der file in your bundle and do:

let certificateData = NSData(contentsOfURL:NSBundle.mainBundle().URLForResource("mycert", withExtension: "der")!)

let certificate = SecCertificateCreateWithData(nil, certificateData!)

var trust: SecTrustRef?

let policy = SecPolicyCreateBasicX509()
let status = SecTrustCreateWithCertificates(certificate!, policy, &trust)

if status == errSecSuccess {
let key = SecTrustCopyPublicKey(trust!)!;
}

Yatta ! Key now contains a SecKey representation of your public key. Happy Pinning.



Related Topics



Leave a reply



Submit