【问题标题】:How to use Base64Url encode json object like JWT header and payload by Swift如何通过 Swift 使用 Base64Url 编码 json 对象,如 JWT 标头和有效负载
【发布时间】:2019-05-24 03:47:07
【问题描述】:

我尝试创建一个 JWT 系统。

但是当我 Base64Url 对我的标头和有效负载 json 对象进行编码时,我遇到了一个问题。我的输出 base64UrlString 与 https://jwt.io/ 输出字符串不同。

为什么会得到两个不同的输出字符串?

如果我将输出字符串粘贴到https://jwt.io/ 编码区域,我会收到错误“无效签名”。

如果我错了,请帮我修正我的代码。

jwt.io 输出字符串:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJUaXRsZSI6Ik5pY2UiLCJuYW1lIjoiSmltbXkiLCJhZ2UiOjU1fQ.DSdqRFRPM4Hep704s3cvWkpH5FFpnIc82uVUswHbaz4

我的输出字符串:

WwogIHsKICAgICJ0eXAiIDogIkpXVCIsCiAgICAiYWxnIiA6ICJIUzI1NiIKICB9Cl0.WwogIHsKICAgICJhZ2UiIDogNTUsCiAgICAibmFtZSIgOiAiSmltbXkiLAogICAgIlRpdGxlIiA6ICJOaWNlIgogIH0KXQ.AhlqiFIcS-ytUKnhazsn7-eYNwgmXfwON7EN2gozRAw P>

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
    }
}

【问题讨论】:

    标签: ios swift jwt


    【解决方案1】:

    你的输出字符串

    WwogIHsKICAgICJ0eXAiIDogIkpXVCIsCiAgICAiYWxnIiA6ICJIUzI1NiIKICB9Cl0.WwogIHsKICAgICJhZ2UiIDogNTUsCiAgICAibmFtZSIgOiAiSmltbXkiLAogICAgIlRpdGxlIiA6ICJOaWNlIgogIH0KXQ.AhlqiFIcS-ytUKnhazsn7-eYNwgmXfwON7EN2gozRAw P>

    包含两个 数组 JSON 对象(加上签名):

    [
     {
      "typ": "JWT",
      "alg": "HS256"
     }
    ]
    .
    [
     {
      "age": 55,
      "name": "Jimmy",
      "Title": "Nice"
     }
    ]
    

    []brackets 用于数组,{}用于对象,因此您有两个 包含 JSON 对象的数组,而不是只需要两个 JSON 对象

    {
      "typ": "JWT",
      "alg": "HS256"
    }
    .
    {
      "age": 55,
      "name": "Jimmy",
      "Title": "Nice"
    }
    

    您需要删除两侧的一对[]brackets,就像 将其声明为字符串数组,而不是字符串数组。

    例如像这样:

    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/ 上列出的软件包之一,您可以在其中找到许多不同语言的 JWT 软件包,包括 Swift。只需在该页面上向下滚动即可找到该列表。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-08-21
      • 2020-09-05
      • 2017-08-17
      • 2020-08-26
      • 2021-06-01
      • 2017-02-11
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多