如何使用Swift在iOS中将String转换为MD5哈希?


111

我想将“ abc”之类的字符串转换为MD5哈希。我想在iOS和Swift中做到这一点。我尝试使用以下解决方案,但它们对我不起作用:

在Swift框架中导入CommonCrypto

如何以快速的语言使用CC_MD5方法。

http://iosdeveloperzone.com/2014/10/03/using-commoncrypto-in-swift/

更清楚地说,我想在Swift中实现类似于此PHP代码输出的输出:

$str = "Hello";

echo md5($str);

输出:8b1a9953c4611296a827abf8c47804d7


5
您提供的链接有什么问题?
jtbandes 2015年

2
您提供的链接应该有效。您能描述出您的确切问题是什么吗?您还可以包括一个第三方库来做您想做的事,即。github.com/krzyzanowskim/CryptoSwift
Eric Amorde 2015年

1
正如我提到的那样,对于快速编程来说我是新手,我很困惑以正确的方式实现它。我将这个文件(#import <CommonCrypto / CommonCrypto.h>)包含在swift控制器文件中。但是,感谢您的答复,现在可以通过下面的扎夫先生答复来解决。
user3606682

如果您想在Swift中进行本地化
onmyway133 '16

Answers:


178

分两个步骤:
1.从字符串创建md5数据
2.将md5数据隐蔽为十六进制字符串

Swift 2.0:

func md5(string string: String) -> String {
    var digest = [UInt8](count: Int(CC_MD5_DIGEST_LENGTH), repeatedValue: 0)
    if let data = string.dataUsingEncoding(NSUTF8StringEncoding) {
        CC_MD5(data.bytes, CC_LONG(data.length), &digest)
    }

    var digestHex = ""
    for index in 0..<Int(CC_MD5_DIGEST_LENGTH) {
        digestHex += String(format: "%02x", digest[index])
    }

    return digestHex
}

//Test:
let digest = md5(string:"Hello")
print("digest: \(digest)")

输出:

摘要:8b1a9953c4611296a827abf8c47804d7

Swift 3.0:

func MD5(string: String) -> Data {
    let messageData = string.data(using:.utf8)!
    var digestData = Data(count: Int(CC_MD5_DIGEST_LENGTH))

    _ = digestData.withUnsafeMutableBytes {digestBytes in
        messageData.withUnsafeBytes {messageBytes in
            CC_MD5(messageBytes, CC_LONG(messageData.count), digestBytes)
        }
    }

    return digestData
}

//Test:
let md5Data = MD5(string:"Hello")

let md5Hex =  md5Data.map { String(format: "%02hhx", $0) }.joined()
print("md5Hex: \(md5Hex)")

let md5Base64 = md5Data.base64EncodedString()
print("md5Base64: \(md5Base64)")

输出:

md5Hex:8b1a9953c4611296a827abf8c47804d7
md5Base64:ixqZU8RhEpaoJ6v4xHgE1w ==

Swift 5.0:

import Foundation
import var CommonCrypto.CC_MD5_DIGEST_LENGTH
import func CommonCrypto.CC_MD5
import typealias CommonCrypto.CC_LONG

func MD5(string: String) -> Data {
        let length = Int(CC_MD5_DIGEST_LENGTH)
        let messageData = string.data(using:.utf8)!
        var digestData = Data(count: length)

        _ = digestData.withUnsafeMutableBytes { digestBytes -> UInt8 in
            messageData.withUnsafeBytes { messageBytes -> UInt8 in
                if let messageBytesBaseAddress = messageBytes.baseAddress, let digestBytesBlindMemory = digestBytes.bindMemory(to: UInt8.self).baseAddress {
                    let messageLength = CC_LONG(messageData.count)
                    CC_MD5(messageBytesBaseAddress, messageLength, digestBytesBlindMemory)
                }
                return 0
            }
        }
        return digestData
    }

//Test:
let md5Data = MD5(string:"Hello")

let md5Hex =  md5Data.map { String(format: "%02hhx", $0) }.joined()
print("md5Hex: \(md5Hex)")

let md5Base64 = md5Data.base64EncodedString()
print("md5Base64: \(md5Base64)")

输出:

md5Hex:8b1a9953c4611296a827abf8c47804d7
md5Base64:ixqZU8RhEpaoJ6v4xHgE1w ==

注意:
#import <CommonCrypto/CommonCrypto.h>必须添加到Bridging-Header文件中

有关如何创建Bridging-Header的信息,请参见SO答案

通常,不应将MD​​5用于新工作,SHA256是当前的最佳实践。

不推荐使用的文档部分中的示例:

MD2,MD4,MD5,SHA1,SHA224,SHA256,SHA384,SHA512(Swift 3+)

这些函数将使用八种密码哈希算法之一来哈希String或Data输入。

名称参数指定的散列函数的字符串名
支持的功能是MD2,MD4,MD5,SHA1,SHA224,SHA256,SHA384和SHA512一个这个例子需要公共的密码
是需要有一个桥接头项目:
#import <CommonCrypto/CommonCrypto.h>
添加安全.framework到项目。



此函数采用哈希名称和要哈希的String并返回一个Data:

name:作为字符串的哈希函数的名称  
string:要散列的字符串  
返回:哈希结果为Data  
func hash(name:String, string:String) -> Data? {
    let data = string.data(using:.utf8)!
    return hash(name:name, data:data)
}

例子:

let clearString = "clearData0123456"
let clearData   = clearString.data(using:.utf8)!
print("clearString: \(clearString)")
print("clearData: \(clearData as NSData)")

let hashSHA256 = hash(name:"SHA256", string:clearString)
print("hashSHA256: \(hashSHA256! as NSData)")

let hashMD5 = hash(name:"MD5", data:clearData)
print("hashMD5: \(hashMD5! as NSData)")

输出:

clearString: clearData0123456
clearData: <636c6561 72446174 61303132 33343536>

hashSHA256: <aabc766b 6b357564 e41f4f91 2d494bcc bfa16924 b574abbd ba9e3e9d a0c8920a>
hashMD5: <4df665f7 b94aea69 695b0e7b baf9e9d6>

3
感谢alottt @zaph,我已经为此花了2天以上的时间。通过上面的答案解决了问题:)是的,我从使用MD5的Web上检索了旧数据,因此我被迫使用MD5。但是,再次感谢您使用SHA256的答案和建议:)
user3606682

String(data: digestData, encoding: String.Encoding.utf8)fatal error: unexpectedly found nil while unwrapping an Optional value
Siddharth

@Siddharth注释中没有足够的信息,不清楚是什么digestData。如果它是散列数据,则可能是UTF-8(或任何字符串编码都是
稀疏

1
可以通过以下方法进行改进:仅导入所需的符号,而不导入整个CommonCrypto,因为这会增加一些开销:导入var CommonCrypto.CC_MD5_DIGEST_LENGTH导入func CommonCrypto.CC_MD5导入类型别名CommonCrypto.CC_LONG
Igor Vasilev

2
@zaph,您可能想将iOS 13 CryptoKit解决方案添加到您的答案中,我在下面的答案中对此进行了详细说明:stackoverflow.com/a/56578995/368085
mluisbrown

40

在阅读完此处的其他答案之后(并且还需要支持其他哈希类型),我编写了一个String扩展,可以处理多种哈希类型和输出类型。

注意:CommonCrypto包含在Xcode 10中,因此,import CommonCrypto如果您安装了最新的Xcode版本,则无需弄乱桥接头就可以了...否则,桥接头是必需的。


更新:Swift 4和5都使用下面相同的String + Crypto.swift文件。

Swift 5有一个单独的Data + Crypto.swift文件(请参见下文),作为“ withUnsafeMutableBytes”和“ withUnsafeBytes”的api在Swift 4和5之间进行了更改。


String + Crypto.swift-(适用于Swift 4和5)

import Foundation
import CommonCrypto

// Defines types of hash string outputs available
public enum HashOutputType {
    // standard hex string output
    case hex
    // base 64 encoded string output
    case base64
}

// Defines types of hash algorithms available
public enum HashType {
    case md5
    case sha1
    case sha224
    case sha256
    case sha384
    case sha512

    var length: Int32 {
        switch self {
        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
        }
    }
}

public extension String {

    /// Hashing algorithm for hashing a string instance.
    ///
    /// - Parameters:
    ///   - type: The type of hash to use.
    ///   - output: The type of output desired, defaults to .hex.
    /// - Returns: The requested hash output or nil if failure.
    public func hashed(_ type: HashType, output: HashOutputType = .hex) -> String? {

        // convert string to utf8 encoded data
        guard let message = data(using: .utf8) else { return nil }
        return message.hashed(type, output: output)
    } 
}

SWIFT 5-数据+加密.swift

import Foundation
import CommonCrypto

extension Data {

    /// Hashing algorithm that prepends an RSA2048ASN1Header to the beginning of the data being hashed.
    ///
    /// - Parameters:
    ///   - type: The type of hash algorithm to use for the hashing operation.
    ///   - output: The type of output string desired.
    /// - Returns: A hash string using the specified hashing algorithm, or nil.
    public func hashWithRSA2048Asn1Header(_ type: HashType, output: HashOutputType = .hex) -> String? {

        let rsa2048Asn1Header:[UInt8] = [
            0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
            0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00
        ]

        var headerData = Data(rsa2048Asn1Header)
        headerData.append(self)

        return hashed(type, output: output)
    }

    /// Hashing algorithm for hashing a Data instance.
    ///
    /// - Parameters:
    ///   - type: The type of hash to use.
    ///   - output: The type of hash output desired, defaults to .hex.
    ///   - Returns: The requested hash output or nil if failure.
    public func hashed(_ type: HashType, output: HashOutputType = .hex) -> String? {

        // setup data variable to hold hashed value
        var digest = Data(count: Int(type.length))

        _ = digest.withUnsafeMutableBytes{ digestBytes -> UInt8 in
            self.withUnsafeBytes { messageBytes -> UInt8 in
                if let mb = messageBytes.baseAddress, let db = digestBytes.bindMemory(to: UInt8.self).baseAddress {
                    let length = CC_LONG(self.count)
                    switch type {
                    case .md5: CC_MD5(mb, length, db)
                    case .sha1: CC_SHA1(mb, length, db)
                    case .sha224: CC_SHA224(mb, length, db)
                    case .sha256: CC_SHA256(mb, length, db)
                    case .sha384: CC_SHA384(mb, length, db)
                    case .sha512: CC_SHA512(mb, length, db)
                    }
                }
                return 0
            }
        }

        // return the value based on the specified output type.
        switch output {
        case .hex: return digest.map { String(format: "%02hhx", $0) }.joined()
        case .base64: return digest.base64EncodedString()
        }
    }
}

SWIFT 4-数据+加密.swift

import Foundation
import CommonCrypto 

extension Data {

    /// Hashing algorithm that prepends an RSA2048ASN1Header to the beginning of the data being hashed.
    ///
    /// - Parameters:
    ///   - type: The type of hash algorithm to use for the hashing operation.
    ///   - output: The type of output string desired.
    /// - Returns: A hash string using the specified hashing algorithm, or nil.
    public func hashWithRSA2048Asn1Header(_ type: HashType, output: HashOutputType = .hex) -> String? {

        let rsa2048Asn1Header:[UInt8] = [
            0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
            0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00
        ]

        var headerData = Data(bytes: rsa2048Asn1Header)
        headerData.append(self)

        return hashed(type, output: output)
    }

    /// Hashing algorithm for hashing a Data instance.
    ///
    /// - Parameters:
    ///   - type: The type of hash to use.
    ///   - output: The type of hash output desired, defaults to .hex.
    ///   - Returns: The requested hash output or nil if failure.
    public func hashed(_ type: HashType, output: HashOutputType = .hex) -> String? {

        // setup data variable to hold hashed value
        var digest = Data(count: Int(type.length))

        // generate hash using specified hash type
        _ = digest.withUnsafeMutableBytes { (digestBytes: UnsafeMutablePointer<UInt8>) in
            self.withUnsafeBytes { (messageBytes: UnsafePointer<UInt8>) in
                let length = CC_LONG(self.count)
                switch type {
                case .md5: CC_MD5(messageBytes, length, digestBytes)
                case .sha1: CC_SHA1(messageBytes, length, digestBytes)
                case .sha224: CC_SHA224(messageBytes, length, digestBytes)
                case .sha256: CC_SHA256(messageBytes, length, digestBytes)
                case .sha384: CC_SHA384(messageBytes, length, digestBytes)
                case .sha512: CC_SHA512(messageBytes, length, digestBytes)
                }
            }
        }

        // return the value based on the specified output type.
        switch output {
        case .hex: return digest.map { String(format: "%02hhx", $0) }.joined()
        case .base64: return digest.base64EncodedString()
        }
    }
}

编辑:由于哈希实际上发生在数据上,所以我将哈希算法拆分为数据扩展。这也允许将相同的算法用于SSL证书固定哈希操作。

这是一个简短示例,说明如何将其用于SSL固定操作:

// Certificate pinning - get certificate as data
let data: Data = SecCertificateCopyData(serverCertificate) as Data

// compare hash of server certificate with local (expected) hash value
guard let serverHash = data.hashWithRSA2048Asn1Header(.sha256, output: .base64), serverHash == storedHash else {
    print("SSL PINNING: Server certificate hash does not match specified hash value.")
    return false
}

回到原始答案

我使用以下方法测试了哈希算法:

let value = "This is my string"

if let md5 = value.hashed(.md5) {
    print("md5: \(md5)")
}
if let sha1 = value.hashed(.sha1) {
    print("sha1: \(sha1)")
}
if let sha224 = value.hashed(.sha224) {
    print("sha224: \(sha224)")
}
if let sha256 = value.hashed(.sha256) {
    print("sha256: \(sha256)")
}
if let sha384 = value.hashed(.sha384) {
    print("sha384: \(sha384)")
}
if let sha512 = value.hashed(.sha512) {
    print("sha512: \(sha512)")
}

这是打印结果:

md5: c2a9ce57e8df081b4baad80d81868bbb
sha1: 37fb219bf98bee51d2fdc3ba6d866c97f06c8223
sha224: f88e2f20aa89fb4dffb6bdc62d7bd75e1ba02574fae4a437c3bf49c7
sha256: 9da6c02379110815278b615f015f0b254fd3d5a691c9d8abf8141655982c046b
sha384: d9d7fc8aefe7f8f0a969b132a59070836397147338e454acc6e65ca616099d03a61fcf9cc8c4d45a2623145ebd398450
sha512: 349cc35836ba85915ace9d7f895b712fe018452bb4b20ff257257e12adeb1e83ad780c6568a12d03f5b2cb1e3da23b8b7ced9012a188ef3855e0a8f3db211883

39

iOS 13开始, Apple已添加CryptoKit框架,因此您不再需要导入CommonCrypto或处理其C API:

import Foundation
import CryptoKit

func MD5(string: String) -> String {
    let digest = Insecure.MD5.hash(data: string.data(using: .utf8) ?? Data())

    return digest.map {
        String(format: "%02hhx", $0)
    }.joined()
}

3
还值得注意的是,这提供了一种避免有关MD5现在不安全的警告的方法。您无需在Objective-C中实现CommonCrypto,就可以支持编译指示来禁用警告。如果您在注重警告处理的环境中工作,则非常方便。
marcus.ramsden

28

SWIFT 3版本md5 function

func md5(_ string: String) -> String {

    let context = UnsafeMutablePointer<CC_MD5_CTX>.allocate(capacity: 1)
    var digest = Array<UInt8>(repeating:0, count:Int(CC_MD5_DIGEST_LENGTH))
    CC_MD5_Init(context)
    CC_MD5_Update(context, string, CC_LONG(string.lengthOfBytes(using: String.Encoding.utf8)))
    CC_MD5_Final(&digest, context)
    context.deallocate(capacity: 1)
    var hexString = ""
    for byte in digest {
        hexString += String(format:"%02x", byte)
    }

    return hexString
}

来自http://iosdeveloperzone.com的原始链接


23

Swift 4。*,Xcode 10更新:

在Xcode 10中,您不必再使用Bridging-Header,您可以直接使用导入

import CommonCrypto

然后编写类似下面的方法:

func MD5(_ string: String) -> String? {
        let length = Int(CC_MD5_DIGEST_LENGTH)
        var digest = [UInt8](repeating: 0, count: length)

        if let d = string.data(using: String.Encoding.utf8) {
            _ = d.withUnsafeBytes { (body: UnsafePointer<UInt8>) in
                CC_MD5(body, CC_LONG(d.count), &digest)
            }
        }

        return (0..<length).reduce("") {
            $0 + String(format: "%02x", digest[$1])
        }
    }

用法:

MD5("This is my string")

输出:

c2a9ce57e8df081b4baad80d81868bbb

您的解决方案是完美的工作。我们可以通过此MD5加密添加SALT值吗?我想在加密字符串时添加。您可以提供一些使用完全链接吗?
普尼塔

我不确定您要达到什么目标。如果您要使用Salting进行自定义加密,请使用“ AES128”。如果您担心安全性,请查看以下内容:stackoverflow.com/a/15775071/3118377
Invictus Cody

感谢Invictus Cody,我将SALT与String连接起来并能够获取MD5。
普尼塔

效果很好。但是,如何将其转换回String?
DocAsh59

1
斯威夫特5:func MD5(_ string: String) -> String? { let length = Int(CC_MD5_DIGEST_LENGTH) var digest = [UInt8](repeating: 0, count: length) if let d = string.data(using: .utf8) { _ = d.withUnsafeBytes { body -> String in CC_MD5(body.baseAddress, CC_LONG(d.count), &digest) return "" } } return (0..<length).reduce("") { $0 + String(format: "%02x", digest[$1]) } }
吉姆B

17

我发布了一个不依赖CommonCrypto或其他任何东西的纯Swift实现。它已获得MIT许可。

该代码包含一个单独的swift文件,您可以将其放入项目中。如果愿意,还可以将包含的Xcode项目与框架和单元测试目标一起使用。

使用简单:

let input = "The quick brown fox jumps over the lazy dog"
let digest = input.utf8.md5
print("md5: \(digest)")

印刷品: md5: 9e107d9d372bb6826bd81d3542a419d6

swift文件包含文档和更多示例。


4
需要Swift 4,此处或Github自述文件中均未提及。没有提供与Common Crypto相比的性能数据,不应考虑使用。注意:Common Crypto已通过FIPS 140认证,而SwiftDigest不是。这是关键问题:在实现上,这比普通加密更好吗?更安全:不,更快:不
-zaph

1
@zaph的主要目的是拥有一个不依赖CommonCrypto的md5实现。在无法使用CommonCrypto的情况下(例如Swift框架目标或非Apple平台),这很有用。
Nikolai Ruhe

4
@zaph我同意与安全有关的实现不应掉以轻心。但是MD5除了安全性之外还有其他用途-或者说,安全性是MD5性能最差的地方。散列算法用于标识,排序,存储,字典,错误检测和其他原因。MD5因其无处不在而特别有用。因此,尽管我同意您的一些意见,但我并不同意要点。我认为您的观点和争论太狭窄了;它没有涵盖整个主题。
Nikolai Ruhe

2
另外,我刚刚进行了测试,对于大消息,我的实现比CommonCrypto更快:)
Nikolai Ruhe

2
我喜欢这个实现。非常感谢@NikolaiRuhe!我能够轻松地将其转换为Swift 3兼容性。我还添加了一些便利的方法,包括计算给定URL的文件内容的摘要,以及检索base64编码(除其他功能外,还用于Content-MD5)。@Siddharth唯一需要的文件是MD5Digest.swift。
biomiker

10

这里只有两个注释:

为此,使用Crypto会产生过多开销。

接受的答案是完美的!不过,我只是想分享一个使用Swift 2.2Swift ier代码方法。

请记住,您仍然必须#import <CommonCrypto/CommonCrypto.h>Bridging-Header文件中

struct MD5Digester {
    // return MD5 digest of string provided
    static func digest(string: String) -> String? {

        guard let data = string.dataUsingEncoding(NSUTF8StringEncoding) else { return nil }

        var digest = [UInt8](count: Int(CC_MD5_DIGEST_LENGTH), repeatedValue: 0)

        CC_MD5(data.bytes, CC_LONG(data.length), &digest)

        return (0..<Int(CC_MD5_DIGEST_LENGTH)).reduce("") { $0 + String(format: "%02x", digest[$1]) }
    }
}

7

Swift 5答案作为String扩展(基于Invictus Cody的出色答案):

import CommonCrypto

extension String {
    var md5Value: String {
        let length = Int(CC_MD5_DIGEST_LENGTH)
        var digest = [UInt8](repeating: 0, count: length)

        if let d = self.data(using: .utf8) {
            _ = d.withUnsafeBytes { body -> String in
                CC_MD5(body.baseAddress, CC_LONG(d.count), &digest)

                return ""
            }
        }

        return (0 ..< length).reduce("") {
            $0 + String(format: "%02x", digest[$1])
        }
    }
}

用法:

print("test".md5Value) /*098f6bcd4621d373cade4e832627b4f6*/

6

这是基于zaph答案的扩展

extension String{
    var MD5:String {
        get{
            let messageData = self.data(using:.utf8)!
            var digestData = Data(count: Int(CC_MD5_DIGEST_LENGTH))

            _ = digestData.withUnsafeMutableBytes {digestBytes in
                messageData.withUnsafeBytes {messageBytes in
                    CC_MD5(messageBytes, CC_LONG(messageData.count), digestBytes)
                }
            }

            return digestData.map { String(format: "%02hhx", $0) }.joined()
        }
    }
}

与swift 3.0完全兼容#import <CommonCrypto/CommonCrypto.h>。您仍然必须在Bridging-Header文件中


3

在快速编程中,最好使用字符串函数,因此使用起来很容易。在这里,我使用上述给定的解决方案之一进行String扩展。谢谢@wajih

import Foundation
import CommonCrypto

extension String {

func md5() -> String {

    let context = UnsafeMutablePointer<CC_MD5_CTX>.allocate(capacity: 1)
    var digest = Array<UInt8>(repeating:0, count:Int(CC_MD5_DIGEST_LENGTH))
    CC_MD5_Init(context)
    CC_MD5_Update(context, self, CC_LONG(self.lengthOfBytes(using: String.Encoding.utf8)))
    CC_MD5_Final(&digest, context)
    context.deallocate()
    var hexString = ""
    for byte in digest {
        hexString += String(format:"%02x", byte)
    }

    return hexString
}
}

用法

let md5String = "abc".md5()

1

我用迦太基和Cyrpto来做到这一点。

  1. 如果尚未安装迦太基,请安装

  2. 将Crypto安装到您的项目中

  3. 执行“升级更新”

  4. 如果您是从命令行运行的,请在swift文件的框架中添加

    #!/usr/bin/env xcrun swift -F Carthage/Build/Mac
  5. 将import Crypto添加到您的swift文件。

  6. 那么就可以了!

    print( "convert this".MD5 )

当只需要一个函数时,使用完整的加密库有点麻烦
Mark Bourke

为旧线程的评论道歉...也许吧,但是(大概)通用库总是随着平台的变化而更新,从而产生通用的结果并最大程度地减少了碎片,而且没有人需要不断地重新发明轮子或使用大量的Internet-找到的代码可能不可靠,不快速或无法在标准模式下使用。我全力减少依赖,但是在这种情况下,我首先查看OS选项,其次查看通用语言选项,然后查看第三方标准选项,并一次性得出结果或“此人的库相当好”选项最后。*耸耸肩*
克里斯·H


1

基于Cody的解决方案,我有一个想法,我们应该弄清楚MD5的结果是什么,因为我们可以将结果用作十六进制字符串或Base64字符串。

func md5(_ string: String) -> [UInt8] {
    let length = Int(CC_MD5_DIGEST_LENGTH)
    var digest = [UInt8](repeating: 0, count: length)

    if let d = string.data(using: String.Encoding.utf8) {
        _ = d.withUnsafeBytes { (body: UnsafePointer<UInt8>) in
            CC_MD5(body, CC_LONG(d.count), &digest)
        }
    }
    return digest
}

上面的函数实际上返回一个[UInt8],根据此结果,我们可以获取任何形式的字符串,例如hex,base64。

如果需要十六进制字符串作为最终结果(如问题所示),我们可以继续使用Cody解决方案的其余部分

extension String {
    var md5Hex: String {
        let length = Int(CC_MD5_DIGEST_LENGTH)
        return (0..<length).reduce("") {
            $0 + String(format: "%02x", digest[$1])
        }
    }
}

如果想要使用Base64字符串作为最终结果

extension String {
    var md5Base64: String {
        let md5edData = Data(bytes: md5(self))
        return md5edData.base64EncodedString()
    }
}

1

使用适当的内存管理并且方法内部没有类的Swift 5答案String

typealias CBridgeCryptoMethodType = (UnsafeRawPointer?,
                                 UInt32,
                                 UnsafeMutablePointer<UInt8>?)
-> UnsafeMutablePointer<UInt8>?

private enum HashType {

    // MARK: - Cases

    case md5
    case sha1
    case sha224
    case sha256
    case sha384
    case sha512
}

extension Data {
    var hexString: String {
        let localHexString = reduce("", { previous, current in
            return previous + String(format: "%02X", current)
        })
        return localHexString
    }
    var md5: Data {
        return hashed(for: .md5)
    }
    var sha1: Data {
        return hashed(for: .sha1)
    }
    var sha224: Data {
        return hashed(for: .sha224)
    }
    var sha256: Data {
        return hashed(for: .sha256)
    }
    var sha384: Data {
        return hashed(for: .sha384)
    }
    var sha512: Data {
        return hashed(for: .sha512)
    }

    private func hashed(for hashType: HashType) -> Data {
        return withUnsafeBytes { (rawBytesPointer: UnsafeRawBufferPointer) -> Data in
            guard let bytes = rawBytesPointer.baseAddress?.assumingMemoryBound(to: Float.self) else {
                return Data()
            }
            let hashMethod: CBridgeCryptoMethodType
            let digestLength: Int
            switch hashType {
            case .md5:
                hashMethod = CC_MD5
                digestLength = Int(CC_MD5_DIGEST_LENGTH)
            case .sha1:
                hashMethod = CC_SHA1
                digestLength = Int(CC_SHA1_DIGEST_LENGTH)
            case .sha224:
                hashMethod = CC_SHA224
                digestLength = Int(CC_SHA224_DIGEST_LENGTH)
            case .sha256:
                hashMethod = CC_SHA256
                digestLength = Int(CC_SHA256_DIGEST_LENGTH)
            case .sha384:
                hashMethod = CC_SHA384
                digestLength = Int(CC_SHA384_DIGEST_LENGTH)
            case .sha512:
                hashMethod = CC_SHA512
                digestLength = Int(CC_SHA512_DIGEST_LENGTH)
            }
            let result = UnsafeMutablePointer<UInt8>.allocate(capacity: digestLength)
            _ = hashMethod(bytes, CC_LONG(count), result)
            let md5Data = Data(bytes: result, count: digestLength)
            result.deallocate()
            return md5Data
        }
    }
}

let str = "The most secure string ever"
print("md5", str.data(using: .utf8)?.md5.hexString)
print("sha1", str.data(using: .utf8)?.sha1.hexString)
print("sha224", str.data(using: .utf8)?.sha224.hexString)
print("sha256", str.data(using: .utf8)?.sha256.hexString)
print("sha384", str.data(using: .utf8)?.sha384.hexString)
print("sha512", str.data(using: .utf8)?.sha512.hexString)

结果:

md5可选(“ 671C121427F12FBBA66CEE71C44CB62C”)

sha1可选(“ A6A40B223AE634CFC8C191DDE024BF0ACA56D7FA”)

sha224可选(“ 334370E82F2F5ECF5B2CA0910C6176D94CBA12FD6F518A7AB8D12ADE”)

sha256可选(“ 8CF5ED971D6EE2579B1BDEFD4921415AC03DA45B49B89665B3DF197287EFC89D”)

sha384可选(“ 04BB3551CBD60035BA7E0BAA141AEACE1EF5E17317A8FD108DA12A7A8E98C245E14F92CC1A241C732209EAC9D600602E”)

sha512可选(“ 1D595EAFEB2162672830885D336F75FD481548AC463BE16A8D98DB33637213F1AEB36FA4977B9C23A82A4FAB8A70C06AFC64C610D3CB1FE77A609DC8EE86AA68”



0

我的两分钱(如果您需要快速使用md5来存储Data / NSData,例如您下载或读取了磁盘或netwkork的二进制文件)

(从“作为字符串扩展的Swift 5答案(基于Invictus Cody的出色答案”中得出的结论是无耻的)):

extension Data {
    var md5Value: String {
        let length = Int(CC_MD5_DIGEST_LENGTH)
        var digest = [UInt8](repeating: 0, count: length)

        _ = self.withUnsafeBytes { body -> String in
            CC_MD5(body.baseAddress, CC_LONG(self.count), &digest)
            return ""
        }


        return (0 ..< length).reduce("") {
            $0 + String(format: "%02x", digest[$1])
        }
    }
} 

测试:

print("test".data.md5Value) /*098f6bcd4621d373cade4e832627b4f6*/
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.