如何用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
相关产品推荐
相关产品推荐

