Swift 5集成Coinbase API时Base64解码返回nil且HMAC签名结果不符的问题排查
Let’s walk through your two Coinbase API integration issues step by step to fix them:
Issue 1: Base64 String Decodes to Nil When Converting to String
The Base64 string you’re working with (fI5GtCKFF+O8j8uJlJVGxIvolVUUrVMT9GBouxlpEOUXmaGbwQvHt0z8kK0fUwQaKa45KUOLWHP/CQaM70bhdQ==) decodes to binary data, not valid UTF-8 text. When you try to convert this data to a String using .utf8 encoding, it returns nil because most binary sequences don’t map to readable UTF-8 characters.
This is actually expected! Your Coinbase API key (represented by this Base64 string) is meant to be used as raw Data, not a human-readable string. You don’t need to convert it to a String at all—just keep it as Data for the HMAC signing step.
Issue 2: HMAC-SHA256 Signature Doesn’t Match Online Tool Output
The problem here is likely in your custom HMAC.sign implementation (since you haven’t shared its code). Let’s replace it with a reliable, standard implementation using either CryptoKit (Apple’s modern framework) or CommonCrypto for older OS versions.
Fix with CryptoKit (Recommended, iOS 13+/macOS 10.15+)
CryptoKit simplifies cryptography tasks and avoids common mistakes. Here’s how to compute the correct signature:
import CryptoKit // Helper function to compute HMAC-SHA256 func computeHMACSHA256(message: String, keyData: Data) -> String { guard let messageData = message.data(using: .utf8) else { fatalError("Failed to convert message to data") } let symmetricKey = SymmetricKey(data: keyData) let hmac = HMAC<SHA256>.authenticationCode(for: messageData, using: symmetricKey) return Data(hmac).base64EncodedString() } // Test with your values let base64Key = "fI5GtCKFF+O8j8uJlJVGxIvolVUUrVMT9GBouxlpEOUXmaGbwQvHt0z8kK0fUwQaKa45KUOLWHP/CQaM70bhdQ==" guard let keyData = Data(base64Encoded: base64Key) else { fatalError("Invalid Base64 key") } let signature = computeHMACSHA256(message: "1", keyData: keyData) print(signature) // This should match your online tool's output
Fix with CommonCrypto (For iOS 12 or Earlier)
If you need to support older operating systems, use CommonCrypto:
import CommonCrypto func computeHMACSHA256(message: String, keyData: Data) -> String { guard let messageData = message.data(using: .utf8) else { fatalError("Failed to convert message to data") } var hmacData = Data(count: Int(CC_SHA256_DIGEST_LENGTH)) hmacData.withUnsafeMutableBytes { hmacBytes in messageData.withUnsafeBytes { messageBytes in keyData.withUnsafeBytes { keyBytes in CCHmac(CCHmacAlgorithm(kCCHmacAlgSHA256), keyBytes.baseAddress, keyData.count, messageBytes.baseAddress, messageData.count, hmacBytes.baseAddress) } } } return hmacData.base64EncodedString() } // Usage is identical to the CryptoKit example
Why Your Original Code Failed
If your custom HMAC.sign method incorrectly handled the raw key data (e.g., converting it to a string first, using the wrong encoding, or miscalculating the hash), it would produce an invalid signature. Using Apple’s standard cryptography APIs eliminates these edge cases.
Final Recap
- For Issue 1: Abandon attempts to convert the decoded key to a
String—it’s binary data, and you only need it asDatafor signing. - For Issue 2: Replace your custom HMAC logic with one of the standard implementations above to ensure correctness.
内容的提问来源于stack exchange,提问作者ineedtolearntoeat

