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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/32163848/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-31 07:22:23  来源:igfitidea点击:

How can I convert a String to an MD5 hash in iOS using Swift?

iosswiftmd5

提问by user3606682

I want to convert a string like "abc" to an MD5 hash. I want to do this in iOS and Swift. I have tried using the solutions below but they were not working for me:

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

Importing CommonCrypto in a Swift framework

在 Swift 框架中导入 CommonCrypto

How to use CC_MD5 method in swift language.

如何在 swift 语言中使用 CC_MD5 方法。

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

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

To be more clear, I want to achieve an output in Swift similar to this PHP code's output:

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

$str = "Hello";

echo md5($str);

Output: 8b1a9953c4611296a827abf8c47804d7

输出:8b1a9953c4611296a827abf8c47804d7

回答by zaph

There are two steps:
1. Create md5 data from a string
2. Covert the md5 data to a hex string

有两个步骤:
1. 从字符串创建 md5 数据
2. 将 md5 数据转换为十六进制字符串

Swift 2.0:

斯威夫特 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)")

Output:

输出:

digest: 8b1a9953c4611296a827abf8c47804d7

摘要:8b1a9953c4611296a827abf8c47804d7

Swift 3.0:

斯威夫特 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", 
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", 
name: A name of a hash function as a String  
string: The String to be hashed  
returns: the hashed result as Data  
) }.joined() print("md5Hex: \(md5Hex)") let md5Base64 = md5Data.base64EncodedString() print("md5Base64: \(md5Base64)")
) }.joined() print("md5Hex: \(md5Hex)") let md5Base64 = md5Data.base64EncodedString() print("md5Base64: \(md5Base64)")

Output:

输出:

md5Hex: 8b1a9953c4611296a827abf8c47804d7
md5Base64: ixqZU8RhEpaoJ6v4xHgE1w==

md5Hex:8b1a9953c4611296a827abf8c47804d7
md5Base64:ixqZU8RhEpaoJ6v4xHgE1w==

Swift 5.0:

斯威夫特 5.0:

func hash(name:String, string:String) -> Data? {
    let data = string.data(using:.utf8)!
    return hash(name:name, data:data)
}

Output:

输出:

md5Hex: 8b1a9953c4611296a827abf8c47804d7
md5Base64: ixqZU8RhEpaoJ6v4xHgE1w==

md5Hex:8b1a9953c4611296a827abf8c47804d7
md5Base64:ixqZU8RhEpaoJ6v4xHgE1w==

Notes:
#import <CommonCrypto/CommonCrypto.h>must be added to a Bridging-Header file

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

For how to create a Bridging-Header see this SO answer.

有关如何创建桥接头,请参阅此 SO 答案

In general MD5 should not be used for new work, SHA256 is a current best practice.

一般来说 MD5 不应用于新工作,SHA256 是当前的最佳实践。

Example from deprecated documentation section:

弃用文档部分的示例:

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

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

These functions will hash either String or Data input with one of eight cryptographic hash algorithms.

这些函数将使用八种加密散列算法之一散列字符串或数据输入。

The name parameter specifies the hash function name as a String
Supported functions are MD2, MD4, MD5, SHA1, SHA224, SHA256, SHA384 and SHA512 a This example requires Common Crypto
It is necessary to have a bridging header to the project:
#import <CommonCrypto/CommonCrypto.h>
Add the Security.framework to the project.

name 参数将哈希函数名称指定为 String
支持的函数有 MD2、MD4、MD5、SHA1、SHA224、SHA256、SHA384 和 SHA512 a 此示例需要 Common Crypto
项目必须有桥接头:
#import <CommonCrypto/CommonCrypto.h>
添加安全性.framework 到项目。





This function takes a hash name and String to be hashed and returns a 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>

Examples:

例子:

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)
    } 
}

Output:

输出:

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", 
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", 
// 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
}
) }.joined() case .base64: return digest.base64EncodedString() } } }
) }.joined() case .base64: return digest.base64EncodedString() } } }

回答by digitalHound

After reading through the other answers on here (and needing to support other hash types as well) I wrote a String extension that handles multiple hash types and output types.

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

NOTE: CommonCrypto is included in Xcode 10, so you can simply import CommonCryptowithout having to mess with a bridging header if you have the latest Xcode version installed... Otherwise a bridging header is necessary.

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



UPDATE: Both Swift 4 & 5 use the same String+Crypto.swift file below.

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

There is a separate Data+Crypto.swift file for Swift 5 (see below) as the api for 'withUnsafeMutableBytes' and 'withUnsafeBytes' changed between Swift 4 & 5.

Swift 5 有一个单独的 Data+Crypto.swift 文件(见下文),因为“withUnsafeMutableBytes”和“withUnsafeBytes”的 api 在 Swift 4 和 5 之间发生了变化。



String+Crypto.swift -- (for both Swift 4 & 5)

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

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)")
}

SWIFT 5-- Data+Crypto.swift

SWIFT 5-- 数据+Crypto.swift

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

SWIFT 4-- Data+Crypto.swift

SWIFT 4-- 数据+Crypto.swift

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", 
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
}
) }.joined() }

Edit: since the hash actually happens on the Data, I split the hashing algorithm out into a Data extension. This allows the same algorithm to be used for SSL Certificate pinning hash operations as well.

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

Here's a short example of how you might use it for an SSL Pinning operation:

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

import CommonCrypto

back to the original answer

回到原来的答案

I tested the hash algorithms using this:

我使用这个测试了哈希算法:

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("") {
            
MD5("This is my string")
+ String(format: "%02x", digest[]) } }

and this is the printed results:

这是打印的结果:

c2a9ce57e8df081b4baad80d81868bbb

回答by mluisbrown

As of iOS 13Apple has added the CryptoKitframework so you no longer need to import CommonCrypto or deal with its C API:

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

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

回答by wajih

SWIFT 3version of md5 function:

SWIFT 3版本md5 function

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("") { 
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", 
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("") {
            
print("test".md5Value) /*098f6bcd4621d373cade4e832627b4f6*/
+ String(format: "%02x", digest[]) } } }
) }.joined() } } }
+ String(format: "%02x", digest[]) } } }

Original link from http://iosdeveloperzone.com

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

回答by Invictus Cody

Swift 4.* , Xcode 10 Update :

Swift 4.* ,Xcode 10 更新:

In Xcode 10 you don't have to use Bridging-HeaderAnymore , you can directly import using

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

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
}
}

And Then write a method something like :

然后写一个类似的方法:

let md5String = "abc".md5()

Usage :

用法 :

##代码##

Output:

输出:

##代码##

回答by Nikolai Ruhe

I released a pure Swift implementationthat does not depend on CommonCrypto or anything else. It's available under MIT license.

我发布了一个不依赖于 CommonCrypto 或其他任何东西的纯 Swift 实现。它在 MIT 许可下可用。

The code consists of a single swift filethat you can just drop into your project. If you prefer you can also use the contained Xcode project with framework and unit test targets.

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

It's simple to use:

使用起来很简单:

##代码##

prints: md5: 9e107d9d372bb6826bd81d3542a419d6

印刷: md5: 9e107d9d372bb6826bd81d3542a419d6

The swift file contains documentation and more examples.

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

回答by Hugo Alonso

Just two notes here:

这里只有两个注意事项:

Using Cryptois too much overhead for achieving just this.

使用Crypto实现这一目标的开销太大。

The accepted answeris perfect! Nevertheless I just wanted to share a Swiftiercode approach using Swift 2.2.

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

Please bear in mind that you still have to #import <CommonCrypto/CommonCrypto.h>in your Bridging-Headerfile

请记住,您仍然必须#import <CommonCrypto/CommonCrypto.h>桥接头文件中

##代码##

回答by Glaubenio Patricio

Here's an extension based on zaph answer

这是基于 zaph answer 的扩展

##代码##

Fully compatible with swift 3.0.you still have to #import <CommonCrypto/CommonCrypto.h>in your Bridging-Header file

与 swift 3.0 完全兼容#import <CommonCrypto/CommonCrypto.h>。你仍然需要在你的 Bridging-Header 文件中

回答by Tamás Sengel

Swift 5answer as a String extension (based on the great answer of Invictus Cody):

Swift 5作为字符串扩展回答(基于Invictus Cody 的精彩回答):

##代码##

Usage:

用法:

##代码##

回答by Rahul K Rajan

In swift programming its better to make a string function, so the use will be easy. Here I am making a String extension using one of the above given solution. Thanks @wajih

在 swift 编程中最好制作一个字符串函数,这样使用起来会很容易。在这里,我使用上面给出的解决方案之一进行字符串扩展。谢谢@wajih

##代码##

Usage

用法

##代码##