Could not decode JWT payload from base64 - c#

I'm going to decode a JWT token from request header, it looks like this:
eyJzdWIiOiIxIiwiZXZlbnRfaWQiOiI3ZTA3Y2JmNC0wYjYyLTQ1MzMtYmE5ZC1mZGFjNDkyNTNjZTUiLCJpYXQiOiIxNTkwODk4Mzg1IiwiZXhwIjoiMTU5MDkwMTk4NSIsImlzcyI6ImxvY2FsaG9zdDo0NDM4NyIsInRpbWV6b25lX29mZnNldCI6LTcsInVzciI6Im1pbmcuaGlldS4xMzEyIiwiYWxpYXMiOiJNaW5nIEhpZXUiLCJwaG9uZSI6IjA4NDQ1OTAyNTIiLCJlbWFpbCI6ImhpZXVhbWlAZ21haWwuY29tIn0
public static string Base64Decode(string base64EncodedData)
{
var base64EncodedBytes = Convert.FromBase64String(base64EncodedData);
return Encoding.UTF8.GetString(base64EncodedBytes);
}
When passing the token above to decode method, it throws an exception that tells:
Invalid length for a Base-64 char array or string
dotnetfiddle referrence: https://dotnetfiddle.net/Z2TUz9
But when using it in javascript (using atob function), it works properly.
Could anyone tell me why, then tell me how can I decode it in C#?

Before I get into detail about the question, I would generally suggest using a JWT library that can do all that needs to be done with a few function calls.
As a Base64 string, it is indeed too short. A valid Base64 encoded string should have a length that is dividable by 4 and should be padded with 1 or 2 padding characters = if necessary. But JWT uses the slightly different Base64url encoding and in this encoding padding is optional.
But the C# Base64 decoder is quite strict in this regard.
Therefore you need to convert from Base64Url to Base64 and add the missing padding. Besides that, there are also two Base64Url specific characters that need to be converted back to Base64, as shown in the following code:
using System;
public class Program
{
public static void Main()
{
string token = "eyJzdWIiOiIxIiwiZXZlbnRfaWQiOiI3ZTA3Y2JmNC0wYjYyLTQ1MzMtYmE5ZC1mZGFjNDkyNTNjZTUiLCJpYXQiOiIxNTkwODk4Mzg1IiwiZXhwIjoiMTU5MDkwMTk4NSIsImlzcyI6ImxvY2FsaG9zdDo0NDM4NyIsInRpbWV6b25lX29mZnNldCI6LTcsInVzciI6Im1pbmcuaGlldS4xMzEyIiwiYWxpYXMiOiJNaW5nIEhpZXUiLCJwaG9uZSI6IjA4NDQ1OTAyNTIiLCJlbWFpbCI6ImhpZXVhbWlAZ21haWwuY29tIn0";
token = token.Replace('_', '/').Replace('-', '+');
switch (token.Length % 4)
{
case 2: token += "=="; break;
case 3: token += "="; break;
}
var decoded = Convert.FromBase64String(token);
var decodedToken = System.Text.Encoding.Default.GetString(decoded);
Console.WriteLine(decodedToken);
}
}
Code in dotnetfiddle
The datatype of decoded is byte[] and the output would be just the name of the type. To convert it to string and print the JSON I added another line.
The next step from here would be to convert the JSON to a C# object or to use a library as mentioned in the beginning.

You can do it without DRY , this problem can be resolved by
System.IdentityModel.Tokens.Base64UrlEncoder.DecodeBytes(someBase64Url);
or
WebEncoders.Base64UrlDecode(someBase64Url);

Related

POST SHA512 Encrypted String as HTTP Header (in C#)

I've been coming against this for a few hours now and I can't seem to find a solution. I'm trying to encrypt a string using SHA512 and put it in a header for HTTP Request. I know that HTTP Headers only like ASCII characters but everytime I generate the encrypted string I only get nonASCII results, is there any way to force ASCII return? Here's my function generating the encryption:
private static string GenerateSignatureHeader(string data, string timestamp){
//Encode to UTF8 required for SHA512
byte[] encData = ASCIIEncoding.UTF8.GetBytes (data);
byte[] hash;
using (SHA512 shaM = new SHA512Managed ()) {
hash = shaM.ComputeHash (encData);
}
return ASCIIEncoding.Default.GetString (hash);
}
And adding to HTTP Request here:
request.Headers.Add("signature", GenerateSignatureHeader(body, timestamp));
Is what I'm doing here correct? The error is thrown when trying to add the header to the request:
Caused by: md52ce486a14f4bcd95899665e9d932190b.JavaProxyThrowable: System.ArgumentException: invalid header value: 4Ɓj�M�P��hM�$�
�s;��6��1!��,�y��.x;��d�G�↌�2�#1'��1�
Parameter name: headerValue
System.Net.WebHeaderCollection.AddWithoutValidate (string,string)
System.Net.WebHeaderCollection.Add (string,string)
So I'm assuming that it's the nonASCII characters that are causing this?
A hash functions return pseudo-random bytes. Such a byte array very likely doesn't correspond to a valid character encoding such as ASCII. That is why you're getting those unprintable character placeholders.
You need to encode the output. For example using Base64 or Hex.
return System.Convert.ToBase64String(hash);

"Specified value has invalid Control characters" when converting SHA512 output to string

I am attempting to create an Hash for an API.
my input is something like this:
FBN|Web|3QTC0001|RS1|260214133217|000000131127897656
And my expected output is like :
17361DU87HT56F0O9967E34FDFFDFG7UO334665324308667FDGJKD66F9888766DFKKJJR466634HH6566734JHJH34766734NMBBN463499876554234343432456
I tried the bellow but I keep getting
"Specified value has invalid Control characters. Parameter name: value"
I am actually doing this in a REST service.
public static string GetHash(string text)
{
string hash = "";
SHA512 alg = SHA512.Create();
byte[] result = alg.ComputeHash(Encoding.UTF8.GetBytes(text));
hash = Encoding.UTF8.GetString(result);
return hash;
}
What am I missing?
The problem is Encoding.UTF8.GetString(result) as the data in result is invalid UTF-8 (it's just binary goo!) so trying to convert it to text is invalid - in general, and specifically for this input - which results in the Exception being thrown.
Instead, convert the byte[] to the hex representation of said byte sequence; don't treat it as UTF-8 encoded text.
See the questions How do you convert Byte Array to Hexadecimal String, and vice versa? and How can I convert a hex string to a byte array?, which discuss several different methods of achieving this task.
In order to make this work you need to convert the individual byte elements into a hex representation
var builder = new StringBuilder();
foreach(var b in result) {
builder.AppendFormat("{0:X2}", b);
}
return builder.ToString();
You might want to consider using Base64 encoding (AKA UUEncode):
public static string GetHash(string text)
{
SHA512 alg = SHA512.Create();
byte[] result = alg.ComputeHash(Encoding.UTF8.GetBytes(text));
return Convert.ToBase64String(result);
}
For your example string, the result is
OJgzW5JdC1IMdVfC0dH98J8tIIlbUgkNtZLmOZsjg9H0wRmwd02tT0Bh/uTOw/Zs+sgaImQD3hh0MlzVbqWXZg==
It has an advantage of being more compact than encoding each byte into two characters: three bytes takes four characters with Base64 encoding or six characters the other way.

string to byte array (to string to XML) and back again

i know there are 1million questions about "string - byte array" conversion out there but none of them fit my problem.
For the installation of my software i need to save some informations from the user (serveraddress, userID, password and so on). Some of these informations need do be protected (encrypted using DPAPI). For that i have to convert the string (SecureString) to byte[]
public static byte[] StringToByte(string s)
{
return Convert.FromBase64String(s);
}
where i get my first problem. If the strings lenght is a not a multiple of 4 (s.lenght % 4 == 0) i get a "Invalid length for a Base-64 char array" error. I've read that i can (have to) add "=" to the end of the string but some of these strings may be passwords (which may contain "="). I need to store the (encrypted) data in a XML-file why i can't use Unicode encoding (i don't know why but it corrupts the XML file ... because of encoding i would suppose).
As last step i have to go back the way to get the stored data on app startup.
Does someone of you can help me solving this problem ? I don't care the output in the XML as long as it is "readable".
best regards Alex
where i get my first problem. If the strings lenght is a not a multiple of 4 (s.lenght % 4 == 0) i get a "Invalid length for a Base-64 char array" error.
That suggests that it's not base64 to start with. It sounds like you're going in the wrong direction here - base64 is used to convert binary data into text. To convert text into a binary form, you should normally just use Encoding.GetBytes:
return Encoding.UTF8.GetBytes(text);
Now if you needed to encode the result of the encryption (which will be binary data) as text, then you'd use base64. (Because the result of encrypting UTF-8-encoded text is not UTF-8-encoded text.)
So something like:
public static string EncryptText(string input)
{
byte[] unencryptedBytes = Encoding.UTF8.GetBytes(input);
byte[] encryptedBytes = EncryptBytes(unencryptedBytes); // Not shown here
return Convert.ToBase64String(encryptedBytes);
}
public static string DecryptText(string input)
{
byte[] encryptedBytes = Convert.FromBase64String(input);
byte[] unencryptedBytes = DecryptBytes(encryptedBytes); // Not shown here
return Encoding.UTF8.GetString(unencryptedBytes);
}

Hashing Query String containing Special Characters not working

I have posted few questions about Tokens and Password reset and have managed to finally figure this all out. Thanks everyone!
So before reading that certain characters will not work in a query string, I decided to hash the query string but as you've guessed, the plus signs are stripped out.
How do you secure or hash a query string?
This is a sample from a company email I received and the string looks like this:
AweVZe-LujIAuh8i9HiXMCNDIRXfSZYv14o4KX0KywJAGlLklGC1hSw-bJWCYfia-pkBbessPNKtQQ&t=pr&ifl
In my setup, I am simply using a GUID. But does it matter?
In my scenario the user cannot access the password page, even without a GIUD. That's because the page is set to redirect onload if the query string don't match the session variable?
Are there ways to handle query string to give the result like above?
This question is more about acquiring knowledge.
UPDATE:
Here is the Hash Code:
public static string QueryStringHash(string input)
{
byte[] inputBytes = Encoding.UTF8.GetBytes();
SHA512Managed sha512 = new SHA512Managed();
byte[] outputBytes = sha512.ComputeHash(inputBytes);
return Convert.ToBase64String(outputBytes);
}
Then I pass the HASH (UserID) to a SESSION before sending it as a query string:
On the next page, the Session HASH is not the same as the Query which cause the values not to match and rendered the query string invalid.
Note: I created a Class called Encryption that handles all the Hash and Encryption.
Session["QueryString"] = Encryption.QueryStringHash(UserID);
Response.Redirect("~/public/reset-password.aspx?uprl=" +
HttpUtility.UrlEncode(Session["QueryString"].ToString()));
I also tried everything mentioned on this page but no luck:
How do I replace all the spaces with %20 in C#
Thanks for reading.
The problem is that base64 encoding uses the '+' and '/' characters, which have special meaning in URLs. If you want to base64 encode query parameters, you have to change those characters. Typically, that's done by replacing the '+' and '/' with '-' and '_' (dash and underscore), respectively, as specified in RFC 4648.
In your code, then, you'd do this:
public static string QueryStringHash(string input)
{
byte[] inputBytes = Encoding.UTF8.GetBytes();
SHA512Managed sha512 = new SHA512Managed();
byte[] outputBytes = sha512.ComputeHash(inputBytes);
string b64 = Convert.ToBase64String(outputBytes);
b64 = b64.Replace('+', '-');
return b64.Replace('/', '_');
}
On the receiving end, of course, you'll need to replace the '-' and '_' with the corresponding '+' and '/' before calling the method to convert from base 64.
They recommend not using the pad character ('='), but if you do, it should be URL encoded. There's no need to communicate the pad character if you always know how long your encoded strings are. You can add the required pad characters on the receiving end. But if you can have variable length strings, then you'll need the pad character.
Any time you see base 64 encoding used in query parameters, this is how it's done. It's all over the place, perhaps most commonly in YouTube video IDs.
I did something before where I had to pass a hash in a query string. As you've experienced Base 64 can be pretty nasty when mixed with URLs so I decided to pass it as a hex string instead. Its a little longer, but much easier to deal with. Here is how I did it:
First a method to transform binary into a hex string.
private static string GetHexFromData(byte[] bytes)
{
var output = new StringBuilder();
foreach (var b in bytes)
{
output.Append(b.ToString("X2"));
}
return output.ToString();
}
Then a reverse to convert a hex string back to binary.
private static byte[] GetDataFromHex(string hex)
{
var bytes = new List<byte>();
for (int i = 0; i < hex.Length; i += 2)
{
bytes.Add((byte)int.Parse(hex.Substring(i, 2), System.Globalization.NumberStyles.HexNumber));
}
return bytes.ToArray();
}
Alternatively if you just need to verify the hashes are the same, just convert both to hex strings and compare the strings (case-insensitive). hope this helps.

Invalid length for a Base-64 char array

I'm getting a "Invalid length for a Base-64 char array." inside of the IF(){...} are variations i have tried to get it to work. it fails in the first line without calling decrypt(...) proving it's not that functions problem. i get the same error inside with the first decrypt(...) call. the last one using the encoding.ascii... will get me inside the function, but then it fails inside the function. I'm getting the proper encrypted info from the database to string SSnum. it's value is: 4+mFeTp3tPF
try
{
string SSnum = dr.GetString(dr.GetOrdinal("Social Security"));
if (isEncrypted)
{
byte[] temp = Convert.FromBase64String(SSnum);
//SSnum = decrypt(Convert.FromBase64String(SSnum), Key, IV);
//SSnum = decrypt(Encoding.ASCII.GetBytes(SSnum), Key, IV);
}
txt_Social_Security.Text = SSnum;
}
catch { txt_Social_Security.Text = ""; }
I've been told to use the Convert.FromBase64String() and not the ASCII method...so why is it failing, how can i fix it?
Base64 data length should be multiple of 4 and with padding char '='
You can change your data as valid base64 data.
string dummyData = imgData.Trim().Replace(" ", "+");
if (dummyData.Length % 4 > 0)
dummyData = dummyData.PadRight(dummyData.Length + 4 - dummyData.Length % 4, '=');
byte[] byteArray = Convert.FromBase64String(dummyData);
https://stackoverflow.com/a/9301545/2024022
This will help you , try once.
Thanks
suribabu.
it's value is: 4+mFeTp3tPF
You are receiving this error because that value, 4+mFeTp3tPF, is in fact not valid Base64.
Is it possible you are simply missing the required padding character, as so 4+mFeTp3tPF=?
Are you certain that you have a Base64 string? Base64 is a means of encoding binary data into a string while only using standard 7-bit ASCII characters. It's not a string encoding like ASCII and has some control bytes present. You have a Base64 string if you're using Convert.ToBase64String to obtain the value (which, if you're trying to store binary data as a string, is your best bet)
Judging by your error (and your example data), I'm assuming that you do not have a Base64 string. If you need to store binary data in the database, you can either create a column using a binary type or encode the string into Base64 by using Convert.ToBase64String.
byte[] inputData = ...;
string base64String = Convert.ToBase64String(inputData);
byte[] outputData = Convert.FromBase64String(base64String);
Here, outputData should contain the same data as inputData.
If what you have is just an ASCII-encoded string, then your original practice of using System.Text.Encoding.ASCII.GetBytes() is correct, but you should change this to use a Base64 string if you can.
Are you sure that string 4+mFeTp3tPF is well-formed Base64 string?
I've tried some online services - no one could convert it.
replace
byte[] temp = Convert.FromBase64String(SSnum);
to this
var temp = UTF8Encoding.UTF8.GetBytes(SSnum);

Categories