Invalid length for a Base-64 char array - c#

As the title says, I am getting:
Invalid length for a Base-64 char
array.
I have read about this problem on here and it seems that the
suggestion is to store ViewState in SQL if it is large. I am
using a wizard with a good deal of data collection so chances
are my ViewState is large. But, before I turn to the "store-in-DB"
solution, maybe somebody can take a look and tell me if I have
other options?
I construct the email for delivery using the below method:
public void SendEmailAddressVerificationEmail(string userName, string to)
{
string msg = "Please click on the link below or paste it into a browser to verify your email account.<BR><BR>" +
"<a href=\"" + _configuration.RootURL + "Accounts/VerifyEmail.aspx?a=" +
userName.Encrypt("verify") + "\">" +
_configuration.RootURL + "Accounts/VerifyEmail.aspx?a=" +
userName.Encrypt("verify") + "</a>";
SendEmail(to, "", "", "Account created! Email verification required.", msg);
}
The Encrypt method looks like this:
public static string Encrypt(string clearText, string Password)
{
byte[] clearBytes = System.Text.Encoding.Unicode.GetBytes(clearText);
PasswordDeriveBytes pdb = new PasswordDeriveBytes(Password, new byte[] { 0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76 });
byte[] encryptedData = Encrypt(clearBytes, pdb.GetBytes(32), pdb.GetBytes(16));
return Convert.ToBase64String(encryptedData);
}
Here is what the HTML looks like in hotmail:
Please click on the link below or
paste it into a browser to verify your
email account.
http://localhost:1563/Accounts/VerifyEmail.aspx?a=YOHY57xYRENEOu3H+FGq1Rf09AZAI56EPjfwuK8XWKg=
On the receiving end, the VerifyEmail.aspx.cs page has the line:
string username = Cryptography.Decrypt(_webContext.UserNameToVerify, "verify");
Here is the getter for UserNameToVerify:
public string UserNameToVerify
{
get
{
return GetQueryStringValue("a").ToString();
}
}
And here is the GetQueryStringValue method:
private static string GetQueryStringValue(string key)
{
return HttpContext.Current.Request.QueryString.Get(key);
}
And the decrypt method looks like:
public static string Decrypt(string cipherText, string password)
{
**// THE ERROR IS THROWN HERE!!**
byte[] cipherBytes = Convert.FromBase64String(cipherText);
Can this error be remedied with a code fix or must I store ViewState in the database?

The length of a base64 encoded string is always a multiple of 4. If it is not a multiple of 4, then = characters are appended until it is. A query string of the form ?name=value has problems when the value contains = charaters (some of them will be dropped, I don't recall the exact behavior). You may be able to get away with appending the right number of = characters before doing the base64 decode.
Edit 1
You may find that the value of UserNameToVerify has had "+"'s changed to " "'s so you may need to do something like so:
a = a.Replace(" ", "+");
This should get the length right;
int mod4 = a.Length % 4;
if (mod4 > 0 )
{
a += new string('=', 4 - mod4);
}
Of course calling UrlEncode (as in LukeH's answer) should make this all moot.

My guess is that you simply need to URL-encode your Base64 string when you include it in the querystring.
Base64 encoding uses some characters which must be encoded if they're part of a querystring (namely + and /, and maybe = too). If the string isn't correctly encoded then you won't be able to decode it successfully at the other end, hence the errors.
You can use the HttpUtility.UrlEncode method to encode your Base64 string:
string msg = "Please click on the link below or paste it into a browser "
+ "to verify your email account.<br /><br /><a href=\""
+ _configuration.RootURL + "Accounts/VerifyEmail.aspx?a="
+ HttpUtility.UrlEncode(userName.Encrypt("verify")) + "\">"
+ _configuration.RootURL + "Accounts/VerifyEmail.aspx?a="
+ HttpUtility.UrlEncode(userName.Encrypt("verify")) + "</a>";

I'm not Reputable enough to upvote or comment yet, but LukeH's answer was spot on for me.
As AES encryption is the standard to use now, it produces a base64 string (at least all the encrypt/decrypt implementations I've seen). This string has a length in multiples of 4 (string.length % 4 = 0)
The strings I was getting contained + and = on the beginning or end, and when you just concatenate that into a URL's querystring, it will look right (for instance, in an email you generate), but when the the link is followed and the .NET page recieves it and puts it into this.Page.Request.QueryString, those special characters will be gone and your string length will not be in a multiple of 4.
As the are special characters at the FRONT of the string (ex: +), as well as = at the end, you can't just add some = to make up the difference as you are altering the cypher text in a way that doesn't match what was actually in the original querystring.
So, wrapping the cypher text with HttpUtility.URLEncode (not HtmlEncode) transforms the non-alphanumeric characters in a way that ensures .NET parses them back into their original state when it is intepreted into the querystring collection.
The good thing is, we only need to do the URLEncode when generating the querystring for the URL. On the incoming side, it's automatically translated back into the original string value.
Here's some example code
string cryptostring = MyAESEncrypt(MySecretString);
string URL = WebFunctions.ToAbsoluteUrl("~/ResetPassword.aspx?RPC=" + HttpUtility.UrlEncode(cryptostring));

My initial guess without knowing the data would be that the UserNameToVerify is not a multiple of 4 in length. Check out the FromBase64String on msdn.
// Ok
byte[] b1 = Convert.FromBase64String("CoolDude");
// Exception
byte[] b2 = Convert.FromBase64String("MyMan");

The encrypted string had two special characters, + and =.
'+' sign was giving the error, so below solution worked well:
//replace + sign
encryted_string = encryted_string.Replace("+", "%2b");
//`%2b` is HTTP encoded string for **+** sign
OR
//encode special charactes
encryted_string = HttpUtility.UrlEncode(encryted_string);
//then pass it to the decryption process
...

string stringToDecrypt = CypherText.Replace(" ", "+");
int len = stringToDecrypt.Length;
byte[] inputByteArray = Convert.FromBase64String(stringToDecrypt);

While Encrypting use
HttpUtility.UrlEncode(Encryptedtext));
While Decrypting,
use
value = HttpUtility.UrlDecode(value);
value = value.Replace(" ", "+");//to remove any empty spaces
value = value.Replace('-', '+').Replace('_', '/');//replace special char
while (value.Length % 4 != 0) value += '='; //it should be divisible by 4 or append =
Then send this value for decryption

Related

Decoding jwt error: Unable to decode the payload as Base64Url encoded string

I have a jwt token 'eyJhbGciOiJIUzI1NiJ9.YUExIQ.srnc87a8Se8arhCopLBpgxEvILA2AZxOB8BIrFDHKL4
'
that was encode in node and I'm trying to decode it in c# but I'm getting an error thrown that it's upset about the payload.
public void Consume(BasicDeliverEventArgs msg)
{
var message = Encoding.UTF8.GetString(msg.Body);
var user = JsonConvert.DeserializeObject<User>(message);
var handler = new JwtSecurityTokenHandler();
try
{
var x = handler.ReadToken(user.Password) as JwtSecurityToken;
}
catch(Exception ex)
{
Console.WriteLine(ex.Message);
}
_repo.UpdateUser(user);
}
returns error: Unable to decode the payload 'YUExIQ' as Base64Url encoded string
I'm not sure if theres somewhere I can put the secret that was used to encode in the decoding somewhere or if I somehow need to convert the payload the base64 or if I'm just missing some steps somewhere. Thanks in advance.
Since you asked about decoding using Base64, I'll focus on that issue, rather than the semantics of JWT in .NET. This should demystify JWT tokens a bit.
Note that only the first segment of your JWT string above is valid—it converts to {"alg":"HS256"}, while the second converts to aA1!—so for example’s sake, I will use the default sample value from http://jwt.io:
var jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ";
So, per JWT.io's introduction, you have just three segments in a JWT token:
Header
Payload
Signature
Assuming the token is not encrypted, the first two segments can be decoded using plain Base64 (once you pad the length to a multiple of four, as #Jacob mentioned above). The last segment is just a cryptographic signature; it does not contain any meaningful encoded information to decode.
So, a simple code snippet to display the decoded JWT contents could look like this:
var decoded = jwt.Split('.')
.Take(2)
.Select(x =>
Encoding.UTF8.GetString(
Convert.FromBase64String(
x.PadRight(x.Length + (x.Length % 4), '='))))
.Aggregate((s1, s2) => s1 + Environment.NewLine + s2);
Console.WriteLine(decoded);
/* Prints:
{
"alg": "HS256",
"typ": "JWT"
}
{
"sub": "1234567890",
"name": "John Doe",
"admin": true
}
*/
A simpler example that separates all the lambdas could look like:
/// <summary>
/// A function to make the Base64 decoding a little less
/// verbose below:
/// </summary>
string Base64Decode(string value)
{
return Encoding.Default.GetString(Convert.FromBase64String(value));
}
// Get the first two segments into an enumerable:
var pieces = jwt.Split('.').Take(2);
// Pad them with equals signs to a length that is a multiple of four:
var paddedPieces = pieces.Select(x => x.PadRight(x.Length + (x.Length % 4), '='));
// Base64 decode the pieces:
var decodedPieces = paddedPieces.Select(x => Base64Decode(x));
// Join it all back into one string with .Aggregate:
Console.WriteLine(decodedPieces.Aggregate((s1, s2) => s1 + Environment.NewLine + s2));
The result here is the same as the one above.
Now of course, if you intend to do meaningful JWT operations like signing and verification, you should use the JwtSecurityToken class in System.IdentityModel.Tokens.Jwt. But if you just need to find out what information a non-encrypted token contains, it's as simple as the above.
I can't add a comment, just want to add to #paul-smith answer.
His answer worked but theres a slight bug in the logic to pad the string to a multiple of 4.
Instead of,
var paddedPieces = pieces.Select(x => x.PadRight(x.Length + (x.Length % 4), '='));
It should be...
var paddedPieces = pieces.Select(x => x.PadRight(x.Length + ((x.Length % 4 != 0) ? (4 - x.Length % 4) : 0), '='));
You need to check "x.Length % 4 != 0" otherwise it might add "====" on the end in the cases when the string is already a multiple of 4 which breaks the Base64 decoding. But the real issue is that is should be (4 - x.Length % 4) not (x.Length % 4)
e.g.
If the string was 7 characters long...
7 + (7 % 4) = 10
7 + (4 - (7 % 4) = 8
With JWT, you omit the padding bytes. .NET requires the padding = characters at the end in order to make the number of characters a multiple of 4. YUExIQ== should be decodable.

Lost end of the string

I have a problem, and I can not solve it for several days.
My simple code (dll file):
public static string RegisterUser(string table, string l, string p)
{
string query = "INSERT INTO " + table + "(login, password) VALUES ('" + l + "','" + p + "');";
return query;
}
Everything works fine, but when I want to write the data received on the network, it does not return the full string, for example
string recieveStr = Encoding.UTF8.GetString(connection.Buffer); //received text: RegUser:nipercop:passwrd
string[] strArr = recieveStr.Split(':');
if (strArr[0].Equals("RegUser"))
{
// string p = strArr[2]; //passwrd
// sendStr = SqlCommands.RegisterUser("users",strArr[1], "passwrd");//works fine
sendStr = SqlCommands.RegisterUser("users",strArr[1], strArr[2]); // doesn't work
}
returns message like that:
INSERT INTO users(mail, password) VALUES ('nipercop','passwrd
Lost end of the string " ') ". No errors, no warnings, no anything.
I tried change encoding to ASCII on server, on client, but no effect.
Try using string.Format:
string.Format(#"INSERT INTO {0}(login, password) VALUES ('{1}','{2}');", table, l, p);
Though ideally you should be using prepared SQL statements. The reason being is any of the values you insert could contain unexpected values, which leaves your code vulnerable to SQL injection and a plethora of other bad things.
For example if strArr[2] would contain " here, it'd cut off the query and result in an error. As it stands, your code doesn't account for that while seemingly accepting input from a remote socket. That's something you may want to revise.
The problem is was reading strings from the buffer.
Encoding.UTF8.GetString(connection.Buffer,0 , bufferLength);
Because the buffer length was (for example) 1024 bytes, and my last variable was length of the remainder of the length of the other variables, ie contained a empty symbols (spaces). Fail.

base64 decode null reference excpetion on URL

So i have this code on my cs page that takes decodes a key from my Url string. The key is "Reauth_URL" and its a link that is decoded in base64 to UTF8.
////base 64 decoding for Reauth_URL key in URL query string
string encodedString = Convert.ToString(HttpContext.Current.Request.Params["Reauth_URL"]).Trim(')');
byte[] data = Convert.FromBase64String(encodedString);
string decodedString = Encoding.UTF8.GetString(data);
I'm trying to use decodedString but i keep on getting a null refence exception but i can see that the key and value are there.
once i can return the string value id like to ba able to send it into a hyperlink that's on my aspx page.
the encoded url is set up from IronPort which allows a user to lg in as differnt user if they've been blocked from a website. so this reauth_url key in the query string allows them to log in as different user. by the reauth_url needs to be decoded and linked to the hyperlink. I know the key and value is there but i cant get by this null exception, and when i say i know they are there obviously i don't mean in the code above, ive had to split the url query by ? and & and print it out somewhere else and they exist. The code below is used earlier and the key and value i need is there.
string currentUrl = HttpContext.Current.Request.Url.Query;
txtBlockedUrl.Visible = true;
string [] result = currentUrl.Split(new Char[]{'?','&'});
foreach (string r in result)
{
txtBlockedUrl.Text += HttpUtility.UrlDecode(r) + "\n";
}
div style="font-size: medium">
LogIn as Different User
</div>
If HttpContext.Current.Request.Params["Reauth_URL"] is null, Convert.ToString will throw a null reference exception.
Please note that the Params indexer will return null when "Reauth_URL" is not available.
So you have to check first if it exists: (what if the url does not provide it?)
string value = HttpContext.Current.Request.Params["Reauth_URL"];
if (value!=null) {
string encodedString = Convert.ToString(HttpContext.Current.Request.Params["Reauth_URL"]).Trim(')');
//...
Ended Up doing this....
//splitting url string for textbox using name value collection
NameValueCollection collection = new NameValueCollection();
string currentUrl = HttpContext.Current.Request.Url.Query;
string [] result = currentUrl.Split('&');
foreach (string r in result)
{
string[] parts = HttpUtility.UrlDecode(r).Split('=');
if (parts.Length > 0)
{
string key = parts[0].Trim(new char[] { '?', ' ' });
string val = parts[1].Trim();
collection.Add(key, val);
}
}

Decode Url with special & or + characters in query parameters value

I have met this difficulty while decoding a Base64 encoded URL with parameters
eg: http://www.example.com/Movements.aspx?fno=hello&vol=Bits & Pieces
My expected results should be:
fno = hello
vol = Bits & Pieces
#Encoding:
//JAVASCRIPT
var base64 = $.base64.encode("&fno=hello&vol=Bits & Pieces");
window.location.replace("Movements.aspx?" + base64);
#Decoding c#
string decodedUrl = System.Text.Encoding.ASCII.GetString(Convert.FromBase64String(Request.Url.Query.Replace("?", ""))); // Replace is used to remove the ? part from the query string.
string fileno = HttpUtility.ParseQueryString(decodedUrl).Get("fno");
string vol = HttpUtility.ParseQueryString(decodedUrl).Get("vol");
Actual Result:
fno = hello
vol = Bits
I have searched stackoverlow and seems I need to add a custom algorithm to parse the decoded string. But as the actual URL is more complicated than shown in this example I taought better asks experts for an alternative solution!
Tks for reading!
Your querystring needs to be properly encoded. Base64 is not the correct way. Use encodeURIComponent instead. you should encode each value separately (although not needed in most parts in the example):
var qs = "&" + encodeURIComponent("fno") + "=" + encodeURIComponent("hello") + "&" + encodeURIComponent("vol") + "=" + encodeURIComponent("Bits & Pieces");
// Result: "&fno=hello&vol=Bits%20%26%20Pieces"
Then you don't need to Base64 decode in C#.
var qs = HttpUtility.ParseQueryString(Request.Url.Query.Replace("?", ""));
var fileno = qs.Get("fno");
var vol = sq.Get("vol");
If the URL were correctly encoded, you would have :
http://www.example.com/Movements.aspx?fno=hello&vol=Bits+%26+Pieces
%26 is the url encoded caract for &
and spaces will be replaced by +
In JS, use escape to encode correctly your url!
[EDIT]
Use encodeURIComponent instead of escape because like Sani Huttunen says, 'escape' is deprecated. Sorry!

CHAR comm port commands

I am trying to use the following to send over an RS232 connected projector to turn it on:
commProj.Parity = "None";
commProj.StopBits = "One";
commProj.DataBits = "8";
commProj.BaudRate = "19200";
commProj.PortName = "COM6";
commProj.CurrentTransmissionType = PCComm.CommunicationManager.TransmissionType.Text; //.Hex
commProj.OpenPort();
commProj.WriteData((char)33 + (char)137 + (char)1 + (char)80 + (char)87 + (char)49 + "\n"); //turn on proj
Problem being is that it doesnt work.
I have done this with a VB6 port and it works just fine:
public static PCComm.CommunicationManager commProj = new PCComm.CommunicationManager();
MSCommProj.CommPort = 6
MSCommProj.Settings = "19200,N,8,1"
MSCommProj.PortOpen = True
MSCommProj.Output = Chr(33) & Chr(137) & Chr(1) & Chr(80) & Chr(87) & Chr(49) & Chr(10)
What am i missing?
David
CommunicationManager.cs: http://snipt.org/xmklh
Okay, the manual helps a lot. Try changing the CurrentTransmissionType to TransmissionType.Hex and sending the string 21890100000a
commProj.CurrentTransmissionType = TransmissionType.Hex;
commProj.WriteData("21890100000a");
EDIT
Sorry, that was "connection check". Use 2189015057310a for on and 2189015057300a for off.
The plus(+) operator for char's doesn't concatenate the values it adds them. So you end up passing "387\n" to write data.
You need to create a char array and then convert that to a string instead:
commProj.WriteData(new string(new char[] { (char)33, (char)37, (char)1, (char)80, (char)87, (char)49, '\n' }));
I don't know what kind of object commProj is (specifically) but my guess is that the problem comes from casting each numeric value to a char. A char is 2 bytes in size. I recommend either trying to write a Byte array with your data in it, or concatenating a string with these chars and then converting the string to ascii text.

Categories