使用hash_hmac和ripemd160在Swift中对字符串进行哈希处理

10

我正在尝试使用ripemd160在Swift 3(和/或Objective-C)中哈希字符串,以模拟以下PHP的输出:

$string = 'string';
$key = 'test';

hash_hmac('ripemd160', $string, $key);

// outputs: 37241f2513c60ae4d9b3b8d0d30517445f451fa5


到目前为止:

我已经尝试过查找可以找到的hash_hmac功能,例如:
在Swift中实现HMAC和SHA1加密

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

    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
        case .RIPEMD160:
            result = kCCHmacAlgRIPEMD160
        }
        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)
    }
}

并且实现:

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

这样可以让我成功使用HMAC和<CommonCrypto/CommonHMAC.h>提供的加密算法来哈希一个字符串。

问题

问题在于该库不支持ripemd160,那么我如何添加此支持以便能够完成我需要的功能?


有一个与此完全相同的Java问题,您可以在此处查看该问题的解决方案,以及我正寻找的内容,但这次是使用Swift 3:
使用ripemd160和密钥在Java中哈希字符串以模拟php函数hash_hmac


2
这是Swift 1.x版本的代码,但将其移植到Swift 3应该不是不可能的。https://rosettacode.org/wiki/RIPEMD-160#Swift - JeremyP
@JeremyP:我试过了,结果非常慢(因子>50),与C语言解决方案(由Jens建议)或我翻译的C代码的Swift版本相比。 - Martin R
2个回答

15

这是一个(自包含的)纯Swift 5实现 RIPEMD-160 哈希函数和相应的HMAC-RIPEMD-160消息认证代码。它已经在RIPEMD-160页面上的所有测试向量上进行了测试(测试于macOS、32位iOS和64位iOS)。

Swift 3的代码可以在编辑历史中找到。

这是从C语言版的 rmd160.hrmd160.c 的RIPEMD-160页面翻译成Swift的参考实现。我尽可能地“逐字逐句”地翻译了辅助宏和算法的核心函数compress()。这使我能够复制/粘贴大的C代码段并仅对Swift进行少量调整。

update()finalize() 方法提供了流式接口,类似于CommonCrypto函数。

RIPEMD160.swift:

import Foundation

public struct RIPEMD160 {

    private var MDbuf: (UInt32, UInt32, UInt32, UInt32, UInt32)
    private var buffer: Data
    private var count: Int64 // Total # of bytes processed.

    public init() {
        MDbuf = (0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0)
        buffer = Data()
        count = 0
    }

    private mutating func compress(_ X: UnsafePointer<UInt32>) {

        // *** Helper functions (originally macros in rmd160.h) ***

        /* ROL(x, n) cyclically rotates x over n bits to the left */
        /* x must be of an unsigned 32 bits type and 0 <= n < 32. */
        func ROL(_ x: UInt32, _ n: UInt32) -> UInt32 {
            return (x << n) | ( x >> (32 - n))
        }

        /* the five basic functions F(), G() and H() */

        func F(_ x: UInt32, _ y: UInt32, _ z: UInt32) -> UInt32 {
            return x ^ y ^ z
        }

        func G(_ x: UInt32, _ y: UInt32, _ z: UInt32) -> UInt32 {
            return (x & y) | (~x & z)
        }

        func H(_ x: UInt32, _ y: UInt32, _ z: UInt32) -> UInt32 {
            return (x | ~y) ^ z
        }

        func I(_ x: UInt32, _ y: UInt32, _ z: UInt32) -> UInt32 {
            return (x & z) | (y & ~z)
        }

        func J(_ x: UInt32, _ y: UInt32, _ z: UInt32) -> UInt32 {
            return x ^ (y | ~z)
        }

        /* the ten basic operations FF() through III() */

        func FF(_ a: inout UInt32, _ b: UInt32, _ c: inout UInt32, _ d: UInt32, _ e: UInt32, _ x: UInt32, _ s: UInt32) {
            a = a &+ F(b, c, d) &+ x
            a = ROL(a, s) &+ e
            c = ROL(c, 10)
        }

        func GG(_ a: inout UInt32, _ b: UInt32, _ c: inout UInt32, _ d: UInt32, _ e: UInt32, _ x: UInt32, _ s: UInt32) {
            a = a &+ G(b, c, d) &+ x &+ 0x5a827999
            a = ROL(a, s) &+ e
            c = ROL(c, 10)
        }

        func HH(_ a: inout UInt32, _ b: UInt32, _ c: inout UInt32, _ d: UInt32, _ e: UInt32, _ x: UInt32, _ s: UInt32) {
            a = a &+ H(b, c, d) &+ x &+ 0x6ed9eba1
            a = ROL(a, s) &+ e
            c = ROL(c, 10)
        }

        func II(_ a: inout UInt32, _ b: UInt32, _ c: inout UInt32, _ d: UInt32, _ e: UInt32, _ x: UInt32, _ s: UInt32) {
            a = a &+ I(b, c, d) &+ x &+ 0x8f1bbcdc
            a = ROL(a, s) &+ e
            c = ROL(c, 10)
        }

        func JJ(_ a: inout UInt32, _ b: UInt32, _ c: inout UInt32, _ d: UInt32, _ e: UInt32, _ x: UInt32, _ s: UInt32) {
            a = a &+ J(b, c, d) &+ x &+ 0xa953fd4e
            a = ROL(a, s) &+ e
            c = ROL(c, 10)
        }

        func FFF(_ a: inout UInt32, _ b: UInt32, _ c: inout UInt32, _ d: UInt32, _ e: UInt32, _ x: UInt32, _ s: UInt32) {
            a = a &+ F(b, c, d) &+ x
            a = ROL(a, s) &+ e
            c = ROL(c, 10)
        }

        func GGG(_ a: inout UInt32, _ b: UInt32, _ c: inout UInt32, _ d: UInt32, _ e: UInt32, _ x: UInt32, _ s: UInt32) {
            a = a &+ G(b, c, d) &+ x &+ 0x7a6d76e9
            a = ROL(a, s) &+ e
            c = ROL(c, 10)
        }

        func HHH(_ a: inout UInt32, _ b: UInt32, _ c: inout UInt32, _ d: UInt32, _ e: UInt32, _ x: UInt32, _ s: UInt32) {
            a = a &+ H(b, c, d) &+ x &+ 0x6d703ef3
            a = ROL(a, s) &+ e
            c = ROL(c, 10)
        }

        func III(_ a: inout UInt32, _ b: UInt32, _ c: inout UInt32, _ d: UInt32, _ e: UInt32, _ x: UInt32, _ s: UInt32) {
            a = a &+ I(b, c, d) &+ x &+ 0x5c4dd124
            a = ROL(a, s) &+ e
            c = ROL(c, 10)
        }

        func JJJ(_ a: inout UInt32, _ b: UInt32, _ c: inout UInt32, _ d: UInt32, _ e: UInt32, _ x: UInt32, _ s: UInt32) {
            a = a &+ J(b, c, d) &+ x &+ 0x50a28be6
            a = ROL(a, s) &+ e
            c = ROL(c, 10)
        }

        // *** The function starts here ***

        var (aa, bb, cc, dd, ee) = MDbuf
        var (aaa, bbb, ccc, ddd, eee) = MDbuf

        /* round 1 */
        FF(&aa, bb, &cc, dd, ee, X[ 0], 11)
        FF(&ee, aa, &bb, cc, dd, X[ 1], 14)
        FF(&dd, ee, &aa, bb, cc, X[ 2], 15)
        FF(&cc, dd, &ee, aa, bb, X[ 3], 12)
        FF(&bb, cc, &dd, ee, aa, X[ 4],  5)
        FF(&aa, bb, &cc, dd, ee, X[ 5],  8)
        FF(&ee, aa, &bb, cc, dd, X[ 6],  7)
        FF(&dd, ee, &aa, bb, cc, X[ 7],  9)
        FF(&cc, dd, &ee, aa, bb, X[ 8], 11)
        FF(&bb, cc, &dd, ee, aa, X[ 9], 13)
        FF(&aa, bb, &cc, dd, ee, X[10], 14)
        FF(&ee, aa, &bb, cc, dd, X[11], 15)
        FF(&dd, ee, &aa, bb, cc, X[12],  6)
        FF(&cc, dd, &ee, aa, bb, X[13],  7)
        FF(&bb, cc, &dd, ee, aa, X[14],  9)
        FF(&aa, bb, &cc, dd, ee, X[15],  8)

        /* round 2 */
        GG(&ee, aa, &bb, cc, dd, X[ 7],  7)
        GG(&dd, ee, &aa, bb, cc, X[ 4],  6)
        GG(&cc, dd, &ee, aa, bb, X[13],  8)
        GG(&bb, cc, &dd, ee, aa, X[ 1], 13)
        GG(&aa, bb, &cc, dd, ee, X[10], 11)
        GG(&ee, aa, &bb, cc, dd, X[ 6],  9)
        GG(&dd, ee, &aa, bb, cc, X[15],  7)
        GG(&cc, dd, &ee, aa, bb, X[ 3], 15)
        GG(&bb, cc, &dd, ee, aa, X[12],  7)
        GG(&aa, bb, &cc, dd, ee, X[ 0], 12)
        GG(&ee, aa, &bb, cc, dd, X[ 9], 15)
        GG(&dd, ee, &aa, bb, cc, X[ 5],  9)
        GG(&cc, dd, &ee, aa, bb, X[ 2], 11)
        GG(&bb, cc, &dd, ee, aa, X[14],  7)
        GG(&aa, bb, &cc, dd, ee, X[11], 13)
        GG(&ee, aa, &bb, cc, dd, X[ 8], 12)

        /* round 3 */
        HH(&dd, ee, &aa, bb, cc, X[ 3], 11)
        HH(&cc, dd, &ee, aa, bb, X[10], 13)
        HH(&bb, cc, &dd, ee, aa, X[14],  6)
        HH(&aa, bb, &cc, dd, ee, X[ 4],  7)
        HH(&ee, aa, &bb, cc, dd, X[ 9], 14)
        HH(&dd, ee, &aa, bb, cc, X[15],  9)
        HH(&cc, dd, &ee, aa, bb, X[ 8], 13)
        HH(&bb, cc, &dd, ee, aa, X[ 1], 15)
        HH(&aa, bb, &cc, dd, ee, X[ 2], 14)
        HH(&ee, aa, &bb, cc, dd, X[ 7],  8)
        HH(&dd, ee, &aa, bb, cc, X[ 0], 13)
        HH(&cc, dd, &ee, aa, bb, X[ 6],  6)
        HH(&bb, cc, &dd, ee, aa, X[13],  5)
        HH(&aa, bb, &cc, dd, ee, X[11], 12)
        HH(&ee, aa, &bb, cc, dd, X[ 5],  7)
        HH(&dd, ee, &aa, bb, cc, X[12],  5)

        /* round 4 */
        II(&cc, dd, &ee, aa, bb, X[ 1], 11)
        II(&bb, cc, &dd, ee, aa, X[ 9], 12)
        II(&aa, bb, &cc, dd, ee, X[11], 14)
        II(&ee, aa, &bb, cc, dd, X[10], 15)
        II(&dd, ee, &aa, bb, cc, X[ 0], 14)
        II(&cc, dd, &ee, aa, bb, X[ 8], 15)
        II(&bb, cc, &dd, ee, aa, X[12],  9)
        II(&aa, bb, &cc, dd, ee, X[ 4],  8)
        II(&ee, aa, &bb, cc, dd, X[13],  9)
        II(&dd, ee, &aa, bb, cc, X[ 3], 14)
        II(&cc, dd, &ee, aa, bb, X[ 7],  5)
        II(&bb, cc, &dd, ee, aa, X[15],  6)
        II(&aa, bb, &cc, dd, ee, X[14],  8)
        II(&ee, aa, &bb, cc, dd, X[ 5],  6)
        II(&dd, ee, &aa, bb, cc, X[ 6],  5)
        II(&cc, dd, &ee, aa, bb, X[ 2], 12)

        /* round 5 */
        JJ(&bb, cc, &dd, ee, aa, X[ 4],  9)
        JJ(&aa, bb, &cc, dd, ee, X[ 0], 15)
        JJ(&ee, aa, &bb, cc, dd, X[ 5],  5)
        JJ(&dd, ee, &aa, bb, cc, X[ 9], 11)
        JJ(&cc, dd, &ee, aa, bb, X[ 7],  6)
        JJ(&bb, cc, &dd, ee, aa, X[12],  8)
        JJ(&aa, bb, &cc, dd, ee, X[ 2], 13)
        JJ(&ee, aa, &bb, cc, dd, X[10], 12)
        JJ(&dd, ee, &aa, bb, cc, X[14],  5)
        JJ(&cc, dd, &ee, aa, bb, X[ 1], 12)
        JJ(&bb, cc, &dd, ee, aa, X[ 3], 13)
        JJ(&aa, bb, &cc, dd, ee, X[ 8], 14)
        JJ(&ee, aa, &bb, cc, dd, X[11], 11)
        JJ(&dd, ee, &aa, bb, cc, X[ 6],  8)
        JJ(&cc, dd, &ee, aa, bb, X[15],  5)
        JJ(&bb, cc, &dd, ee, aa, X[13],  6)

        /* parallel round 1 */
        JJJ(&aaa, bbb, &ccc, ddd, eee, X[ 5],  8)
        JJJ(&eee, aaa, &bbb, ccc, ddd, X[14],  9)
        JJJ(&ddd, eee, &aaa, bbb, ccc, X[ 7],  9)
        JJJ(&ccc, ddd, &eee, aaa, bbb, X[ 0], 11)
        JJJ(&bbb, ccc, &ddd, eee, aaa, X[ 9], 13)
        JJJ(&aaa, bbb, &ccc, ddd, eee, X[ 2], 15)
        JJJ(&eee, aaa, &bbb, ccc, ddd, X[11], 15)
        JJJ(&ddd, eee, &aaa, bbb, ccc, X[ 4],  5)
        JJJ(&ccc, ddd, &eee, aaa, bbb, X[13],  7)
        JJJ(&bbb, ccc, &ddd, eee, aaa, X[ 6],  7)
        JJJ(&aaa, bbb, &ccc, ddd, eee, X[15],  8)
        JJJ(&eee, aaa, &bbb, ccc, ddd, X[ 8], 11)
        JJJ(&ddd, eee, &aaa, bbb, ccc, X[ 1], 14)
        JJJ(&ccc, ddd, &eee, aaa, bbb, X[10], 14)
        JJJ(&bbb, ccc, &ddd, eee, aaa, X[ 3], 12)
        JJJ(&aaa, bbb, &ccc, ddd, eee, X[12],  6)

        /* parallel round 2 */
        III(&eee, aaa, &bbb, ccc, ddd, X[ 6],  9)
        III(&ddd, eee, &aaa, bbb, ccc, X[11], 13)
        III(&ccc, ddd, &eee, aaa, bbb, X[ 3], 15)
        III(&bbb, ccc, &ddd, eee, aaa, X[ 7],  7)
        III(&aaa, bbb, &ccc, ddd, eee, X[ 0], 12)
        III(&eee, aaa, &bbb, ccc, ddd, X[13],  8)
        III(&ddd, eee, &aaa, bbb, ccc, X[ 5],  9)
        III(&ccc, ddd, &eee, aaa, bbb, X[10], 11)
        III(&bbb, ccc, &ddd, eee, aaa, X[14],  7)
        III(&aaa, bbb, &ccc, ddd, eee, X[15],  7)
        III(&eee, aaa, &bbb, ccc, ddd, X[ 8], 12)
        III(&ddd, eee, &aaa, bbb, ccc, X[12],  7)
        III(&ccc, ddd, &eee, aaa, bbb, X[ 4],  6)
        III(&bbb, ccc, &ddd, eee, aaa, X[ 9], 15)
        III(&aaa, bbb, &ccc, ddd, eee, X[ 1], 13)
        III(&eee, aaa, &bbb, ccc, ddd, X[ 2], 11)

        /* parallel round 3 */
        HHH(&ddd, eee, &aaa, bbb, ccc, X[15],  9)
        HHH(&ccc, ddd, &eee, aaa, bbb, X[ 5],  7)
        HHH(&bbb, ccc, &ddd, eee, aaa, X[ 1], 15)
        HHH(&aaa, bbb, &ccc, ddd, eee, X[ 3], 11)
        HHH(&eee, aaa, &bbb, ccc, ddd, X[ 7],  8)
        HHH(&ddd, eee, &aaa, bbb, ccc, X[14],  6)
        HHH(&ccc, ddd, &eee, aaa, bbb, X[ 6],  6)
        HHH(&bbb, ccc, &ddd, eee, aaa, X[ 9], 14)
        HHH(&aaa, bbb, &ccc, ddd, eee, X[11], 12)
        HHH(&eee, aaa, &bbb, ccc, ddd, X[ 8], 13)
        HHH(&ddd, eee, &aaa, bbb, ccc, X[12],  5)
        HHH(&ccc, ddd, &eee, aaa, bbb, X[ 2], 14)
        HHH(&bbb, ccc, &ddd, eee, aaa, X[10], 13)
        HHH(&aaa, bbb, &ccc, ddd, eee, X[ 0], 13)
        HHH(&eee, aaa, &bbb, ccc, ddd, X[ 4],  7)
        HHH(&ddd, eee, &aaa, bbb, ccc, X[13],  5)

        /* parallel round 4 */
        GGG(&ccc, ddd, &eee, aaa, bbb, X[ 8], 15)
        GGG(&bbb, ccc, &ddd, eee, aaa, X[ 6],  5)
        GGG(&aaa, bbb, &ccc, ddd, eee, X[ 4],  8)
        GGG(&eee, aaa, &bbb, ccc, ddd, X[ 1], 11)
        GGG(&ddd, eee, &aaa, bbb, ccc, X[ 3], 14)
        GGG(&ccc, ddd, &eee, aaa, bbb, X[11], 14)
        GGG(&bbb, ccc, &ddd, eee, aaa, X[15],  6)
        GGG(&aaa, bbb, &ccc, ddd, eee, X[ 0], 14)
        GGG(&eee, aaa, &bbb, ccc, ddd, X[ 5],  6)
        GGG(&ddd, eee, &aaa, bbb, ccc, X[12],  9)
        GGG(&ccc, ddd, &eee, aaa, bbb, X[ 2], 12)
        GGG(&bbb, ccc, &ddd, eee, aaa, X[13],  9)
        GGG(&aaa, bbb, &ccc, ddd, eee, X[ 9], 12)
        GGG(&eee, aaa, &bbb, ccc, ddd, X[ 7],  5)
        GGG(&ddd, eee, &aaa, bbb, ccc, X[10], 15)
        GGG(&ccc, ddd, &eee, aaa, bbb, X[14],  8)

        /* parallel round 5 */
        FFF(&bbb, ccc, &ddd, eee, aaa, X[12] ,  8)
        FFF(&aaa, bbb, &ccc, ddd, eee, X[15] ,  5)
        FFF(&eee, aaa, &bbb, ccc, ddd, X[10] , 12)
        FFF(&ddd, eee, &aaa, bbb, ccc, X[ 4] ,  9)
        FFF(&ccc, ddd, &eee, aaa, bbb, X[ 1] , 12)
        FFF(&bbb, ccc, &ddd, eee, aaa, X[ 5] ,  5)
        FFF(&aaa, bbb, &ccc, ddd, eee, X[ 8] , 14)
        FFF(&eee, aaa, &bbb, ccc, ddd, X[ 7] ,  6)
        FFF(&ddd, eee, &aaa, bbb, ccc, X[ 6] ,  8)
        FFF(&ccc, ddd, &eee, aaa, bbb, X[ 2] , 13)
        FFF(&bbb, ccc, &ddd, eee, aaa, X[13] ,  6)
        FFF(&aaa, bbb, &ccc, ddd, eee, X[14] ,  5)
        FFF(&eee, aaa, &bbb, ccc, ddd, X[ 0] , 15)
        FFF(&ddd, eee, &aaa, bbb, ccc, X[ 3] , 13)
        FFF(&ccc, ddd, &eee, aaa, bbb, X[ 9] , 11)
        FFF(&bbb, ccc, &ddd, eee, aaa, X[11] , 11)

        /* combine results */
        MDbuf = (MDbuf.1 &+ cc &+ ddd,
                 MDbuf.2 &+ dd &+ eee,
                 MDbuf.3 &+ ee &+ aaa,
                 MDbuf.4 &+ aa &+ bbb,
                 MDbuf.0 &+ bb &+ ccc)
    }

    public mutating func update(data: Data) {
        var X = [UInt32](repeating: 0, count: 16)
        var pos = data.startIndex
        var length = data.count

        // Process remaining bytes from last call:
        if buffer.count > 0 && buffer.count + length >= 64 {
            let amount = 64 - buffer.count
            buffer.append(data[..<amount])
            X.withUnsafeMutableBytes {
                _ = buffer.copyBytes(to: $0)
            }
            compress(X)
            pos += amount
            length -= amount
        }

        // Process 64 byte chunks:
        while length >= 64 {
            X.withUnsafeMutableBytes {
                _ = data[pos..<pos+64].copyBytes(to: $0)
            }
            compress(X)
            pos += 64
            length -= 64
        }

        // Save remaining unprocessed bytes:
        buffer = data[pos...]
        count += Int64(data.count)
    }

    public mutating func finalize() -> Data {
        var X = [UInt32](repeating: 0, count: 16)
        /* append the bit m_n == 1 */
        buffer.append(0x80)
        X.withUnsafeMutableBytes {
            _ = buffer.copyBytes(to: $0)
        }

        if (count & 63) > 55 {
            /* length goes to next block */
            compress(X)
            X = [UInt32](repeating: 0, count: 16)
        }

        /* append length in bits */
        let lswlen = UInt32(truncatingIfNeeded: count)
        let mswlen = UInt32(UInt64(count) >> 32)
        X[14] = lswlen << 3
        X[15] = (lswlen >> 29) | (mswlen << 3)
        compress(X)

        buffer = Data()
        let result = [MDbuf.0, MDbuf.1, MDbuf.2, MDbuf.3, MDbuf.4]
        return result.withUnsafeBytes { Data($0) }
    }
}

这里是用于计算消息哈希的“一次性”函数(以DataString形式给出):

RIPEMD160-Ext.swift:

import Foundation

public extension RIPEMD160 {

    static func hash(message: Data) -> Data {
        var md = RIPEMD160()
        md.update(data: message)
        return md.finalize()
    }

    static func hash(message: String) -> Data {
        return RIPEMD160.hash(message: message.data(using: .utf8)!)
    }
}

最后,对应的消息认证码。算法取自于 Wikipedia:基于哈希的消息认证码中的伪代码:

RIPEMD160-HMAC.swift:

import Foundation

public extension RIPEMD160 {

    static func hmac(key: Data, message: Data) -> Data {

        var key = key
        key.count = 64 // Truncate to 64 bytes or fill-up with zeros.

        let outerKeyPad = Data(key.map { $0 ^ 0x5c })
        let innerKeyPad = Data(key.map { $0 ^ 0x36 })

        var innerMd = RIPEMD160()
        innerMd.update(data: innerKeyPad)
        innerMd.update(data: message)

        var outerMd = RIPEMD160()
        outerMd.update(data: outerKeyPad)
        outerMd.update(data: innerMd.finalize())

        return outerMd.finalize()
    }

    static func hmac(key: Data, message: String) -> Data {
        return RIPEMD160.hmac(key: key, message: message.data(using: .utf8)!)
    }

    static func hmac(key: String, message: String) -> Data {
        return RIPEMD160.hmac(key: key.data(using: .utf8)!, message: message)
    }
}

例子

以下是从如何在Swift中将数据转换为十六进制字符串的方法,用于将消息摘要打印为十六进制编码字符串:

extension Data {
    func hexEncodedString() -> String {
        return map { String(format: "%02hhx", $0) }.joined()
    }
}

示例 1:

let msg = "Hello World"
let hash = RIPEMD160.hash(message: msg).hexEncodedString()
print(hash) // a830d7beb04eb7549ce990fb7dc962e499a27230
示例2(您的示例):
let hmac = RIPEMD160.hmac(key: "test", message: "string").hexEncodedString()
print(hmac) // 37241f2513c60ae4d9b3b8d0d30517445f451fa5

以上代码假定了一个小端架构(所有当前的iOS和macOS平台都是如此)。如果需要,它可以推广到任意字节顺序。 - Martin R
1
太棒了!我用你的代码制作了一个Swift包,并在参考页面上为所有向量示例包括了测试。https://github.com/darkFunction/SwiftRIPEMD160 - Sam
我已经将基于Sjors Provoost的工作的Swift 5中的RIPEMD160实现迁移,可以在此处找到:https://gist.github.com/Sajjon/cd17a0e09bcc5ff9b1a4d5be3da29b64 然而,Martin R在这里提供的答案要快10倍。但是,将Sjors迁移到Swift 5更容易。所有那些在Swift 5中出现的“withUnsafeBytes”警告都很棘手。 - Sajjon
@Sajjon:我已将上述代码更新为Swift 5,欢迎提供反馈。 - Martin R
太快了,@MartinR!你真棒! - Sajjon
显示剩余3条评论

3

正如您已经发现的那样,ripemd160在CommonCrypto中不受支持。因此,您需要使用一些外部加密库。在Swift/Objective-C中,有(至少)两个库可供使用:OpenSSLCrypto++

OpenSSL

以下是使用OpenSSL计算带有ripemd160的hmac哈希的代码:

+ (NSString *)ripemd160WithKey:(NSString *)aKey andData:(NSString *)aData {
   const char* key = [aKey cStringUsingEncoding:NSUTF8StringEncoding];
   const char* data = [aData cStringUsingEncoding:NSUTF8StringEncoding];
   unsigned char* digest;
   digest = HMAC(EVP_ripemd160(), key, (int)strlen(key), (unsigned char*)data, strlen(data), NULL, NULL);
   NSMutableString* str = [NSMutableString string];
   for(int i = 0; i < RIPEMD160_DIGEST_LENGTH; i++) {
      [str appendFormat:@"%02x", (unsigned int)digest[i]];
   }
   return str;
}

我已经基于Felix Schulze的OpenSSL for iPhone library创建了一个iOS示例,您也可以编译为watchOS、macOS或tvOS。
  1. 从我的GitHub帐户中克隆存储库
  2. 打开终端并更改目录到存储库文件夹cd OpenSSL-for-iPhone
  3. 运行./build-libssl.sh下载和编译openSSL源代码
  4. 使用Xcode打开OpenSSL-for-iOS.xcodeproj
  5. 在iOS模拟器或手机上运行OpenSSL-for-iOS

正如你在ViewController.swift中看到的那样,你只需要使用相应的参数调用FSOpenSSL.ripemd160(withKey: "test", andData: "string")。您可以通过UI尝试不同的值。

希望能对您有所帮助。

Screenshot of iOS example app


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接