How can I convert a String to an MD5 hash in iOS using Swift?
我想将字符串(如"abc")转换为MD5哈希。我想用iOS和Swift来做这个。我尝试过使用以下解决方案,但它们不适用于我:
在swift框架中导入CommonCrypto
如何在swift语言中使用cc-md5方法。
http://iosdeveloperzone.com/2014/10/03/using-commoncrypto-in-swift/
更清楚地说,我希望以类似于此PHP代码输出的swift格式实现输出:
1 2 3 | $str ="Hello"; echo md5($str); |
输出:8B1A9953C4611296A827ABF8C47804D7
有两个步骤:1。从字符串创建MD5数据2。将MD5数据转换为十六进制字符串
斯威夫特2.0:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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)") |
输出:
digest: 8b1a9953c4611296a827abf8c47804d7
号
斯威夫特3.0:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | 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==
号
斯威夫特5.0:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | 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==
号
笔记:必须将
有关如何创建桥接头的信息,请参阅此so答案。
一般来说,MD5不应用于新工作,SHA256是当前的最佳实践。
不推荐使用的文档部分的示例: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.
号
name参数将哈希函数名指定为字符串支持的功能有md2、md4、md5、sha1、sha224、sha256、sha384、sha512一此示例需要公共加密必须有一个到项目的桥接头:
此函数接受要散列的散列名称和字符串,并返回数据:
1 2 3 | name: A name of a hash function as a String string: The String to be hashed returns: the hashed result as Data |
。
1 2 3 4 | func hash(name:String, string:String) -> Data? { let data = string.data(using:.utf8)! return hash(name:name, data:data) } |
示例:
1 2 3 4 5 6 7 8 9 10 | 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)") |
。
输出:
1 2 3 4 5 | clearString: clearData0123456 clearData: <636c6561 72446174 61303132 33343536> hashSHA256: hashMD5: <4df665f7 b94aea69 695b0e7b baf9e9d6> |
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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的原始链接
在阅读了这里的其他答案(也需要支持其他散列类型)之后,我编写了一个字符串扩展来处理多个散列类型和输出类型。
注意:Xcode 10中包含了CommonCrypto,因此如果安装了最新的Xcode版本,您可以简单地执行
更新:两个swift 4&5都使用下面相同的string+crypto.swift文件。
swift 5有一个单独的data+crypto.swift文件(见下文),作为在swift 4&5之间更改的"withunsafemablebytes"和"withunsafebytes"的API。
string+crypto.swift——(对于swift 4和5)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | 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固定操作:
1 2 3 4 5 6 7 8 | // 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 } |
。
回到原来的答案
我用这个测试了哈希算法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | 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)") } |
打印结果如下:
1 2 3 4 5 6 | md5: c2a9ce57e8df081b4baad80d81868bbb sha1: 37fb219bf98bee51d2fdc3ba6d866c97f06c8223 sha224: f88e2f20aa89fb4dffb6bdc62d7bd75e1ba02574fae4a437c3bf49c7 sha256: 9da6c02379110815278b615f015f0b254fd3d5a691c9d8abf8141655982c046b sha384: d9d7fc8aefe7f8f0a969b132a59070836397147338e454acc6e65ca616099d03a61fcf9cc8c4d45a2623145ebd398450 sha512: 349cc35836ba85915ace9d7f895b712fe018452bb4b20ff257257e12adeb1e83ad780c6568a12d03f5b2cb1e3da23b8b7ced9012a188ef3855e0a8f3db211883 |
。
swift 4.*,xcode 10更新:
在Xcode10中,您不再需要使用桥接头,可以使用
1 | import CommonCrypto |
然后写一个方法,比如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 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]) } } |
。
用法:
1 | MD5("This is my string") |
输出:
1 | c2a9ce57e8df081b4baad80d81868bbb |
。
我发布了一个纯粹的Swift实现,它不依赖于CommonCrypto或其他任何东西。它在麻省理工学院许可证下可用。
代码由一个Swift文件组成,您可以直接放入项目中。如果愿意,还可以将包含的Xcode项目与框架和单元测试目标一起使用。
使用简单:
1 2 3 | let input ="The quick brown fox jumps over the lazy dog" let digest = input.utf8.md5 print("md5: \(digest)") |
号
印刷品:
swift文件包含文档和更多示例。
Just two notes here:
号
使用加密对于实现这一点来说开销太大了。
公认的答案是完美的!不过,我只是想使用Swift2.2共享一种更快速的代码方法。
请记住,您的桥接头文件中仍然需要
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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]) } } } |
这是一个基于扎夫答案的扩展
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | 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完全兼容。您仍然需要在桥接头文件中使用
Swift 5作为字符串扩展的答案(基于invictus cody的伟大答案):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | 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]) } } } |
号
用途:
1 | print("test".md5Value) /*098f6bcd4621d373cade4e832627b4f6*/ |
号
MD5是一种散列算法,不需要为此使用庞大的CommonCrypto库(并被AppleReview拒绝),只需使用任何MD5散列库即可。
我使用的一个这样的库是Swifthash,它是MD5的一个简单的快速实现(基于http://pajhome.org.uk/crypt/md5/md5.html)
我用迦太基和Cyrpto做这个。
如果还没有安装迦太基
在项目中安装加密
执行"cartage update"
如果从命令行运行,请在swift文件的框架中添加
1 | #!/usr/bin/env xcrun swift -F Carthage/Build/Mac |
将导入加密添加到swift文件。
那就行了!
1 | print("convert this".MD5 ) |
。
基于Cody的解决方案,我有一个想法,我们应该澄清MD5的结果是什么,因为我们可以将结果用作十六进制字符串或base64字符串。
1 2 3 4 5 6 7 8 9 10 11 | 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 } |
号
上面的函数实际上返回一个
如果需要十六进制字符串作为最终结果(如问题所问),我们可以继续使用cody解决方案的其余部分。
1 2 3 4 5 6 7 8 | extension String { var md5Hex: String { let length = Int(CC_MD5_DIGEST_LENGTH) return (0..<length).reduce("") { $0 + String(format:"%02x", digest[$1]) } } } |
号
如果需要base64字符串作为最终结果
1 2 3 4 5 6 | extension String { var md5Base64: String { let md5edData = Data(bytes: md5(self)) return md5edData.base64EncodedString() } } |
号
我的2美分(如果您需要快速MD5用于数据/nsdata,例如您下载或读取磁盘或网络的二进制文件)
(不知羞耻地从"斯威夫特5号回答作为一个字符串扩展"(基于被邀请者科迪的伟大回答)):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | 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]) } } } |
号
测试:
1 | print("test".data.md5Value) /*098f6bcd4621d373cade4e832627b4f6*/ |
号
在Swift编程中,它更好地生成一个字符串函数,因此使用起来很容易。这里我使用上面给出的解决方案之一进行字符串扩展。谢谢@wajih
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | 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 } } |
号
用法
1 | let md5String ="abc".md5() |
号
我发现这间图书馆似乎运转得很好。
https://github.com/onmyway133/swifthash
1 | MD5("string") |
。