Reputation: 33
Edit Added a screnshot of the request needed.
I'm trying to make a MacOs app in Swift 4. This app communicates with a api that requires RSA encryption.
Google didn't give results that could explaining how to do this. Apple's documentation on this subject is quite extensive (https://developer.apple.com/library/content/documentation/Security/Conceptual/CertKeyTrustProgGuide/KeyRead.html#//apple_ref/doc/uid/TP40001358-CH222-SW2), but still not what I need.
The function SecKeyCopyExternalRepresentation gives a Data object that cannot be transformed to a String. The documentation says that it is a PCKS #1 response, but I can't work it out.
I've tried a lot of things, including below, but I can't get it to work.
func externalRepresentation(_ key: SecKey) -> String? {
var error: Unmanaged<CFError>?
guard let data = SecKeyCopyExternalRepresentation(key, &error) as Data? else {
return nil
}
return data.base64EncodedString()
}
The request needs to be the following: Request
Is this even possible in Swift?
Upvotes: 3
Views: 3646
Reputation: 531
SecKeyCopyExternalRepresentation returns data in the PKCS #1 format for an RSA key. The RSA Public key PEM file looks like below
-----BEGIN RSA PUBLIC KEY-----
BASE64 ENCODED DATA
-----END RSA PUBLIC KEY-----
Within the base64 encoded data the following DER structure is present:
RSAPublicKey ::= SEQUENCE {
modulus INTEGER, -- n
publicExponent INTEGER -- e
}
The PKCS #1 format for an RSA key should be preppended by appropriate “precoded” ASN.1 binary data structure. Refer to the pemPrefixBuffer in the example below:
// creating client public and private key
var publicKeySec, privateKeySec: SecKey?
var error: Unmanaged<CFError>?
let keyattribute = [
kSecAttrKeyType as String: kSecAttrKeyTypeRSA,
kSecAttrKeySizeInBits as String : 1024,
kSecAttrIsPermanent as String: false
] as CFDictionary
SecKeyGeneratePair(keyattribute, &publicKeySec, &privateKeySec)
// client public key to pem string
let keyData = SecKeyCopyExternalRepresentation(publicKeySec!, &error)
let data = keyData! as Data
let pemPrefixBuffer :[UInt8] = [
0x30, 0x81, 0x9f, 0x30, 0x0d, 0x06, 0x09, 0x2a,
0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01,
0x05, 0x00, 0x03, 0x81, 0x8d, 0x00
]
var finalPemData = Data(bytes: pemPrefixBuffer as [UInt8], count: pemPrefixBuffer.count)
finalPemData.append(data)
let finalPemString = finalPemData.base64EncodedString(options: .lineLength64Characters)
let clientPublicKeyString = "-----BEGIN PUBLIC KEY-----\r\n\(finalPemString)\r\n-----END PUBLIC KEY-----\r\n"
Now you can send clientPublicKeyString to your server expecting a PEM encoded RSA Public key.
Upvotes: 1
Reputation: 86651
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") }
Upvotes: 2