yourkarma / JWT

A JSON Web Token implementation in Objective-C.
MIT License
351 stars 107 forks source link

Error Domain=io.jwt Code=-97 "Invalid signature!" on RS256 #152

Closed aluco100 closed 6 years ago

aluco100 commented 6 years ago

Hi, I'm using a valid RS256's secret for an access token, but I have an invalid signature by the JWTDecodingBuilder. I write the following code in swift:

let publickey = "-----BEGIN PUBLIC KEY-----..... -----END PUBLIC KEY-----"
let algorithm = JWTAlgorithmNameRS256

let holder = JWTAlgorithmRSFamilyDataHolder().verifyKey(publickey as? JWTCryptoKeyProtocol)!.algorithmName(algorithm)!
let decoding: JWTDecodingBuilder = JWTDecodingBuilder.decodeMessage(user.access_token).addHolder(holder) as! JWTDecodingBuilder
print(decoding.decode.successResult as Any)
print(decoding.decode.errorResult.error as Any)

And I get back this error:

Error Domain=io.jwt Code=-97 "Invalid signature! It seems that signed part of jwt mismatch generated part by algorithm provided in header." UserInfo={NSLocalizedDescription=Invalid signature! It seems that signed part of jwt mismatch generated part by algorithm provided in header., errorDescription=JWTInvalidSignatureError}

I'm using xCode 9 with the last pod version (3.0.0-beta.5). So, what am I doing wrong?

Best Regards

captura de pantalla 2017-10-11 a la s 16 20 14
lolgear commented 6 years ago

@aluco100 well, you shouldn't use exclamation marks in this library in case of unknown nullability modifier. ( It is intended to support iOS prior to 6 version, I don't know if anybody use it. So, we don't put any modern clang items into it for Xcode 5 compatibility. ) (I am not sure about this :))

In fact, correct usage in Swift:

func verify(parameters: [String: AnyObject]) -> Bool {
  // retrieve items from params
  let algorithm = JWTAlgorithmNameRS256

  guard let holder = JWTAlgorithmRSFamilyDataHolder().verifyKey(publickey as? JWTCryptoKeyProtocol)?.algorithmName(algorithm)? else {
   return false
  }

  guard let decoding: JWTDecodingBuilder = JWTDecodingBuilder.decodeMessage(user.access_token).addHolder(holder) as? JWTDecodingBuilder else {
   return false
  }

  print(decoding.decode.successResult as Any)
  print(decoding.decode.errorResult.error as Any)
  return decoding.decode.successResult != nil // or check for errorResult is nil
}

Next, I can't see your key. Library has troubles in retrieving keys from files. Now library has troubles in retrieving keys from strings, according to your post.

Ok, assume, that you have correct key format in string. Please, open JWTCryptoKeySecurity.h and test your key against extraction function.

+ (NSString *)keyFromPemFileContent:(NSString *)content

and I suppose it has swift signature

class func key(fromPemFileContent: String!) -> String!

Your format should be either base64 without PEM headers or PEM-base64 with headers and new lines. ( As PEM-format has new lines alignment for long key strings )

aluco100 commented 6 years ago

My issue it's still occuring. My code is this:

let publickey = "-----BEGIN PUBLIC KEY-----(valid key)-----END PUBLIC KEY-----"
let crypto = JWTCryptoSecurity.key(fromPemFileContent: publickey)
let algorithm = JWTAlgorithmNameRS256
guard let holder = JWTAlgorithmRSFamilyDataHolder().verifyKey(crypto as? JWTCryptoKeyProtocol)?.algorithmName(algorithm) else {
                 reject(NSError(domain: "ahumada", code: 119, userInfo: nil))
                 return
                        }

guard let decoding: JWTDecodingBuilder = JWTDecodingBuilder.decodeMessage(user.access_token).addHolder(holder) as? JWTDecodingBuilder else{
                            reject(NSError(domain: "ahumada", code: 119, userInfo: nil))
                            return
                        }
print(decoding.decode.successResult as Any)
 print(decoding.decode.errorResult.error as Any)

What can be happen?

Best Regards

lolgear commented 6 years ago

@aluco100 Could you post output of extraction function ( in JWTCryptoSecurity )?

class func key(fromPemFileContent: String!) -> String!

As is:

let extractedKey = JWTCryptoKeySecurity.key(yourKey)
print("key is: \(extractedKey)")
aluco100 commented 6 years ago

@lolgear my output is:

MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoPryo3IisfK3a028bwgso/CW5kB84mk6Y7rO76FxJRTWnOAla0Uf0OpIID7go4Qck66yT4/uPpiOQIR0oW0plTekkDP75EG3d/2mtzhiCtELV4F1r9b/InCN5dYYK8USNkKXgjbeVyatdUvCtokz10/ibNZ9qikgKf58qXnn2anGvpE6ded5FOUEukOjr7KSAfD0KDNYWgZcG7HZBxn/3N7ND9D0ATu2vxlJsNGOkH6WL1EmObo/QygBXzuZm5o0N0W15EXpWVbl4Ye7xqPnvc1i2DTKxNUcyhXfDbLw1ee2d9T/WU5895Ko2bQ/O/zPwUSobM3m+fPMW8kp5914kwIDAQAB

Best Regards

lolgear commented 6 years ago

@aluco100 try to set secretData to Data()

I suppose it is the same issue:

149

aluco100 commented 6 years ago

Well, I can use JWTCryptoSecurity.publicKey(fromCertificate: publickey.data(using: .utf8)!) or anything else? @lolgear

Best Regards

lolgear commented 6 years ago

try

guard let holder = JWTAlgorithmRSFamilyDataHolder()
.verifyKey(crypto as? JWTCryptoKeyProtocol)?
.secretData(Data())
.algorithmName(algorithm) 
else {
  reject(NSError(domain: "ahumada", code: 119, userInfo: nil))
  return
}
aluco100 commented 6 years ago

I use this:

let crypto = JWTCryptoSecurity.key(fromPemFileContent: publickey)
let algorithm = JWTAlgorithmNameRS256
guard let holder = JWTAlgorithmRSFamilyDataHolder().signKey(crypto as? JWTCryptoKeyProtocol)?.verifyKey(crypto as? JWTCryptoKeyProtocol)?.secretData(Data())?.algorithmName(algorithm) else {
                            reject(NSError(domain: "ahumada", code: 119, userInfo: nil))
                            return
                        }

guard let decoding: JWTDecodingBuilder = JWTDecodingBuilder.decodeMessage(user.access_token).addHolder(holder) as? JWTDecodingBuilder else{
                            reject(NSError(domain: "ahumada", code: 119, userInfo: nil))
                            return
                        }
print(decoding.decode.successResult as Any)
print(decoding.decode.errorResult.error as Any)

As you said but I'm still getting the same issue. Any idea?

lolgear commented 6 years ago

@aluco100 Could you attach sample swift project with fresh generated keys and test data?

Besides, try to encode and decode any data by the same holder that you posted above. ( If your data comes somewhere outside )

aluco100 commented 6 years ago

there is my sample code:

SampleSignature.zip

Best Regards

lolgear commented 6 years ago

@aluco100 well... Short story is I need time to investigate your issue. Somehow in deep of CryptoKey your key is not properly formed. And after that I have found another issue related or not. I have found that base64 secrets are not well formed. ( So that's is why it is still in beta, heh )

aluco100 commented 6 years ago

Ok, so is it a base 64 bug?

Best Regards

lolgear commented 6 years ago

@aluco100 I've solved this puzzle! :) I could post your project here and if you don't mind, I would like to post your token/secret as a part of JWTDesktop app. ( default setup for rs256 )

The core thing is to use JWTCryptoKeyPublic instead of "abstract" JWTCryptoKey.

aluco100 commented 6 years ago

It's Ok. Can you give me an example to use this?

Best Regards

lolgear commented 6 years ago

@aluco100 Here it is SampleSignature.zip

aluco100 commented 6 years ago

Ok @lolgear that's works perfectly. Thanks a lot for your patience!

davidpasztor commented 6 years ago

I still experience the same issue when encoding a JWT payload, then trying to verify it using an RSA keypair generated with OpenSSL and using RS256 as the algorithm. I use JWT 3.0.0-beta7 through Carthage and I have also tried below code in your SampleSignature project, both yielding the same results. As a starting point I have used the SampleSignature project you included in this thread. I have tried using different keypairs and I always get the same results. What's even more weird is that when I plug the encoded JWT token to jwt.io, it says the signature is verified. However, the JWT framework throws below error

Error Domain=io.jwt Code=-97 "Invalid signature! It seems that signed part of jwt mismatch generated part by algorithm provided in header."

and the Box API also doesn't accept the JWT token, where I would actually use the JWT token for authentication.

The code I used for encoding the JWT payload and verifying can be found below along with the output it produces.

let publickey = """
-----BEGIN PUBLIC KEY-----
MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKk/ZQxuEsWosbTVEGDNRx5touAR2vdW
Y+3hnFbTrFl6KPHrKV3cax5jTMdrzLmV9b7vpAsdOYPKFukRgQIfhNUCAwEAAQ==
-----END PUBLIC KEY-----
"""
let privateKeyString = """
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAoZh3JTinfA2/9NmiDsirluyqsuefd6/etaCLQ9lZVPbvaZJN
RI+vFx8dy8/vVM4YC1Ig8GLbYAU/EBOvYSOuuqIDyGiG7CFWJEZfjz5yJ85SuB2h
7vLXnIhInHYwEhW/cmxRG9BIJGipsw9LvYWtDWk439RlLzqlz6iUP83a13x1JqYO
CTTKkH/LzQTBGjOyexF+0VDKeiBz/fp+xbwxc5nwSmqRQjVKh+/qqGaR8gguSzx9
oKaugGeytiYEDQoOZgk18w+ZdaPj6ChhoEN0WkSNona4Ab2VG4W8cKhmYxzEB4NY
mSXara8XScakjAw8EymOWCB53kS0vm60R1ILUwIDAQABAoIBACrwHkiPo0E9ThI7
eeo/vAhgXoMnNKJS4vST8i1XJXuEGd2NeGaNX1Lfe8VaPRflo1Huwr3dJrl+62DU
OjeviZAh2Kt5ytAXk5IT+PRQ/XhCRIyToyGg+VWoiNK6PM+xY6t9yJeBYon6HY1s
tQ1o5bC4kto2z6QbC1AAVVQW2N2E0mYSnJ6JdUos95NFA6C/qxbobOPuFjl6vP2o
jSu3nfn1WLuFybntBWsgoV8WA98yq/8qAltxDTB1XQznTGYpVUVPwjMKNPIK7iFQ
gOMlCJztBQ/CRg8hXCNEYLNS2GlAeyVJb2Luw1Q7QAZyzWHGA8RN9XFh7eZfzOM8
/BF9kqECgYEA0BdhNEXMQta6a1pVYcIY05vHqk4xGYCkwS2bkDrgN788ra7ILcqW
jKo1pX08jettjI3hqisLQE28d+VgT6xuYLitPuhxaQUOuXhq8ZLZxCpBJgp3i61+
J9WlxT16lPB+p5JsLTz6Oz4E+HcC2K3WqPZSxOkyi1N/lF3Qe9ZacgsCgYEAxsyw
VTQxHFOvTL7TnIhN+RcKgIGrMyRWLNMrs7Svq/GHlqAB7OyRm9ij8m/EY71bSHqN
39Dv82yF7SHqaHhl4bztTfDisfrNM+Z0V5/zpO1lBKzaMEcVmV29zSj0MeI2jYKT
ywFnxlHmzTV0NWlROA0VcgguETtFvXR+1GjyINkCgYA6CFsmfdSySzogGEg5hCzy
Tr6HQ1k0GIlaCoMY3SKMyXt9oIHZK2eDATEG3v9VQPQDP8pQb8Kqg0NtWH3cmmUJ
+lbudVqzhCKQHQIOLvsmVvZmSoL+ZDsLluytLjESemOIDjC3CQtsHCa8uEwF660y
38dDfRmIe5fX47Xx41zD8wKBgQCcz7K70sp2TLB3OgMTcxUGSZ14FlBjq+Lpwryz
KPB1EbThJF/Aci69Htrbjjv8bM4HUtx8UoQjoFPX5CvRsj6zHGKXW06GE44vxTW6
NKpsSvGnCW2mLTwlWKMxpH9SpdxgLULFG9uuvMXw0pxS0TtGmlNzOm6UGwv80FqG
1C1BqQKBgQCWchFf5m90GUGJRVAi4akyYnOKnoQdLbku6sD41le0vSSvN9cs5x2C
e+9vTop0bd1f3N1nzMb1ElZUF50orqoRpkxMp3mEqjZS0qy6WG3NtOB+WrcIiKhL
J2FS/hfBlZfefhuYokJwddiOZtINMnYD3SqoiQjF3ta5jMv7EYbArA==
-----END RSA PRIVATE KEY-----
"""
let payload:[String:Any] = ["iss":"gadsarewrhg12klkj","sub":"43","aud":"https://api.box.com/oauth2/token","exp":Int(Date().addingTimeInterval(59).timeIntervalSince1970),"jti":UUID().uuidString,"box_sub_type": "enterprise"]
let headers = ["alg":"RS256","typ":"jwt","kid":"rewrew21"]

override func viewDidLoad() {
    super.viewDidLoad()

    var crypto: JWTCryptoKeyProtocol? = nil
    do {
        crypto = try JWTCryptoKeyPublic(pemEncoded: publickey, parameters: nil)
    }
    catch {
        NSLog("error: \(error)")
    }

    guard let theCrypto = crypto else {
        return
    }

    do {
        let privateKey = try JWTCryptoKeyPrivate(pemEncoded: privateKeyString, parameters: nil)
        guard let holder = JWTAlgorithmRSFamilyDataHolder().signKey(privateKey)?.secretData(privateKeyString.data(using: .utf8)!)?.algorithmName(JWTAlgorithmNameRS256) else {return}
        guard let encoding = JWTEncodingBuilder.encodePayload(payload).headers(headers)?.addHolder(holder) else {return}
        let result = encoding.result
        print(result?.successResult?.encoded ?? "Encoding failed")
        print(result?.errorResult?.error ?? "No encoding error")
        let verifyDataHolder = JWTAlgorithmRSFamilyDataHolder().signKey(theCrypto)?.secretData(publickey.data(using: .utf8)!)?.algorithmName(JWTAlgorithmNameRS256)
        let verifyResult = JWTDecodingBuilder.decodeMessage(result?.successResult?.encoded).addHolder(verifyDataHolder)?.result
        if verifyResult?.successResult != nil, let result = verifyResult?.successResult.encoded {
            print("Verification successful, result: \(result)")
        } else {
            print("Verification error: \(verifyResult!.errorResult.error)")
        }
    } catch {
        print(error)
    }
}

Output:

eyJhbGciOiJSUzI1NiIsInR5cCI6Imp3dCIsImtpZCI6InYwb2xjbnA2In0.eyJleHAiOjE1MTYxMjIxOTcsImp0aSI6IjIzQjFBNUM3LUQwMjctNDNGNC1BRjkxLTAzNDQ0OUQyRjY2RCIsImlzcyI6ImZyMjNocjdxNWZ1dHV0bGI3MDI4a2M3ZWNxYmV1eXd1IiwiYm94X3N1Yl90eXBlIjoiZW50ZXJwcmlzZSIsImF1ZCI6Imh0dHBzOlwvXC9hcGkuYm94LmNvbVwvb2F1dGgyXC90b2tlbiIsInN1YiI6IjM3MjQ4Njc0In0.g1e4Lu-uf_1opcmP7gbpAkH0LmatiVLS7PgiCHFaACerEZsAS488k4qTD9twi4Ei-dojGvT2exgw-aVjxkj0qk92BXpkN4n4ZSTUqs_qoYiJeX2jytGs0kO-OIhR267hkzphcBh5VHXg4OwI5_6oq-hP3DA6gQRYdSDZtJ2YVlS26bpRURSUq28-4TfX8iaqiiia_0oO2UvtvJanLhSrrVEW5IUabygxVBWYiQhgrVu11L_MzrHPtx3JF_e1n7--SE_tzQ19EBPrOdGt3OHYxmr8scmz3tzgPyfbS61izIlTfCvD0Oe900GnSzdaVUB46t_NJM8uk249moA0sYsHnA No encoding error Verification error: Optional(Error Domain=io.jwt Code=-97 "Invalid signature! It seems that signed part of jwt mismatch generated part by algorithm provided in header." UserInfo={NSLocalizedDescription=Invalid signature! It seems that signed part of jwt mismatch generated part by algorithm provided in header., errorDescription=JWTInvalidSignatureError})

@lolgear Do you have any idea what might go wrong?

lolgear commented 6 years ago

@davidpasztor

let verifyDataHolder = JWTAlgorithmRSFamilyDataHolder().signKey(theCrypto)?.secretData(publickey.data(using: .utf8)!)?.algorithmName(JWTAlgorithmNameRS256)

try to set verifyKey to publicKey and set secretData(Data()) to empty data. (cheats, yes)

let publicKey = JWTCryptoKeyPublic(pemEncoded: publicKeyString, parameters: nil)

Also, check the length of the RSA key. ( Or try to use this key in Objective-C JWTDesktop application to exclude possible crypto-related issues with keys. )

davidpasztor commented 6 years ago

@lolgear sadly setting verifyKey to publicKey and secretData(Data()) doesn't help either.

let verifyDataHolder = JWTAlgorithmRSFamilyDataHolder().signKey(publicKey)?.verifyKey(publicKey)?.secretData(Data())?.algorithmName(JWTAlgorithmNameRS256) produces the same error as before.

What should be the length of the RSA key that I should check for?

Sadly the Obj-C JWTDesktop application also yields above error.

davidpasztor commented 6 years ago

@lolgear Finally managed to solve the issue. It was caused by the lack of the Base64 framework. After importing it to my project, everything started working. However, I have a question and a request about this. Firstly, how can the JWT framework work at all without importing the Base64 framework if that framework is a hard dependency on JWT? Secondly, for the request, it would be appreciated if you could change the readme file and include in the Carthage installation instructions the need for Base64 as a dependency.

lolgear commented 6 years ago

@davidpasztor

  1. I hope that Base64 framework is a weak dependency for JWT.

in Base64Coder:

#if __has_include(<Base64/MF_Base64Additions.h>)
#import <Base64/MF_Base64Additions.h>
#elif __has_include("MF_Base64Additions.h")
#import "MF_Base64Additions.h"
#endif

Also, it has various respondsToSelector to guard unknown selectors invocation ( which are present in Base64 framework )

Yes, Base64 framework has more accurate implementation of Base64 algorithm ( with additional features of base64 url encoding sanitizers ), here could be a hidden problem.

  1. feel free to make a PR and change readme if needed! :)
sarajoel commented 5 years ago

@lolgear

I tried both way to decode on objective C for iOS

    id <JWTCryptoKeyProtocol> publicKey = [[JWTCryptoKeyPublic alloc] 
    initWithPemEncoded:publickey_ parameters:nil error:nil];
    id <JWTAlgorithmDataHolderProtocol> holder = [JWTAlgorithmRSFamilyDataHolder new].verifyKey(publicKey).algorithmName(JWTAlgorithmNameRS256);
    JWTCodingBuilder *verifyBuilder = [JWTDecodingBuilder decodeMessage:token].addHolder(holder);
    JWTCodingResultType *verifyResult = verifyBuilder.result;
    if (verifyResult.successResult){
             NSLog(@"%@ success: %@", self.debugDescription, verifyResult.successResult.payload);
             token = verifyResult.successResult.encoded;
    }
    else 
      {
        NSLog(@"%@ error: %@", self.debugDescription, verifyResult.errorResult.error);
      }`

Also this one too which given in example

          NSString *algorithmName = @"RS256";
         JWTBuilder *decodeBuilder = [JWTBuilder decodeMessage:token].secret(publickey_).algorithmName(algorithmName);
         NSDictionary *envelopedPayload = decodeBuilder.decode;

Both cases I got same error while doing decoding

Key and Token I used same what you had used Here

      Domain=io.jwt Code=-97 "Invalid signature! It seems that signed part of jwt mismatch generated part by algorithm provided in header."

Can you please guide me what I need to do for make it working ? Thank you