JWT - how is signature calculated - encryption

https://jwt.io/ has the following example of a jwt token:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
However if I use http://www.freeformatter.com/hmac-generator.html to do an HMAC SHA 256 signing of:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9
(using the secret key "secret"), it gives me this as the signature:
4c9540f793ab33b13670169bdf444c1eb1c37047f18e861981e14e34587b1e04
What do I do to get the correct signature:
TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

The result you have is hexadecimal. The result you are after is encoded as base64.
You simply need to convert the hex to raw data and then encode that into base64. I didn't check to see if the values were actually the same but this is the most likely problem!

Related

How to handle non-ascii characters in HTTP request header?

In our application, we are sending passwords as part of the header for authentication to our auth service. However, we're running into a situation where users are using non-ascii characters as part of their password, and I found out that non-ascii characters are not supported in HTTP.
What are some approaches to handling this?
You need to encode it in an ASCII compatible format.
Base 64 is such an encoding.
Here is an exemple of how they did it for the HTTP Basic Authentication using Base 64 encoding.
The Authorization field is constructed as follows:
The username and password are combined with a single colon (:). This means that the username itself cannot contain a colon.
The resulting string is encoded into an octet sequence. The character set to use for this encoding is by default unspecified, as long as it is compatible with US-ASCII, but the server may suggest use of UTF-8 by sending the charset parameter.
The resulting string is encoded using a variant of Base64.
The authorization method and a space (e.g. "Basic ") is then prepended to the encoded string.
For example, if the browser uses Aladdin as the username and OpenSesame as the password, then the field's value is the base64-encoding of Aladdin:OpenSesame, or QWxhZGRpbjpPcGVuU2VzYW1l. Then the Authorization header will appear as:
Authorization: Basic QWxhZGRpbjpPcGVuU2VzYW1l
So let's say your password is ǁǂǃDŽDždžLJLjljNJNjnjǍǎǏǐǑǒǓǔǕǖǗǘǙǚǛǜǝǞǟ, which cannot be represented using the ASCII charset.
Here is some pseudo code showing you how to do it
var password = 'ǁǂǃDŽDždžLJLjljNJNjnjǍǎǏǐǑǒǓǔǕǖǗǘǙǚǛǜǝǞǟ'
var base64EncodedPassword = base64Encode(password)
var httpHeader = new HttpHeader('Password', base64EncodedPassword)
And it would results in the following header. Represented using only ASCII char
Password: x4HHgseDx4THhceGx4fHiMeJx4rHi8eMx43HjsePx5DHkceSx5PHlMeVx5bHl8eYx5nHmsebx5zHnceex58=

How to use HMAC-SHA256 Authorization header with Unicode bytes instead of UTF-8?

I'm creating HMAC-SHA256 Authorization header for my rest request.
My hunch is that internally Paw is using UTF-8 (or some other non-Unicode) encoding to calculate the checksum. My server side API uses Unicode to calculate the same thing for comparison but with the same inputs I receive different outputs on each end :(
Is there a way to configure Paw to use Unicode?
For unicode inputs for HMAC-SHA256 you can use the Escape Sequence dynamic value. Choose ``Custom` escape sequence and type your sequence in the input field (\u + code for unicode characters and \x + code for hex bytes).
If this doesn't work for you, don't hesitate ti send us a support e-mail to support#luckymarmot.com

Encrypt for SagePay forms using ColdFusion

I am trying to follow a specification for an encrypted field in SagePay 3.00 using ColdFusion 10.
The requirement is to encrypt the string as AES(block size 128-bit) in CBC mode with PKCS#5 padding using the provided password as both the key and initialisation vector and encode the result in hex.
It's the "using the provided password" that is causing the problem.
At the moment I have
myStr = 'assortednamevaluepairsetc';
providedPassword = 'abcdefghijklmnop';
myCrypt = Encrypt(myStr,providedPassword,'AES/CBC/PKCS5Padding','HEX',providedPassword,1);
but that won't work because the value I have been given by SagePay causes an error - "key specified is not a valid key for this encryption: Invalid AES key length" as its only 16 characters long
According to the CF docs you need to use generateSecretKey to guarantee the key length for AES, so I've tried this but although it gives a result, it's not the right result in terms of the encryption
myStr = 'assortednamevaluepairsetc';
providedPassword = 'abcdefghijklmnop';
mySecret = GenerateSecretKey('AES');
myCrypt = Encrypt(myStr,mySecret,'AES/CBC/PKCS5Padding','HEX',providedPassword,1);
Any help on this gratefully received.
use generateSecretKey to
guarantee the key length for AES
That function is only used when you need to generate a completely new encryption key. You already have one. The primary purpose of generateSecretKey is to ensure you generate a strong encryption key, that is sufficiently random.
won't work because the value I have been given by SagePay causes an
error - "key specified is not a valid key for this encryption: Invalid
AES key length" as its only 16 characters long
A key length of 16 bytes (ie 128 bits) is acceptable for AES. The problem is encrypt() expects the "key" to be a base64 encoded string, which is about thirty-three percent longer than a plain string. When you invoke encrypt(..), CF decodes the provided "key" string into bytes, ie Essentially doing this:
<cfset keyBytes = binaryDecode(yourPassword, "base64")>
<cfoutput>length = #arrayLen(keyBytes)# bytes</cfoutput>
Since your password string is not base64 encoded, the resulting key length is too small, ie (12) instead of (16) bytes. Hence the error message.
The solution is to base64 encode it first. How you do that depends on the encoding of the string. It sounds like it is just a plain text string (hopefully a sufficiently random one...). If so, use charsetDecode to decode the string from the relevant charset (ie utf-8, etcetera), then binaryEncode it to base64:
<cfset keyIVBytes = charsetDecode(yourKeyString, "utf-8")>
<cfset base64Key = binaryEncode(keyIVBytes, "base64")>
Also, the iv parameter should be binary. Since key and iv are one in the same, simply use the byte array from the previous step. Also, drop the iterations parameter, as it does not apply. With those changes it should work as expected:
encrypt(myStr, base64Key,"AES/CBC/PKCS5Padding", "hex", keyIVBytes)
NB: I am not an encryption expert but ... using the key as an iv is NOT a great idea... Might want to check with them to see if there are other options.

'=' symbol at the end shows encryption

I am using 128-bit Rijndael with ECB cipher mode encryption to convert my string. It formats some kind of string with symbols == or = at the end.
In my code I need some preliminary decision was this string encrypted. Can I suggest that if it contains = symbol at the end it is encrypted or possible cases when I will not get = symbol in the end in encrypted string?
First of all, if you are using ECB you are not really "encrypting" since it is a broken cipher mode (see: http://bobnalice.wordpress.com/2009/01/28/friends-don%E2%80%99t-let-friends-use-ecb-mode-encryption/ )
Secondly, the = signs you are seeing are the Base64 padding characters. They are only tagentially related to encryption since Base64 is used for any kind of binary data, not just encrypted date.
Thirdly, you can't even rely on the = sign always being present for Base64 data... it is only added for certain lengths of data (i.e. you could have encrypted, Base64 data that has no = sign)
As #JoelFan points out, those '=' characters are Base64 padding which aren't part of the encryption at all, and aren't always there either if the data comes out to the right number of characters naturally.
You need to add something "out of band". I would replace the potentially-encrypted string with a class which has a string member for the data, and an "encrypted" flag. Once you're there, throw in a getData() method that checks if the instance's data is encrypted, returns the unencrypted value of the data if so (and may cache the plaintext in a private member for later, if it'll be accessed a lot), or just returns the plain text if it's not encrypted.

Are there other encoding methods besides base64 that end in "="?

I've inherited a project where the previous developer is using an ASP object called ActiveCrypt.Crypt to encrypt the users password before sending it to the database.
The call uses the encryptvariant() function with a mode of 7, which the only documentation I can find indicates that the encrpytion is 3DES (company is now defunct). The problem is, that the value derived from the function appears to be a base64-encoded string (the trailing single and double "==" are a dead give-away).
Are there any other encodings that frequently end in "=" or "=="? Is anyone familiar with this ActiveCrypt object? I've tried 3DES encoding the password, with the key, then converting to base64, but with no luck. I've also tried inverting the key and the password in case the developer swapped the arguments. Any help would be appreciated.
Some examples using the key "key" (without quotes)
abcdefg: xiupz3RT148=
123456: iDLXPSPPjd4=
test: AWulSF10FR0=
1234567890: 8I48MAg9YWvE3y52VfMYew==
The encodings you show look like 8 and 16 bytes encoded with normal base64. Base64 encodes 3 bytes using 4 characters. DES and 3DES operate with block size of 8 bytes. So the sizes of the base64 text seem to reflect the block size. Furthermore, the output of the base 64 decoding looks fully random.
So after base64 decoding you will have 8 or 16 bytes, which you then will have to decrypt. The key is of course unknown to us, as is the block mode of operation and the padding mode. So you will have to find out those yourself. If the key is not given, it could be hard coded within the application.
Happy hunting.

Resources