'Verify signature of JWT token using iOS swift4

I have the following JWT token which i was able to decode using JWTDecode Cocoapod.

let token = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJEZXYyLVNES1Byb2ZpbGVTZXJ2aWNlIiwiaWF0IjoxNTI0MDQxNzI4LCJuYmYiOjE1MjQwNDE3MjgsImV4cCI6MTUyNjYzMzcyOCwianRpIjoiMzA3MGEyYzJiMDNiNDIwMTljNjc1NjU5NGExZTMyZTEiLCJpZGVudGlmaWVyIjoiMzEyNGJjZTYtYTQ3ZS00OTBjLWFjZmItYThjNjAwODc5NDYxIiwiY3VzdG9tZXIiOiIzNDM5IiwiT2JzZXJ2ZWRUeiI6IkFtZXJpY2EvTmV3X1lvcmsiLCJkZXZpY2VzIjoiW1wiYnBjdWZmXypcIixcIndlaWdodHNjYWxlXypcIixcIip3ZWxjaGFsbHluKlwiXSIsInRhcmdldHMiOiJbe1wiVXJsXCI6XCJodHRwczovL2RldjItdml0YWxzc2VydmljZS5oaG1hZXN0cm8ubmV0XCIsXCJTdGF0dXNcIjp0cnVlLFwiSGVhZGVyc1wiOntcIkF1dGhvcml6YXRpb25cIjpcImV5SmhiR2NpT2lKU1V6STFOaUlzSW5SNWNDSTZJa3BYVkNKOS5leUpwYzNNaU9pSkVaWFl5TFZORVMxQnliMlpwYkdWVFpYSjJhV05sSWl3aWFXRjBJam94TlRJME1EUXhOekk0TENKdVltWWlPakUxTWpRd05ERTNNamdzSW1WNGNDSTZNVFV5TmpZek16Y3lPQ3dpYW5ScElqb2lOR1JpTURZMFpHSTFZMkpqTkRrMFpHRmpOalJtWWpCaVl6ZG1aREJrT1dVaUxDSnBaR1Z1ZEdsbWFXVnlJam9pTXpFeU5HSmpaVFl0WVRRM1pTMDBPVEJqTFdGalptSXRZVGhqTmpBd09EYzVORFl4SWl3aVkzVnpkRzl0WlhJaU9pSXpORE01SWl3aVQySnpaWEoyWldSVWVpSTZJa0Z0WlhKcFkyRXZUbVYzWDFsdmNtc2lmUS5WbVAycm5OdzFiRjhJSVBUTnNQNGVCajJfUVJrRWFuYlpFZmt2NEZ2NjMzSFRlaHNSX010N3FMVUV2TERvQVlEQTA5NmxNbUdTZXA1enhLQkpEYkZwR3VWZmxjUTlCaTJ6MlJfY29SdFktcjU3U19NRVlZQVFORW1XZnRBVVVFZm1MTWFGM0piUHg5V0EwbE41V0hCOWhhcHBjT1NMYXRfcmdwX1pUd1VVd1V5WURDRTF4SXZjNWJockJLaXluM01qNTZlSlB0bEtUZElBZ01YZ29WOGRoSGd3XzZGZXo2dlMwZ2J1djdDMmd1TkNRUFJzRmZvcWFxUEZCNzZtU0VLUWtZUWFlQUlnRW1oSG9wRjRoRXVpcmRIR0JaT0tLYTNNejFvRnNqSjQ2OTVsLUcwX3NFbmV6b3FJSXlYZTN2X3ZsUVhlYmN4eHhfSk5vVjBfTmhDdmdcIn19XSJ9.pDp8cDssRcdB5FA_ykm-c0-g_jPEHPbod252d-bQzpo5PgsKTh4CRFrZ8bt6fam26IMOG_oYcXGZw9NUJowJ_qq5txQXJ7NPeX36Qy77-IpFttEDdAKEvwd6Y3j-hA-BUEzBuHUEPQASAfpFX9gY_ZqJsb6rIsqwi-_hh8vgBJdTODl4_n7vdAW2jtrZvp_BTSTDJ1-ZdJ_U0Oq_11_d5YgmU2s3bee_oVlLRs7o7dGEltbcgVThr4NfL8IVdoZ8H9YiUVeL69mh_LZZ1c7zYLZ4XNMyGSspdBVN8HewnNUD5_f9MGjXDanzX2U8Qc4BlsYd8nxZBSL02OfAkM53Uw"

do{
  let jwt = try decode(jwt: token3)
  print(jwt)
 }

This code using the JWTDecode Library decodes the token successfully. Next step is I have to verify the signature of this token.The public and private key strings are available.

let publicKey = "LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0NCk1JSUJJakFOQmdrcWhraUc5dzBCQVFFRkFBT0NBUThBTUlJQkNnS0NBUUVBcWtuU2dVcVVBZW5kUGR6bU9LSDYNClp1djd1czY4cFlsYWhkc0RHa1BCKzcwd2VSWWZHUUU3Wkl4cWR1WHhwbi80eUhWMDNHZmhKbXFLdktyOTZIWk8NCklkWkp5S3UvQWpkZGFWMGFsTmtwSisxRzRqSlg3RlVhSGplN3pYVk5WOEw2akVpVmpLcUNOMVNkKzc3dmpKNDUNClk5TXNMKzBQOVlGSFRkRVdNUWdjMmUrekRqdjVmc3E0SEpQcUJVU0VybUZRN2puRXVCa1lJemRLaXpFdTluSUwNCmE4a0Q1VVlhT0NUL2xJbEoveTJpbzdMSGJQRmQrTDBTYkREZUtwNGF1WVdsb3dQWkYrWVovKzBxdkRVMml4cUkNCi96bSt3TUlXUFdmanYxOHhEV3hLY2ErN2RBWlJ3bTVGNUxxU2ZGckcxNGhQcjg3RXJyNldXeUtsNkdXMnFhQ0oNCk9RSURBUUFCDQotLS0tLUVORCBQVUJMSUMgS0VZLS0tLS0NCg=="

The privateKey used is

let privateKey = "LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQ0KTUlJRXBBSUJBQUtDQVFFQXFrblNnVXFVQWVuZFBkem1PS0g2WnV2N3VzNjhwWWxhaGRzREdrUEIrNzB3ZVJZZg0KR1FFN1pJeHFkdVh4cG4vNHlIVjAzR2ZoSm1xS3ZLcjk2SFpPSWRaSnlLdS9BamRkYVYwYWxOa3BKKzFHNGpKWA0KN0ZVYUhqZTd6WFZOVjhMNmpFaVZqS3FDTjFTZCs3N3ZqSjQ1WTlNc0wrMFA5WUZIVGRFV01RZ2MyZSt6RGp2NQ0KZnNxNEhKUHFCVVNFcm1GUTdqbkV1QmtZSXpkS2l6RXU5bklMYThrRDVVWWFPQ1QvbElsSi95MmlvN0xIYlBGZA0KK0wwU2JERGVLcDRhdVlXbG93UFpGK1laLyswcXZEVTJpeHFJL3ptK3dNSVdQV2ZqdjE4eERXeEtjYSs3ZEFaUg0Kd201RjVMcVNmRnJHMTRoUHI4N0VycjZXV3lLbDZHVzJxYUNKT1FJREFRQUJBb0lCQVFDbDFHQWZzazJ5RTFsMQ0KWGdJQVVwVHoxNGo3NFVuS2RwamwwMk1SRjd6M2RzU1dsbGxRVTJmUVFnR0hxZU9LdmdLNnk4OHl1Q0tFODZvSg0Ka3diU2N5c2hQbm41NW02TExQbFZtdXBBMjcxOWVVN1hCaW1qSnpqWkJuTm40SHlpSTJrMFpaYmxOa0s2dVRkaw0KS2d0RHgrMmhiY3NSSE8yMnFkK1RRek0yS20xV09NVWlja0RLalhsN0hucmgvSU9FTGh5WTRCZk5oODB0ZU9Ibg0KOFpnclJBNTRLcktJSTAvN1lzR3pDdlBRZ3NCQm1haTdVYm83QlFxR3BZWXZPb0g0d2kzQU45c1VnTkdJWXBWMQ0KdE5BcUdxUWlvRkNXZkVxck8xMHRManBveXE2aXl2WnZpa0FkcjhBQitiejRyK0FYMUIzY3ZlbjZEMVBnekN2Yw0KNEdaTHpUaUJBb0dCQU5ncnFuL2g5Z0luN0owbWNJNEJMTWxmMXpIdVJBNHRTK1BOdzBOVXJyS2JsdDhyUjZrUQ0KOGtqT056MjQrMi9pMUJRa0l5eVM4bzdOTnJHNFoxbnVRaERWN2hiL0tUMXEyK2hYRmIvQWdZUXpYWXU5R2g0Ug0KVUZXdEt2bFBpV0plZUI3TksyYTI2TDNtWEJ4MUE5Y1BmcS91OWpSYXhwT3lqWHJxM0phNDUyU3BBb0dCQU1tcA0KL0FVa3hpaHQ4ZVdibFJMY1VvemxLajI2WHRxQXR5cHNpSHB2ayt0WnA5bVJxV2RYeDIzM3hhOFdkTkJCcXA4UQ0KWGFIRzVjZThuS2NaSWt1S2ZvWXBueEt6WE5RenVRSlh1M3dqZ005Z3V4RFUyNU8vMENNNDJyMjZlUEhJeVdNQQ0KSWxUTkVVVnkyQUZWSkUzTk1mZFNvblVqMjJVRVloeEkrVHpXR2tvUkFvR0JBS3dYNXo2ejF6UFVNT3pUQTF3cA0KMTB2aHZ1SURPNjdGcE5zUW5samwrOFk1VTUwTFNadHc0RkhSeWV5YmJhQ2ZSaE5heVozY3hybWs2ZHdHWUZFWg0KK3dLSUxXbWxiV0YxeHVockcrZHlEQ29BOG9JaTQ3MzRMcXBtbUFXdXFrTGp6bUZIR1R4R2RYZHBBditzc0lmdg0Kei8ya0VlR1FPdkt1ZlMvVDloVVAwemN4QW9HQVp5MmtkeEZBblpEYkVlb1BWSzRMUW5GQnNvRjNaSDQwdU96OA0KeXYvcGc2SEVna25IamN0WWl3Z1pTYUxJczREVmhqcStYVFpCZkhjaEExR1Z2V2FubzRjS0QyeGJrMnEvUHRhYQ0KWTBKYTlqOThsbmtCdTArSmMydjBadHhRWXd5akZSY05lYXZPS1dVLzVUYWxzM1RJR3MxWnQydFlKaEFmRG0rNw0KcllleHZiRUNnWUJxN3JqM05YRjdwSjZnQllMSURWOEttTCtTdnFhbS9KQkN6RjFQMnUvYTFabHd6b0Q3ZVhoWg0KUmd0eStrZ3lPU0pwUTFBZTdPUk1VSlFGRmtiY2VCanluQzdnSXFTTnlxV3B4SjJBTUpBL1pwYXE3WHFoVVZYSQ0KWEx2TVE1RGhoY0U4RG1CY09SYkFKaTg4S1JVZVVVYjdHWlBJa3lmRmYvSURFTlB1a01aZHBBPT0NCi0tLS0tRU5EIFJTQSBQUklWQVRFIEtFWS0tLS0tDQo="

The algorithm used is RSA 256. Can someone guide me on how to verify the signature for the token using the public key.



Solution 1:[1]

Often, JWT libraries provide functionality to verify a JWT's signature.

Anyway, in case you want to do it yourself: The input to a JWT's sign function is the following concatenation: header + "." + payload. So if you want to verify a signature, you need to do that against that concatenation.

Once you have imported your keys as SecKey, e.g. using SecKeyCreateWithData, you should be able to just use iOS's Security framework to verify the signature somewhat like this:

let parts = token.components(separatedBy: ".")

let header = parts[0]
let payload = parts[1]
let signature = Data(base64URLEncoded: parts[2])!

let signingInput = (header + "." + payload).data(using: .ascii)!

SecKeyVerifySignature(publicKey, .rsaSignatureMessagePKCS1v15SHA256, signingInput as CFData, signature as! CFData, nil)

Solution 2:[2]

Accepted answer is good, but you have to remember that base64 string must be correctly encoded when creating Data() object. So complete code is:

let parts = token.components(separatedBy: ".")

let header = parts[0]
let payload = parts[1]
let signature = Data(base64URLEncoded: base64StringWithPadding (base64str:parts[2]))!
let signingInputStr = (header + "." + payload)
let signingInput = signingInputStr.data(using: .ascii)!

SecKeyVerifySignature(publicKey, .rsaSignatureMessagePKCS1v15SHA256, signingInput as CFData, signature as! CFData, nil)

func base64StringWithPadding(base64str: String) -> String {
   var newStr = base64str.replacingOccurrences(of: "-", with: "+")
               .replacingOccurrences(of: "_", with: "/")
   let count = newStr.count % 4
   if count > 0 {
     let amount = 4 - count
     for _ in 0..<amount {
         newStr += "="
     }
   }
   return newStr
}

Also few words about public key. When using text form .pem, inside you have to have something like this:

-----BEGIN PUBLIC KEY-----
key data
-----END PUBLIC KEY-----

and BEGIN and END strings have to be removed. Also remember that when your key is containing not valid base64 chars, you may need to skip them. (NSDataBase64DecodingIgnoreUnknownCharacters)

You can generate keys like this: (on Mac, similarly on other platforms)

ssh-keygen -t rsa -b 4096 -m PEM -f jwtRS256.key

Don't add passphrase

openssl rsa -in jwtRS256.key -pubout -outform PEM -out jwtRS256.key.pub

Also, there is no need to use any library to decode base64 strings. This functionality is already on iOS. Do this: Convert base64 to base64Withpadding, create Data(), create String from data using Ascii encoding, done. (writing this for future self)

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2