swift sha1

¿Cómo hash NSString con SHA1 en Swift?



(7)

Con CryptoKit agregado en iOS13, ahora tenemos la API nativa de Swift:

import Foundation import CryptoKit // CryptoKit.Digest utils extension Digest { var bytes: [UInt8] { Array(makeIterator()) } var data: Data { Data(bytes) } var hexStr: String { bytes.map { String(format: "%02X", $0) }.joined() } } func example() { guard let data = "hello world".data(using: .utf8) else { return } let digest = Insecure.SHA1.hash(data: data) print(digest.data) // 20 bytes print(digest.hexStr) // 2AAE6C35C94FCFB415DBE95F408B9CE91EE846ED }

En el objetivo-c se ve así:

#include <sys/xattr.h> @implementation NSString (reverse) -(NSString*)sha1 { NSData *data = [self dataUsingEncoding:NSUTF8StringEncoding]; uint8_t digest[CC_SHA1_DIGEST_LENGTH]; CC_SHA1(data.bytes, (int)data.length, digest); NSMutableString *output = [NSMutableString stringWithCapacity:CC_SHA1_DIGEST_LENGTH * 2]; for (int i = 0; i < CC_SHA1_DIGEST_LENGTH; i++) [output appendFormat:@"%02x", digest[i]]; return output; } @end

Necesito algo como esto con Swift, ¿es posible?

Por favor, muestre un ejemplo de trabajo.


Para obtener el resultado como NSData , siempre que haya incluido <CommonCrypto/CommonCrypto.h> en su encabezado de puente:

extension NSData { func sha1() -> NSData? { let len = Int(CC_SHA1_DIGEST_LENGTH) let digest = UnsafeMutablePointer<UInt8>.alloc(len) CC_SHA1(bytes, CC_LONG(length), digest) return NSData(bytesNoCopy: UnsafeMutablePointer<Void>(digest), length: len) } }

También utiliza la asignación de puntero adecuada. Invocarlo así:

myString.dataUsingEncoding(NSUTF8StringEncoding)?.sha1()

Si necesita una representación hexadecimal de NSData un vistazo a mi otra answer .


Podemos extraer la lógica para encriptar cadenas usando sha1 para tres pasos:

  1. Convertir cadena a objeto de datos
  2. Cifrar datos usando la función SHA1 a Datos
  3. Convertir objeto de datos a cadena hexadecimal

En mi humilde opinión es mucho más legible y esta versión no requiere NSData.

extension String { var sha1: String { guard let data = data(using: .utf8, allowLossyConversion: false) else { // Here you can just return empty string or execute fatalError with some description that this specific string can not be converted to data } return data.digestSHA1.hexString } } fileprivate extension Data { var digestSHA1: Data { var bytes: [UInt8] = Array(repeating: 0, count: Int(CC_SHA1_DIGEST_LENGTH)) withUnsafeBytes { _ = CC_SHA1($0, CC_LONG(count), &bytes) } return Data(bytes: bytes) } var hexString: String { return map { String(format: "%02x", UInt8($0)) }.joined() } }



Sí, es posible: hacer que el código de Objective-C sea accesible desde Swift

Ver documentation .

Evitaría reescribirlo rápidamente si no obtiene ningún beneficio (como el uso de funciones específicas de swift).

Además, en un proyecto en el que estoy trabajando, utilicé un código de Objective-C similar al tuyo para manejar hashes. Al principio comencé a escribirlo rápidamente, luego me di cuenta de que era más fácil y mejor reutilizar el viejo obj-c.


Su código de Objective-C (usando una categoría NSString ) se puede traducir directamente a Swift (usando una extensión de String ).

Primero tienes que crear un "encabezado de puente" y agregar

#import <CommonCrypto/CommonCrypto.h>

Entonces:

extension String { func sha1() -> String { let data = self.dataUsingEncoding(NSUTF8StringEncoding)! var digest = [UInt8](count:Int(CC_SHA1_DIGEST_LENGTH), repeatedValue: 0) CC_SHA1(data.bytes, CC_LONG(data.length), &digest) let output = NSMutableString(capacity: Int(CC_SHA1_DIGEST_LENGTH)) for byte in digest { output.appendFormat("%02x", byte) } return output as String } } println("Hello World".sha1())

Esto se puede escribir un poco más corto y más rápido como

extension String { func sha1() -> String { let data = self.dataUsingEncoding(NSUTF8StringEncoding)! var digest = [UInt8](count:Int(CC_SHA1_DIGEST_LENGTH), repeatedValue: 0) CC_SHA1(data.bytes, CC_LONG(data.length), &digest) let hexBytes = map(digest) { String(format: "%02hhx", $0) } return "".join(hexBytes) } }

Actualización para Swift 2:

extension String { func sha1() -> String { let data = self.dataUsingEncoding(NSUTF8StringEncoding)! var digest = [UInt8](count:Int(CC_SHA1_DIGEST_LENGTH), repeatedValue: 0) CC_SHA1(data.bytes, CC_LONG(data.length), &digest) let hexBytes = digest.map { String(format: "%02hhx", $0) } return hexBytes.joinWithSeparator("") } }

Para devolver una cadena codificada Base-64 en lugar de una cadena codificada hexadecimal, simplemente reemplace

let hexBytes = digest.map { String(format: "%02hhx", $0) } return hexBytes.joinWithSeparator("")

con

return NSData(bytes: digest, length: digest.count).base64EncodedStringWithOptions([])

Actualización para Swift 3:

extension String { func sha1() -> String { let data = self.data(using: String.Encoding.utf8)! var digest = [UInt8](repeating: 0, count:Int(CC_SHA1_DIGEST_LENGTH)) data.withUnsafeBytes { _ = CC_SHA1($0, CC_LONG(data.count), &digest) } let hexBytes = digest.map { String(format: "%02hhx", $0) } return hexBytes.joined() } }

Para devolver una cadena codificada Base-64 en lugar de una cadena codificada hexadecimal, simplemente reemplace

let hexBytes = digest.map { String(format: "%02hhx", $0) } return hexBytes.joined()

por

return Data(bytes: digest).base64EncodedString()

Actualización para Swift 4:

El archivo de encabezado de puente ya no es necesario, uno puede import CommonCrypto en import CommonCrypto lugar:

import CommonCrypto extension String { func sha1() -> String { let data = Data(self.utf8) var digest = [UInt8](repeating: 0, count:Int(CC_SHA1_DIGEST_LENGTH)) data.withUnsafeBytes { _ = CC_SHA1($0, CC_LONG(data.count), &digest) } let hexBytes = digest.map { String(format: "%02hhx", $0) } return hexBytes.joined() } }

Actualización para Swift 5:

El método Data.withUnsafeBytes() ahora llama al cierre con un UnsafeRawBufferPointer a, y baseAddress se usa para pasar la dirección inicial a la función C:

import CommonCrypto extension String { func sha1() -> String { let data = Data(self.utf8) var digest = [UInt8](repeating: 0, count:Int(CC_SHA1_DIGEST_LENGTH)) data.withUnsafeBytes { _ = CC_SHA1($0.baseAddress, CC_LONG(data.count), &digest) } let hexBytes = digest.map { String(format: "%02hhx", $0) } return hexBytes.joined() } }


Una versión para Swift 5 que usa CryptoKit en iOS 13 y de lo contrario recurre a CommonCrypto:

import CommonCrypto import CryptoKit import Foundation private func hexString(_ iterator: Array<UInt8>.Iterator) -> String { return iterator.map { String(format: "%02x", $0) }.joined() } extension Data { public var sha1: String { if #available(iOS 13.0, *) { return hexString(Insecure.SHA1.hash(data: self).makeIterator()) } else { var digest = [UInt8](repeating: 0, count: Int(CC_SHA1_DIGEST_LENGTH)) self.withUnsafeBytes { bytes in _ = CC_SHA1(bytes.baseAddress, CC_LONG(self.count), &digest) } return hexString(digest.makeIterator()) } } }

Uso:

let string = "The quick brown fox jumps over the lazy dog" let hexDigest = string.data(using: .ascii)!.sha1 assert(hexDigest == "2fd4e1c67a2d28fced849ee1bb76e7391b93eb12")

También disponible a través del administrador de paquetes Swift:
https://github.com/ralfebert/TinyHashes