web-dev-qa-db-fra.com

Implémentation du chiffrement HMAC et SHA1 dans swift

Je suis relativement nouveau dans Swift et je suis bloqué pour le chiffrement avec HMAC et SHA1. J'ai trouvé la réponse suivante https://stackoverflow.com/a/24411522/4188344 mais je ne peux pas trouver comment mettre cela en œuvre correctement. Toute aide serait incroyable.

13
David Wood

Problème résolu! Tout d'abord, je n'utilisais pas correctement la fonction string ... J'ai fini avec ceci:

    let hmacResult:String = "myStringToHMAC".hmac(HMACAlgorithm.SHA1, key: "myKey")

Ensuite, j'avais oublié que j'avais besoin de coder en base64 le résultat hmac. J'ai donc modifié la fonction de chaîne liée dans ma question à ...

enum HMACAlgorithm {
    case MD5, SHA1, SHA224, SHA256, SHA384, SHA512

    func toCCHmacAlgorithm() -> 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 hmac(algorithm: HMACAlgorithm, key: String) -> String {
        let cKey = key.cStringUsingEncoding(NSUTF8StringEncoding)
        let cData = self.cStringUsingEncoding(NSUTF8StringEncoding)
        var result = [CUnsignedChar](count: Int(algorithm.digestLength()), repeatedValue: 0)
        CCHmac(algorithm.toCCHmacAlgorithm(), cKey!, strlen(cKey!), cData!, strlen(cData!), &result)
        var hmacData:NSData = NSData(bytes: result, length: (Int(algorithm.digestLength())))
        var hmacBase64 = hmacData.base64EncodedStringWithOptions(NSDataBase64EncodingOptions.Encoding76CharacterLineLength)
        return String(hmacBase64)
    }
}

Cela me donne le résultat correct de 

lGCtbW+DNHFraNoxPGK3trgM/98=
31
David Wood

Voici la solution de @David Wood mise à jour pour Swift 3:

enum HMACAlgorithm {
    case MD5, SHA1, SHA224, SHA256, SHA384, SHA512

    func toCCHmacAlgorithm() -> 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 hmac(algorithm: HMACAlgorithm, key: String) -> String {
        let cKey = key.cString(using: String.Encoding.utf8)
        let cData = self.cString(using: String.Encoding.utf8)
        var result = [CUnsignedChar](repeating: 0, count: Int(algorithm.digestLength()))
        CCHmac(algorithm.toCCHmacAlgorithm(), cKey!, Int(strlen(cKey!)), cData!, Int(strlen(cData!)), &result)
        let hmacData:NSData = NSData(bytes: result, length: (Int(algorithm.digestLength())))
        let hmacBase64 = hmacData.base64EncodedString(options: NSData.Base64EncodingOptions.lineLength76Characters)
        return String(hmacBase64)
    }
}

// usage:
let hmacResult: String = "myStringToHMAC".hmac(algorithm: HMACAlgorithm.SHA1, key: "foo")
13

Voici comment créer une extension Swift 4:

Fichier d'en-têtes de pontage

#import <CommonCrypto/CommonCrypto.h>

Code

extension String {

    func hmac(key: String) -> String {
        var digest = [UInt8](repeating: 0, count: Int(CC_SHA1_DIGEST_LENGTH))
        CCHmac(CCHmacAlgorithm(kCCHmacAlgSHA1), key, key.count, self, self.count, &digest)
        let data = Data(bytes: digest)
        return data.map { String(format: "%02hhx", $0) }.joined()
    }

}

Exemple

let result = "test".hmac(key: "test")

Résultat

0c94515c15e5095b8a87a50ba0df3bf38ed05fe6
10
sundance

Si vous voulez le même résultat au format hexadécimal, vous pouvez utiliser l'extension suivante: 

extension String {
    func hmac(algorithm: HMACAlgorithm, key: String) -> String {
        let cKey = key.cStringUsingEncoding(NSUTF8StringEncoding)
        let cData = self.cStringUsingEncoding(NSUTF8StringEncoding)
        var result = [CUnsignedChar](count: Int(algorithm.digestLength()), repeatedValue: 0)
        let length : Int = Int(strlen(cKey!))
        let data : Int = Int(strlen(cData!))
        CCHmac(algorithm.toCCHmacAlgorithm(), cKey!,length , cData!, data, &result)

        let hmacData:NSData = NSData(bytes: result, length: (Int(algorithm.digestLength())))

        var bytes = [UInt8](count: hmacData.length, repeatedValue: 0)
        hmacData.getBytes(&bytes, length: hmacData.length)

        var hexString = ""
        for byte in bytes {
            hexString += String(format:"%02hhx", UInt8(byte))
        }
        return hexString
    }
}
8
Kautham Krishna

J'ai utilisé ce module que j'ai ajouté à mon projet comme cadre:

https://github.com/CryptoCoinSwift/SHA256-Swift

Et j'ai aussi ajouté l'extension String suivante à SHA256.Swift:

public extension String {

    func sha256(key: String) -> String {
        let inputData: NSData = self.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
        let keyData: NSData = key.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!

        let algorithm = HMACAlgorithm.SHA256
        let digestLen = algorithm.digestLength()
        let result = UnsafeMutablePointer<CUnsignedChar>.alloc(digestLen)

        CCHmac(algorithm.toCCEnum(), keyData.bytes, UInt(keyData.length), inputData.bytes, UInt(inputData.length), result)
        let data = NSData(bytes: result, length: digestLen)
        result.destroy()
        return data.base64EncodedStringWithOptions(NSDataBase64EncodingOptions.Encoding64CharacterLineLength)
    }

}

De cette façon, produire une signature encodée en base64 à partir d'une String peut être fait comme ceci:

let signature: String = "\(payload)".sha256(secretKey)
7
maksimov

J'ai vérifié les réponses ci-dessus et je l'ai trouvé si long.

Solution: J'ai un tiers: IDZSwiftCommonCrypto

Utilisez pod: pod 'IDZSwiftCommonCrypto'

et utilisez la fonction suivante pour obtenir le résultat souhaité:

func getHMacSHA1(forMessage message: String, key: String) -> String? {
    let hMacVal = HMAC(algorithm: HMAC.Algorithm.sha1, key: key).update(string: message)?.final()
    if let encryptedData = hMacVal {
        let decData = NSData(bytes: encryptedData, length: Int(encryptedData.count))
        let base64String = decData.base64EncodedString(options: .lineLength64Characters)
        print("base64String: \(base64String)")
        return base64String
    } else {
        return nil
    }
}

Pour vérifier le résultat, utilisez le site Web suivant:

https://hash.online-convert.com/sha1-generator

Testé dans Swift 4.0

0
g212gs

Dans Swift 4 vous avez besoin de la bibliothèque CommonCrypto https://forums.developer.Apple.com/thread/46477

#import <CommonCrypto/CommonCrypto.h>

Et vous pouvez créer une extension avec base64

extension String {
    func hmac(key: String) -> String {
        var digest = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH))
        CCHmac(CCHmacAlgorithm(kCCHmacAlgSHA256), key, key.count, self, self.count, &digest)
        let data = Data(bytes: digest)
        return data.base64EncodedString(options: Data.Base64EncodingOptions(rawValue: 0))
    }
}

Usege:

print("HMAC_SHA256:".hmac(key: "MyKey"))

Résultat:

6GM2evJeNZYdP3OjPcKmg8TDzILSQAjy4NGhCHnBH5M=
0
Sergei

Utiliser des octets bruts pour la clé et le message et ne pas encoder en utf8:

    static func getHmac_X_Sha1() -> [UInt8] {

        let msg:[UInt8] = message_uint8;
        let msgLen = message_uint8.count;
        let digestLen = Int(CC_SHA1_DIGEST_LENGTH)
        let digest = UnsafeMutablePointer<CUnsignedChar>.allocate(capacity: digestLen)
        let keyStr:[UInt8] = key_uint8
        let keyLen = key_uint8.count

        CCHmac(CCHmacAlgorithm(kCCHmacAlgSHA1), keyStr, keyLen, msg, msgLen, digest)


        //Build a hex string of result
        let hash_hex_string = NSMutableString()
        for i in 0..<digestLen {
            hash_hex_string.appendFormat("%02x", result[i])
         }
         //print(hash_hex_string)
         result.deallocate()

         // Resolve hash_hex_string to byte array
         let hash_bytes:[UInt8] = hexStringToBytes(String(hash_hex_string))
         return hash_bytes
     }



    //Return a byte array from hex string input
     private static func hexStringToBytes(_ string: String) -> [UInt8]? {
        let length = string.characters.count
        if length & 1 != 0 {
            return nil
        }
        var bytes = [UInt8]()
        bytes.reserveCapacity(length/2)
        var index = string.startIndex
        for _ in 0..<length/2 {
            let nextIndex = string.index(index, offsetBy: 2)
            if let b = UInt8(string[index..<nextIndex], radix: 16) {
                bytes.append(b)
            } else {
                return nil
            }
            index = nextIndex
        }
        return bytes
     }
0
Matt