当前位置: > > > Swift - 如何实现字符串的HMAC_SHA1加密

Swift - 如何实现字符串的HMAC_SHA1加密

(本文代码已升级至Swift4)

前段时间有个网友问是否有 Swift HMAC_SHA1 算法。这次就专门写篇相关文章进行介绍。要说明 HMAC-SHA1,首先要先了解什么是 HMAC,什么是 SHA。 

一、基本概念

1,HMAC(散列消息身份验证码:Hashed Message Authentication Code)

(1)它不是散列函数,而是采用了将 MD5 SHA1 散列函数与共享机密密钥(与公钥/私钥对不同)一起使用的消息身份验证机制。基本来说,消息与密钥组合并运行散列函数。然后运行结果与密钥组合并再次运行散列函数。这个 128 位的结果被截断成 96 位,成为 MAC

hmac主要应用在身份验证中,它的使用方法是这样的:
  • 1. 客户端发出登录请求(假设是浏览器的 GET 请求)
  • 2. 服务器返回一个随机值,并在会话中记录这个随机值
  • 3. 客户端将该随机值作为密钥,用户密码进行 hmac 运算,然后提交给服务器
  • 4. 服务器读取用户数据库中的用户密码和步骤2中发送的随机值做与客户端一样的 hmac 运算,然后与用户发送的结果比较,如果结果一致则验证用户合法

(2)在这个过程中,可能遭到安全攻击的是服务器发送的随机值和用户发送的 hmac 结果,而对于截获了这两个值的黑客而言这两个值是没有意义的,绝无获取用户密码的可能性,随机值的引入使 hmac 只在当前会话中有效,大大增强了安全性和实用性。大多数的语言都实现了 hmac 算法,比如 php mhashpython hmac.pyjava MessageDigest 类,在 web 验证中使用 hmac 也是可行的,用 js 进行 md5 运算的速度也是比较快的。

2,SHA(安全散列算法:Secure Hash Algorithm)

(1)这个是美国国家标准和技术局发布的国家标准 FIPS PUB 180-1,一般称为 SHA-1。其对长度不超过 264 二进制位的消息产生 160 位的消息摘要输出,按 512 比特块处理其输入。
(2)SHA 是一种数据加密算法,该算法经过加密专家多年来的发展和改进已日益完善,现在已成为公认的最安全的散列算法之一,并被广泛使用。该算法的思想是接收一段明文,然后以一种不可逆的方式将它转换成一段(通常更小)密文,也可以简单的理解为取一串输入码(称为预映射或信息),并把它们转化为长度较短、位数固定的输出序列即散列值(也称为信息摘要或信息认证代码)的过程。散列函数值可以说时对明文的一种“指纹”或是“摘要”所以对散列值的数字签名就可以视为对此明文的数字签名。

3,HMAC_SHA1(Hashed Message Authentication Code, Secure Hash Algorithm)

(1)这是一种安全的基于加密 hash 函数和共享密钥的消息认证协议。它可以有效地防止数据在传输过程中被截获和篡改,维护了数据的完整性、可靠性和安全性。HMAC_SHA1 消息认证机制的成功在于一个加密的 hash 函数、一个加密的随机密钥和一个安全的密钥交换机制。
(2)HMAC_SHA1 算法在身份验证和数据完整性方面可以得到很好的应用,在目前网络安全也得到较好的实现。

二、使用Swift进行HMAC_SHA1计算

(1)首先创建桥接头文件 bridge.h 来包含需要引用的 Objective-C 头文件,并在项目中配置
#import <CommonCrypto/CommonHMAC.h>

(2)下面是一个封装类,同时对 String 进行 HMAC 扩展。(除了 SHA1,还可以使用其它算法比如 MD5SHA224 等)
//HMAC.swift
import Foundation
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)
    }
}

(3)测试样例
let str = "welcome to hangge.com"
let key = "67FG"
let hmacStr = str.hmac(algorithm: .SHA1, key: key)

print("原始字符串:\(str)")
print("key:\(key)")
print("HMAC运算结果:\(hmacStr)")
运行结果如下:

评论8
  • 8楼
    2018-01-04 11:20
    航哥的粉丝

    航哥,为什么我改成swift3之后,用你的字符串和key加密得到的结果却和你不一致呢?结果是6319f7bd649b47bb42bedec713a46b4c4ac5bd40

    站长回复

    我把代码更新了,你可以再试试看。

  • 7楼
    2017-09-27 14:30
    fan

    大神?接口加密一般如何选择和操作呢?

    站长回复

    你说的接口指的是什么,是HTTP网络请求接口吗?通常我们网络请求使用HTTPS协议就可以了,该协议会自动对传输的数据进行加密,可以防止中途被拦截篡改等,不用我们特别做加密处理。

  • 6楼
    2017-03-15 10:03
    MG明明

    MD5, SHA1, SHA224, SHA256, SHA384, SHA512 有木有哪个是可逆的加密算法??

    站长回复

    这些都不是可逆的。如果像要加密解密的话,可以使用:AES、DES等对称加密算法,或者RSA、DSA等非对称加密算法。
    关于RSA加密我原来也写过相关文章嘛,你可以看下:Swift - 使用RSA算法进行数据加密,解密以及数字签名

  • 5楼
    2016-07-19 16:57
    大豆

    就是返回原始二进制数据表示的信息摘要,不是 16 进制字符串格式表示的信息摘要。

    站长回复

    这个我也没研究过,暂时帮不了你了。

  • 4楼
    2016-07-10 19:15
    大豆

    怎么输入原始格式(outputs raw binary data)的结果呀?
    在对 raw binary data 做 base64_encode

    站长回复

    不太清楚你的意思,这个和HMAC_SHA1有什么关系吗?

  • 3楼
    2016-01-11 16:37
    xxg

    忍不住的感谢站长了。

    站长回复

    不客气^_^

  • 2楼
    2015-09-03 16:27
    Nyx

    %>_<% 这是有屏蔽么 为什么不能留言%>_<%

    站长回复

    可以留言的,需要我审核后才能显示出来。

  • 1楼
    2015-09-03 15:14
    Nyx

    初接触 Swift 找到你的文章 受益良多 %>_<%

    站长回复

    欢迎常来看看