在 Swift 5 中获取字符串 md5
Get string md5 in Swift 5
在 Swift 4 中我们可以使用
var md5: String? {
guard let data = self.data(using: .utf8) else { return nil }
let hash = data.withUnsafeBytes { (bytes: UnsafePointer<Data>) -> [UInt8] in
var hash: [UInt8] = [UInt8](repeating: 0, count: Int(CC_MD5_DIGEST_LENGTH))
CC_MD5(bytes, CC_LONG(data.count), &hash)
return hash
}
return hash.map { String(format: "%02x", [=11=]) }.joined()
}
但在 Swift 5 withUnsafeBytes
中使用 UnsafeRawBufferPointer
而不是 UnsafePointer
。如何修改md5函数?
Swift 5 版本:使用 UnsafeRawBufferPointer
作为闭包参数的类型,并使用 bytes.baseAddress
将地址传递给 Common Crypto 函数:
import Foundation
import CommonCrypto
extension String {
var md5: String {
let data = Data(self.utf8)
let hash = data.withUnsafeBytes { (bytes: UnsafeRawBufferPointer) -> [UInt8] in
var hash = [UInt8](repeating: 0, count: Int(CC_MD5_DIGEST_LENGTH))
CC_MD5(bytes.baseAddress, CC_LONG(data.count), &hash)
return hash
}
return hash.map { String(format: "%02x", [=10=]) }.joined()
}
}
(注意字符串转UTF-8数据不能失败,不需要return一个optional。)
CC_MD5 已被 iOS 13 弃用。相反,您可以使用 CC_SHA256.
爱斯基摩人的解决方案
下面是基于爱斯基摩人在 Swift 论坛 post 中提出的解决方案的变体 withUnsafeBytes Data API confusion:
extension String {
func md5() -> String {
let data = Data(utf8)
var hash = [UInt8](repeating: 0, count: Int(CC_MD5_DIGEST_LENGTH))
data.withUnsafeBytes { buffer in
_ = CC_MD5(buffer.baseAddress, CC_LONG(buffer.count), &hash)
}
return hash.map { String(format: "%02hhx", [=10=]) }.joined()
}
}
请注意,它实际上与 Martin R 的解决方案相同,但更短了一行(没有 return hash
)。
使用 NSData 的解决方案
这是一个使用桥接到 NSData 的更短的解决方案。
extension String {
func md5() -> String {
let data = Data(utf8) as NSData
var hash = [UInt8](repeating: 0, count: Int(CC_MD5_DIGEST_LENGTH))
CC_MD5(data.bytes, CC_LONG(data.length), &hash)
return hash.map { String(format: "%02hhx", [=11=]) }.joined()
}
}
在 iOS 13 及更高版本中有一个框架 CryptoKit
,它是围绕 CommonCrypto
框架和 MD5 哈希函数的包装器。
import CryptoKit
let d = "Hello"
let r = Insecure.MD5.hash(data: d.data(using: .utf8)!)
print(r)
/*Output: MD5 digest: 8b1a9953c4611296a827abf8c47804d7*/
CC_MD5 回馈
'CC_MD5' 在 iOS 13.0 中已弃用:此函数在密码学上已损坏,不应在安全上下文中使用。客户端应迁移到 SHA256(或更强)。
所以要有一个灵活的解决方案:
//OLD
import CommonCrypto
//new:
import CryptoKit
extension String {
var md5: String {
if #available(iOS 13.0, *) {
guard let d = self.data(using: .utf8) else { return ""}
let digest = Insecure.MD5.hash(data: d)
let h = digest.reduce("") { (res: String, element) in
let hex = String(format: "%02x", element)
//print(ch, hex)
let t = res + hex
return t
}
return h
} else {
// Fall back to pre iOS13
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 ""
}
}
let result = (0 ..< length).reduce("") {
[=10=] + String(format: "%02x", digest[])
}
return result
}// end of fall back
}
}
测试:
func MD5Test() -> Bool{
let HASHED = "5D41402ABC4B2A76B9719D911017C592"
let s = "hello"
let md5_1 = s.md5
if md5_1.uppercased() != HASHED{
return false
}
return true
}
在iOS13及以上有一个框架CryptoKit。试试这个:
extension Data {
var md5: String {
Insecure.MD5
.hash(data: self)
.map {String(format: "%02x", [=10=])}
.joined()
}
}
在 Swift 4 中我们可以使用
var md5: String? {
guard let data = self.data(using: .utf8) else { return nil }
let hash = data.withUnsafeBytes { (bytes: UnsafePointer<Data>) -> [UInt8] in
var hash: [UInt8] = [UInt8](repeating: 0, count: Int(CC_MD5_DIGEST_LENGTH))
CC_MD5(bytes, CC_LONG(data.count), &hash)
return hash
}
return hash.map { String(format: "%02x", [=11=]) }.joined()
}
但在 Swift 5 withUnsafeBytes
中使用 UnsafeRawBufferPointer
而不是 UnsafePointer
。如何修改md5函数?
Swift 5 版本:使用 UnsafeRawBufferPointer
作为闭包参数的类型,并使用 bytes.baseAddress
将地址传递给 Common Crypto 函数:
import Foundation
import CommonCrypto
extension String {
var md5: String {
let data = Data(self.utf8)
let hash = data.withUnsafeBytes { (bytes: UnsafeRawBufferPointer) -> [UInt8] in
var hash = [UInt8](repeating: 0, count: Int(CC_MD5_DIGEST_LENGTH))
CC_MD5(bytes.baseAddress, CC_LONG(data.count), &hash)
return hash
}
return hash.map { String(format: "%02x", [=10=]) }.joined()
}
}
(注意字符串转UTF-8数据不能失败,不需要return一个optional。)
CC_MD5 已被 iOS 13 弃用。相反,您可以使用 CC_SHA256.
爱斯基摩人的解决方案
下面是基于爱斯基摩人在 Swift 论坛 post 中提出的解决方案的变体 withUnsafeBytes Data API confusion:
extension String {
func md5() -> String {
let data = Data(utf8)
var hash = [UInt8](repeating: 0, count: Int(CC_MD5_DIGEST_LENGTH))
data.withUnsafeBytes { buffer in
_ = CC_MD5(buffer.baseAddress, CC_LONG(buffer.count), &hash)
}
return hash.map { String(format: "%02hhx", [=10=]) }.joined()
}
}
请注意,它实际上与 Martin R 的解决方案相同,但更短了一行(没有 return hash
)。
使用 NSData 的解决方案
这是一个使用桥接到 NSData 的更短的解决方案。
extension String {
func md5() -> String {
let data = Data(utf8) as NSData
var hash = [UInt8](repeating: 0, count: Int(CC_MD5_DIGEST_LENGTH))
CC_MD5(data.bytes, CC_LONG(data.length), &hash)
return hash.map { String(format: "%02hhx", [=11=]) }.joined()
}
}
在 iOS 13 及更高版本中有一个框架 CryptoKit
,它是围绕 CommonCrypto
框架和 MD5 哈希函数的包装器。
import CryptoKit
let d = "Hello"
let r = Insecure.MD5.hash(data: d.data(using: .utf8)!)
print(r)
/*Output: MD5 digest: 8b1a9953c4611296a827abf8c47804d7*/
CC_MD5 回馈 'CC_MD5' 在 iOS 13.0 中已弃用:此函数在密码学上已损坏,不应在安全上下文中使用。客户端应迁移到 SHA256(或更强)。
所以要有一个灵活的解决方案:
//OLD
import CommonCrypto
//new:
import CryptoKit
extension String {
var md5: String {
if #available(iOS 13.0, *) {
guard let d = self.data(using: .utf8) else { return ""}
let digest = Insecure.MD5.hash(data: d)
let h = digest.reduce("") { (res: String, element) in
let hex = String(format: "%02x", element)
//print(ch, hex)
let t = res + hex
return t
}
return h
} else {
// Fall back to pre iOS13
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 ""
}
}
let result = (0 ..< length).reduce("") {
[=10=] + String(format: "%02x", digest[])
}
return result
}// end of fall back
}
}
测试:
func MD5Test() -> Bool{
let HASHED = "5D41402ABC4B2A76B9719D911017C592"
let s = "hello"
let md5_1 = s.md5
if md5_1.uppercased() != HASHED{
return false
}
return true
}
在iOS13及以上有一个框架CryptoKit。试试这个:
extension Data {
var md5: String {
Insecure.MD5
.hash(data: self)
.map {String(format: "%02x", [=10=])}
.joined()
}
}