我正在使用下面的代码生成HMAC-SHA1
签名,但在身份验证请求期间出现此错误:
“oauth_signature与期望值不匹配”
有谁能告诉我如何生成正确的签名?我还检查了oAuthSwift库allHTTPHeaderFields
以获取Tumblr,它适用于此代码。
Tumblr_oAuth-Bridging-Header.h
#import <CommonCrypto/CommonHMAC.h>
和
class ViewController: UIViewController {
let consumerKey = "SpFweFQePdB1I6GnbX2TKCrnD8sObObExERLtw1RBcaEZPFV26"
let consumerSecret = "HCLK7WnNuZM7ohCoSqNixNhCs2YmASfnL8tkYaQvuh9DEj05o3"
let requestTokenUrl = "www.tumblr.com/oauth/request_token"
let authorizeUrl = "https://www.tumblr.com/oauth/authorize"
let accessTokenUrl = "https://www.tumblr.com/oauth/access_token"
let oauthTokenSecret = ""
@IBAction func login(_ sender: Any) {
let oauth_timestamp = String(Int64(Date().timeIntervalSince1970))
let oauth_nonce = generateNonce()//: CFString = CFUUIDCreateString(nil, uuid)
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": "SpFweFQePdB1I6GnbX2TKCrnD8sObObExERLtw1RBcaEZPFV26", "oauth_nonce": "\(oauth_nonce)"]
let url = URL(string:"https://www.tumblr.com/oauth/request_token")
let method = "POST"
let encodedTokenSecret = self.oauthTokenSecret.urlEncoded
let encodedConsumerSecret = self.consumerSecret.urlEncoded
let signingKey = "\(encodedConsumerSecret)&\(encodedTokenSecret)"
print(parameters)
var parameterComponents = parameters.urlEncodedQuery.components(separatedBy: "&")
parameterComponents.sort {
let p0 = $0.components(separatedBy: "=")
let p1 = $1.components(separatedBy: "=")
if p0.first == p1.first { return p0.last ?? "" < p1.last ?? "" }
return p0.first ?? "" < p1.first ?? ""
}
let parameterString = parameterComponents.joined(separator: "&")
let encodedParameterString = parameterString.urlEncoded
let encodedURL = url?.absoluteString.urlEncoded
let signatureBaseString = "\(method)&\(String(describing: encodedURL))&\(encodedParameterString)"
let key = signingKey//.data(using: .utf8)!
let msg = signatureBaseString//.data(using: .utf8)!
let signature : String = msg.hmac(algorithm: HMACAlgorithm.SHA1, key: key)
print("signature",signature)
var request = URLRequest(url: URL(string: "https://www.tumblr.com/oauth/request_token")!)
request.allHTTPHeaderFields = ["Authorization": "OAuth oauth_callback=\"oauth-swift%3A%2F%2Foauth-callback%2Ftumblr\", oauth_consumer_key=\"SpFweFQePdB1I6GnbX2TKCrnD8sObObExERLtw1RBcaEZPFV26\", oauth_nonce=\"\(oauth_nonce)\", oauth_signature=\"\(signature.urlEncoded)\", oauth_signature_method=\"HMAC-SHA1\", oauth_timestamp=\"\(oauth_timestamp)\", oauth_version=\"1.0\""]
request.httpMethod = "POST"
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data, error == nil else { // check for fundamental networking error
print("error=\(String(describing: error))")
return
}
if let httpStatus = response as? HTTPURLResponse, httpStatus.statusCode != 200 { // check for http errors
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()
}
override func viewDidLoad() {
super.viewDidLoad()
}
}
public func generateNonce() -> String {
let uuidString = UUID().uuidString
return uuidString.substring(to: 8)
}
enum HMACAlgorithm {
case MD5, SHA1, SHA224, SHA256, SHA384, SHA512
func toCCHmacAlgorithm() -> CCHmacAlgorithm {
var result: Int = 0
switch self {
case .MD5:
result = kCCHmacAlgMD5
case .SHA1:
result = kCCHmacAlgSHA1
case .SHA224:
result = kCCHmacAlgSHA224
case .SHA256:
result = kCCHmacAlgSHA256
case .SHA384:
result = kCCHmacAlgSHA384
case .SHA512:
result = kCCHmacAlgSHA512
}
return CCHmacAlgorithm(result)
}
func digestLength() -> Int {
var result: CInt = 0
switch self {
case .MD5:
result = CC_MD5_DIGEST_LENGTH
case .SHA1:
result = CC_SHA1_DIGEST_LENGTH
case .SHA224:
result = CC_SHA224_DIGEST_LENGTH
case .SHA256:
result = CC_SHA256_DIGEST_LENGTH
case .SHA384:
result = CC_SHA384_DIGEST_LENGTH
case .SHA512:
result = CC_SHA512_DIGEST_LENGTH
}
return Int(result)
}
}
extension String {
func hmac(algorithm: HMACAlgorithm, key: String) -> String {
let cKey = key.cString(using: String.Encoding.utf8)
let cData = self.cString(using: String.Encoding.utf8)
var result = [CUnsignedChar](repeating: 0, count: Int(algorithm.digestLength()))
CCHmac(algorithm.toCCHmacAlgorithm(), cKey!, strlen(cKey!), cData!, strlen(cData!), &result)
let hmacData:NSData = NSData(bytes: result, length: (Int(algorithm.digestLength())))
let hmacBase64 = hmacData.base64EncodedString(options: NSData.Base64EncodingOptions.lineLength76Characters)// Encoding76CharacterLineLength)
return String(hmacBase64)
}
var urlEncoded: String {
let customAllowedSet = CharacterSet(charactersIn: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._~")
return self.addingPercentEncoding(withAllowedCharacters: customAllowedSet)!
}
var urlQueryEncoded: String? {
return self.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed)
}
func substring(to offset: String.IndexDistance) -> String {
let to = self.index(self.startIndex, offsetBy: offset)
return String(self[..<to])
}
}
extension Dictionary {
var urlEncodedQuery: String {
var parts = [String]()
for (key, value) in self {
let keyString = "\(key)".urlEncoded
let valueString = "\(value)".urlEncoded
let query = "\(keyString)=\(valueString)"
parts.append(query)
}
return parts.joined(separator: "&")
}
}