Swift 3 making sha1, sha256 and md5 functions

2019-04-05 17:44发布

In Swift 2, I used the following code to extend string variables and to be able to make sha1, sha256, and md5.

After moving to swift 3, the code is not working any more! I tried to convert it but run into continuous errors.

Any idea how can I solve this?

extension NSData {
    func hexString() -> String {
        var string = String()
        for i in UnsafeBufferPointer<UInt8>(start: UnsafeMutablePointer<UInt8>(bytes), count: length) {
            string += Int(i).hexString()
        }
        return string
    }

    func MD5() -> NSData {
        let result = NSMutableData(length: Int(CC_MD5_DIGEST_LENGTH))!
        CC_MD5(bytes, CC_LONG(length), UnsafeMutablePointer<UInt8>(result.mutableBytes))
        return NSData(data: result)
    }

    func SHA1() -> NSData {
        let result = NSMutableData(length: Int(CC_SHA1_DIGEST_LENGTH))!
        CC_SHA1(bytes, CC_LONG(length), UnsafeMutablePointer<UInt8>(result.mutableBytes))
        return NSData(data: result)
    }

    func SHA256() -> NSData {
        let result = NSMutableData(length: Int(CC_SHA256_DIGEST_LENGTH))
        CC_SHA256(bytes, CC_LONG(length), UnsafeMutablePointer(result!.mutableBytes))
        return NSData(data: result!)
    }
}

extension String {
    func hexString() -> String {
        return (self as NSString).dataUsingEncoding(NSUTF8StringEncoding)!.hexString()
    }

    func MD5() -> String {
        return (self as NSString).dataUsingEncoding(NSUTF8StringEncoding)!.MD5().hexString()
    }

    func SHA1() -> String {
        return (self as NSString).dataUsingEncoding(NSUTF8StringEncoding)!.SHA1().hexString()
    }

    func SHA256() -> String {
        return (self as NSString).dataUsingEncoding(NSUTF8StringEncoding)!.SHA256().hexString()
    }

}

标签: swift3
4条回答
戒情不戒烟
2楼-- · 2019-04-05 17:52

You'd better use Swift Data in Swift 3.

Data

And when working with Data, you need to use withUnsafeBytes(_:) or withUnsafeMutableBytes(_:), where you were using bytes or mutableBytes respectively.

withUnsafeBytes(_:)

withUnsafeMutableBytes(_:)

extension Data {
    func hexString() -> String {
        let string = self.map{Int($0).hexString()}.joined()
        return string
    }

    func MD5() -> Data {
        var result = Data(count: Int(CC_MD5_DIGEST_LENGTH))
        _ = result.withUnsafeMutableBytes {resultPtr in
            self.withUnsafeBytes {(bytes: UnsafePointer<UInt8>) in
                CC_MD5(bytes, CC_LONG(count), resultPtr)
            }
        }
        return result
    }

    /*
    ... nearly the same for `SHA1` and `SHA256`.
     */
}

extension String {
    func hexString() -> String {
        return self.data(using: .utf8)!.hexString()
    }

    func MD5() -> String {
        return self.data(using: .utf8)!.MD5().hexString()
    }

    /*
    ... nearly the same for `SHA1` and `SHA256`.
     */
}

I prefer making computed properties than no-argument methods (for relatively light-tasks). You need to fix all parts using them, but you can write something like this:

extension Int {
    var hexString: String {
        return ...
    }
}
extension Data {
    var hexString: String {
        let string = self.map{Int($0).hexString}.joined()
        return string
    }

    var MD5: Data {
        var result = Data(count: Int(CC_MD5_DIGEST_LENGTH))
        _ = result.withUnsafeMutableBytes {resultPtr in
            self.withUnsafeBytes {(bytes: UnsafePointer<UInt8>) in
                CC_MD5(bytes, CC_LONG(count), resultPtr)
            }
        }
        return result
    }

    /*
    ... nearly the same for `SHA1` and `SHA256`.
     */
}

extension String {
    var hexString: String {
        return self.data(using: .utf8)!.hexString
    }

    var MD5: String {
        return self.data(using: .utf8)!.MD5.hexString
    }

    /*
    ... nearly the same for `SHA1` and `SHA256`.
     */
}

There may be a quicker fix for your code using NSData, but I recommend you to move to Data in Swift 3.

查看更多
老娘就宠你
3楼-- · 2019-04-05 17:56

For completion, the shortest and most flexible solution in Swift 4 is:

extension Data {

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

    var md5: Data {
        var digest = [Byte](repeating: 0, count: Int(CC_MD5_DIGEST_LENGTH))
        self.withUnsafeBytes({
            _ = CC_MD5($0, CC_LONG(self.count), &digest)
        })
        return Data(bytes: digest)
    }

    var sha1: Data {
        var digest = [UInt8](repeating: 0, count: Int(CC_SHA1_DIGEST_LENGTH))
        self.withUnsafeBytes({
            _ = CC_SHA1($0, CC_LONG(self.count), &digest)
        })
        return Data(bytes: digest)
    }

    var sha256: Data {
        var digest = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH))
        self.withUnsafeBytes({
            _ = CC_SHA256($0, CC_LONG(self.count), &digest)
        })
        return Data(bytes: digest)
    }

}

extension String {

    var md5: Data {
        return self.data(using: .utf8)!.md5
    }

    var sha1: Data {
        return self.data(using: .utf8)!.sha1
    }

    var sha256: Data {
        return self.data(using: .utf8)!.sha256
    }

}
查看更多
Animai°情兽
4楼-- · 2019-04-05 17:59

I find most of the answer ok, but if we should have a true universal solution I think we need to step it up a level.

CC_LONG is just an UInt32 and will not support really large data structures.

This is my solution in Swift 3:

First we create a foundation that work with Data:

struct Sha256 {
    let context = UnsafeMutablePointer<CC_SHA256_CTX>.allocate(capacity:1)

    init() {
        CC_SHA256_Init(context)
    }

    func update(data: Data) {
        data.withUnsafeBytes { (bytes: UnsafePointer<Int8>) -> Void in
            let end = bytes.advanced(by: data.count)
            for f in sequence(first: bytes, next: { $0.advanced(by: Int(CC_LONG.max)) }).prefix(while: { (current) -> Bool in current < end})  {
                _ = CC_SHA256_Update(context, f, CC_LONG(Swift.min(f.distance(to: end), Int(CC_LONG.max))))
            }
        }
    }

    func final() -> Data {
        var digest = [UInt8](repeating: 0, count:Int(CC_SHA256_DIGEST_LENGTH))
        CC_SHA256_Final(&digest, context)

        return Data(bytes: digest)
    }
}

For convenience we do an extension for Data:

extension Data {
    func sha256() -> Data {
        let s = Sha256()
        s.update(data: self)
        return s.final()
    }
}

And last an extension for String:

extension String {
    func sha256() -> Data {
        return self.data(using: .utf8)!.sha256()
    }
}

If needed, convert the result from Data to hex string or something else depending on your use case.

This solution can be used for Sha512, MD5 etc. to get true universal solutions with Apple's CommonCrypto that are easy to extend to many different use cases.

查看更多
该账号已被封号
5楼-- · 2019-04-05 18:01
func MD5() -> String {

    let length = Int(CC_MD5_DIGEST_LENGTH)
    var digest = [UInt8](repeating: 0, count: length)
    if let d = self.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])
    }

}
查看更多
登录 后发表回答