Swift=で文字列のHMAC SHA-1ハッシュを作成しようとしていますが、CommonCryptoフレームワークをインポートしていないように見えるため、APIとのやり取り方法がわかりません。 。「CommonCryptoのインポート」のさまざまな形式を試し、ブリッジングヘッダーファイルを作成しましたが、どれも違いはありませんでした。
奇妙なことに、Objective-Cクラスを作成すれば、問題なくAPIとやり取りできるため、これはSwiftに固有のようです。
また、誰かがuint8_t digest[CC_SHA1_DIGEST_LENGTH]
はSwiftに感謝します
Swiftでできます。必ず#import <CommonCrypto/CommonHMAC.h>
をブリッジングObjective-Cブリッジングヘッダーに追加してください。
更新:Swift 4の場合、Swiftパッケージを使用したより良いソリューションを参照してください。ここにマネージャー: 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 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)
}
}
#import <CommonCrypto/CommonHMAC.h>
をHeader.h
に追加することを忘れないでください
物事を最小限に抑えたかった。さまざまなダイジェストタイプをすべて処理できる汎用クラスを作成する複雑さを避け、代わりに必要に応じてクラスにドロップできる小さなメソッドを用意するだけです。また、コアクラスに拡張機能を追加することも避けたいです。
_-Bridging-Header.h
_ファイルに次のインクルードを追加します。
_#import <CommonCrypto/CommonHMAC.h>
_
次に、CCHmac()
を呼び出す必要があるクラスにプライベートメソッドを追加します。
_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)
}
_
別のCCHmacAlgorithm
が必要な場合は、そのメソッドの2つの定数を適切なものに置き換えるだけです。私の場合、_kCCHmacAlgSHA256
_と_CC_SHA256_DIGEST_LENGTH
_が必要でした。
他の答えをくれたJernej Strasnerと他の人たちに感謝します。
Swiftは、Obj-Cフレームワークへのマッピング方法を理解できますが、直接的なC関数についてはあまり理解できません。 AppleはGCDやAudioToolboxのようなものにいくつかのバインディングを提供しましたが、すべてではありません。CommonCryptoにはまだ適切なバインディングがありません。
この場合、Obj-Cで独自の基本ラッパーを作成し、Swiftでこれらのラッパークラスを使用することをお勧めします。
たとえば、Obj-CでHMAC
クラスを作成できます。
// 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;
}
}
その後、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
}
}
#import "HMAC.h"
をSwift=ブリッジングヘッダーに追加するだけでなく、#import "<##Your-Project-Name##>-Swift.h"
をObj-C実装(.m)ファイルに追加することを忘れないでください。
以下は@ jernej-strasnerが投稿したものの修正版です
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)
}
}
OS Xの場合(ただし、9.3.1が現在のiOSの場合ではありません)、SecTransform
を使用して、SwiftでHMAC SHA-1を計算できます。ブリッジングヘッダーもObjective-Cもありません。 RFC 2202 の最初のHMAC-SHA-1テストケースからの入力を使用した例を次に示します。
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
これは、「Swift Obj-cブリッジングヘッダーのないプロジェクトでCommonCryptoをインポートする方法」の完全なソースです。Swift 3.0に変更されました。実際のコード作業は「ミハエル・イザエフ」の。
//
// 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)
}
}
以下は使用例です。
// 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)")
これが私のようなユーザーにとってすぐに参照できることを願っています。 :)
ありがとう Jernej Strasner すばらしい回答をありがとう。ここで、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)
}
また、それを使用するには:
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!
}
after Swift 1.2 func digestの行を置き換えます
let strLen = UInt(self.lengthOfBytesUsingEncoding(NSUTF8StringEncoding))
let keyLen = UInt(key.lengthOfBytesUsingEncoding(NSUTF8StringEncoding))
によって
let strLen = self.lengthOfBytesUsingEncoding(NSUTF8StringEncoding)
let keyLen = key.lengthOfBytesUsingEncoding(NSUTF8StringEncoding)
GithubでのSwift 4バージョンのデモ repo