swift - SHA256 en veloz
md5 swift 4 (6)
Quiero usar sha256 en mi proyecto, pero tuve algunos problemas para reescribir el código ObjC para convertirlo en código. Ayudame por favor. Utilicé esta respuesta: ¿Cómo puedo calcular un hash SHA-2 (idealmente SHA 256 o SHA 512) en iOS?
Aquí está mi código
var hash : [CUnsignedChar]
CC_SHA256(data.bytes, data.length, hash)
var res : NSData = NSData.dataWithBytes(hash, length: CC_SHA256_DIGEST_LENGTH)
me da error todo porque swift no puede convertir Int
a CC_LONG
, por ejemplo.
Aquí está mi función simple de 3 líneas Swift 4 para esto usando Security Transforms API, que es parte de Foundation en macOS. (Desafortunadamente los programadores de iOS no pueden usar esta técnica).
import Foundation
extension Data {
public func sha256Hash() -> Data {
let transform = SecDigestTransformCreate(kSecDigestSHA2, 256, nil)
SecTransformSetAttribute(transform, kSecTransformInputAttributeName, self as CFTypeRef, nil)
return SecTransformExecute(transform, nil) as! Data
}
}
Aquí hay un método que usa la API de Transformaciones de seguridad de CoreFoundation, por lo que ni siquiera necesita vincularse a CommonCrypto. Por alguna razón, en 10.10 / Xcode 7 vincular a CommmonCrypto con Swift es drama, así que usé esto en su lugar.
Este método se lee desde un NSInputStream
, que puede obtener de un archivo, o puede hacer uno que lea un NSData
, o puede hacer flujos enlazados de lectura / NSData
para un proceso en búfer.
// digestType is from SecDigestTransform and would be kSecDigestSHA2, etc
func digestForStream(stream : NSInputStream,
digestType type : CFStringRef, length : Int) throws -> NSData {
let transform = SecTransformCreateGroupTransform().takeRetainedValue()
let readXform = SecTransformCreateReadTransformWithReadStream(stream as CFReadStreamRef).takeRetainedValue()
var error : Unmanaged<CFErrorRef>? = nil
let digestXform : SecTransformRef = try {
let d = SecDigestTransformCreate(type, length, &error)
if d == nil {
throw error!.takeUnretainedValue()
} else {
return d.takeRetainedValue()
}
}()
SecTransformConnectTransforms(readXform, kSecTransformOutputAttributeName,
digestXform, kSecTransformInputAttributeName,
transform, &error)
if let e = error { throw e.takeUnretainedValue() }
if let output = SecTransformExecute(transform, &error) as? NSData {
return output
} else {
throw error!.takeUnretainedValue()
}
}
Funciones que le dan al SHA de NSData
& String
(Swift 3):
func sha256(_ data: Data) -> Data? {
guard let res = NSMutableData(length: Int(CC_SHA256_DIGEST_LENGTH)) else { return nil }
CC_SHA256((data as NSData).bytes, CC_LONG(data.count), res.mutableBytes.assumingMemoryBound(to: UInt8.self))
return res as Data
}
func sha256(_ str: String) -> String? {
guard
let data = str.data(using: String.Encoding.utf8),
let shaData = sha256(data)
else { return nil }
let rc = shaData.base64EncodedString(options: [])
return rc
}
Incluir en su encabezado de puente:
#import "CommonCrypto/CommonCrypto.h"
La respuesta principal no funcionó para mí. Encontré algo en la web y lo cambié un poco y ahora funciona: D. Es para Swift 3 y 4.
Coloque esta extensión en algún lugar de su proyecto y utilícela en una cadena como esta: mystring.sha256 ()
extension String {
func sha256() -> String{
if let stringData = self.data(using: String.Encoding.utf8) {
return hexStringFromData(input: digest(input: stringData as NSData))
}
return ""
}
private func digest(input : NSData) -> NSData {
let digestLength = Int(CC_SHA256_DIGEST_LENGTH)
var hash = [UInt8](repeating: 0, count: digestLength)
CC_SHA256(input.bytes, UInt32(input.length), &hash)
return NSData(bytes: hash, length: digestLength)
}
private func hexStringFromData(input: NSData) -> String {
var bytes = [UInt8](repeating: 0, count: input.length)
input.getBytes(&bytes, length: input.length)
var hexString = ""
for byte in bytes {
hexString += String(format:"%02x", UInt8(byte))
}
return hexString
}
}
Por cierto, necesita un encabezado de puente que importe CommonCrypto. Si no tiene uno, siga estos pasos:
- Crear nuevo archivo -> Archivo de encabezado -> Guardar como
BridgingHeader
- En Configuración de compilación -> Encabezado de puente de Objective-C -> agregar
ProjectName/BridgingHeader.h
- Coloque
#import <CommonCrypto/CommonHMAC.h>
en su archivo de encabezado
Prefiero usar:
extension String {
var sha256:String? {
guard let stringData = self.data(using: String.Encoding.utf8) else { return nil }
return digest(input: stringData as NSData).base64EncodedString(options: [])
}
private func digest(input : NSData) -> NSData {
let digestLength = Int(CC_SHA256_DIGEST_LENGTH)
var hash = [UInt8](repeating: 0, count: digestLength)
CC_SHA256(input.bytes, UInt32(input.length), &hash)
return NSData(bytes: hash, length: digestLength)
}
}
La cadena hasded es codificada en base64.
CC_LONG
convertir explícitamente entre Int
y CC_LONG
, porque Swift no realiza conversiones implícitas, como en (Objetivo-) C.
También debe definir hash
como una matriz del tamaño requerido.
func sha256(data : NSData) -> NSData {
var hash = [UInt8](count: Int(CC_SHA256_DIGEST_LENGTH), repeatedValue: 0)
CC_SHA256(data.bytes, CC_LONG(data.length), &hash)
let res = NSData(bytes: hash, length: Int(CC_SHA256_DIGEST_LENGTH))
return res
}
Alternativamente, puede usar NSMutableData
para asignar el búfer necesario:
func sha256(data : NSData) -> NSData {
let res = NSMutableData(length: Int(CC_SHA256_DIGEST_LENGTH))
CC_SHA256(data.bytes, CC_LONG(data.length), UnsafeMutablePointer(res.mutableBytes))
return res
}
Actualización para Swift 3:
func sha256(data : Data) -> Data {
var hash = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH))
data.withUnsafeBytes {
_ = CC_SHA256($0, CC_LONG(data.count), &hash)
}
return Data(bytes: hash)
}