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.
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=
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")
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
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
}
}
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)
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
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=
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
}