CRITTOGRAFIA ASIMMETRICA 02 – CREAZIONE DELLE CHIAVI PRIVATE E PUBBLICHE


Eccoci alla seconda puntata del tutorial sulla Crittografia Asimmetrica.

Come spiegavo nella prima puntata, per far si che questo tipo di crittografia funzioni, abbiamo bisogno di creare una coppia di chiavi (una privata ed una pubblica) in relazione tra loro.

Prima di iniziare a vedere e scrivere il codice che ci permetterà di creare la coppia di chiavi, dobbiamo fare una azione preliminare molto importante. Visto che tutto il sistema di crittografia in iOS si basa sull’utilizzo del Security framework e sulle funzioni contenute nella libreria CommonCrypto, la prima cosa da fare per poter usare le funzioni (in C) messe a disposizione da CommonCrypto è quello di creare, se già non lo avete, il file Bridging Header nel vostro progetto e aggiungerci la seguente linea di codice :

#import <CommonCrypto/CommonCrypto.h>

Quasi tutta la gestione delle chiavi nel Security Framework avviene a livello del portachiavi (keyChain). Il portachiavi è un luogo o enclave sicura dove vengono memorizzate tutte le chiavi e certificati di un utente. Operando sul portachiavi noi potremo quindi aggiungere chiavi, recuperarle ed eventualmente anche eliminarle qualora non ne avessimo più bisogno.

Fatta questa piccola premessa vediamo come si procede per creare la coppia di chiavi e salvarla nel portachiavi. La funzione, che ricordo essere in C, di CommonCrypto che fa al caso nostro è la seguente :

SecKeyGeneratePair(_ parameters: CFDictionary, _ publicKey: UnsafeMutablePointer<SecKey?>?, _ privateKey: UnsafeMutablePointer<SecKey?>?) -> OSStatus

Vediamo di capirci un po’. Questa funzione necessità che gli vengano passati 3 parametri :

  1. un dizionario contenente informazioni sulle chiavi che volgiamo creare
  2. un puntatore per la chiave pubblica
  3. un puntatore per la chiave privata

Infine la funzione restituisce un codice che ci permetterà di capire se l’operazione è andata a buon fine o, in caso contrario l’errore.

Se qualcuno fosse interessato a questa pagina OSStatus.com è possibile vedere i vari codici di errore.

Arrivati a questo punto è il momento di creare il nostro manager. Creiamo quindi un nuovo file swift, che chiameremo CryptoManager, e sostituiamo il contenuto con il seguente :

import Foundation

private let _singletonInstance = CryptoManager()

class CryptoManager: NSObject
{
    class var sharedInstance: CryptoManager
    {
        return _singletonInstance
    }
}

Il passo successivo è quello di creare il dizionario contente tutte le informazioni necessaria alla creazione delle chiavi. Iniziamo pertanto a definire alcune costanti aggiungendo subito dopo la riga private let _singletonInstance = CryptoManager() il seguente codice :

private let kAsymmetricCryptoManagerApplicationTag = “com.asymcryptotest.keypair”
private let kAsymmetricCryptoManagerKeyType = kSecAttrKeyTypeRSA
private let kAsymmetricCryptoManagerKeySize = 2048

Questi sono dei parametri globali che definisco :

  1. un id univoco per la nostra coppia di chiavi che ci servirà in seguito per poterle recuperare dal portachiavi (KeyChain)
  2. il tipo di chiave che intendiamo creare (nel nostro caso RSA)
  3. le dimensioni pari a 2048 bits

Definiti questi parametri globali, dobbiamo creare i parametri specifici per ognuna chiave pubblica e privata. Creiamo quindi un primo dizionario con i parametri per la chiave privata, aggiungendo il seguente codice subito dopo la definizione delle costanti globali :

let privateKeyParams: [String: AnyObject] = [kSecAttrIsPermanent as String : true as AnyObject,  kSecAttrApplicationTag as String : kAsymmetricCryptoManagerApplicationTag as AnyObject]

quindi subito dopo il dizionario con i parametri per la chiave pubblica

let publicKeyParams: [String: AnyObject] = [kSecAttrIsPermanent as String : true as AnyObject, kSecAttrApplicationTag as String : kAsymmetricCryptoManagerApplicationTag as AnyObject]

Come potete notare i due dizionari sono praticamente uguali; con il primo attributo kSecAttrIsPermanet impostato a true abbiamo specificato che vogliamo che le nostre chiavi siano memorizzate permanentemente nel portachiavi, mentre con l’attributo kSecAttrApplicationTag abbiamo impostato l’id delle nostre chiavi al valore impostato precedentemente nei parametri globali.

L’ultima operazione preparatoria sarà quella di creare il dizionario finale che conterrà tutti i parametri necessari e che verrà passato alla funzione SecKeyGeneratePair per la creazione della nostra coppia di chiavi. Creiamo quindi il dizionario finale aggiungendo il seguente codice subito dopo la creazione del dizionario con i parametri della chiave publica

let parameters: [String: AnyObject] = [kSecAttrKeyType as String : kAsymmetricCryptoManagerKeyType, kSecAttrKeySizeInBits as String : kAsymmetricCryptoManagerKeySize as AnyObject, kSecPublicKeyAttrs as String : publicKeyParams as AnyObject, kSecPrivateKeyAttrs as String : privateKeyParams as AnyObject]

Bene; le operazioni preliminari per la generazione della coppia di chiavi sono concluse. Ora non ci resta che creare la nostra funzione per la generazione della coppia di chiavi. Creiamo quindi la seguente funzione all’interno del corpo della classe subito dopo la definizione della variabile di classe sharedInstance.

func createSecureKeyPair(_ completion: ((_ success: Bool, _ error: AsymmetricCryptoException?) -> Void)? = nil)
{

}

Siccome la creazione delle chiavi potrebbe essere una operazione onerosa, in termini di tempo, per il sistema e non non volgiamo bloccare l’intera applicazione, eseguire l’operazione in background invece che ne thread principale. Inseriamo quindi all’interno del corpo della nostra funzione le seguenti righe di codice che andremo successivamente a completare.

DispatchQueue.global(qos: DispatchQoS.QoSClass.default).async { () -> Void in

}

Prima di continuare vediamo di sistemare alcuni probabili errori che vi si staranno presentano. La funzione createSecureKeyPair prevede un completion block il quale a sua volta ci restituisce un bool ed un non meglio specificato AsymmetricCryptoException. Quest’ultimo non è che un enum di tipo Error che definisce una serie di eventuali errori che si possono verificare durante la creazione della coppia di chiavi. Per tanto per eliminare l’errore che xCode ci segnala dobbiamo definire il nostro enum nel seguente modo aggiungendo il seguente codice subito dopo import Foundation.

enum AsymmetricCryptoException: Error {
    case unknownError
    case duplicateFoundWhileTryingToCreateKey
    case keyNotFound
    case authFailed
}

Fatto questo andiamo a vedere cosa mettere all’interno del blocco di codice che dovrà essere eseguito in background. Inseriamo quindi all’interno del DispatchQueue

let status = SecKeyGeneratePair(parameters as CFDictionary, &pubKey, &privKey)
var pubKey, privKey: SecKey?

if status == errSecSuccess
{
   DispatchQueue.main.async(execute: { completion?(true, nil) })
}
else
{
   var error = AsymmetricCryptoException.unknownError

   switch (status)
 {
  case errSecDuplicateItem:
       error = .duplicateFoundWhileTryingToCreateKey
   case errSecItemNotFound:
       error = .keyNotFound
  case errSecAuthFailed:
       error = .authFailed
   default:
       break
  }

DispatchQueue.main.async(execute: { completion?(false, error) })

Cerchiamo di andare con ordine e di spiegare un po’ il codice :

Come prima cosa dichiariamo due variabili che conterranno il riferimento (nella realtà il tipo SecKey è un oggetto astratto che rappresenta una chiave asimmetrica) alle nostre chiavi, quindi chiamiamo la funzione SecKeyGeneratePair passandogli come argomenti il dizionario precedentemente creato e i due riferimenti come puntatori. All’inizio abbiamo visto come la funzione SecKeyGeneratePair restituisca un codice rappresentativo del buon esito o meno della operazione; questo è il motivo per cui assegnamo il risultato della chiamata ad una costante, che ci tornerà utile per verificare il buon esito della operazione stessa.

Ora che abbiamo la nostra costante che contiene il risultato dell’operazione (ATTENZIONE : la costante con contiene le chiavi o riferimenti ad esse, ma solo un codice rappresentativo dell’esito della chiamata alla funzione), possiamo verificare, con un if il risultato. Se il codice ritornato è errSecSuccess vuol dire che l’operazione è andata a buon fine senza errori e pertanto eseguiamo il completion block sul thread principale. Diversamente verifichiamo che tipo di errore si è verificato. Anche in questo secondo caso eseguiamo il completion block sul thread principale passando il tipo di errore.

Bene, siamo arrivati alla fine ed ora non ci resta che chiamare la funzione di CryptoManager dove ci serve. Un esempio, che eventualmente vedremo più avanti, potrebbe essere quella di generare la coppia di chiavi in fase di registrazione dell’utente ad un servizio di messaggistica, per poi poter condividere la chiave pubblica che criptare i messaggi. Per ora vi basti sapere che per chiamare la funzione sarà sufficiente fare :

CryptoManager.sharedInstance.createSecureKeyPair({ (success, error) in

if success
{
//CHIAVI GENERATE CON SUCCESSO
}
else
{
//SI E’ VERIFICATO UN ERRORE; STAMPO L’ERRORE
print(error!)
}

Questo è tutto per questa seconda puntata. L’argomento non è dei più semplici da trattare e molte cose devono essere date per scontate, pertanto spero che sia tutto abbastanza chiaro. Se avete domande in merito lasciate un commento sulla pagina FaceBook di xCoding.it nel post relativo.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s