Keep getting "bad signature" from parsing #10
-
I'm doing some test locally After my client logs in, and the backend generate
The auth middleware I have to check if key/token valid:
This is where I keep getting Can anyone help me here? One quick note: I notice two hex values are different between using
and
But I have tried both public keys to verify and both give me |
Beta Was this translation helpful? Give feedback.
Replies: 21 comments
-
In the step:
Are you importing a truly random set of 64 bytes, or is this a previously exported secret key that was generated? |
Beta Was this translation helpful? Give feedback.
-
Oh, sorry, I should be more clear. I already have a hard-coded 64 characters in my .env file, and I just use os.Getenv() |
Beta Was this translation helpful? Give feedback.
-
Was this a previously generated key that was exported, or is this a set of 64 bytes that you're wanting to use as a key? |
Beta Was this translation helpful? Give feedback.
-
I'm not sure if I understand you correctly. It's a pre-generated key and it exists in
Because it's a string so I convert it to byte as Don't worry about the key exposed here, it's not a real one |
Beta Was this translation helpful? Give feedback.
-
The reason I ask is because an ed25519 key (at least in Go's private format) can't just be an arbitrary string of characters. The 64 bytes consist of a seed value (which is generally random) and a public key (which is mathematically related to the seed). If you pass in an arbitrary string of bytes/characters here, the public portion of the key won't be mathematically related to the seed, and so it won't be possible to validate signatures which were built on this value. If you're wanting to pass in your own random bytes, you can use the seed constructors which accept 32 bytes to use as the seed (these will calculate the the public key/rest of the private key for you). I'd also caution against using a string of printable characters even using the seed constructor in anything other than testing, because it'll significantly reduce the security of signatures by restricting the seed to only bytes which happen to be printable characters (instead it is better to import as hex if you need to store the key in a text file, so you can retain the full range of the passed in bytes). Also, you've mentioned a symmetric key in the above, so just wanted to point out that we're currently discussing the asymmetric PASETO mode (which signs but does not encrypt the contents of the token). If you're actually wanting to use a symmetric key, "local" mode is suited for that (and will encrypt the token) 🙂 |
Beta Was this translation helpful? Give feedback.
-
I see. Thank you so much for the detailed explanation. I will test it out later again and keep you posted! |
Beta Was this translation helpful? Give feedback.
-
Did you manage to get this working? I'm debating introspecting the private key to try to catch ones that embed a public key that is incorrect (which would catch this kind of issue). |
Beta Was this translation helpful? Give feedback.
-
I'm sorry I haven't tried it yet. It's been crazy busy on my end. I will try to find a time on Thursday night or the weekend. The latest would be the weekend, and I will keep you posted. |
Beta Was this translation helpful? Give feedback.
-
No rush at all 🙂 |
Beta Was this translation helpful? Give feedback.
-
Alright, I found some time to test it, and unfortunately, it's still not working. I listened to your suggestions. I converted the string to hex first before I do the rest of the code. Let me show you what I have tried.
And I still get A side question, in your example in README, you have Hopefully this makes sense. Please let me know if you have any questions! |
Beta Was this translation helpful? Give feedback.
-
The value that you're using is still not a valid ed25519 private key 😄 The reason I suggested using hex was so that you could have a convenient string representation of the key in your file, but you still need the underlying value to be a valid key. Hope that makes sense! If you need help generating a new valid private key, you can run this once and save the output to use as a key later: paseto.NewV4AsymmetricSecretKey().ExportHex()
Unless you need the client side to be able to inspect the values inside the token, or a party other than your server to be able to verify your token (which doesn't seem to be the case here?), I'd recommend instead using the symmetric (aka "local") PASETO mode. For asymmetric modes, whilst the public key can be known by others, you still need the public key to be trustworthy (i.e. you get it directly from the service you expect to have signed the token, rather than consuming it at the same time as the token that you are verifying). The setup you've described would actually allow users to generate their own tokens using whatever fields they like, so long as they generate their own keypair and give you their public key. To use an analogy: imagine that someone gave you a signed document and you wanted to make sure it was correct; instead of checking your own records to verify the signature, you asked the person that handed you this document to also tell you whether or not the signature was correct. If they wanted to trick you, they could simply say it was correct 😄 When you consume the public key (i.e. the thing that verifies the token) from the same place as you get the token, you're essentially doing this with the token. |
Beta Was this translation helpful? Give feedback.
-
I finally understand what you meant haha, sorry I was confused by myself. I have tested
And I sent the |
Beta Was this translation helpful? Give feedback.
-
The function you're looking for is I named the methods Possibly these names are now over generalised though, and might help if I renamed these to |
Beta Was this translation helpful? Give feedback.
-
Got it got it. However, I have a bit question about the arguments passing Sorry if I bring lots of questions, I'm still learning this new encrypted solution. |
Beta Was this translation helpful? Give feedback.
-
Yes, the token should be the string
No problem at all, it is useful for me to understand where there may be gaps in my documentation 🙂 |
Beta Was this translation helpful? Give feedback.
-
Hmmmm, interesting, doesn't that mean I will have to store the symmetric key into the database first? And when it needs to verify, I will have to query the key corresponding to the user in the database?
Once I figure this out, I will have think a logic to do refresh token |
Beta Was this translation helpful? Give feedback.
-
You'd need to store the symmetric key you use, yes, but this is true of both modes. The difference between the symmetric vs. asymmetric approach is that you only need to deal with one type of key in the symmetric approach (the same key is used to mint new tokens as is to verify them), whereas asymmetric you have one key to mint and another to verify. The key doesn't necessarily need to be per user, it could be set at the application level instead (e.g. set it through an environment variable). Equally you could go so far as to generate a new key for each token you issue, so long as you have a means to store that key and a sensible way to look it up when it comes to verify the token; a key per user falls somewhere in the middle of these extremes. How you set this up has different security and operational trade-offs, and depending on your use case you may decide a different option is the better suited 🙂 |
Beta Was this translation helpful? Give feedback.
-
I figured out what you meant but I do have a quick question. I'm doing the key as an environment variable as you suggested, but wouldn't another user overwrite the value when the user signs in? |
Beta Was this translation helpful? Give feedback.
-
The decision to make here is how many keys you want to manage operationally. If using an environment variable, the approach would be to generate a key once for the app to use, and then to keep using that same key both to mint new tokens and to verify incoming ones. If you wanted more granularity (such as a key per user), then you'd need a different approach to storing the keys than an environment variable (such as using a KMS or DB). More granular keys can offer advantages (e.g. if you had a key per user, you'd be able to revoke all tokens for a user by deleting and regenerating their key), but are operationally it can be more difficult to manage than a single key. |
Beta Was this translation helpful? Give feedback.
-
Thank you very much!! I have decided to use the granularity way (key for each user in DB) because later on, I will need a refresh token from the DB too (I have some idea, but not sure if it will work). I have learned so much from you, really appreciated it!! If you need me to close the question, please let me know. I might need to ask you about refresh token later ;P |
Beta Was this translation helpful? Give feedback.
-
Happy to help! 🙂 I'll convert this issue to a discussion, so feel free to add on if you have anything else |
Beta Was this translation helpful? Give feedback.
The value that you're using is still not a valid ed25519 private key 😄 The reason I suggested using hex was so that you could have a convenient string representation of the key in your file, but you still need the underlying value to be a valid key. Hope that makes sense!
If you need help generating a new valid private key, you can run this once and save the output to use as a key later: