如何在Swift中将字符串哈希为SHA512?

7

我正在开发一款社交媒体应用程序,希望能够在Swift中将密码字符串编码为SHA512。我在GitHub上找到了CryptoSwift库,但是我很难将其加载到我的Swift项目中,并将其链接到我的项目文件。是否有人知道如何相对容易地完成这个任务? 提前感谢, Kyle


1
我相信CryptoSwift是最简单的方式 :) - gutenmorgenuhu
2
您在使用CryptoSwift时遇到了什么问题?- 您不一定需要第三方框架,也可以使用苹果的CommonCrypto与薄的Swift包装器。示例:https://dev59.com/noHba4cB1Zd3GeqPYf2S(您自己的问题:),https://dev59.com/6G025IYBdhLWcg3wFxqa,https://dev59.com/kl8e5IYBdhLWcg3wb56S,https://dev59.com/Fl8e5IYBdhLWcg3wwMv8。 - Martin R
@MartinR 感谢分享!这个答案真的帮了我很多!这篇文章回答了我的问题(https://dev59.com/kl8e5IYBdhLWcg3wb56S)。我所要做的就是将SHA1更改为SHA512。 - TechnologyGuy
最好避免使用CryptoSwift,因为它比基于Common Crypto的实现慢500到1000倍。苹果的Common Crypto已获得FIPS认证,因此经过了充分的审查,而使用CryptoSwift则存在正确性和安全性方面的风险。 - zaph
5个回答

8

针对 Swift 3 的解决方案:

extension String {

    func sha512() -> String {
        let data = self.data(using: .utf8)!
        var digest = [UInt8](repeating: 0, count: Int(CC_SHA512_DIGEST_LENGTH))
        data.withUnsafeBytes({
            _ = CC_SHA512($0, CC_LONG(data.count), &digest)
        })
        return digest.map({ String(format: "%02hhx", $0) }).joined(separator: "")
    }

}

是的,你说得对。Swift字符串始终是Unicode字符串,因此self.data(using: .utf8)永远不会失败。我已经相应地修改了我的答案。 - sundance
在 playground 中,我需要导入什么才能访问 CC_ 函数?Foundation 不够用。 - Jeff
你好@sundance,感谢您的回答。我不太理解String(format:)调用中的"%02hhx"是什么意思,不知道您能否简要解释一下。另外,为什么需要迭代字节并使用String(format:)将它们转换为字符串,而不是直接使用base64EncodedString()呢?提前致谢 - ayushn21
通常,哈希值不会被编码为base64字符串,而是作为十六进制字符串,这就是我的函数返回的内容。摘要由512位组成,存储为64字节数组,并作为128个字符的十六进制字符串返回。在此将摘要映射到字符串数组中,其中每个字节都格式化为十六进制字符串。格式“%02hhx”来自C ++中的printf格式(请参见http://www.cplusplus.com/reference/cstdio/printf/)。%是起始符号,“02”强制输出两个字符,“hh”定义输入为字节,“x”将输出设置为十六进制。 - sundance
@sundance 请在这里提供帮助 https://stackoverflow.com/questions/66757704/aes256-encryption-in-swift-string-not-matching - Amit
显示剩余2条评论

3

我认为所有的答案都可以,但如果我们需要一个真正通用的解决方案,我认为我们需要提高一个级别。

CC_LONG只是一个UInt32,不支持非常大的数据结构。

这是我在Swift 3中的解决方案:

首先,我们创建一个基础:

struct Sha512 {
    let context = UnsafeMutablePointer<CC_SHA512_CTX>.allocate(capacity:1)

    init() {
        CC_SHA512_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_SHA512_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_SHA512_DIGEST_LENGTH))
        CC_SHA512_Final(&digest, context)

        return Data(bytes: digest)
    }
}

为了方便,我们对 Data 做了扩展:

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

最后是一个针对 String 的扩展:

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

这个解决方案可以用于Sha256、MD5等技术,通过使用苹果的CommonCrypto可以得到良好的通用解决方案。


2

Swift 3

func sha512() -> String {
    let data = self.data(using: .utf8)!
    var digest = [UInt8](repeating: 0, count: Int(CC_SHA512_DIGEST_LENGTH))
    data.withUnsafeBytes({
        _ = CC_SHA512($0, CC_LONG(data.count), &digest)
    })

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

Swift 2.3

func sha512() -> String {
    let data = self.dataUsingEncoding(NSUTF8StringEncoding)!
    var digest = [UInt8](count:Int(CC_SHA512_DIGEST_LENGTH), repeatedValue: 0)
    CC_SHA512(data.bytes, CC_LONG(data.length), &digest)
    let hexBytes = digest.map { String(format: "%02hhx", $0) }

    return hexBytes.joinWithSeparator("")
}

2

您需要导入C库CommonCrypto。由于它不是独立模块,因此不能只将CommonCrypto导入到您的swift文件中。

如果您有一个桥接头文件,那就太幸运了!只需将以下内容添加到该文件中:

#import <CommonCrypto/CommonCrypto.h>

有一些关于不同方法的文章可以做到这一点。

然后,您可以使用此代码片段使sha512适用于您选择的任何字符串。

Swift 5

extension String {

    public var sha512: String {
        let data = self.data(using: .utf8) ?? Data()
        var digest = [UInt8](repeating: 0, count: Int(CC_SHA512_DIGEST_LENGTH))
        data.withUnsafeBytes {
            _ = CC_SHA512($0.baseAddress, CC_LONG(data.count), &digest)
        }
        return digest.map({ String(format: "%02hhx", $0) }).joined(separator: "")
    }
}

0

Swift 5

对于字符串,这个方法对我来说很有效。

func SHA512(string: String) -> String {
    let length = Int(CC_SHA512_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_SHA512(messageBytesBaseAddress, messageLength, digestBytesBlindMemory)
            }
            return 0
        }
    }
    return digestData.map { String(format: "%02hhx", $0) }.joined()
}

如果您只需要数据,请删除.map { String(format: "%02hhx", $0):

func SHA512(string: String) -> Data {
    let length = Int(CC_SHA512_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_SHA512(messageBytesBaseAddress, messageLength, digestBytesBlindMemory)
            }
            return 0
        }
    }
    return digestData
}

你需要导入CommonCrypto。 - Axel López

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接