J'essaie de créer un hachage HMAC SHA-1 d'une chaîne en Swift mais je ne peux pas comprendre comment interagir avec les API car il ne semble pas importer le cadre CommonCrypto J'ai essayé différentes formes d '"import CommonCrypto" et de création d'un fichier d'en-tête de pontage mais rien de tout cela n'a fait la différence.
La chose étrange est que si je crée une classe Objective-C, je suis capable d'interagir avec les API sans aucun problème, donc cela semble être unique à Swift.
Aussi, si quelqu'un pouvait me dire ce que l'équivalent de uint8_t digest[CC_SHA1_DIGEST_LENGTH]
est en Swift je serais très reconnaissant
Vous pouvez le faire dans Swift. Assurez-vous simplement d'ajouter #import <CommonCrypto/CommonHMAC.h>
à l'en-tête de pontage Objective-C de pontage.
Mise à jour: Pour Swift 4 voyez une bien meilleure solution en utilisant le Swift Package Manager ici: 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)
}
}
Essayez ceci pour 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)
}
}
N'oubliez pas d'ajouter #import <CommonCrypto/CommonHMAC.h>
à Header.h
Je voulais garder les choses minimales. Évitez la complexité supplémentaire de créer une classe générique qui pourrait gérer tous les différents types de condensés et à la place, ayez simplement une petite méthode que je pourrais déposer dans une classe si nécessaire. Je préfère également éviter d'ajouter des extensions aux classes principales.
Ajoutez l'inclusion suivante à votre fichier -Bridging-Header.h
:
#import <CommonCrypto/CommonHMAC.h>
Ensuite, dans la classe qui doit appeler CCHmac()
ajoutez une méthode privée:
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 j'ai besoin d'un CCHmacAlgorithm
différent, je remplacerais simplement les deux constantes de cette méthode par les constantes appropriées. Dans mon cas, j'avais besoin de kCCHmacAlgSHA256
Et CC_SHA256_DIGEST_LENGTH
.
Merci à Jernej Strasner et aux autres pour les autres réponses, je voulais juste quelque chose de plus simple pour mon cas.
Swift peut comprendre comment mapper aux frameworks Obj-C mais pas tant pour les fonctions C directes. Apple a fourni quelques liaisons pour des choses comme GCD et AudioToolbox, mais pas tout. Il semble que CommonCrypto ne dispose pas encore de liaisons appropriées.
Dans ce cas, je recommanderais d'écrire vos propres wrappers de base dans Obj-C, puis d'utiliser ces classes de wrapper dans Swift.
Par exemple, vous pouvez créer une classe HMAC
dans 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;
}
}
Puis dans 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
}
}
N'oubliez pas d'ajouter #import "HMAC.h"
à votre Swift ainsi que #import "<##Your-Project-Name##>-Swift.h"
au fichier d'implémentation Obj-C (.m).
Voici une version corrigée de ce que @ jernej-strasner a publié
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)
}
}
Pour OS X (mais pas pour iOS à ce jour, lorsque 9.3.1 est à jour), vous pouvez utiliser un SecTransform
pour calculer un HMAC SHA-1 dans Swift with pas d'en-tête de pontage et pas d'objectif C. Voici un exemple utilisant l'entrée du premier cas de test HMAC-SHA-1 dans 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
Ceci est la source complète de "Comment importer CommonCrypto dans Swift sans en-tête de brigade Obj-c", modifié pour Swift 3.0. Le travail de code réel est 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)
}
}
Voici un exemple d'utilisation.
// 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)")
J'espère que cela pourrait être une référence prête pour un utilisateur comme moi. :)
Merci Jernej Strasner pour votre excellente réponse. Ici, je viens de mettre à jour sa réponse pour 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)
}
Et aussi pour l'utiliser:
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!
}
après Swift 1.2 remplacer les lignes dans func digest
let strLen = UInt(self.lengthOfBytesUsingEncoding(NSUTF8StringEncoding))
let keyLen = UInt(key.lengthOfBytesUsingEncoding(NSUTF8StringEncoding))
par
let strLen = self.lengthOfBytesUsingEncoding(NSUTF8StringEncoding)
let keyLen = key.lengthOfBytesUsingEncoding(NSUTF8StringEncoding)
Démo de la version Swift 4 sur Github repo