Advertisement
Scroll to top

() translation by (you can also view the original English article)

El objetivo principal de una firma digital es verificar la integridad de la información. Para un ejemplo sencillo, digamos que tuvieras un archivo que fue transferido por la red y desea comprobar que el archivo fue transferido correctamente. En ese caso, usaría una suma de comprobación.

"Una suma de comprobación es un dato de tamaño pequeño derivado de un bloque de datos digitales para detectar errores que se han introducido durante su transmisión o almacenamiento": Wikipedia

¿Cómo derivamos esa suma de comprobación? La mejor opción es utilizar un hash. Una función hash llevará una cantidad variable de datos y la salida de una firma de longitud fija. Por ejemplo, podríamos publicar un archivo junto con su hash en línea. Cuando alguien descarga el archivo, luego puede ejecutar la misma función hash en la versión del archivo y comparar el resultado. Si los algoritmos hash son iguales, entonces el archivo copiado o descargado es el mismo que el original.

Un hash es una función unidireccional. No dado el resultado, hay ninguna forma computacionalmente factible revertir ese hash para revelar lo que era la entrada original. SHA, algoritmo de Hash seguro, es un estándar bien conocido que se refiere a un grupo de funciones hash que tienen esta propiedad y algunos otros, que los hacen útiles para las firmas digitales.

Sobre SHA

SHA ha sufrido muchas iteraciones desde que fue publicado por primera vez. Las iteraciones primeras y segunda, SHA-0 y SHA-1, ahora se saben que tienen debilidades importantes. Ya no son aprobados para las implementaciones de seguridad: generalmente no debe utilizarse para aplicaciones confiando en la seguridad. Sin embargo, la familia SHA-2 incluye versiones llamadas SHA-256 y SHA-512, y estos se consideran seguros. "256" y "512" simplemente se refieren a la cantidad resultante de bits producida. Para este tutorial, vamos a utilizar SHA-512.

Nota: Otro algoritmo hash populares mayores era MD5. También fue encontrado para tener defectos significativos.

Usando SHA es ideal para comprobar si los datos fue accidentalmente dañados, pero esto no impide que un usuario malintencionado la alteración de los datos. Dado que una salida de hash es de un tamaño fijo, todo un atacante tiene que hacer es averiguar qué algoritmo fue utilizado dado el tamaño de salida, modificar los datos y calcular el hash. Lo que necesitamos es información secreta añadida a la mezcla cuando el hash de los datos para que el atacante no puede calcular el hash sin conocimiento del secreto. Esto se llama un código Hash de autenticación de mensajes (HMAC).

HMAC

HMAC puede autenticar una pieza de información o mensaje para asegurarse de que se originó del remitente correcto y que la información no ha sido alterada. Un escenario común es cuando se habla a un servidor con una API de back-end para su aplicación. Puede ser importante autenticar para asegurarse de que sólo su aplicación está autorizado a hablar con la API. La API tendría control de acceso a un recurso específico, como un extremo /register_user. El cliente tendría que firmar su petición de que el extremo /register_user para utilizarlo con éxito.

Al firmar una solicitud, es práctica común tomar las partes seleccionadas de la solicitud, tales como parámetros POST y la URL y unirse en una cadena. Tomando elementos convenidos y ponerlas en un orden determinado se llama resolución de nombres canónicos. En HMAC, la cadena se unió a es molida junto con la clave secreta para realizar la firma. En lugar de llamar a un hash, utilizamos la firma de término de la misma manera que la firma de la persona en la vida real se utiliza para verificar la identidad o la integridad. La firma se agrega a la solicitud del cliente como un encabezado de solicitud (generalmente también nombrada "firma"). Una firma a veces se llama un resumen de mensaje, pero los dos términos pueden usarse indistintamente.

Sobre el lado de la API, el servidor repite el proceso de unirse a las secuencias y la creación de una firma. Si las firmas coinciden, demuestra que la aplicación debe tener la posesión del secreto. Esto demuestra la identidad de la aplicación. Puesto que los parámetros específicos de la solicitud eran también parte de la cadena firmado, también garantiza la integridad de la solicitud. Evita que un atacante realice un ataque man in the middle, por ejemplo y alterar los parámetros de la petición a su gusto.

1
class func hmacExample()
2
{
3
    //Some URL example...

4
    let urlString = "https://example.com"
5
    let postString = "id=123"
6
    let url = URL.init(string: urlString)
7
    var request = URLRequest(url: url!)
8
    request.httpMethod = "POST"
9
    request.httpBody = postString.data(using: .utf8)
10
    let session = URLSession.shared
11
12
    //Create a signature

13
    let stringToSign = request.httpMethod! + "&" + urlString + "&" + postString
14
    print("The string to sign is : ", stringToSign)
15
    if let dataToSign = stringToSign.data(using: .utf8)
16
    {
17
        let signingSecret = "4kDfjgQhcw4dG6J80QnvRFbtuJfkgitH6phkLN90"
18
        if let signingSecretData = signingSecret.data(using: .utf8)
19
        {
20
            let digestLength = Int(CC_SHA512_DIGEST_LENGTH)
21
            let digestBytes = UnsafeMutablePointer<UInt8>.allocate(capacity:digestLength)
22
            
23
            CCHmac(CCHmacAlgorithm(kCCHmacAlgSHA512), [UInt8](signingSecretData), signingSecretData.count, [UInt8](dataToSign), dataToSign.count, digestBytes)
24
            
25
            //base64 output

26
            let hmacData = Data(bytes: digestBytes, count: digestLength)
27
            let signature = hmacData.base64EncodedString()
28
            print("The HMAC signature in base64 is " + signature)
29
            
30
            //or HEX output

31
            let hexString = NSMutableString()
32
            for i in 0..<digestLength
33
            {
34
                hexString.appendFormat("%02x", digestBytes[i])
35
            }
36
            print("The HMAC signature in HEX is", hexString)
37
            
38
            //Set the Signature header

39
            request.setValue(signature, forHTTPHeaderField: "Signature")
40
        }
41
    }
42
    
43
    //Start your request...

44
    //let task = session.dataTask(with: request, completionHandler: { (data, response, error) in

45
        // ...

46
    //})

47
    //task.resume()

48
    
49
}

En este código, la función CCHmac acepta un parámetro para el tipo de función de hash que se utilizará, junto con dos cadenas de bytes y su longitud, el mensaje y una clave secreta. Para la mejor seguridad, utilice al menos una clave de 256 bits (32 bytes) generada a partir de un generador de números aleatorios criptográficamente seguro. Para comprobar que todo está funcionando correctamente en el otro lado, ejecutar el ejemplo y la clave secreta y el mensaje en este servidor remoto de entrada y verificar que la salida es el mismo.

También puede Agregar un encabezado de fecha y hora para la solicitud y firma de la cadena para realizar la solicitud única. Esto puede ayudar a la maleza API ataques de replay. Por ejemplo, la API podría bajar la solicitud si la marca de hora es duros 10 minutos.

Mientras que es bueno para pegarse a usar versiones SHA que son seguras, pues resulta que muchas de las vulnerabilidades de las versiones SHA inseguras no se aplican a HMAC. Por esta razón, se puede ver SHA1 en código de producción. Sin embargo, desde un punto de vista de relaciones públicas, puede quedar mal si tienes que explicar por qué, criptográficamente hablando, es aceptable utilizar SHA1 en este contexto. Muchas de las debilidades de SHA1 son debido a lo que se llaman ataques de colisión. Auditores de código o investigadores de seguridad pueden contar con su código para ser resistente a la colisión, sin importar el contexto. Además, si escribe código modular, donde usted puede intercambiar la función de firma de otro en el futuro, podría olvidar actualizar las funciones de hash inseguros. Por lo tanto, todavía nos se adhieren al SHA-512 como nuestro algoritmo de elección.

Las operaciones de la CPU de HMAC están rápidas, pero una desventaja es el problema de intercambio de claves. ¿Cómo nos deje uno a saber cuál es la clave secreta sin que ser interceptada? Por ejemplo, tal vez su API necesitará dinámicamente añadir o quitar varias aplicaciones o plataformas de una lista blanca. En este escenario, se necesitarían aplicaciones para registrar, y el secreto tendría que pasarse a la aplicación al registro de éxito. Te podría enviar la clave sobre HTTPS y SSL, pero aún así siempre existe la preocupación de que de alguna manera la clave es robada durante el intercambio. La solución al problema de intercambio de claves es generar una clave que no necesitas dejar el dispositivo en primer lugar. Esto puede lograrse usando criptografía de clave pública, y un estándar muy popular y aceptado es RSA.

RSA

RSA está parado para Rivest-Shamir-Adleman (los autores de la criptografía). Se trata de tomar ventaja de la dificultad de factoring el producto de dos números primos muy grandes. RSA puede utilizarse para el cifrado o autenticación, aunque para este ejemplo vamos a utilizar sólo para la autenticación. RSA genera dos llaves, una pública y una privada, que podemos lograr mediante la función SecKeyGeneratePair. Cuando se utiliza para la autenticación, la clave privada se utiliza para crear la firma, mientras que la clave pública verifica la firma. Dada una clave pública, es computacionalmente inviable para derivar la clave privada.

En el ejemplo siguiente se muestra lo que Apple y todas las empresas de consola del popular juego de utilizar al distribuir su software. Digamos que su empresa crea y entrega un archivo periódicamente que los usuarios se arrastre en la parte de su aplicación en iTunes para compartir archivos. Usted quiere asegurarse de que los archivos que envía no son adulterados nunca antes de ser analizadas en la aplicación. Su empresa será sostener y guardar la clave privada que utiliza para firmar los archivos. En el paquete de la aplicación es una copia de la clave pública para verificar el archivo. Dado que la clave privada nunca es transmitida o incluida en la aplicación, hay ninguna manera para que un usuario malintencionado poder firmar sus propias versiones de los archivos (aparte de romperse en la empresa y robar la clave privada).

Utilizamos SecKeyRawSign para firmar el archivo. Sería lento a firmar todo el contenido del archivo mediante RSA, por lo que el hash del archivo está firmado en su lugar. Además, los datos pasados a RSA también deben ser molidos antes de firmar a causa de algunas debilidades de seguridad.

1
@available(iOS 10.0, *)
2
class FileSigner
3
{
4
    private var publicKey : SecKey?
5
    private var privateKey : SecKey?
6
    
7
    func generateKeys()  -> String?
8
    {
9
        var publicKeyString : String?
10
        //generate a new keypair

11
        let parameters : [String : AnyObject] =
12
        [
13
            kSecAttrKeyType as String : kSecAttrKeyTypeRSA,
14
            kSecAttrKeySizeInBits as String : 4096 as AnyObject,
15
        ]
16
        let status = SecKeyGeneratePair(parameters as CFDictionary, &publicKey, &privateKey)
17
        
18
        //---Save your key here--- //

19
        
20
        //Convert the SecKey object into a representation that we can send over the network

21
        if status == noErr && publicKey != nil
22
        {
23
            if let cfData = SecKeyCopyExternalRepresentation(publicKey!, nil)
24
            {
25
                let data = cfData as Data
26
                publicKeyString = data.base64EncodedString()
27
            }
28
        }
29
        
30
        return publicKeyString
31
    }
32
    
33
    func signFile(_ path : String) -> String?
34
    {
35
        var signature : String?
36
        
37
        if let fileData = FileManager.default.contents(atPath: path)
38
        {
39
            if (privateKey != nil)
40
            {
41
                //hash the message first

42
                let digestLength = Int(CC_SHA512_DIGEST_LENGTH)
43
                let hashBytes = UnsafeMutablePointer<UInt8>.allocate(capacity: digestLength)
44
                CC_SHA512([UInt8](fileData), CC_LONG(fileData.count), hashBytes)
45
                
46
                //sign

47
                let blockSize = SecKeyGetBlockSize(privateKey!) //in the case of RSA, modulus is the same as the block size

48
                var signatureBytes = [UInt8](repeating:0, count:blockSize)
49
                var signatureDataLength = blockSize
50
                let status = SecKeyRawSign(privateKey!, .PKCS1SHA512, hashBytes, digestLength, &signatureBytes, &signatureDataLength)
51
                if status == noErr
52
                {
53
                    let data = Data(bytes: signatureBytes, count: signatureDataLength)
54
                    signature = data.base64EncodedString()
55
                }
56
            }
57
        }
58
        return signature
59
    }
60
}

En este código, hemos utilizado la función CC_SHA512 para especificar otra vez SHA-512. (RSA, a diferencia de HMAC, llega a ser insegura si la función hash subyacente es insegura.) También estamos utilizando 4096 como el tamaño de la clave, que es definido por el parámetro kSecAttrKeySizeInBits. 2048 es el mínimo tamaño recomendado. Esto es para evitar una poderosa red de ordenadores se agrieta la clave RSA (por craqueo quiero decir teniendo la clave RSA, también conocido como factorización de un módulo público). El grupo RSA ha estimado que las claves de 2048 bits podrían ser manipulable algún tiempo antes de 2030. Si desea que sus datos para estar seguro más allá de ese tiempo entonces es una buena idea elegir un tamaño de clave más alto como 4096.

Las claves generadas son en forma de objetos SecKey. Un problema con la implementación de Apple de SecKey es que no incluye toda la información esencial que constituye una clave pública, por lo que no es un certificado X.509 con codificación DER válido. Agregar la información que falta en el formato para un iOS o OS X app, incluso plataformas de servidor como PHP, requiere un trabajo y consiste en trabajar en un formato conocido como ASN.1. Afortunadamente, esto fue fijado en 10 de iOS con nuevas funciones de SecKey para generar, exportar e importar claves.

El código siguiente muestra el otro lado de la comunicación, la clase que acepta una clave pública a través de SecKeyCreateWithData para verificar los archivos utilizando la función SecKeyRawVerify.

1
@available(iOS 10.0, *)
2
class FileVerifier
3
{
4
    private var publicKey : SecKey?
5
    
6
    func addPublicKey(_ keyString : String) -> Bool
7
    {
8
        var success = false
9
        if let keyData = Data.init(base64Encoded: keyString)
10
        {
11
            let parameters : [String : AnyObject] =
12
            [
13
                kSecAttrKeyType as String : kSecAttrKeyTypeRSA,
14
                kSecAttrKeyClass as String : kSecAttrKeyClassPublic,
15
                kSecAttrKeySizeInBits as String : 4096 as AnyObject,
16
                kSecReturnPersistentRef as String : true as AnyObject
17
            ]
18
            publicKey = SecKeyCreateWithData(keyData as CFData, parameters as CFDictionary, nil)
19
                
20
            if (publicKey != nil)
21
            {
22
                success = true
23
            }
24
        }
25
        return success
26
    }
27
    
28
    func verifyFile(_ path : String, withSignature signature : String) -> Bool
29
    {
30
        var success = false
31
        if (publicKey != nil)
32
        {
33
            if let fileData = FileManager.default.contents(atPath: path)
34
            {
35
                if let signatureData = Data.init(base64Encoded: signature)
36
                {
37
                    //hash the message first

38
                    let digestLength = Int(CC_SHA512_DIGEST_LENGTH)
39
                    let hashBytes = UnsafeMutablePointer<UInt8>.allocate(capacity:digestLength)
40
                    CC_SHA512([UInt8](fileData), CC_LONG(fileData.count), hashBytes)
41
                    
42
                    //verify

43
                    let status = signatureData.withUnsafeBytes {signatureBytes in
44
                        return SecKeyRawVerify(publicKey!, .PKCS1SHA512, hashBytes, digestLength, signatureBytes, signatureData.count)
45
                    }
46
                    if status == noErr
47
                    {
48
                        success = true
49
                    }
50
                    else
51
                    {
52
                        print("Signature verify error") //-9809 : errSSLCrypto, etc

53
                    }
54
                }
55
            }
56
        }
57
        return success
58
    }
59
}

Podría probar esto y comprobar que funciona utilizando una simple prueba como la siguiente:

1
if #available(iOS 10.0, *)
2
{
3
    guard let plistPath = Bundle.main.path(forResource: "Info", ofType: "plist") else { print("Couldn't get plist"); return }
4
    
5
    let fileSigner = FileSigner()
6
    
7
    DispatchQueue.global(qos: .userInitiated).async // RSA key gen can be long running work

8
    {
9
        guard let publicKeyString = fileSigner.generateKeys() else { print("Key generation error"); return }
10
        
11
        // Back to the main thread

12
        DispatchQueue.main.async
13
        {
14
            guard let signature = fileSigner.signFile(plistPath) else { print("No signature"); return }
15
            
16
            //Save the signature on the other end

17
            let fileVerifier = FileVerifier()
18
            guard fileVerifier.addPublicKey(publicKeyString) else { print("Key was not added"); return }
19
            
20
            let success = fileVerifier.verifyFile(plistPath, withSignature: signature)
21
            if success
22
            {
23
                print("Signatures match!")
24
            }
25
            else
26
            {
27
                print("Signatures do not match.")
28
            }
29
        }
30
    }
31
}

Hay una desventaja a RSA, generación de claves es lento! El tiempo para generar las claves es dependiente en el tamaño de la clave. En dispositivos más una clave de 4096 bit toma sólo unos segundos, pero si ejecuta este código en un iPod Touch 4ta generación, puede tardar unos minutos. Esto está bien si sólo están generando las teclas varias veces en una computadora, pero ¿qué sucede cuando tenemos que generar las claves con frecuencia en un dispositivo móvil? No podemos sólo reducir el tamaño de la clave ya rebaja la seguridad.

¿Cuál es la solución? Bueno, criptografía de curva elíptica (ECC) es un enfoque emergente, un nuevo conjunto de algoritmos basados en curvas elípticas sobre campos finitos. Las claves de la ECC son mucho más pequeños en tamaño y más rápido para generar que las claves RSA. Una clave de 256-bits sólo ofrece un nivel muy fuerte de seguridad! Para aprovechar las ventajas de ECC, no tenemos que cambiar un montón de código. Podemos firmar nuestros datos utilizando la misma función SecKeyRawSign y luego ajustar los parámetros para usar la elíptica curva Digital firma algoritmo (ECDSA).

Consejo: Para más ideas de puesta en práctica de RSA, usted puede consultar la biblioteca auxiliar de SwiftyRSA, que se centra en el cifrado y firma de mensajes.

ECDSA

Imagina el siguiente escenario: una aplicación de chat permite a los usuarios enviar mensajes privados entre sí, pero usted quiere asegurarse de que un adversario no ha cambiado el mensaje en su camino al otro usuario. Vamos a ver cómo podría asegurar su comunicación con criptografía.

En primer lugar, cada usuario genera un par de claves de claves públicas y privadas en su dispositivo móvil. Sus claves privadas se almacenan en la memoria y no dejan nunca el aparato, mientras que las claves públicas se transmiten entre sí. Como antes, se utiliza la clave privada para firmar los datos ser enviado, mientras que la clave pública se utiliza para verificar. Si un atacante captura una clave pública durante el transporte, todo lo que se podría hacer es verificar la integridad del mensaje original del remitente. Un atacante no puede modificar un mensaje porque no tienen la clave privada necesaria para reconstruir la firma.

Hay otra pro uso de ECDSA en iOS. Podemos hacer uso del hecho de que en la actualidad, claves de curva elíptica son los únicos que pueden almacenarse en el enclave seguro del dispositivo. Otras claves son almacenadas en el llavero que cifra sus artículos en el área de almacenamiento predeterminada del dispositivo. En los dispositivos que tienen uno, el enclave seguro se sienta separado del procesador y almacenamiento de claves está implementado en hardware sin acceso directo al software. El enclave seguro puede almacenar una clave privada y operar sobre ella para producir la salida que se envía a la aplicación sin exponer nunca la clave privada real por carga en la memoria!

Voy a añadir soporte para la creación de la clave privada ECDSA sobre el enclave seguro añadiendo la opción de kSecAttrTokenIDSecureEnclave para el parámetro kSecAttrTokenID. Podemos comenzar este ejemplo con un objeto de User que va a generar un par de claves en la inicialización.

1
@available(iOS 9.0, *)
2
class User
3
{
4
    public var publicKey : SecKey?
5
    private var privateKey : SecKey?
6
    private var recipient : User?
7
    
8
    init(withUserID id : String)
9
    {
10
        //if let access = SecAccessControlCreateWithFlags(nil, kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly, [.privateKeyUsage /*, .userPresence] authentication UI to get the private key */], nil) //Force store only if passcode or Touch ID set up...

11
        if let access = SecAccessControlCreateWithFlags(nil, kSecAttrAccessibleWhenUnlockedThisDeviceOnly, [.privateKeyUsage], nil)  //Keep private key on device

12
        {
13
            let privateTagString = "com.example.privateKey." + id
14
            let privateTag = privateTagString.data(using: .utf8)! //Store it as Data, not as a String

15
            let privateKeyParameters : [String : AnyObject] = [kSecAttrIsPermanent as String : true as AnyObject,
16
                                                               kSecAttrAccessControl as String : access as AnyObject,
17
                                                               kSecAttrApplicationTag as String : privateTag as AnyObject,
18
                ]
19
        
20
            let publicTagString = "com.example.publicKey." + id
21
            let publicTag = publicTagString.data(using: .utf8)! //Data, not String

22
            let publicKeyParameters : [String : AnyObject] = [kSecAttrIsPermanent as String : false as AnyObject,
23
                                                              kSecAttrApplicationTag as String : publicTag as AnyObject,
24
                ]
25
            
26
            let keyPairParameters : [String : AnyObject] = [kSecAttrKeySizeInBits as String : 256 as AnyObject,
27
                                                            kSecAttrKeyType as String : kSecAttrKeyTypeEC,
28
                                                            kSecPrivateKeyAttrs as String : privateKeyParameters as AnyObject,
29
                                                            kSecAttrTokenID as String : kSecAttrTokenIDSecureEnclave as AnyObject, //Store in Secure Enclave

30
                                                            kSecPublicKeyAttrs as String : publicKeyParameters as AnyObject]
31
            
32
            let status = SecKeyGeneratePair(keyPairParameters as CFDictionary, &publicKey, &privateKey)
33
            if status != noErr
34
            {
35
                print("Key generation error")
36
            }
37
        }
38
    }
39
    
40
    //...

A continuación, vamos a crear algunas funciones de ayudante y ejemplo. Por ejemplo, la clase permitirá al usuario iniciar una conversación y enviar un mensaje. Por supuesto, en su aplicación, deberá configurar esto para incluir la configuración de red específica.

1
    //...

2
    
3
    private func sha512Digest(forData data : Data) -> Data
4
    {
5
        let len = Int(CC_SHA512_DIGEST_LENGTH)
6
        let digest = UnsafeMutablePointer<UInt8>.allocate(capacity: len)
7
        CC_SHA512((data as NSData).bytes, CC_LONG(data.count), digest)
8
        return NSData(bytesNoCopy: UnsafeMutableRawPointer(digest), length: len) as Data
9
    }
10
    
11
    public func initiateConversation(withUser user : User) -> Bool
12
    {
13
        var success = false
14
        if publicKey != nil
15
        {
16
            user.receiveInitialization(self)
17
            recipient = user
18
            success = true
19
        }
20
        return success
21
    }
22
    
23
    public func receiveInitialization(_ user : User)
24
    {
25
         recipient = user
26
    }
27
    
28
    public func sendMessage(_ message : String)
29
    {
30
        if let data = message.data(using: .utf8)
31
        {
32
            let signature = self.signData(plainText: data)
33
            if signature != nil
34
            {
35
                self.recipient?.receiveMessage(message, withSignature: signature!)
36
            }
37
        }
38
    }
39
    
40
    public func receiveMessage(_ message : String, withSignature signature : Data)
41
    {
42
        let signatureMatch = verifySignature(plainText: message.data(using: .utf8)!, signature: signature)
43
        if signatureMatch
44
        {
45
            print("Received message. Signature verified. Message is : ", message)
46
        }
47
        else
48
        {
49
            print("Received message. Signature error.")
50
        }
51
    }
52
    
53
    //...

A continuación, haremos la real firma y verificación. ECDSA, a diferencia de RSA, no necesita ser molida antes de firmar. Sin embargo, si quisiera tener una función donde el algoritmo puede ser fácilmente intercambiado sin hacer muchos cambios, entonces es perfectamente bien continuar hacer un hash de los datos antes de firmar.

1
    //...

2
    
3
    func signData(plainText: Data) -> Data?
4
    {
5
        guard privateKey != nil else
6
        {
7
            print("Private key unavailable")
8
            return nil
9
        }
10
        
11
        let digestToSign = self.sha512Digest(forData: plainText)
12
        let signature = UnsafeMutablePointer<UInt8>.allocate(capacity: 512) //512 - overhead

13
        var signatureLength = 512
14
        
15
        let status = SecKeyRawSign(privateKey!, .PKCS1SHA512, [UInt8](digestToSign), Int(CC_SHA512_DIGEST_LENGTH), signature, &signatureLength)
16
        if status != noErr
17
        {
18
            print("Signature fail: \(status)")
19
        }
20
        
21
        return Data.init(bytes: signature, count: signatureLength) //resize to actual signature size

22
    }
23
    
24
    func verifySignature(plainText: Data, signature: Data) -> Bool
25
    {
26
        guard recipient?.publicKey != nil else
27
        {
28
            print("Recipient public key unavailable")
29
            return false
30
        }
31
        
32
        let digestToVerify = self.sha512Digest(forData: plainText)
33
        let signedHashBytesSize = signature.count
34
        
35
        let status = SecKeyRawVerify(recipient!.publicKey!, .PKCS1SHA512, [UInt8](digestToVerify), Int(CC_SHA512_DIGEST_LENGTH), [UInt8](signature as Data), signedHashBytesSize)
36
        return status == noErr
37
    }
38
}

Esto comprueba el mensaje, así como la «identificar» de un usuario específico puesto que sólo ese usuario tiene posesión de su clave privada.

Esto no significa que nos estamos conectando la clave con la que el usuario está en la vida real, el problema de que una clave pública a un usuario concreto es otro dominio. Mientras que las soluciones están fuera del alcance de este tutorial, aplicaciones de chat seguro popular como señal y telegrama permiten a los usuarios verificar una huella digital o el número a través de un canal de comunicación secundaria. De igual manera, Pidgin ofrece una pregunta y respuesta esquema por el que haga una pregunta que sólo el usuario debe saber. Estas soluciones abren todo un mundo de debate sobre cuál debe ser el mejor enfoque.

Sin embargo, nuestra solución criptográfica se verifica que el mensaje puede sólo han sido enviado por alguien que está en posesión de una clave privada específica.

Vamos a realizar una prueba simple de nuestro ejemplo:

1
if #available(iOS 9.0, *)
2
{
3
    let alice = User.init(withUserID: "aaaaaa1")
4
    let bob = User.init(withUserID: "aaaaaa2")
5
    
6
    let accepted = alice.initiateConversation(withUser: bob)
7
    if (accepted)
8
    {
9
        alice.sendMessage("Hello there")
10
        bob.sendMessage("Test message")
11
        alice.sendMessage("Another test message")
12
    }
13
}

OAuth y SSO

A menudo cuando se trabaja con servicios de terceros, usted notará otros términos de alto nivel utilizados para la autenticación OAuth y SSO. Aunque este tutorial es sobre la creación de una firma, voy a explicar brevemente lo que significan que los otros términos.

OAuth es un protocolo para la autenticación y autorización. Actúa como un intermediario para utilizar cuenta de alguien para servicios de terceros y tiene como objetivo resolver el problema de forma selectiva autorizar acceso a sus datos. Si inicia una sesión servicio X vía Facebook, una pantalla te pide, por ejemplo, si servicio X se permite acceder a tus fotos de Facebook. Logra esto al proporcionar un token sin revelar la contraseña del usuario.

Sola sesión, o SSO, describe el flujo donde un usuario autenticado puede utilizar sus mismas credenciales de inicio de sesión para acceder a múltiples servicios. Un ejemplo de esto es cómo funciona tu cuenta de Gmail para iniciar sesión YouTube. Si tuvieras varios diferentes servicios a su empresa, no puede crear cuentas de usuario separadas para todos los diferentes servicios.

Conclusión

En este tutorial, usted vio cómo crear firmas utilizando los estándares más populares. Ahora que hemos cubierto los principales conceptos, recapitulemos!

  • Uso de HMAC cuando usted necesita velocidad y está seguro de que la clave secreta se puede intercambiar con seguridad.
  • Si las teclas tienen que viajar a través de una red, es mejor utilizar RSA o ECDSA.
  • RSA es todavía el más popular. Su paso de verificación es bastante rápido. Utilice RSA si ya está familiarizado con el resto de su equipo o con el estándar.
  • Si usted necesita constantemente generar claves en un dispositivo lento, sin embargo, utilizar ECDSA. Mientras que la verificación de ECDSA es un pelín más lenta que la verificación RSA, no compara con la cantidad de segundos guardados sobre RSA para generación de claves.

Por lo es para las firmas digitales en Swift. Si usted tiene cualesquiera preguntas, sienta libre mándenme una línea en la sección de comentarios, y mientras tanto revisa algunos de nuestros otros tutoriales sobre seguridad de datos y desarrollo de aplicaciones en Swift!


Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.