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

如何生成正确的OAuth HMAC-SHA1签名?解决签名不匹配问题

Fixing Tumblr OAuth HMAC-SHA1 Signature Mismatch

Hey there, let's work through why your Tumblr OAuth signature is failing to match. I went through your code and spotted several key issues that are almost certainly causing the validation error. Let's break them down and fix them step by step:

Key Issues in Your Code

  • Line breaks in Base64 signature: Tumblr expects a clean, line-break-free Base64 string, but your current code adds line wraps.
  • Inaccurate data length calculation: Using strlen() for HMAC data counts can lead to errors if your strings contain unexpected null characters.
  • Deprecated Swift syntax: Your nonce generation uses an outdated substring method that could cause warnings or inconsistencies.
  • Minor URL inconsistency: Your initial requestTokenUrl lacks the https:// prefix (even though you use the correct URL later, consistency avoids mistakes).

Corrected Code Components

1. Fix the HMAC Extension Method

Update the HMAC implementation to use reliable byte counts and line-break-free Base64 encoding:

extension String {
    func hmac(algorithm: HMACAlgorithm, key: String) -> String {
        guard let keyData = key.data(using: .utf8), let messageData = self.data(using: .utf8) else {
            return ""
        }
        var result = [CUnsignedChar](repeating: 0, count: algorithm.digestLength())
        CCHmac(algorithm.toCCHmacAlgorithm(), keyData.bytes, keyData.count, messageData.bytes, messageData.count, &result)
        let hmacData = Data(bytes: result, count: algorithm.digestLength())
        return hmacData.base64EncodedString(options: []) // No line breaks in output
    }

    // Replace deprecated substring method
    func substring(to offset: Int) -> String {
        return String(self.prefix(offset))
    }

    // Keep your existing urlEncoded and other extensions
    var urlEncoded: String {
        let customAllowedSet = CharacterSet(charactersIn: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._~")
        return self.addingPercentEncoding(withAllowedCharacters: customAllowedSet)!
    }

    var urlQueryEncoded: String? {
        return self.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed)
    }
}

2. Update Nonce Generation

Use modern Swift syntax to generate a clean nonce:

public func generateNonce() -> String {
    let uuidString = UUID().uuidString
    return uuidString.prefix(8).lowercased() // Lowercase is optional but aligns with common OAuth practices
}

3. Fix URL Consistency

Update your initial URL constant to match the secure format:

let requestTokenUrl = "https://www.tumblr.com/oauth/request_token"

4. Corrected Login Method

Here's the updated login() method with all fixes applied, plus improved safety checks:

@IBAction func login(_ sender: Any) {
    let oauth_timestamp = String(Int64(Date().timeIntervalSince1970))
    let oauth_nonce = generateNonce()
    
    // Use stored constants instead of hardcoding keys to avoid typos
    let parameters = ["oauth_timestamp": oauth_timestamp,
                      "oauth_callback": "oauth-swift://oauth-callback/tumblr",
                      "oauth_signature_method": "HMAC-SHA1",
                      "oauth_version": "1.0",
                      "oauth_consumer_key": consumerKey,
                      "oauth_nonce": oauth_nonce]
    
    guard let url = URL(string: requestTokenUrl) else {
        print("Invalid request token URL")
        return
    }
    let method = "POST"
    
    let encodedConsumerSecret = consumerSecret.urlEncoded
    let encodedTokenSecret = oauthTokenSecret.urlEncoded
    let signingKey = "\(encodedConsumerSecret)&\(encodedTokenSecret)"
    
    // Sort parameters correctly per OAuth standards
    var parameterComponents = parameters.urlEncodedQuery.components(separatedBy: "&")
    parameterComponents.sort {
        let p0 = $0.components(separatedBy: "=")
        let p1 = $1.components(separatedBy: "=")
        if let key0 = p0.first, let key1 = p1.first {
            if key0 == key1 {
                return p0.last ?? "" < p1.last ?? ""
            }
            return key0 < key1
        }
        return false
    }
    let parameterString = parameterComponents.joined(separator: "&")
    let encodedParameterString = parameterString.urlEncoded
    
    guard let encodedURL = url.absoluteString.urlEncoded else {
        print("Failed to encode URL")
        return
    }
    let signatureBaseString = "\(method)&\(encodedURL)&\(encodedParameterString)"
    
    // Generate corrected signature
    let signature = signatureBaseString.hmac(algorithm: .SHA1, key: signingKey)
    print("signature", signature)
    
    // Build Authorization header with properly encoded values
    let encodedCallback = "oauth-swift://oauth-callback/tumblr".urlEncoded
    let authHeader = "OAuth oauth_callback=\"\(encodedCallback)\", oauth_consumer_key=\"\(consumerKey)\", oauth_nonce=\"\(oauth_nonce)\", oauth_signature=\"\(signature.urlEncoded)\", oauth_signature_method=\"HMAC-SHA1\", oauth_timestamp=\"\(oauth_timestamp)\", oauth_version=\"1.0\""
    
    var request = URLRequest(url: url)
    request.httpMethod = method
    request.setValue(authHeader, forHTTPHeaderField: "Authorization")
    
    let task = URLSession.shared.dataTask(with: request) { data, response, error in
        guard let data = data, error == nil else {
            print("error=\(String(describing: error))")
            return
        }
        
        if let httpStatus = response as? HTTPURLResponse, httpStatus.statusCode != 200 {
            print("statusCode should be 200, but is \(httpStatus.statusCode)")
            print("response = \(String(describing: response))")
        }
        
        let responseString = String(data: data, encoding: .utf8)
        print("responseString = \(String(describing: responseString))")
    }
    task.resume()
}

Final Validation Checks

Before testing, confirm these details:

  • Your consumerKey and consumerSecret exactly match the values in your Tumblr app dashboard (no extra spaces or typos).
  • The oauth_callback value matches exactly what you registered in the Tumblr app settings (including the custom scheme).
  • The oauth_token_secret remains empty for the request token step (this is correct, as you don't have a token yet).

These fixes ensure your signature is generated exactly to Tumblr's OAuth 1.0a specifications, which should resolve the "oauth_signature does not match expected value" error.

内容的提问来源于stack exchange,提问作者Zafar Ahmad

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 08:02:30