I'm trying to create an RSA key for signing data in my Xamarin.iOS app on a physical device. Below is my code
using (var access = new SecAccessControl(SecAccessible.WhenUnlockedThisDeviceOnly, SecAccessControlCreateFlags.BiometryCurrentSet|SecAccessControlCreateFlags.PrivateKeyUsage))
{
var keyParameters = new SecKeyGenerationParameters
{
KeyType = SecKeyType.RSA,
KeySizeInBits = 2048,
Label = AppInfo.PackageName,
TokenID = SecTokenID.SecureEnclave,
PrivateKeyAttrs = new SecKeyParameters
{
IsPermanent = true,
ApplicationTag = NSData.FromString(AppInfo.PackageName, NSStringEncoding.UTF8),
AccessControl = access,
CanSign = true,
CanVerify = true
}
};
var privateKey = SecKey.CreateRandomKey(keyParameters.Dictionary, out NSError nsError);
var publicKey = privateKey.GetPublicKey();
NSData keyData = publicKey.GetExternalRepresentation();
}
the above code is giving below NSError and null private key
{The operation couldn’t be completed. (OSStatus error -50 - Key generation failed, error -50)}.
The above code is working fine without SecAccessControlCreateFlags.PrivateKeyUsage and TokenID = SecTokenID.SecureEnclave
Please let me know how to resolve this.
The Secure Enclave does not support RSA keys. It can only create elliptic curve keys with curve P256 (aka NIST P-256 or secp256r1). If you want to use RSA, you're left with the default Security implementation (without the Secure Enclave flag).
If you insist on using the secure enclave, you'll have to use the above mentioned curve.
Related
I am exchanging data with a device that requires encryption. According to the instructions, my iOS app generates an RSA 2048-bit public-private key pair and I send the device the public key.
I created the public-private key pair using a macOS command line with openssl shown here (as I cannot figure out how to make iOS do it - but that's a future question):
openssl req -x509 -newkey rsa:2048 -out cert.pem
openssl x509 -in cert.pem -noout -pubkey
I copied the values from the cert.pem and the private and public keys into my app:
let certificate = "MIIDej....GWB"
let privateKey = "MIIFhz...Q=="
let publicKey = "MIIBI...AQAB"
When I send the device the public key, the device is happy and sends back data in AES-128 encrypted using the public key I sent.
I am in over my head here, but I'm the one trying to get this device to work.
Is there a way to take the private key or cert that I have set as Strings in my app to decode the data coming from the device?
I have been looking at the Security and CryptoKit docs and it makes my head swim.
FWIW, I was able to use SecCertificateCreateWithData on the String set from the cert.pem file and read some of its info like the summary. But I have no idea how to apply the private key or use the cert to decode the data. I need like a Dummies cliff notes or something.
Thank you.
Apple documentation describe how to generate key pair on the iOS side.
Example:
let tag = "com.example.keys.mykey".data(using: .utf8)!
let attributes: [String: Any] =
[kSecAttrKeyType as String: kSecAttrKeyTypeRSA,
kSecAttrKeySizeInBits as String: 2048,
kSecPrivateKeyAttrs as String:
[kSecAttrIsPermanent as String: true,
kSecAttrApplicationTag as String: tag]
]
var publicKey: SecKey?
var privateKey: SecKey?
SecKeyGeneratePair(attributes, &publicKey, &privateKey)
More attributes are available here.
And then you can get External representations of private or public key using method:
SecKeyCopyExternalRepresentation.
Example:
var error: Unmanaged<CFError>?
SecKeyCopyExternalRepresentation(secKey, &error) as Data?
To decrypt AES-128 data you need to use CCCrypt. Here you have example from Apple support how the data is encrypted, but to decrypt data you need to change first argument to kCCDecrypt.
Example:
extern NSData * SecDecryptAES128CBCPad(NSData * data, NSData * key, NSData * iv) {
CCCryptorStatus err;
NSMutableData * result;
size_t resultLength;
NSCParameterAssert(key.length == kCCKeySizeAES128);
NSCParameterAssert(iv.length == kCCBlockSizeAES128);
// Padding can expand the data, so we have to allocate space for that. The rule for block
// cyphers, like AES, is that the padding only adds space on encryption (on decryption it
// can reduce space, obviously, but we don't need to account for that) and it will only add
// at most one block size worth of space.
result = [[NSMutableData alloc] initWithLength:[data length] + kCCBlockSizeAES128];
err = CCCrypt(
kCCDecrypt,
kCCAlgorithmAES128,
kCCOptionPKCS7Padding,
key.bytes, key.length,
iv.bytes,
data.bytes, data.length,
result.mutableBytes, result.length,
&resultLength
);
assert(err == kCCSuccess);
// Set the output length to the value returned by CCCrypt. This is necessary because
// we have padding enabled, meaning that we might have allocated more space than we needed.
[result setLength:resultLength];
return result;
}
Arguments in CCCrypt method (like kCCOptionPKCS7Padding, iv) you need to adjust to your example.
I am generating a PKCS12 file using OpenSSL library in Swift. Following is the code that I use to generate the file:
guard let p12 = PKCS12_create(passPhrase, name, privateKey, certificate, nil, NID_aes_256_cbc, 0, 0, 0, 0) else {
ERR_print_errors_fp(stderr)
return
}
Please note that I am using NID_aes_256_cbc to encrypt the private key.
Then when I use the following code to import the p12 file in swift, I had the error code of -26275.
var items: CFArray?
let certOptions: NSDictionary = [kSecImportExportPassphrase as NSString: passwordStr as NSString]
self.securityError = SecPKCS12Import(p12Data as NSData, certOptions, &items)
print(securityError!.description)
After consulting OSStatus for error code explanation, I found that it's a errSecInvalidKey.
If I change the parameter from NID_aes_256_cbc back to 0, which uses the default 3DES algorithm, everything works fine.
So I was wondering if SecPKCS12Import decrypting the private key with 3DES algorithm by default. If yes, how should I make SecPKCS12Import to decrypt the AES encrypted private key?
I have generated private key and public key to my Swift-based iOS application using SecKeyGeneratePair function.Then, I generated Certificate Signing Request using iOS CSR generationand my server replied with certificate chain in PEM format.I converted PEM-certificate to DER-format using following code:
var modifiedCert = certJson.replacingOccurrences(of: "-----BEGIN CERTIFICATE-----", with: "")
modifiedCert = modifiedCert.replacingOccurrences(of: "-----END CERTIFICATE-----", with: "")
modifiedCert = modifiedCert.replacingOccurrences(of: "\n", with: "")
let dataDecoded = NSData(base64Encoded: modifiedCert, options: [])
Now, I should create certificate from DER-data using let certificate = SecCertificateCreateWithData(nil, certDer)
My question is following : How can I connect the certificate with private key I have created in the beginning and get the identity where both of these(keys and certificate) belongs?Maybe, add certificate to keychain and get the identity using SecItemCopyMatching? I have followed the procedure presented in question SecIdentityRef procedure
Edit:
When adding the certificate to keychain, I get the status response 0, which I believe means that certificate has been added to keychain.
let certificate: SecCertificate? = SecCertificateCreateWithData(nil, certDer)
if certificate != nil{
let params : [String: Any] = [
kSecClass as String : kSecClassCertificate,
kSecValueRef as String : certificate!
]
let status = SecItemAdd(params as CFDictionary, &certRef)
print(status)
}
Now when I'm trying to get the identity, I get status -25300 (errSecItemNotFound). Following code is used to get the identity. tag is the private key tag I have used to generate private/public key.
let query: [String: Any] = [
kSecClass as String : kSecClassIdentity,
kSecAttrApplicationTag as String : tag,
kSecReturnRef as String: true
]
var retrievedData: SecIdentity?
var extractedData: AnyObject?
let status = SecItemCopyMatching(query as NSDictionary, &extractedData)
if (status == errSecSuccess) {
retrievedData = extractedData as! SecIdentity?
}
I'm able to get the private key & public key & certificate from the keychain using SecItemCopyMatching and add the certificate to keychain, but querying the SecIdentity does not work. Is it possible that my certificate does not match to my keys? How is that checked?
I printed public key from iOS in base64 format. The following was printed:
MIIBCgKCAQEAo/MRST9oZpO3nTl243o+ocJfFCyKLtPgO/QiO9apb2sWq4kqexHy
58jIehBcz4uGJLyKYi6JHx/NgxdSRKE3PcjU2sopdMN35LeO6jZ34auH37gX41Sl
4HWkpMOB9v/OZvMoKrQJ9b6/qmBVZXYsrSJONbr+74/mI/m1VNtLOM2FIzewVYcL
HHsM38XOg/kjSUsHEUKET/FfJkozgp76r0r3E0khcbxwU70qc77YPgeJHglHcZKF
ZHFbvNz4E9qUy1mWJvoCmAEItWnyvuw+N9svD1Rri3t5qlaBwaIN/AtayHwJWoWA
/HF+Jg87eVvEErqeT1wARzJL2xv5V1O4ZwIDAQAB
Then from the certificate signing request I extracted the public key using openssl (openssl req -in ios.csr -pubkey -noout). The following response was printed:
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAo/MRST9oZpO3nTl243o+
ocJfFCyKLtPgO/QiO9apb2sWq4kqexHy58jIehBcz4uGJLyKYi6JHx/NgxdSRKE3
PcjU2sopdMN35LeO6jZ34auH37gX41Sl4HWkpMOB9v/OZvMoKrQJ9b6/qmBVZXYs
rSJONbr+74/mI/m1VNtLOM2FIzewVYcLHHsM38XOg/kjSUsHEUKET/FfJkozgp76
r0r3E0khcbxwU70qc77YPgeJHglHcZKFZHFbvNz4E9qUy1mWJvoCmAEItWnyvuw+
N9svD1Rri3t5qlaBwaIN/AtayHwJWoWA/HF+Jg87eVvEErqeT1wARzJL2xv5V1O4
ZwIDAQAB
-----END PUBLIC KEY----
It seems that there is a minor difference in the beginning of the key generated from CSR. (MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A). Based on the question RSA encryption, it seems that MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A is base64-formatted identifier for RSA encryption "1.2.840.113549.1.1.1". So I guess the public key might be fine?
We don't use that same method of CSR, but we have an equivalent thing where we do the following:
Generate key pair
Ship the public key to the remote server
Remote server generates a signed client certificate using the public key
Ship the client certificate back to the iOS device
Add the client certificate to the keychain
Later on, use the client certificate in an NSURLSession or similar.
As you seem to have discovered, iOS needs this extra thing called an "identity" to tie the client cert.
We also discovered that iOS has a weird thing where you need to DELETE the public key from the keychain before you add the client cert and identity into it, otherwise the identity doesn't seem to locate the client certificate properly instead. We chose to add the public key back in but as a "generic password" (i.e arbitrary user data) - we only do this because iOS doesn't have a sensible API for extracting a public key from a cert on the fly, and we need the public key for other strange things we happen to be doing.
If you're just doing TLS client certificate auth, once you have the certificate you won't need an explicit copy of the public key so you can simplify the process by simply deleting it, and skip the "add-back-in-as-generic-password" bit
Please excuse the giant pile of code, crypto stuff always seems to require a lot of work.
Here's bits of code to perform the above tasks:
Generating the keypair, and deleting/re-saving the public key
/// Returns the public key binary data in ASN1 format (DER encoded without the key usage header)
static func generateKeyPairWithPublicKeyAsGenericPassword(privateKeyTag: String, publicKeyAccount: String, publicKeyService: String) throws -> Data {
let tempPublicKeyTag = "TMPPUBLICKEY:\(privateKeyTag)" // we delete this public key and replace it with a generic password, but it needs a tag during the transition
let privateKeyAttr: [NSString: Any] = [
kSecAttrApplicationTag: privateKeyTag.data(using: .utf8)!,
kSecAttrAccessible: kSecAttrAccessibleAlwaysThisDeviceOnly,
kSecAttrIsPermanent: true ]
let publicKeyAttr: [NSString: Any] = [
kSecAttrApplicationTag: tempPublicKeyTag.data(using: .utf8)!,
kSecAttrAccessible: kSecAttrAccessibleAlwaysThisDeviceOnly,
kSecAttrIsPermanent: true ]
let keyPairAttr: [NSString: Any] = [
kSecAttrKeyType: kSecAttrKeyTypeRSA,
kSecAttrKeySizeInBits: 2048,
kSecPrivateKeyAttrs: privateKeyAttr,
kSecPublicKeyAttrs: publicKeyAttr ]
var publicKey: SecKey?, privateKey: SecKey?
let genKeyPairStatus = SecKeyGeneratePair(keyPairAttr as CFDictionary, &publicKey, &privateKey)
guard genKeyPairStatus == errSecSuccess else {
log.error("Generation of key pair failed. Error = \(genKeyPairStatus)")
throw KeychainError.generateKeyPairFailed(genKeyPairStatus)
}
// Would need CFRelease(publicKey and privateKey) here but swift does it for us
// we store the public key in the keychain as a "generic password" so that it doesn't interfere with retrieving certificates
// The keychain will normally only store the private key and the certificate
// As we want to keep a reference to the public key itself without having to ASN.1 parse it out of the certificate
// we can stick it in the keychain as a "generic password" for convenience
let findPubKeyArgs: [NSString: Any] = [
kSecClass: kSecClassKey,
kSecValueRef: publicKey!,
kSecAttrKeyType: kSecAttrKeyTypeRSA,
kSecReturnData: true ]
var resultRef:AnyObject?
let status = SecItemCopyMatching(findPubKeyArgs as CFDictionary, &resultRef)
guard status == errSecSuccess, let publicKeyData = resultRef as? Data else {
log.error("Public Key not found: \(status))")
throw KeychainError.publicKeyNotFound(status)
}
// now we have the public key data, add it in as a generic password
let attrs: [NSString: Any] = [
kSecClass: kSecClassGenericPassword,
kSecAttrAccessible: kSecAttrAccessibleAlwaysThisDeviceOnly,
kSecAttrService: publicKeyService,
kSecAttrAccount: publicKeyAccount,
kSecValueData: publicKeyData ]
var result: AnyObject?
let addStatus = SecItemAdd(attrs as CFDictionary, &result)
if addStatus != errSecSuccess {
log.error("Adding public key to keychain failed. Error = \(addStatus)")
throw KeychainError.cannotAddPublicKeyToKeychain(addStatus)
}
// delete the "public key" representation of the public key from the keychain or it interferes with looking up the certificate
let pkattrs: [NSString: Any] = [
kSecClass: kSecClassKey,
kSecValueRef: publicKey! ]
let deleteStatus = SecItemDelete(pkattrs as CFDictionary)
if deleteStatus != errSecSuccess {
log.error("Deletion of public key from keychain failed. Error = \(deleteStatus)")
throw KeychainError.cannotDeletePublicKeyFromKeychain(addStatus)
}
// no need to CFRelease, swift does this.
return publicKeyData
}
NOTE that publicKeyData isn't strictly in DER format, it's in "DER with the first 24 bytes trimmed off" format. I'm not sure what this is called officially, but both microsoft and apple seem to use it as the raw format for public keys. If your server is a microsoft one running .NET (desktop or core) then it will probably be happy with the public key bytes as-is. If it's Java and expects DER you may need to generate the DER header - this is a fixed sequence of 24 bytes you can probably just concatenate on.
Adding the client certificate to the keychain, generating an Identity
static func addIdentity(clientCertificate: Data, label: String) throws {
log.info("Adding client certificate to keychain with label \(label)")
guard let certificateRef = SecCertificateCreateWithData(kCFAllocatorDefault, clientCertificate as CFData) else {
log.error("Could not create certificate, data was not valid DER encoded X509 cert")
throw KeychainError.invalidX509Data
}
// Add the client certificate to the keychain to create the identity
let addArgs: [NSString: Any] = [
kSecClass: kSecClassCertificate,
kSecAttrAccessible: kSecAttrAccessibleAlwaysThisDeviceOnly,
kSecAttrLabel: label,
kSecValueRef: certificateRef,
kSecReturnAttributes: true ]
var resultRef: AnyObject?
let addStatus = SecItemAdd(addArgs as CFDictionary, &resultRef)
guard addStatus == errSecSuccess, let certAttrs = resultRef as? [NSString: Any] else {
log.error("Failed to add certificate to keychain, error: \(addStatus)")
throw KeychainError.cannotAddCertificateToKeychain(addStatus)
}
// Retrieve the client certificate issuer and serial number which will be used to retrieve the identity
let issuer = certAttrs[kSecAttrIssuer] as! Data
let serialNumber = certAttrs[kSecAttrSerialNumber] as! Data
// Retrieve a persistent reference to the identity consisting of the client certificate and the pre-existing private key
let copyArgs: [NSString: Any] = [
kSecClass: kSecClassIdentity,
kSecAttrIssuer: issuer,
kSecAttrSerialNumber: serialNumber,
kSecReturnPersistentRef: true] // we need returnPersistentRef here or the keychain makes a temporary identity that doesn't stick around, even though we don't use the persistentRef
let copyStatus = SecItemCopyMatching(copyArgs as CFDictionary, &resultRef);
guard copyStatus == errSecSuccess, let _ = resultRef as? Data else {
log.error("Identity not found, error: \(copyStatus) - returned attributes were \(certAttrs)")
throw KeychainError.cannotCreateIdentityPersistentRef(addStatus)
}
// no CFRelease(identityRef) due to swift
}
In our code we chose to return a label, and then look up the identity as-required using the label, and the following code. You could also chose to just return the identity ref from the above function rather than the label. Here's our getIdentity function anyway
Getting the identity later on
// Remember any OBJECTIVE-C code that calls this method needs to call CFRetain
static func getIdentity(label: String) -> SecIdentity? {
let copyArgs: [NSString: Any] = [
kSecClass: kSecClassIdentity,
kSecAttrLabel: label,
kSecReturnRef: true ]
var resultRef: AnyObject?
let copyStatus = SecItemCopyMatching(copyArgs as CFDictionary, &resultRef)
guard copyStatus == errSecSuccess else {
log.error("Identity not found, error: \(copyStatus)")
return nil
}
// back when this function was all ObjC we would __bridge_transfer into ARC, but swift can't do that
// It wants to manage CF types on it's own which is fine, except they release when we return them out
// back into ObjC code.
return (resultRef as! SecIdentity)
}
// Remember any OBJECTIVE-C code that calls this method needs to call CFRetain
static func getCertificate(label: String) -> SecCertificate? {
let copyArgs: [NSString: Any] = [
kSecClass: kSecClassCertificate,
kSecAttrLabel: label,
kSecReturnRef: true]
var resultRef: AnyObject?
let copyStatus = SecItemCopyMatching(copyArgs as CFDictionary, &resultRef)
guard copyStatus == errSecSuccess else {
log.error("Identity not found, error: \(copyStatus)")
return nil
}
// back when this function was all ObjC we would __bridge_transfer into ARC, but swift can't do that
// It wants to manage CF types on it's own which is fine, except they release when we return them out
// back into ObjC code.
return (resultRef as! SecCertificate)
}
And finally
Using the identity to authenticate against a server
This bit is in objc because that's how our app happens to work, but you get the idea:
SecIdentityRef _clientIdentity = [XYZ getClientIdentityWithLabel: certLabel];
if(_clientIdentity) {
CFRetain(_clientIdentity);
}
SecCertificateRef _clientCertificate = [XYZ getClientCertificateWithLabel:certLabel];
if(_clientCertificate) {
CFRetain(_clientCertificate);
}
...
- (void)URLSession:(nullable NSURLSession *)session
task:(nullable NSURLSessionTask *)task
didReceiveChallenge:(nullable NSURLAuthenticationChallenge *)challenge
completionHandler:(nullable void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler {
if (challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodClientCertificate) {
// supply the appropriate client certificate
id bridgedCert = (__bridge id)_clientCertificate;
NSArray* certificates = bridgedCert ? #[bridgedCert] : #[];
NSURLCredential* credential = [NSURLCredential credentialWithIdentity:identity certificates:certificates persistence:NSURLCredentialPersistenceForSession];
completionHandler(NSURLSessionAuthChallengeUseCredential, credential);
}
}
This code took a lot of time to get right. iOS certificate stuff is exceedingly poorly documented, hopefully this helps.
The usual way to generate SSL certificates is that private key is used to generate the CSR, Certificate Signing Request info. In fact, you're hidding as well company, email, etc info with that key signature. With that CSR, then, you sign your certificate, so it will be associated with your private key and info stored in CSR, nevermind the public key. I'm currently not able to see in IOS CSR Generation project where you can pass your generated key: seems to me that CSR generated with IOS CSR Generation project is using it's own generated key, or no private key at all. That will got then logic with the fact that you cannot extract private key from CER or DER, because it isn't there.
I using NetworkExtension framework to creating an application, it connect to VPN server via NEVPNProtocolIKEv2.
After research, I found an tutorial about working with NetworkExtension framework, and I try to follow it.
(http://ramezanpour.net/post/2014/08/03/configure-and-manage-vpn-connections-programmatically-in-ios-8/)
But, I stuck when I configure identityData of this protocol.
Here is m code:
self.vpnManager.loadFromPreferencesWithCompletionHandler { [unowned self] (error) in
if error != nil {
printError("\(error?.errorDescription)")
return
}
let p = NEVPNProtocolIKEv2()
p.username = server.userName
p.serverAddress = server.serverUrl
// Get password persistent reference from keychain
self.createKeychainValue(server.password, forIdentifier: KeychainId_Password)
p.passwordReference = self.searchKeychainCopyMatching(KeychainId_Password)
p.authenticationMethod = NEVPNIKEAuthenticationMethod.None
self.createKeychainValue(kVPNsecret, forIdentifier: KeychainId_PSK)
p.sharedSecretReference = self.searchKeychainCopyMatching(KeychainId_PSK)
// certificate
p.identityData = ??????
p.useExtendedAuthentication = true
p.disconnectOnSleep = false
self.vpnManager.`protocol` = p
self.vpnManager.localizedDescription = server.serverName
self.vpnManager.saveToPreferencesWithCompletionHandler({ [unowned self] (error) in
if error != nil {
printError("Save config failed " + error!.localizedDescription)
}
})
}
In tutorial, p.identityData is NSData, that was loading from a P12 file.
But I have only a string that call: server.certificate
This server.certificate has a value like this
"-----BEGIN CERTIFICATE-----\nMIIEdDCCA1ygAwIBAgIBADANBgkqhki......1iEtCZg7SAlsBiaxpJzpZm5C6OifUCkUfZNdPQ==\n-----END CERTIFICATE-----\n"
This is a very very long string, that call x509Certificate... or something like that, I do not remember exactly.
I found an library support write an String to file p12, It is "openssl".
But demo code is Objective-C. I keep trying port this code to Swift, but it is so hard.
(democode: iOS: How to create PKCS12 (P12) keystore from private key and x509certificate in application programmatically?)
Finally, I have only a String certificate, and I want to configure p.identityData for my application.
How I do it?
I want to encrypt data using RSA , I tried to generate the key in my code and it's working , But what I actually need is to get the public key as a string from server and then use it as Seckey so I can use it to encrypt data using RSA,
I tried this code:
//KeyString is the string of the key from server
let KeyData = (keyString as NSString).dataUsingEncoding(NSUTF8StringEncoding) as NSData!
var cert : Unmanaged<SecCertificateRef>!;
var policy : Unmanaged<SecPolicy>!;
cert = SecCertificateCreateWithData(kCFAllocatorDefault, KeyData);
policy = SecPolicyCreateBasicX509();
var status : OSStatus = noErr
var trust: SecTrust?
var certArray : [Unmanaged<SecCertificateRef>!] = [cert];
var certArrayPointer = UnsafeMutablePointer<UnsafePointer<Void>>(certArray)
status = SecTrustCreateWithCertificates(cert, policy, trust);
let publicKey: SecKeyRef = SecTrustCopyPublicKey(trust!).takeUnretainedValue()
I couldn't run this code because SecTrustCreateWithCertificates Method is expecting certificate as anyObject! , I don't Know how to fix this,And if solving this will let me get the SecKey.
I got the code above from this answer in objective-c
So if any one can help me getting the right code to solve this , I will be very thankful :)
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.
Here's how I did this:
let cert = SecCertificateCreateWithData(kCFAllocatorDefault, certData)?.takeRetainedValue()
if cert != nil {
var trust: Unmanaged<SecTrust>?
let policy = SecPolicyCreateBasicX509().takeRetainedValue()
let status = SecTrustCreateWithCertificates(cert, policy, &trust)
if status == errSecSuccess {
let trustRef = trust!.takeRetainedValue()
let key = SecTrustCopyPublicKey(trustRef)!.takeRetainedValue();
}
}
This works, but you need to make sure that what you pass to SecCertificateCreateWithData() is a DER-encoded certificate, and not just a DER-encoded key. You need a certificate signed by your server's private key to the get the associated public key.
I Did this used Alamofire:
private static func publicKeyForCertificate(certificate: SecCertificate) -> SecKey? {
var publicKey: SecKey?
var trust: Unmanaged<SecTrust>?
let policy = SecPolicyCreateBasicX509().takeRetainedValue()
let status = SecTrustCreateWithCertificates(certificate, policy, &trust)
if status == errSecSuccess {
let trustRef = trust!.takeRetainedValue()
publicKey = SecTrustCopyPublicKey(trustRef)!.takeRetainedValue()
}
return publicKey
}