macos hash swift hmacsha1 commoncrypto

macos - CommonHMAC en Swift



hash hmacsha1 (10)

Estoy intentando crear un hash HMAC SHA-1 de una cadena en Swift, pero no puedo entender cómo interactuar con las API, ya que parece que no está importando el marco CommonCrypto. He intentado varias formas diferentes de "importar CommonCrypto" y crear un archivo de encabezado puente, pero nada de eso hizo la diferencia.

Lo curioso es que si creo una clase Objective-C, puedo interactuar con las API sin ningún problema, así que esto parece ser exclusivo de Swift.

Además, si alguien pudiera decirme qué es lo que equivale a uint8_t digest[CC_SHA1_DIGEST_LENGTH] en Swift, estaría muy agradecido.


A continuación se muestra una versión corregida de lo que @ jernej-strasner publicó

enum HMACAlgorithm { case MD5, SHA1, SHA224, SHA256, SHA384, SHA512 func toCCEnum() -> CCHmacAlgorithm { var result: Int = 0 switch self { case .MD5: result = kCCHmacAlgMD5 case .SHA1: result = kCCHmacAlgSHA1 case .SHA224: result = kCCHmacAlgSHA224 case .SHA256: result = kCCHmacAlgSHA256 case .SHA384: result = kCCHmacAlgSHA384 case .SHA512: result = kCCHmacAlgSHA512 } return CCHmacAlgorithm(result) } func digestLength() -> Int { var result: CInt = 0 switch self { case .MD5: result = CC_MD5_DIGEST_LENGTH case .SHA1: result = CC_SHA1_DIGEST_LENGTH case .SHA224: result = CC_SHA224_DIGEST_LENGTH case .SHA256: result = CC_SHA256_DIGEST_LENGTH case .SHA384: result = CC_SHA384_DIGEST_LENGTH case .SHA512: result = CC_SHA512_DIGEST_LENGTH } return Int(result) } } extension String { func digest(algorithm: HMACAlgorithm, key: String) -> String! { let str = self.cStringUsingEncoding(NSUTF8StringEncoding) let strLen = UInt(self.lengthOfBytesUsingEncoding(NSUTF8StringEncoding)) let digestLen = algorithm.digestLength() let result = UnsafeMutablePointer<CUnsignedChar>.alloc(digestLen) let keyStr = key.cStringUsingEncoding(NSUTF8StringEncoding) let keyLen = UInt(key.lengthOfBytesUsingEncoding(NSUTF8StringEncoding)) CCHmac(algorithm.toCCEnum(), keyStr!, keyLen, str!, strLen, result) var hash = NSMutableString() for i in 0..<digestLen { hash.appendFormat("%02x", result[i]) } result.destroy() return String(hash) } }


Esta es la fuente completa de "Cómo importar CommonCrypto en el proyecto Swift sin el encabezado Obj-c briging", modificado para Swift 3.0. El trabajo del código real es de "Mihael Isaev".

// // HMAC.swift // // Created by Mihael Isaev on 21.04.15. // Copyright (c) 2014 Mihael Isaev inc. All rights reserved. // // *********************************************************** // // How to import CommonCrypto in Swift project without Obj-c briging header // // To work around this create a directory called CommonCrypto in the root of the project using Finder. // In this directory create a file name module.map and copy the following into the file. // You will need to alter the paths to ensure they point to the headers on your system. // // module CommonCrypto [system] { // header "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/usr/include/CommonCrypto/CommonCrypto.h" // export * // } // To make this module visible to Xcode, go to Build Settings, Swift Compiler – Search Paths // and set Import Paths to point to the directory that contains the CommonCrypto directory. // // You should now be able to use import CommonCrypto in your Swift code. // // You have to set the Import Paths in every project that uses your framework so that Xcode can find it. // // *********************************************************** // // Modification for Swift 3.0 by Sanjay Sampat on 04.Jan.2017 // // *********************************************************** import Foundation import CommonCrypto extension String { var md5: String { return HMAC.hash(inp: self, algo: HMACAlgo.MD5) } var sha1: String { return HMAC.hash(inp: self, algo: HMACAlgo.SHA1) } var sha224: String { return HMAC.hash(inp: self, algo: HMACAlgo.SHA224) } var sha256: String { return HMAC.hash(inp: self, algo: HMACAlgo.SHA256) } var sha384: String { return HMAC.hash(inp: self, algo: HMACAlgo.SHA384) } var sha512: String { return HMAC.hash(inp: self, algo: HMACAlgo.SHA512) } func aesEncrypt(key:String, iv:String, options:Int = kCCOptionPKCS7Padding) -> String? { if let keyData = key.data(using: String.Encoding.utf8), let data = self.data(using: String.Encoding.utf8), let cryptData = NSMutableData(length: Int((data.count)) + kCCBlockSizeAES128) { let keyLength = size_t(kCCKeySizeAES128) let operation: CCOperation = UInt32(kCCEncrypt) let algoritm: CCAlgorithm = UInt32(kCCAlgorithmAES128) let options: CCOptions = UInt32(options) var numBytesEncrypted :size_t = 0 let base64cryptStringOut = keyData.withUnsafeBytes {(keyBytes: UnsafePointer<CChar>)->String? in return data.withUnsafeBytes {(dataBytes: UnsafePointer<CChar>)->String? in let cryptStatus = CCCrypt(operation, algoritm, options, keyBytes, keyLength, iv, dataBytes, data.count, cryptData.mutableBytes, cryptData.length, &numBytesEncrypted) if UInt32(cryptStatus) == UInt32(kCCSuccess) { cryptData.length = Int(numBytesEncrypted) let base64cryptString = cryptData.base64EncodedString(options: .lineLength64Characters) return base64cryptString } else { return nil } } } return base64cryptStringOut } return nil } func aesDecrypt(key:String, iv:String, options:Int = kCCOptionPKCS7Padding) -> String? { if let keyData = key.data(using: String.Encoding.utf8), let data = NSData(base64Encoded: self, options: .ignoreUnknownCharacters), let cryptData = NSMutableData(length: Int((data.length)) + kCCBlockSizeAES128) { let keyLength = size_t(kCCKeySizeAES128) let operation: CCOperation = UInt32(kCCDecrypt) let algoritm: CCAlgorithm = UInt32(kCCAlgorithmAES128) let options: CCOptions = UInt32(options) var numBytesEncrypted :size_t = 0 let unencryptedMessageOut = keyData.withUnsafeBytes {(keyBytes: UnsafePointer<CChar>)->String? in let cryptStatus = CCCrypt(operation, algoritm, options, keyBytes, keyLength, iv, data.bytes, data.length, cryptData.mutableBytes, cryptData.length, &numBytesEncrypted) if UInt32(cryptStatus) == UInt32(kCCSuccess) { cryptData.length = Int(numBytesEncrypted) let unencryptedMessage = String(data: cryptData as Data, encoding:String.Encoding.utf8) return unencryptedMessage } else { return nil } } return unencryptedMessageOut } return nil } } public struct HMAC { static func hash(inp: String, algo: HMACAlgo) -> String { if let stringData = inp.data(using: String.Encoding.utf8, allowLossyConversion: false) { return hexStringFromData(input: digest(input: stringData as NSData, algo: algo)) } return "" } private static func digest(input : NSData, algo: HMACAlgo) -> NSData { let digestLength = algo.digestLength() var hash = [UInt8](repeating: 0, count: digestLength) switch algo { case .MD5: CC_MD5(input.bytes, UInt32(input.length), &hash) break case .SHA1: CC_SHA1(input.bytes, UInt32(input.length), &hash) break case .SHA224: CC_SHA224(input.bytes, UInt32(input.length), &hash) break case .SHA256: CC_SHA256(input.bytes, UInt32(input.length), &hash) break case .SHA384: CC_SHA384(input.bytes, UInt32(input.length), &hash) break case .SHA512: CC_SHA512(input.bytes, UInt32(input.length), &hash) break } return NSData(bytes: hash, length: digestLength) } private static 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 } } enum HMACAlgo { case MD5, SHA1, SHA224, SHA256, SHA384, SHA512 func digestLength() -> Int { var result: CInt = 0 switch self { case .MD5: result = CC_MD5_DIGEST_LENGTH case .SHA1: result = CC_SHA1_DIGEST_LENGTH case .SHA224: result = CC_SHA224_DIGEST_LENGTH case .SHA256: result = CC_SHA256_DIGEST_LENGTH case .SHA384: result = CC_SHA384_DIGEST_LENGTH case .SHA512: result = CC_SHA512_DIGEST_LENGTH } return Int(result) } }

El siguiente es un ejemplo de uso.

// TEST for Encryption and Decryption through HMAC Swift 3.0 let iv = "iv-salt-Sanjay--" // fixed 16 chars. let cryptoKeyString = "01234567890123456789012345678901" let originalString = "My Name is Sanjay Sampat, Password is IL0ve2view2Kill@4#" print("Original String: /(originalString)") if let encodedString = originalString.aesEncrypt(key: cryptoKeyString, iv: iv){ print("String Encoded: /(encodedString)") if let decryptedString = encodedString.aesDecrypt(key: cryptoKeyString, iv: iv) { print("String Decoded: /(decryptedString)") } else{ print("Decoding failed") } } else{ print("Encoding failed") } // Example To create sha1 from string let testString = "This is string to test sha1 hash string." let sha1Digest = testString.sha1 print("sha1-hash-string: /(sha1Digest)")

Espero que esta sea una referencia fácil para algunos usuarios como yo. :)


Gracias, por tu excelente respuesta. Aquí solo estoy actualizando su respuesta para Swift 3.1:

enum CryptoAlgorithm { case MD5, SHA1, SHA224, SHA256, SHA384, SHA512 var HMACAlgorithm: CCHmacAlgorithm { var result: Int = 0 switch self { case .MD5: result = kCCHmacAlgMD5 case .SHA1: result = kCCHmacAlgSHA1 case .SHA224: result = kCCHmacAlgSHA224 case .SHA256: result = kCCHmacAlgSHA256 case .SHA384: result = kCCHmacAlgSHA384 case .SHA512: result = kCCHmacAlgSHA512 } return CCHmacAlgorithm(result) } var digestLength: Int { var result: Int32 = 0 switch self { case .MD5: result = CC_MD5_DIGEST_LENGTH case .SHA1: result = CC_SHA1_DIGEST_LENGTH case .SHA224: result = CC_SHA224_DIGEST_LENGTH case .SHA256: result = CC_SHA256_DIGEST_LENGTH case .SHA384: result = CC_SHA384_DIGEST_LENGTH case .SHA512: result = CC_SHA512_DIGEST_LENGTH } return Int(result) } } extension String { func hmac(algorithm: CryptoAlgorithm, key: String) -> String { let str = self.cString(using: String.Encoding.utf8) let strLen = Int(self.lengthOfBytes(using: String.Encoding.utf8)) let digestLen = algorithm.digestLength let result = UnsafeMutablePointer<CUnsignedChar>.allocate(capacity: digestLen) let keyStr = key.cString(using: String.Encoding.utf8) let keyLen = Int(key.lengthOfBytes(using: String.Encoding.utf8)) CCHmac(algorithm.HMACAlgorithm, keyStr!, keyLen, str!, strLen, result) let digest = stringFromResult(result: result, length: digestLen) result.deallocate(capacity: digestLen) return digest } private func stringFromResult(result: UnsafeMutablePointer<CUnsignedChar>, length: Int) -> String { let hash = NSMutableString() for i in 0..<length { hash.appendFormat("%02x", result[i]) } return String(hash) }

Y también para usarlo:

func sha256(StringToSign : String, secretKey : String) -> String{ let hex = StringToSign.hmac(algorithm: .SHA256, key: secretKey) let hexData = hex.data(using: String.Encoding.utf8) let finalString = hexData?.base64EncodedString(options: [.lineLength64Characters]) return finalString! }


Para OS X (pero no para iOS al momento de escribir esto, cuando 9.3.1 es actual), puede usar un SecTransform para calcular un HMAC SHA-1 en Swift sin encabezado de puente y sin Objective-C. Aquí hay un ejemplo que usa la entrada del primer caso de prueba HMAC-SHA-1 en RFC 2202 :

import Foundation import Security var error: Unmanaged<CFError>? let transform = SecDigestTransformCreate(kSecDigestHMACSHA1, 0, &error) let input = "Hi There" let inputData = input.dataUsingEncoding(NSUTF8StringEncoding)! let key = [UInt8](count: 20, repeatedValue: 0x0b) let keyData = key.withUnsafeBufferPointer { buffer in NSData(bytes: buffer.baseAddress, length: buffer.count) } SecTransformSetAttribute(transform, kSecTransformInputAttributeName, inputData, &error) SecTransformSetAttribute(transform, kSecDigestHMACKeyAttribute, keyData, &error) let outputData = SecTransformExecute(transform, &error) as! NSData


Prueba esto para Swift 3.1 :

enum CryptoAlgorithm { case MD5, SHA1, SHA224, SHA256, SHA384, SHA512 var HMACAlgorithm: CCHmacAlgorithm { var result: Int = 0 switch self { case .MD5: result = kCCHmacAlgMD5 case .SHA1: result = kCCHmacAlgSHA1 case .SHA224: result = kCCHmacAlgSHA224 case .SHA256: result = kCCHmacAlgSHA256 case .SHA384: result = kCCHmacAlgSHA384 case .SHA512: result = kCCHmacAlgSHA512 } return CCHmacAlgorithm(result) } var digestLength: Int { var result: Int32 = 0 switch self { case .MD5: result = CC_MD5_DIGEST_LENGTH case .SHA1: result = CC_SHA1_DIGEST_LENGTH case .SHA224: result = CC_SHA224_DIGEST_LENGTH case .SHA256: result = CC_SHA256_DIGEST_LENGTH case .SHA384: result = CC_SHA384_DIGEST_LENGTH case .SHA512: result = CC_SHA512_DIGEST_LENGTH } return Int(result) } } extension String { func hmac(algorithm: CryptoAlgorithm, key: String) -> String { let str = self.cString(using: String.Encoding.utf8) let strLen = Int(self.lengthOfBytes(using: String.Encoding.utf8)) let digestLen = algorithm.digestLength let result = UnsafeMutablePointer<CUnsignedChar>.allocate(capacity: digestLen) let keyStr = key.cString(using: String.Encoding.utf8) let keyLen = Int(key.lengthOfBytes(using: String.Encoding.utf8)) CCHmac(algorithm.HMACAlgorithm, keyStr!, keyLen, str!, strLen, result) let digest = stringFromResult(result: result, length: digestLen) result.deallocate(capacity: digestLen) return digest } private func stringFromResult(result: UnsafeMutablePointer<CUnsignedChar>, length: Int) -> String { let hash = NSMutableString() for i in 0..<length { hash.appendFormat("%02x", result[i]) } return String(hash) } }

No olvides agregar #import <CommonCrypto/CommonHMAC.h> a Header.h


Puedes hacerlo en Swift. Solo asegúrese de agregar #import <CommonCrypto/CommonHMAC.h> al encabezado bridging Objective-C bridging.

Actualización: para Swift 4, vea una solución mucho mejor usando Swift Package Manager aquí: https://github.com/jernejstrasner/SwiftCrypto

enum CryptoAlgorithm { case MD5, SHA1, SHA224, SHA256, SHA384, SHA512 var HMACAlgorithm: CCHmacAlgorithm { var result: Int = 0 switch self { case .MD5: result = kCCHmacAlgMD5 case .SHA1: result = kCCHmacAlgSHA1 case .SHA224: result = kCCHmacAlgSHA224 case .SHA256: result = kCCHmacAlgSHA256 case .SHA384: result = kCCHmacAlgSHA384 case .SHA512: result = kCCHmacAlgSHA512 } return CCHmacAlgorithm(result) } var digestLength: Int { var result: Int32 = 0 switch self { case .MD5: result = CC_MD5_DIGEST_LENGTH case .SHA1: result = CC_SHA1_DIGEST_LENGTH case .SHA224: result = CC_SHA224_DIGEST_LENGTH case .SHA256: result = CC_SHA256_DIGEST_LENGTH case .SHA384: result = CC_SHA384_DIGEST_LENGTH case .SHA512: result = CC_SHA512_DIGEST_LENGTH } return Int(result) } } extension String { func hmac(algorithm: CryptoAlgorithm, key: String) -> String { let str = self.cStringUsingEncoding(NSUTF8StringEncoding) let strLen = Int(self.lengthOfBytesUsingEncoding(NSUTF8StringEncoding)) let digestLen = algorithm.digestLength let result = UnsafeMutablePointer<CUnsignedChar>.alloc(digestLen) let keyStr = key.cStringUsingEncoding(NSUTF8StringEncoding) let keyLen = Int(key.lengthOfBytesUsingEncoding(NSUTF8StringEncoding)) CCHmac(algorithm.HMACAlgorithm, keyStr!, keyLen, str!, strLen, result) let digest = stringFromResult(result, length: digestLen) result.dealloc(digestLen) return digest } private func stringFromResult(result: UnsafeMutablePointer<CUnsignedChar>, length: Int) -> String { var hash = NSMutableString() for i in 0..<length { hash.appendFormat("%02x", result[i]) } return String(hash) } }


Swift 4 versión demo en Github repo


Swift puede encontrar la forma de mapear los marcos Obj-C pero no tanto para las funciones C directas. Apple ha proporcionado algunos enlaces para cosas como GCD y AudioToolbox, pero no todo. Parece que CommonCrypto aún no tiene enlaces adecuados.

Para este caso, recomendaría escribir sus propios envoltorios básicos en Obj-C, luego usar estas clases de envoltura en Swift.

Por ejemplo, podría crear una clase HMAC en Obj-C:

// This enum is in HMAC.h typedef NS_ENUM(NSInteger, HMACAlgorithm) { SHA1, MD5, SHA256, SHA384, SHA512, SHA224 }; // Class methods here + (NSData *)calculateWithAlgorithm:(HMACAlgorithm)algorithm forKey:(const void *)key andData:(const void *)data { NSInteger digestLength = [self digestLengthForAlgorithm:algorithm]; unsigned char hmac[digestLength]; CCHmac(algorithm, &key, strlen(key), &data, strlen(data), &hmac); NSData *hmacBytes = [NSData dataWithBytes:hmac length:sizeof(hmac)]; return hmacBytes; } + (NSInteger)digestLengthForAlgorithm:(HMACAlgorithm)algorithm { switch (algorithm) { case MD5: return CC_MD5_DIGEST_LENGTH; case SHA1: return CC_SHA1_DIGEST_LENGTH; case SHA224: return CC_SHA224_DIGEST_LENGTH; case SHA256: return CC_SHA256_DIGEST_LENGTH; case SHA384: return CC_SHA384_DIGEST_LENGTH; case SHA512: return CC_SHA512_DIGEST_LENGTH; default: return 0; } }

Luego en Swift:

class SwiftHMAC { // Swift will automatically pull the enum from Obj-C func calculate(algorithm:HMACAlgorithm, key:Byte[], data:Byte[]) -> Byte[] { let computedHMAC = HMAC.calculateWithAlgorithm(algorithm, forKey: key, andData: data) var rawBytes = Byte[](count: computedHMAC.length, repeatedValue: 0) computedHMAC.getBytes(&rawBytes) return rawBytes } }

Solo recuerde agregar #import "HMAC.h" a su encabezado Swift bridging así como #import "<##Your-Project-Name##>-Swift.h" al archivo Obj-C implementation (.m).


Yo quería mantener las cosas mínimas. Evite la complejidad añadida de crear una clase genérica que pueda manejar todos los diferentes tipos de resumen y, en su lugar, solo tenga un pequeño método que podría incluir en una clase si fuera necesario. También prefiero evitar agregar extensiones a las clases principales.

Agregue lo siguiente a su archivo -Bridging-Header.h :

#import <CommonCrypto/CommonHMAC.h>

Luego, en la clase que necesita llamar a CCHmac() agregue un método privado:

private func hmac(string: NSString, key: NSData) -> NSData { let keyBytes = UnsafePointer<CUnsignedChar>(key.bytes) let data = string.cStringUsingEncoding(NSUTF8StringEncoding) let dataLen = Int(string.lengthOfBytesUsingEncoding(NSUTF8StringEncoding)) let digestLen = Int(CC_SHA1_DIGEST_LENGTH) let result = UnsafeMutablePointer<CUnsignedChar>.alloc(digestLen) CCHmac(CCHmacAlgorithm(kCCHmacAlgSHA1), keyBytes, key.length, data, dataLen, result); return NSData(bytes: result, length: digestLen) }

Si necesito un CCHmacAlgorithm diferente, simplemente reemplazaría las dos constantes en ese método con las apropiadas. En mi caso, necesitaba kCCHmacAlgSHA256 y CC_SHA256_DIGEST_LENGTH .

Gracias a Jernej Strasner y otros por las otras respuestas, solo quería algo más simple para mi caso.


después de swift 1.2 reemplaza las líneas en el resumen de func

let strLen = UInt(self.lengthOfBytesUsingEncoding(NSUTF8StringEncoding)) let keyLen = UInt(key.lengthOfBytesUsingEncoding(NSUTF8StringEncoding))

por

let strLen = self.lengthOfBytesUsingEncoding(NSUTF8StringEncoding) let keyLen = key.lengthOfBytesUsingEncoding(NSUTF8StringEncoding)