ฉันจะแปลงสตริงเป็นแฮช MD5 ใน iOS โดยใช้ Swift ได้อย่างไร


111

ฉันต้องการแปลงสตริงเช่น "abc" เป็นแฮช MD5 ฉันต้องการทำสิ่งนี้ใน iOS และ Swift ฉันได้ลองใช้วิธีแก้ไขปัญหาด้านล่าง แต่ไม่ได้ผลสำหรับฉัน:

การนำเข้า CommonCrypto ในเฟรมเวิร์ก Swift

วิธีใช้วิธี CC_MD5 ในภาษาที่รวดเร็ว

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

เพื่อให้ชัดเจนยิ่งขึ้นฉันต้องการให้ได้ผลลัพธ์ใน Swift ที่คล้ายกับเอาต์พุตของโค้ด PHP นี้:

$str = "Hello";

echo md5($str);

เอาต์พุต: 8b1a9953c4611296a827abf8c47804d7


5
ลิงก์ที่คุณให้มามีอะไรผิดพลาด
jtbandes

2
ลิงก์ที่คุณให้ควรใช้งานได้ คุณอธิบายได้ไหมว่าปัญหาที่แท้จริงของคุณคืออะไร? คุณยังสามารถรวมไลบรารีของบุคคลที่สามเพื่อทำสิ่งที่คุณต้องการได้เช่น github.com/krzyzanowskim/CryptoSwift
Eric Amorde

1
ดังที่ฉันได้กล่าวไปแล้วว่าฉันยังใหม่กับการเขียนโปรแกรมที่รวดเร็วฉันสับสนที่จะใช้มันอย่างถูกต้อง ฉันรวมไฟล์นี้ (#import <CommonCrypto / CommonCrypto.h>) ในไฟล์ตัวควบคุมที่รวดเร็ว แต่ขอบคุณสำหรับการตอบกลับของคุณตอนนี้ได้รับการแก้ไขแล้วโดย Mr.zaph คำตอบด้านล่าง
user3606682

หากคุณต้องการใช้งานที่บ้านใน Swift github.com/onmyway133/SwiftHash
onmyway133

คำตอบ:


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นี้

โดยทั่วไปไม่ควรใช้ MD5 สำหรับงานใหม่ SHA256 เป็นแนวทางปฏิบัติที่ดีที่สุดในปัจจุบัน

ตัวอย่างจากส่วนเอกสารที่เลิกใช้แล้ว:

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

ฟังก์ชันเหล่านี้จะแฮชสตริงหรืออินพุตข้อมูลด้วยอัลกอริทึมแฮชการเข้ารหัสหนึ่งในแปดแบบ

พารามิเตอร์ name ระบุชื่อฟังก์ชันแฮชเป็น
ฟังก์ชันที่รองรับสตริงได้แก่ MD2, MD4, MD5, SHA1, SHA224, SHA256, SHA384 และ SHA512 a ตัวอย่างนี้ต้องการ Common Crypto
จำเป็นต้องมีส่วนหัวเชื่อมโยงกับโปรเจ็กต์:
#import <CommonCrypto/CommonCrypto.h>
เพิ่มความปลอดภัย .framework กับโครงการ



ฟังก์ชันนี้ใช้ชื่อแฮชและสตริงที่จะแฮชและส่งคืนข้อมูล:

ชื่อ: ชื่อของฟังก์ชันแฮชเป็นสตริง  
สตริง: สตริงที่จะแฮช  
ส่งกลับ: ผลลัพธ์ที่แฮชเป็นข้อมูล  
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
ขอบคุณมาก @zaph ฉันดิ้นรนเพื่อสิ่งนี้มานานกว่า 2 วันแล้ว ได้รับการแก้ไขด้วยคำตอบข้างต้นของคุณ :) และใช่ฉันกำลังดึงข้อมูลเก่าจากเว็บที่ใช้ MD5 ดังนั้นฉันจึงถูกบังคับให้ใช้ 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 (หรือการเข้ารหัสสตริงใด ๆ บางลงถึงไม่มีอยู่จริง
zaph

1
นี่คือวิธีที่คุณสามารถปรับปรุงได้: นำเข้าเฉพาะสัญลักษณ์ที่จำเป็นเท่านั้นไม่ใช่ CommonCrypto ทั้งหมดเนื่องจากเป็นค่าใช้จ่ายเล็กน้อยอย่างอื่น: import var CommonCrypto.CC_MD5_DIGEST_LENGTH import func CommonCrypto.CC_MD5 ประเภทการนำเข้า CommonCrypto.CC_LONG
Igor Vasilev

2
@zaph คุณอาจต้องการเพิ่มโซลูชัน iOS 13 CryptoKit ในคำตอบของคุณซึ่งฉันได้ระบุรายละเอียดไว้ในคำตอบด้านล่าง: stackoverflow.com/a/56578995/368085
mluisbrown

40

หลังจากอ่านคำตอบอื่น ๆ ที่นี่ (และต้องรองรับประเภทแฮชอื่น ๆ ด้วย) ฉันเขียนนามสกุลสตริงที่จัดการประเภทแฮชและประเภทเอาต์พุตหลายรายการ

หมายเหตุ: CommonCrypto รวมอยู่ใน Xcode 10 ดังนั้นคุณสามารถทำได้import CommonCryptoโดยไม่ต้องยุ่งกับส่วนหัวเชื่อมต่อหากคุณติดตั้ง Xcode เวอร์ชันล่าสุดไว้ ... มิฉะนั้นจำเป็นต้องใช้ส่วนหัวเชื่อมต่อ


อัปเดต: ทั้ง Swift 4 และ 5 ใช้ไฟล์ String + Crypto.swift เดียวกันด้านล่าง

มีไฟล์ Data + Crypto.swift แยกต่างหากสำหรับ Swift 5 (ดูด้านล่าง) เป็น api สำหรับ 'withUnsafeMutableBytes' และ 'withUnsafeBytes' ที่เปลี่ยนไประหว่าง 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 - Data + Crypto.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 - Data + Crypto.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

40

ตั้งแต่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 ในขณะนี้ที่ไม่ปลอดภัย คุณไม่จำเป็นต้องใช้ CommonCrypto ใน Objective-C เพื่อให้คุณได้รับการสนับสนุนสำหรับ pragmas เพื่อปิดใช้งานคำเตือน มีประโยชน์หากคุณกำลังทำงานในสภาพแวดล้อมที่ให้ความสำคัญกับการจัดการกับคำเตือน
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

โซลูชันของคุณทำงานได้อย่างสมบูรณ์แบบ เราสามารถเพิ่มค่า SALT ด้วยการเข้ารหัส MD5 นี้ได้หรือไม่? ฉันต้องการเพิ่มในขณะที่เข้ารหัสสตริง คุณสามารถให้ลิงค์สำหรับใช้งานแบบเต็มได้หรือไม่?
Punita

ฉันไม่แน่ใจว่าคุณกำลังพยายามทำอะไรอยู่ ใช้ "AES128" หากคุณต้องการเข้ารหัสแบบกำหนดเองด้วยการใส่เกลือ ถ้าการรักษาความปลอดภัยของความกังวลของคุณตรวจสอบนี้: stackoverflow.com/a/15775071/3118377
Invictus Cody

ขอบคุณ Invictus Cody ฉันได้เชื่อม SALT กับ String และสามารถรับ MD5 ได้
Punita

ใช้งานได้ดี แต่คุณจะแปลงกลับเป็น String ได้อย่างไร?
DocAsh59

1
Swift 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]) } }
Jim B

17

ฉันเปิดตัวการใช้งาน Swiftที่ไม่ได้ขึ้นอยู่กับ CommonCrypto หรือสิ่งอื่นใด สามารถใช้ได้ภายใต้ใบอนุญาต MIT

รหัสประกอบด้วยไฟล์ที่รวดเร็วเพียงไฟล์เดียวที่คุณสามารถวางลงในโครงการของคุณได้ หากคุณต้องการคุณสามารถใช้โครงการ Xcode ที่มีอยู่กับกรอบงานและเป้าหมายการทดสอบหน่วย

ใช้งานง่าย:

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

พิมพ์: md5: 9e107d9d372bb6826bd81d3542a419d6

ไฟล์ที่รวดเร็วประกอบด้วยเอกสารและตัวอย่างอื่น ๆ


4
ต้องการ Swift 4 ซึ่งไม่ได้กล่าวถึงที่นี่หรือบน Github ReadMe ไม่ควรพิจารณาการใช้งานโดยไม่มีตัวเลขประสิทธิภาพเมื่อเทียบกับ Common Crypto หมายเหตุ: Crypto ทั่วไปได้รับการรับรอง FIPS 140, SwiftDigest ไม่ใช่ นี่คือคำถามสำคัญ: สิ่งนี้ดีกว่า Common Crypto สำหรับการนำไปใช้อย่างไร? ปลอดภัยมากขึ้น: ไม่เร็วกว่า: ไม่
zaph

1
@zaph จุดประสงค์หลักคือการมีการใช้งาน md5 ที่ไม่ขึ้นอยู่กับ CommonCrypto สิ่งนี้มีประโยชน์ในสถานการณ์ที่ 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 ierวิธีการใช้รหัสSwift 2.2

โปรดจำไว้ว่าคุณยังคงต้อง#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

ฉันใช้ Carthage และ Cyrpto เพื่อทำสิ่งนี้

  1. ติดตั้ง Carthage หากคุณยังไม่ได้ทำ

  2. ติดตั้ง Crypto ลงในโครงการของคุณ

  3. ดำเนินการ 'ปรับปรุงการขนส่งสินค้า'

  4. หากคุณกำลังเรียกใช้จาก commandline เพิ่มในกรอบในไฟล์ที่รวดเร็ว

    #!/usr/bin/env xcrun swift -F Carthage/Build/Mac
  5. เพิ่มการนำเข้า Crypto ไปยังไฟล์ที่รวดเร็วของคุณ

  6. แล้วมันก็ใช้งานได้!

    print( "convert this".MD5 )

เหนือกว่าเล็กน้อยในการใช้ไลบรารีการเข้ารหัสเต็มรูปแบบเมื่อต้องการเพียงฟังก์ชันเดียว
Mark Bourke

ขออภัยสำหรับความคิดเห็นของเธรดเก่า ... บางที แต่ไลบรารีทั่วไปมักจะ (น่าจะ) ทันสมัยอยู่เสมอเมื่อมีการเปลี่ยนแปลงแพลตฟอร์มดังนั้นจึงให้ผลลัพธ์ทั่วไปและลดการแยกส่วนน้อยที่สุดและไม่มีใครต้องสร้างล้อใหม่อย่างต่อเนื่องหรือใช้อินเทอร์เน็ตจำนวนมาก - พบรหัสที่อาจเชื่อถือได้รวดเร็วหรือมีแบบแผนตามมาตรฐานหรือไม่ก็ได้ ฉันทุกคนต้องการลดการพึ่งพา แต่ในทำนองนี้ฉันดูที่ตัวเลือกระบบปฏิบัติการก่อนตัวเลือกภาษาทั่วไปอันดับสองและตัวเลือกมาตรฐานของบุคคลที่สามถัดไปและส่งผลให้เป็นแบบครั้งเดียวหรือ "ห้องสมุดของผู้ชายคนนี้ค่อนข้างดี" ตัวเลือกสุดท้าย * shrug *
ChrisH

1

MD5 เป็นอัลกอริธึมการแฮชไม่จำเป็นต้องใช้ไลบรารี CommonCrypto ขนาดใหญ่สำหรับสิ่งนี้ (และถูกปฏิเสธจากการตรวจสอบของ Apple) เพียงใช้ไลบรารีการแฮช md5 ใด ๆ

ไลบรารีหนึ่งที่ฉันใช้คือSwiftHashซึ่งเป็นการใช้งาน MD5 อย่างรวดเร็ว (อ้างอิงจากhttp://pajhome.org.uk/crypt/md5/md5.html )


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
}

ฟังก์ชันด้านบนจะส่งกลับค่า a จริง ๆ[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 อย่างรวดเร็วสำหรับข้อมูล / NSData ตัวอย่างเช่นคุณดาวน์โหลดหรืออ่านไบนารีสำหรับดิสก์หรือ netwkork)

(ไร้ยางอายจาก "คำตอบ Swift 5 เป็นส่วนขยาย String (อิงตามคำตอบที่ยอดเยี่ยมของ 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*/
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.