如何通过Swift

时间:2019-05-24 03:47:07

标签: ios swift jwt

我尝试创建一个JWT系统。

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

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

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

如果我错了,请帮助我修复代码。

  

jwt.io输出字符串:

     

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJUaXRsZSI6Ik5pY2UiLCJuYW1lIjoiSmltbXkiLCJhZ2UiOjU1fQ.DSdqRFRPM4Hep704s3cvWkpH

enter image description here

  

我的输出字符串:

     

WwogIHsKICAgICJ0eXAiIDogIkpXVCIsCiAgICAiYWxnIiA6ICJIUzI1NiIKICB9Cl0.WwogIHsKICAgICJhZ2UiIDogNTUsCiAgICAibmFtZSIgOiAiSmltbXkiLAogICAgIlRpdGxlIiA6ICJOaWNlIgogIH0KXQ.AhlqiFIcS-ytUKnhazsn7-eYNwgmXfwON7EN2gozRAw

enter image description here

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

1 个答案:

答案 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软件包。只需在该页面上向下滚动即可找到列表。