美文网首页Swift 基础
Swift md5 和 SHA 系列 hash 函数

Swift md5 和 SHA 系列 hash 函数

作者: Ian_ | 来源:发表于2020-06-08 11:08 被阅读0次
import CommonCrypto
import CryptoKit

// Defines types of hash string outputs available
public enum HashOutputType {
    // standard hex string output lowercased
    case hexLowercased
    // standard hex string output uppercased
    case hexUppercased
    // 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.
    func hashed(_ type: HashType, output: HashOutputType = .hexLowercased) -> String? {

        // convert string to utf8 encoded data
        guard let message = data(using: .utf8) else { return nil }
        return message.hashed(type, output: output)
    }
}

extension Data {
    // From Stackoverflow, see https://stackoverflow.com/questions/39075043/how-to-convert-data-to-hex-string-in-swift
    /// conert to hexencoded string
    public func hexString1(_ lowercased: Bool = true) -> String {
        let hexAlphabet = (lowercased ? "0123456789abcdef" : "0123456789ABCDEF").unicodeScalars.map { $0 }
        return String(reduce(into: "".unicodeScalars, { (result, value) in
            result.append(hexAlphabet[Int(value/16)])
            result.append(hexAlphabet[Int(value%16)])
        }))
    }
    
    // It's not as elegant, but it's about 10 times faster than function hexString1
    /// conert to hexencoded string
    public func hexString2(_ lowercased: Bool = true) -> String {
        let format = lowercased ? "%02hhx" : "%02hhX"
        return map { String(format: format, $0) }.joined()
    }
    
    /// 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 = .hexLowercased) -> String {
        if #available(iOS 13.0, OSX 10.15, watchOS 6.0, tvOS 13.0, *),
            type == .md5 {
            var md5 = Insecure.MD5()
            md5.update(data: self)
            let digest = md5.finalize().withUnsafeBytes { Data($0) }
            
            // return the value based on the specified output type.
            switch output {
            case .hexLowercased: return digest.hexString2()
            case .hexUppercased: return digest.hexString2(false)
            case .base64: return digest.base64EncodedString()
            }
        }
        
        // setup data variable to hold hashed value
        var digest = Data(count: Int(type.length))
        _ = digest.withUnsafeMutableBytes { (digestBytes) -> Bool in
            withUnsafeBytes({ (messageBytes) -> Bool in
                let length = CC_LONG(self.count)
                let messageAddress = messageBytes.baseAddress
                let digestAddress = digestBytes.bindMemory(to: UInt8.self).baseAddress
                switch type {
                case .md5:
                    if #available(iOS 13.0, OSX 10.15, watchOS 6.0, tvOS 13.0, *) {
                        return true
                    } else {
                        CC_MD5(messageAddress, length, digestAddress)
                    }
                case .sha1: CC_SHA1(messageAddress, length, digestAddress)
                case .sha224: CC_SHA224(messageAddress, length, digestAddress)
                case .sha256: CC_SHA256(messageAddress, length, digestAddress)
                case .sha384: CC_SHA384(messageAddress, length, digestAddress)
                case .sha512: CC_SHA512(messageAddress, length, digestAddress)
                }
                return true
            })
        }

        // return the value based on the specified output type.
        switch output {
        case .hexLowercased: return digest.hexString2()
        case .hexUppercased: return digest.hexString2(false)
        case .base64: return digest.base64EncodedString()
        }
    }
}

print("asdfasdfasdf".hashed(.md5)!)

print("asdfasdfasdf".hashed(.md5, output: .base64)!)

print("asdfasdfasdf".hashed(.md5, output: .hexUppercased)!)

print("asdfasdfasdf".hashed(.sha1)!)

print("asdfasdfasdf".hashed(.sha1, output: .base64)!)

print("asdfasdfasdf".hashed(.sha1, output: .hexUppercased)!)

reference: stackoverflow

iOS 13 CC_MD5 方法已经被弃用, 替代的是 Insecure 这个枚举, 代表不安全的, 所以现在的 md5sha1 都别标记为不安全的, 不建议使用, 应该是容易撞库成功.

/// Algorithms that we support for legacy reasons but that are insecure to use.
/// Do not adopt in new protocols.

相关文章

网友评论

    本文标题:Swift md5 和 SHA 系列 hash 函数

    本文链接:https://www.haomeiwen.com/subject/qelnzhtx.html