You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Data或十六进制字符串创建CryptoKit的SHA256Digest?

如何从Data或十六进制字符串创建CryptoKit的摘要类型

问题背景

需要实现下述toHex函数的逆逻辑——将十六进制字符串或Data转换为CryptoKit中的SHA256Digest(或其他遵循Digest协议的类型):

func toHex(hash: SHA256Digest) -> String {
    return String(hash.flatMap { byte in
        String(format:"%02x", byte)
    })
}

已能通过不安全指针实现,但希望获得更安全的方案。

安全实现方案

1. 从Data创建Digest类型

所有遵循Digest协议的类型(如SHA256Digest、SHA512Digest)都支持通过UnsafeRawBufferPointer初始化,我们可以利用Data的withUnsafeBytes方法安全访问其内存:

extension Digest {
    init?(data: Data) {
        guard data.count == Self.byteCount else {
            return nil
        }
        self = data.withUnsafeBytes { buffer in
            buffer.bindMemory(to: Self.self).baseAddress!.pointee
        }
    }
}

使用示例:

let sampleData = Data(repeating: 0x01, count: SHA256Digest.byteCount)
if let digest = SHA256Digest(data: sampleData) {
    print(digest.toHex())
}

2. 从十六进制字符串创建Digest类型

先将十六进制字符串转换为Data,再利用上面的init(data:)方法生成Digest。给Digest扩展一个安全的fromHex方法:

extension Digest {
    // 十六进制字符串转Data的安全逻辑
    private static func hexStringToData(_ hexString: String) -> Data? {
        let cleanedString = hexString.lowercased().replacingOccurrences(of: " ", with: "")
        guard cleanedString.count % 2 == 0 else { return nil }
        
        var data = Data(capacity: cleanedString.count / 2)
        var index = cleanedString.startIndex
        
        while index < cleanedString.endIndex {
            let nextIndex = cleanedString.index(index, offsetBy: 2)
            let byteString = cleanedString[index..<nextIndex]
            guard let byte = UInt8(byteString, radix: 16) else { return nil }
            data.append(byte)
            index = nextIndex
        }
        return data
    }
    
    static func fromHex(_ hexString: String) -> Self? {
        guard let data = Self.hexStringToData(hexString),
              data.count == Self.byteCount else {
            return nil
        }
        return Self(data: data)
    }
}

完整扩展代码

搭配原有的toHex方法,完整的Digest扩展如下:

extension Digest {
    func toHex() -> String {
        return self.map { String(format: "%02x", $0) }.joined()
    }
    
    init?(data: Data) {
        guard data.count == Self.byteCount else {
            return nil
        }
        self = data.withUnsafeBytes { buffer in
            buffer.bindMemory(to: Self.self).baseAddress!.pointee
        }
    }
    
    private static func hexStringToData(_ hexString: String) -> Data? {
        let cleanedString = hexString.lowercased().replacingOccurrences(of: " ", with: "")
        guard cleanedString.count % 2 == 0 else { return nil }
        
        var data = Data(capacity: cleanedString.count / 2)
        var index = cleanedString.startIndex
        
        while index < cleanedString.endIndex {
            let nextIndex = cleanedString.index(index, offsetBy: 2)
            let byteString = cleanedString[index..<nextIndex]
            guard let byte = UInt8(byteString, radix: 16) else { return nil }
            data.append(byte)
            index = nextIndex
        }
        return data
    }
    
    static func fromHex(_ hexString: String) -> Self? {
        guard let data = Self.hexStringToData(hexString),
              data.count == Self.byteCount else {
            return nil
        }
        return Self(data: data)
    }
}

使用示例:

// 生成测试用的SHA256Digest
let inputData = "test".data(using: .utf8)!
let originalDigest = SHA256.hash(data: inputData)
let hexString = originalDigest.toHex()

// 从十六进制字符串恢复Digest
if let restoredDigest = SHA256Digest.fromHex(hexString) {
    print(restoredDigest == originalDigest) // 输出true
}

实现说明

  • 避免手动分配/释放内存的不安全操作,完全依赖Data的安全内存访问API
  • 对十六进制字符串做容错处理:支持大小写、忽略空格
  • 严格校验输入长度,确保符合目标Digest类型的字节数要求

内容的提问来源于stack exchange,提问作者Lawrence D'Anna

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.23 12:18:09