HMAC SHA256 JWT签名不正确

时间:2016-07-05 09:32:21

标签: ios swift cryptography

答案:

而不是下面的hmac func是我现在使用的函数......

func base64Encoded(algorithm: CryptoAlgorithm, key: String) -> String {
  let hmac = self.hmac(algorithm: algorithm, key: key)
  let digestLen = algorithm.digestLength
  let dataResult = NSData(bytes: hmac, length: digestLen)
  hmac.deallocateCapacity(digestLen)

  return dataResult.base64EncodedString()
}

func hash(algorithm: CryptoAlgorithm, key: String) -> String {
  let hmac = self.hmac(algorithm: algorithm, key: key)
  let digestLen = algorithm.digestLength
  let hash = NSMutableString()

  for i in 0..<digestLen {
    hash.appendFormat("%02x", hmac[i])
  }

  hmac.deallocateCapacity(digestLen)

  return hash as String 
}

func hmac(algorithm: CryptoAlgorithm, key: String) -> UnsafeMutablePointer<CUnsignedChar> {
  let str = self.cString(using: String.Encoding.utf8)
  let strLen = Int(self.lengthOfBytes(using: String.Encoding.utf8))
  let digestLen = algorithm.digestLength

  let result = UnsafeMutablePointer<CUnsignedChar>(allocatingCapacity: digestLen)

  let keyStr = key.cString(using: String.Encoding.utf8)
  let keyLen = Int(key.lengthOfBytes(using: String.Encoding.utf8))

  CCHmac(algorithm.HMACAlgorithm, keyStr!, keyLen, str!, strLen, result)

  return result
}

原始帖子

我有一个JWT,我正在尝试验证签名。这是JWT ......

  

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJhcGkudGVzdC5jb20vdjEvYXV0aCIsImV4cCI6MTQ2OTk3ODQ5OCwic3ViIjoiMTIzNDU2Nzg5MCIsImVtYWlsIjoidGVzdEB0ZXN0LmNvbSIsInJvbGVzIjpbImFkbWluIiwiY3VzdG9tZXIiXSwicGVybWlzc2lvbnMiOlsidGVzdC5wcm9maWxlIiwidGVzdC5wcm9maWxlLmNvbnRhY3QiLCJ0ZXN0LnByb2ZpbGUuZGV2aWNlIiwidGVzdC5wcm9maWxlLmFwcCJdfQ.GfLxXOL978Pm5GYMI0WTBEVcMrfVj2jJb-Il_XzO7g4

我在Swift 3工作,我更新了这个SO答案https://stackoverflow.com/a/24411522/741626中的方法。这就是那些方法现在的样子。

import Foundation

enum CryptoAlgorithm {
  case MD5, SHA1, SHA224, SHA256, SHA384, SHA512

    var HMACAlgorithm: 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)
}

var digestLength: Int {
  var result: Int32 = 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: CryptoAlgorithm, key: String) -> String {
  let str = self.cString(using: String.Encoding.utf8)
  let strLen = Int(self.lengthOfBytes(using: String.Encoding.utf8))
  let digestLen = algorithm.digestLength

  let result = UnsafeMutablePointer<CUnsignedChar>(allocatingCapacity: digestLen)

  let keyStr = key.cString(using: String.Encoding.utf8)
  let keyLen = Int(key.lengthOfBytes(using: String.Encoding.utf8))

  CCHmac(algorithm.HMACAlgorithm, keyStr!, keyLen, str!, strLen, result)

  let hash = NSMutableString()
  for i in 0..<digestLen {
    hash.appendFormat("%02x", result[i])
  }

  result.deallocateCapacity(digestLen)

  return hash as String

}

我能够base64Decode标题&amp;有效载荷成功,但当我尝试验证签名时,它总是错误的(看起来不像是太长了)。

我尝试了什么。

1 我已经尝试了几种JWT - 总是以同样的方式出错

2 我已经对sha256常量进行了硬编码,以确保我没有使用错误的编码/长度

3 我尝试了很多类型的String.Encoding,但是它们总是按预期生成不同的结果,但它们都没有生成所需的签名。

4 我已经使用Objective-C方法生成hmac以尝试排除我转换为Swift 3是否破坏了任何内容。相同的结果,这是Objective C代码。

+ (NSData *)hmacSha256:(NSString *)string key:(NSString *)key;
{
  NSData *dataIn = [string dataUsingEncoding:NSUTF8StringEncoding];
  NSData *keyIn = [key dataUsingEncoding:NSUTF8StringEncoding];
  NSMutableData *macOut = [NSMutableData dataWithLength:CC_SHA256_DIGEST_LENGTH];

  CCHmac( kCCHmacAlgSHA256,
       keyIn.bytes,
       keyIn.length,
       dataIn.bytes,
       dataIn.length,
       macOut.mutableBytes);

  return macOut;
}

代码来自:https://stackoverflow.com/a/31003443/741626。我宁愿不将Objective-C代码插入到我的Swift项目中,但如果必须的话,我会这样做!

这是我的调用函数

func decodeToken(token: String) {
  let array = token.characters.split(isSeparator: { $0 == "." })
.map(String.init)

  let header:String = String(array[0])
  let payload:String = String(array[1])
  let signature:String = String(array[2])

  let encodedString = header + "." + payload
  let hmac = encodedString.hmac(algorithm: .SHA256, key: "")
}

修改

当我运行代码时,生成的hmac是

  

19f2f15ce2fdefc3e6e4660c23459304455c32b7d58f68c96fe225fd7cceee0e

我已经三次检查秘密是否正确,这是“”(空字符串)

我做错了什么?

我真的想在我的客户端应用程序中测试我收到的令牌是值得信赖的而不是掩盖这一点。如果有人有任何想法我会做错的那将是伟大的。

1 个答案:

答案 0 :(得分:3)

实际上,您生成的值是正确的。只是你正在查看HMAC值的十六进制表示(以及哈希函数),而JWT哈希当然是base64url编码。

要验证散列,比较字节值而不是编码值很重要。编码值仅供人类消费(十六进制)或通过需要文本的协议(base64url)传输。

执行字节数组比较时,请确保它是时间常数,否则可能会引入漏洞。

一种方法是对字节进行异或,然后将结果OR转换为结果字节。然后针对00h测试结果字节(任何其他值表示无效比较)。首先拒绝不正确大小的数组。