我尝试创建一个JWT系统。
但是当我对Base64Url进行标头和有效负载json对象编码时,我遇到了一个问题。我的输出base64UrlString与https://jwt.io/输出字符串不同。
为什么我会得到两个不同的输出字符串?
如果我将输出字符串粘贴到https://jwt.io/编码区域,则会收到错误“无效签名”。
如果我错了,请帮助我修复代码。
jwt.io输出字符串:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJUaXRsZSI6Ik5pY2UiLCJuYW1lIjoiSmltbXkiLCJhZ2UiOjU1fQ.DSdqRFRPM4Hep704s3cvWkpH
我的输出字符串:
WwogIHsKICAgICJ0eXAiIDogIkpXVCIsCiAgICAiYWxnIiA6ICJIUzI1NiIKICB9Cl0.WwogIHsKICAgICJhZ2UiIDogNTUsCiAgICAibmFtZSIgOiAiSmltbXkiLAogICAgIlRpdGxlIiA6ICJOaWNlIgogIH0KXQ.AhlqiFIcS-ytUKnhazsn7-eYNwgmXfwON7EN2gozRAw
class ViewController: UIViewController {
let headerJson: [[String: Any]] = [
[
"alg": "HS256",
"typ": "JWT"
]
]
let payloadJson: [[String: Any]] = [
[
"Title": "Nice",
"name": "Jimmy",
"age": 55
]
]
var base64UrlHeaderString: String = ""
var base64UrlPayloadString: String = ""
override func viewDidLoad() {
super.viewDidLoad()
let headerData = jsonToData(json: headerJson)
let headerString = headerData?.base64EncodedString()
if let headerString = headerString{
let str = base64ToBase64url(base64: headerString)
base64UrlHeaderString = str
print("base64UrlHeaderString : \(base64UrlHeaderString)")
}
let payloadData = jsonToData(json: payloadJson)
let payloadString = payloadData?.base64EncodedString()
if let payloadString = payloadString{
let str = base64ToBase64url(base64: payloadString)
base64UrlPayloadString = str
print("base64UrlPayloadString : \(base64UrlPayloadString)")
}
let totalString: String = base64UrlHeaderString + "." + base64UrlPayloadString
let signature = totalString.hmac(algorithm: .SHA256, key: "hello")
print("signature : \(signature)")
let finalString: String = base64UrlHeaderString + "." + base64UrlPayloadString + "." + signature
print("finalString : \(finalString)")
}
}
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)
}
}
func base64ToBase64url(base64: String) -> String {
let base64url = base64
.replacingOccurrences(of: "+", with: "-")
.replacingOccurrences(of: "/", with: "_")
.replacingOccurrences(of: "=", with: "")
return base64url
}
func jsonToData(json: Any) -> Data? {
do {
return try JSONSerialization.data(withJSONObject: json, options: JSONSerialization.WritingOptions.prettyPrinted)
} catch let myJSONError {
print(myJSONError)
}
return nil
}
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: .lineLength64Characters)
let hmacString = base64ToBase64url(base64: String(hmacBase64))
return hmacString
}
}
答案 0 :(得分:1)
您的输出字符串
WwogIHsKICAgICJ0eXAiIDogIkpXVCIsCiAgICAiYWxnIiA6ICJIUzI1NiIKICB9Cl0.WwogIHsKICAgICJhZ2UiIDogNTUsCiAgICAibmFtZSIgOiAiSmltbXkiLAogICAgIlRpdGxlIiA6ICJOaWNlIgogIH0KXQ.AhlqiFIcS-ytUKnhazsn7-eYNwgmXfwON7EN2gozRAw
包含两个数组 JSON对象(带有签名):
[
{
"typ": "JWT",
"alg": "HS256"
}
]
.
[
{
"age": 55,
"name": "Jimmy",
"Title": "Nice"
}
]
[]
括号用于数组,{}
用于对象,因此,您有两个包含JSON对象的数组,而不仅仅是两个 JSON对象:
{
"typ": "JWT",
"alg": "HS256"
}
.
{
"age": 55,
"name": "Jimmy",
"Title": "Nice"
}
您需要卸下两侧的[]
支架,就像
将其声明为字符串数组,而不是字符串数组。
例如像这样:
let headerJson: [String: Any] = [
"alg": "HS256",
"typ": "JWT"
]
现在您将获得语法上正确的结果,但由于包含换行符和空格,因此它的时间仍比必要的长。
通常,序列化程序会删除所有空格(空格,换行符),但是您可以在代码中使用prettyPrinted
选项:
JSONSerialization.data(withJSONObject: json, options: JSONSerialization.WritingOptions.prettyPrinted)
仅当您要在某处显示JSON时才应使用此选项,对于JWT,请删除该选项:
JSONSerialization.data(withJSONObject: json)
一旦得到的结果已经显示在jwt.io屏幕截图中,则首先将密钥放入右侧的密钥字段中(验证签名),然后将令牌粘贴至左侧。然后您将获得一个经过验证的签名。
虽然您在这里所做的事情当然对学习有好处,但是对于更认真的使用,我建议您在https://jwt.io/上列出的软件包之一,在其中可以找到许多针对许多不同语言(包括Swift)的JWT软件包。只需在该页面上向下滚动即可找到列表。