JWT 使用 HMACSHA256 验证签名并通过 Swift 显示 "Invalid Signature"
JWT verify signature using HMACSHA256 and show "Invalid Signature" by Swift
我有一个关于 JWT 的问题。
我尝试创建自己的签名并使用硬编码密钥 "hello1234567890987654321test1234"。
然后我使用我的函数创建签名并使用 post 到 https://jwt.io/ 进行解码。
然后这个网页告诉我 "Invalid Signature".
我的 HMACSHA256 函数有什么问题?
我在网页上找到了“-”,“_”,并在我的输出签名中转换了“+”,“/”。
如何修复我的输出签名?
这个签名正确吗?
我也在 Google 中搜索了两个 HMACSHA256 方法。我不知道哪个更好。
请给我一些选择的建议。
谢谢。
图片:
方法一:
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 hmac1(algorithm: CryptoAlgorithm, key: String) -> String {
var result: [CUnsignedChar]
if let ckey = key.cString(using: String.Encoding.utf8), let cdata = self.cString(using: String.Encoding.utf8) {
result = Array(repeating: 0, count: Int(algorithm.digestLength))
CCHmac(algorithm.HMACAlgorithm, ckey, ckey.count-1, cdata, cdata.count-1, &result)
} else {
fatalError("Nil returned when processing input strings as UTF8")
}
return Data(bytes: result, count: result.count).base64EncodedString()
}
}
方法二:
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 hmac2(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: .lineLength76Characters)
return String(hmacBase64)
}
}
用法:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let headerString: String = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9"
let payloadString: String = "eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ"
let totalString: String = headerString + "." + payloadString
let signature1 = totalString.hmac1(algorithm: .SHA256, key: "hello1234567890987654321test1234")
let signature2 = totalString.hmac2(algorithm: .SHA256, key: "hello1234567890987654321test1234")
print("signature1 : \(signature1)") // signature1 : L9YSDasvO2B5i8FZUczC+MAtSsTuM0Dj+FEpfn6uoRs=
print("signature2 : \(signature2)") // signature2 : L9YSDasvO2B5i8FZUczC+MAtSsTuM0Dj+FEpfn6uoRs=
}
}
问题出在编码上。
JWT uses base64url encoding :
A JWT is represented as a sequence of URL-safe parts separated by
period ('.') characters. Each part contains a base64url-encoded
value.
但是您在签名中使用了 base64 编码,如您的代码示例所示。
base64url encoding和base64编码的区别在于,普通base64输出的字符'+'和'/'将被替换为'-'和'_'以及结尾的'=' (padding) 将被省略。
你说:
And I found the "-","_" in webside and convert "+","/" in my output signature.
使用“-”和“_”而不是“+”和“/”,您的输出是正确的。
当你有像这样的 base64url 编码签名时
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.L9YSDasvO2B5i8FZUczC-MAtSsTuM0Dj-FEpfn6uoRs
签名将被验证。
我有一个关于 JWT 的问题。
我尝试创建自己的签名并使用硬编码密钥 "hello1234567890987654321test1234"。
然后我使用我的函数创建签名并使用 post 到 https://jwt.io/ 进行解码。
然后这个网页告诉我 "Invalid Signature".
我的 HMACSHA256 函数有什么问题?
我在网页上找到了“-”,“_”,并在我的输出签名中转换了“+”,“/”。
如何修复我的输出签名?
这个签名正确吗?
我也在 Google 中搜索了两个 HMACSHA256 方法。我不知道哪个更好。
请给我一些选择的建议。
谢谢。
图片:
方法一:
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 hmac1(algorithm: CryptoAlgorithm, key: String) -> String {
var result: [CUnsignedChar]
if let ckey = key.cString(using: String.Encoding.utf8), let cdata = self.cString(using: String.Encoding.utf8) {
result = Array(repeating: 0, count: Int(algorithm.digestLength))
CCHmac(algorithm.HMACAlgorithm, ckey, ckey.count-1, cdata, cdata.count-1, &result)
} else {
fatalError("Nil returned when processing input strings as UTF8")
}
return Data(bytes: result, count: result.count).base64EncodedString()
}
}
方法二:
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 hmac2(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: .lineLength76Characters)
return String(hmacBase64)
}
}
用法:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let headerString: String = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9"
let payloadString: String = "eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ"
let totalString: String = headerString + "." + payloadString
let signature1 = totalString.hmac1(algorithm: .SHA256, key: "hello1234567890987654321test1234")
let signature2 = totalString.hmac2(algorithm: .SHA256, key: "hello1234567890987654321test1234")
print("signature1 : \(signature1)") // signature1 : L9YSDasvO2B5i8FZUczC+MAtSsTuM0Dj+FEpfn6uoRs=
print("signature2 : \(signature2)") // signature2 : L9YSDasvO2B5i8FZUczC+MAtSsTuM0Dj+FEpfn6uoRs=
}
}
问题出在编码上。 JWT uses base64url encoding :
A JWT is represented as a sequence of URL-safe parts separated by period ('.') characters. Each part contains a base64url-encoded value.
但是您在签名中使用了 base64 编码,如您的代码示例所示。
base64url encoding和base64编码的区别在于,普通base64输出的字符'+'和'/'将被替换为'-'和'_'以及结尾的'=' (padding) 将被省略。
你说:
And I found the "-","_" in webside and convert "+","/" in my output signature.
使用“-”和“_”而不是“+”和“/”,您的输出是正确的。
当你有像这样的 base64url 编码签名时
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.L9YSDasvO2B5i8FZUczC-MAtSsTuM0Dj-FEpfn6uoRs
签名将被验证。