How do I configure identityData of NEVPNProtocolIKEv2 with String certificate? - ios

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?

Related

How can I create a Public Key from a PEM File (String) in Swift?

I'm pretty new to RSA Keys and Certificates and all of that. So I'm getting a PEM File from a network request, I understand the PEM file is basically the certificate without header and footer, so essentially I have a string. I need to create a PublicKey from that in order to Encrypt some text, but I think I'm doing something wrong. This is my code so far, I have talked to other teams (Android) and when they print out the Encrypted Data, they are getting a regular String and I'm getting lots of weird characters. Thanks in advance guys! (:
func encryptBase64(text: String, certificateStr: String) throws -> Data {
let encryptionError = EncrpytionError(message: "There has been an issue with encryption")
guard
let certData = Data(base64Encoded: certificateStr),
let certificate = SecCertificateCreateWithData(nil, certData as CFData),
let publicKey = SecCertificateCopyKey(certificate)
else {
throw encryptionError
}
let algorithm: SecKeyAlgorithm = .rsaEncryptionOAEPSHA256AESGCM
guard SecKeyIsAlgorithmSupported(publicKey, .encrypt, algorithm) else {
throw encryptionError
}
guard let cipherText = SecKeyCreateEncryptedData(
publicKey,
algorithm,
text.data(using: .utf8)! as CFData, nil)
else {
throw encryptionError
}
return cipherText as Data
}
And when I try to print cipherText as a String, I get this weird thing:
"D�aj�\\m꒤h,�A�{��8�~�\nY\u0003F�Cˤ�#��\"�\u0018�\u0007\u001fX#VC�U_��E\u0005dž1���X��\/4Px��P\u0016�8}% ��<��#�I_�K\u000e�\bR*�� ���斋�7,�%���F^q�\u0000\\�'�ZTD\u0013Q�_\u0010\u001f>i]&��B���#1\u0006\b��E\u0004�F���yS\u0013�3����SB)��m\u0017%��5ʲ����s\u0003��r�&�?�8b��W#\u001e��؞ۡ��8�s~��ӹ�u\"�2��U�&\b�3XV���˔Y��xt[\fm&P:\\�\f� y��6jy"
Android team is doing something like this on Kotlin:
private fun extractPublicKey(certificateString: String): PublicKey {
val encodedCertificateBytes = certificateString.toByteArray()
val decodedCertificateBytes = Base64.decode(encodedCertificateBytes, Base64.DEFAULT)
val inStream = decodedCertificateBytes.inputStream()
val certificateFactory = CertificateFactory.getInstance("X.509")
val certificate = certificateFactory.generateCertificate(inStream)
inStream.close()
return certificate.publicKey
}

How to Create random key in secure enclave in Xamarin.iOS

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.

How do I store the certificateID into the keychain for the .connect method on AWSIoTDataManager?

I'm having trouble using the AWSIotDataManager class to connect to the AWS MQTT broker in order to subscribe to events. I was passing the the certificateID string to the .connect method , but then realised that above the .connect method definition it mentions that: "the certificateID must be stored in the KeyChain".
I came across the AWSIoTKeychain class but I not sure how to use it.
I also came across the AWSIoTManager.createKeysAndCertificateFromCsr, but I also cannot figure out how to utilise it given the fact that I'm using the AWSIoTDataManager class instead.
How do I store the certificateID in the keychain? and then how do I use it to connect to the AWS MQTT broker? (I have the certID, pem, and privateKey)
let credentialsProvider:AWSCognitoCredentialsProvider = AWSCognitoCredentialsProvider(regionType: AWSRegionType.EUWest1, identityPoolId: consumerPoolId);
let iotEndPoint = AWSEndpoint(urlString: endPoint);
let iotDataConfiguration = AWSServiceConfiguration(
region: AWSRegionType.EUWest1,
endpoint: iotEndPoint,
credentialsProvider: credentialsProvider
);
AWSServiceManager.default()?.defaultServiceConfiguration = iotDataConfiguration;
AWSIoTDataManager.register(with: iotDataConfiguration!, forKey: KEYSTORE_NAME);
iotDataManager = AWSIoTDataManager(forKey: KEYSTORE_NAME);
func mqttEventCallback(status: AWSIoTMQTTStatus) {
if(status == AWSIoTMQTTStatus.connected) {
iotDataManager.subscribe(
toTopic: topic,
qoS: .messageDeliveryAttemptedAtMostOnce,
messageCallback: {
(payload) ->Void in
let stringValue = String(data: payload, encoding: String.Encoding.utf8)!;
// do stuff with the payload
// ...
} );
}
}
let isConnected: Bool = iotDataManager.connect(withClientId: clientID,
cleanSession: true,
certificateId: certID,
statusCallback: mqttEventCallback);

Realm Encryption, Secure Enclave, and Keychain

Sorry to be yet another post about encryption, but I am struggling with setting up some truly strong encryption in my application. I currently have a basic setup for login that utilizes keychain and is based off of Tim Mitra's tutorial, which works wonderfully. However, I am uncomfortable about storing the account email / username in UserDefaults as it isn't particularly secure. Is there a better method that anyone can come up with? Additionally, I am working on utilizing Realm's built in encryption features, however I am unsure as to how I should properly store the key for said encryption given that it is of type Data. I also have heard that I should double encrypt the user's credentials using Secure Enclave and possibly utilize the same technique with Realm's key. Is there a guide that someone could point me to? How would you better optimize my code to be brutally secure? I have already set the application to check the device for Cydia and other signs of jailbreaking so as to avoid keychain data dumps and plan on checking any / all urls called too.
Here's my implementation of Keychain:
private func setupAccount()
{
let newAccountName = inputFields[0].text
let newPassword = inputFields[1].text
let hasLoginKey = UserDefaults.standard.bool(forKey: "hasSetup")
if !hasLoginKey {
UserDefaults.standard.setValue(inputFields[0].text, forKey: "username")
}
do {
// This is a new account, create a new keychain item with the account name.
let passwordItem = KeychainLIPassItem(service: KeychainConfiguration.serviceName,
account: newAccountName!,
accessGroup: KeychainConfiguration.accessGroup)
// Save the password for the new item.
try passwordItem.savePassword(newPassword!)
} catch {
fatalError("Error updating keychain - \(error)")
}
UserDefaults.standard.set(true, forKey: "hasSetup")
}
Here is what I currently have for Realm Encryption:
private func keyValue() -> Data
{
var key = Data(count: 64)
_ = key.withUnsafeMutableBytes { bytes in
SecRandomCopyBytes(kSecRandomDefault, 64, bytes)
}
return key
}
private func securitySettings() -> Realm.Configuration
{
let key = keyValue()
let config = Realm.Configuration(encryptionKey: key)
return config
}
private func setupObject()
{
do {
let realm = try Realm(configuration: securitySettings())
let profile = UserProfile()
profile.firstName = firstName
profile.lastName = lastName
profile.dateOfBirth = dateOfBirth
profile.gender = gender
try! realm.write {
realm.add(profile)
}
} catch let error as NSError {
fatalError("Error opening realm: \(error)")
}
}
Thank you so much!

SSKeychain: Accounts not stored in iCloud?

I'm using sskeychain (https://github.com/soffes/sskeychain) to store my accounts and passwords in the IOS keychain. I assume, that if I store an account, it should be available on my other device. But it doesn't appear there.
I read my accounts with this code:
NSArray *arr=[SSKeychain accountsForService:#"Login"];
for (NSString *s in arr) {
NSLog(#"Account: %#",s);
}
and get this (only shown one entry, the others are similar):
Account: {
acct = "friXXXXXter#XXXX.com";
agrp = "3B4384Z34A.de.gondomir.LocalButler";
cdat = "2014-05-09 22:55:08 +0000";
mdat = "2014-05-09 22:55:08 +0000";
pdmn = ak;
svce = Login;
sync = 0;
tomb = 0;
}
But this doesn't appear on the other device. Both devices have IOS 7.1.1.
I store the password with this line:
[SSKeychain setPassword:self.passwortField.text forService:#"Login" account:self.userField.text];
I have switched on keychain sharing in Xcode and have a keychain group "de.gondomir.LocalButler" listed there.
Am I missing something? Must the service name something special?
Thanks!
In case this is still relevant for you I managed to find the solution. (works for >=iOS7)
Don't use the static methods of SSKeychain to write your credentials. Instead use SSKeychainQuery and set the synchronizationMode to SSKeychainQuerySynchronizationModeYes like this
NSError *error;
[SSKeychain setAccessibilityType:self.keychainAccessibilityType];
SSKeychainQuery *query = [[SSKeychainQuery alloc] init];
query.service = service;
query.account = account;
query.password = password;
query.synchronizationMode = SSKeychainQuerySynchronizationModeYes;
[query save:&error];
if (error) {
NSLog(#"Error writing credentials %#", [error description]);
}
The static convenience methods on SSKeychain use the default synchronization mode SSKeychainQuerySynchronizationModeAny causing credentials not to be synchronized with the iCloud keychain.
Additionally, make sure your devices have Keychain via iCloud enabled (Settings>iCloud>Keychain). You might also want to enable Keychain Sharing in your targets Capabilities.
After I have a new project I tried the answer of MarkHim, it works.
I used swift now, so here is my working code:
let account = defaults.objectForKey("Sync_toPhoneNumber") as? String
SSKeychain.setAccessibilityType(kSecAttrAccessibleAfterFirstUnlock)
var error:NSError?
let lookupQuery = SSKeychainQuery()
lookupQuery.synchronizationMode = .Yes
lookupQuery.service = "DasDing"
lookupQuery.account = account
let password = SSKeychain.passwordForService("DasDing", account: account, error: &error)
if error == nil {
commandKey = password!
} else {
print("Error für \(account): \(error!.localizedDescription)")
commandKey = ""
}
// query all accounts for later use
let allQuery = SSKeychainQuery()
allQuery.service = "DasDing"
do {
let dict = try allQuery.fetchAll()
print("Accounts:")
for acc in dict {
print(acc["acct"]!)
}
} catch let error as NSError {
print("keine Accounts")
print("Error: \(error.localizedDescription)")
}
That's for reading, for writing you must delete the account first (if you want to change the password):
let account = defaults.objectForKey("Sync_toPhoneNumber") as? String
SSKeychain.setAccessibilityType(kSecAttrAccessibleAfterFirstUnlock)
SSKeychain.deletePasswordForService("DasDing", account: account)
let newQuery = SSKeychainQuery()
newQuery.service = "DasDing"
newQuery.account = account
newQuery.password = str?.uppercaseString
newQuery.synchronizationMode = .Yes
try! newQuery.save()

Resources