I got a method that's supposed to generate a 64 byte (512 bit) salt for me:
public static string GenerateSalt()
{
var rngCrypto = new RNGCryptoServiceProvider();
byte[] saltBytes = new byte[64];
rngCrypto.GetBytes(saltBytes);
string result = Convert.ToBase64String(saltBytes);
return result;
}
This seems to be running fine, the saltBytes bytearray has the size of 64 byte. However, I can't enter the results in my MS SQL Database Table, consisting of a char(64) typed column.
My assumption is, that the Convert.ToBase64String(saltBytes); method is faulty on my side, but I'd like to know how I can improve this. A quick run through System.Text.ASCIIEncoding.Unicode.GetByteCount(secondSalt); reveals a string size of 176 byte instead of 64 byte.
A byte array is logically a number in base 256. Converting that to a number in base 64 is going to make it longer. Just like when you convert from hex F0 to binary 1111111100000000, it gets longer.
If you want to store the salt in the database in a human-readable base-64-encoded string then it is going to have to be much longer than 64 single-byte characters.
As for running it through the ASCII encoder -- I have no idea what you're trying to do here. That sounds like an odd thing to do to non-textual data. Can you explain?
When you start with 64 bytes (512 bits), then convert to base 64, you're storing only 6 bits in each byte, so you need ceiling(512/6) = 86 bytes to store the result (not sure where your 176 bytes is coming from though).
Related
I am trying to convert a AS3 (ActionScript 3) function to C#.
This ActionScript function contains a class called ByteArray which from what I am aware of it's basically what it sounds like lmao. I think it's kind of similar of how byte[] would be in C#. Anyway, I have tried my best to convert the code to C# using MemoryStream and then writing bytes to it, and then returning UTF8 string as you can see in my code below. However, I feel as if my way of doing how the ActionScript code does isn't accurate and that is where my question above comes in.
With them negative numbers being written into "loc1" (The ByteArray) and "loc1.uncompress()", that's where I feel like I am failing and was wondering if someone could help me out in converting this function so it's fully accurate?
On top of that question, I would also like to ask if what I was doing with the negative numbers was correct in my C# code just like how the ActionScript code was doing it? Would mean a lot (:
(Sorry if not fully understandable and if what I say doesn't match up as much)
ActionScript Code:
private function p() : String
{
var _loc1_:ByteArray = new ByteArray();
_loc1_.writeByte(120);
_loc1_.writeByte(-38);
_loc1_.writeByte(99);
_loc1_.writeByte(16);
_loc1_.writeByte(12);
_loc1_.writeByte(51);
_loc1_.writeByte(41);
_loc1_.writeByte(-118);
_loc1_.writeByte(12);
_loc1_.writeByte(50);
_loc1_.writeByte(81);
_loc1_.writeByte(73);
_loc1_.writeByte(49);
_loc1_.writeByte(-56);
_loc1_.writeByte(13);
_loc1_.writeByte(48);
_loc1_.writeByte(54);
_loc1_.writeByte(54);
_loc1_.writeByte(14);
_loc1_.writeByte(48);
_loc1_.writeByte(46);
_loc1_.writeByte(2);
_loc1_.writeByte(0);
_loc1_.writeByte(45);
_loc1_.writeByte(-30);
_loc1_.writeByte(4);
_loc1_.writeByte(-16);
_loc1_.uncompress();
_loc1_.position = 0;
return _loc1_.readUTF();
}
My C# Code:
public string p()
{
MemoryStream loc1 = new MemoryStream();
loc1.WriteByte((byte)120);
loc1.WriteByte((byte)~-38);
loc1.WriteByte((byte)99);
loc1.WriteByte((byte)16);
loc1.WriteByte((byte)12);
loc1.WriteByte((byte)51);
loc1.WriteByte((byte)41);
loc1.WriteByte((byte)~-118);
loc1.WriteByte((byte)12);
loc1.WriteByte((byte)50);
loc1.WriteByte((byte)81);
loc1.WriteByte((byte)73);
loc1.WriteByte((byte)49);
loc1.WriteByte((byte)~-56);
loc1.WriteByte((byte)13);
loc1.WriteByte((byte)48);
loc1.WriteByte((byte)54);
loc1.WriteByte((byte)54);
loc1.WriteByte((byte)14);
loc1.WriteByte((byte)48);
loc1.WriteByte((byte)46);
loc1.WriteByte((byte)2);
loc1.WriteByte((byte)0);
loc1.WriteByte((byte)45);
loc1.WriteByte((byte)~-30);
loc1.WriteByte((byte)4);
loc1.WriteByte((byte)~-16);
loc1.Position = 0;
return Encoding.UTF8.GetString(loc1.ToArray());
}
1) In C#, bytes are unsigned. You cannot convert a signed byte to an unsigned byte with the complement, because your intention is that the bitwise representation should be identical, rather than opposite, which is what the complement does.
one simple way to convert is to mask with 0xFF: -37 & 0xFF = 219. There are other, mathematically equivalent ways, such as checking for negatives with sbyte sb = -37; byte b = sb < 0 ? 256 + sb : sb;
2) The builtin System.IO.Compression namespace is lacking in a number of ways. For one, it doesn't even support decompressing zlib data, which is what your byte array holds. the best way is to use a third party package on Nuget instead. The DotNetZip library does what you need, specifically the Ionic.Zlib.ZlibStream.UncompressBuffer function.
(1)
#Jimmy has given you a good Answer.
This is what he meant when he told you "to mask with 0xFF" so that your -38 becomes masked as:
loc1.WriteByte( (byte)(-38 & 0xFF) );
Do the same above logic for any other values that have a minus sign.
(2)
It might be easier if you just use values written in hex instead of decimal. This means instead of decimal 255 you write equivalent hex of 0xFF since bytes are supposed to be in hex. The WriteByte is auto-converting your decimals but it's not helping you to learn what it is going on...
For example your beginning two byte values are 120 -38 but in hex that is 0x78 0xDA.
Now if you google search bytes 0x78 0xDA you will find out those two bytes are header for ZLIB's DEFLATE compression algorithm.
This ZLIB detail is important to know for the next step...
(3)
Sometimes the variable names are not always recovered during de-compiling. This is why all your code has these silly _loc_ as generic names (real var names are unknown, only their data type).
Your _loc1_.uncompress(); is supposed to contain a String variable specifying the algorithm.
public function uncompress(algorithm:String) :void //from AS3 documentation
During decompilation that important info was lost. Luckily there only 3 options "ZLIB", "DEFLATE" or "LZMA". From the above notice (2) we can see it should be _loc1_.uncompress("DEFLATE");
Solution:
Create a byte array (not Memory Stream) and manually fill with hex values (eg: -13 is written 0xDA).
First convert each of your numbers to hex. You can use Windows Calculator in Programmer mode (under View option), where you type a decimal in dec mode then press hex to see same value as hex format. Maybe some online tool can do it too.
The final hex values should look like 78 DA 63 10 0C 33 29 8A 0C 32 51 49 31 C8 .... etc until the ending hex value F0 which equals your ending decimal -16.
Then you can easily do...
public string p()
{
byte[] loc_Data = new byte[] {
0x78, 0xDA, 0x63, 0x10, 0x0C, 0x33, 0x29, 0x8A,
0x0C, 0x32, 0x51, 0x49, 0x31, 0xC8, 0x0D, 0x30, etc etc ... until 0xF0
};
var loc_Uncompressed = Ionic.Zlib.ZlibStream.UncompressBuffer( loc_Data );
return Encoding.UTF8.GetString( loc_Uncompressed ); //or try: loc_Uncompressed.ToArray()
}
I'm working on a parser to receive UDP information, parse it, and store it. To do so I'm using a BinaryReader since it will mostly be binary information. Some of it will be strings though. MSDN says for the ReadString() function:
Reads a string from the current stream. The string is prefixed with
the length, encoded as an integer seven bits at a time.
And I completely understand it up until "seven bits at a time" which I tried to simply ignore until I started testing. I'm creating my own byte array before putting it into a MemoryStream and attempting to read it with a BinaryReader. Here's what I first thought would work:
byte[] data = new byte[] { 3, 0, 0, 0, (byte)'C', (byte)'a', (byte)'t', }
BinaryReader reader = new BinaryReader(new MemoryStream(data));
String str = reader.ReadString();
Knowing an int is 4 bytes (and toying around long enough to find out that BinaryReader is Little Endian) I pass it the length of 3 and the corresponding letters. However str ends up holding \0\0\0. If I remove the 3 zeros and just have
byte[] data = new byte[] { 3, (byte)'C', (byte)'a', (byte)'t', }
Then it reads and stores Cat properly. To me this conflicts with the documentation saying that the length is supposed to be an integer. Now I'm beginning to think they simply mean a number with no decimal place and not the data type int. Does this mean that a BinaryReader can never read a string larger than 127 characters (since that would be 01111111 corresponding to the 7 bits part of the documentation)?
I'm writing up a protocol and need to completely understand what I'm getting into before I pass our documentation along to our clients.
I found the source code for BinaryReader. It uses a function called Read7BitEncodedInt() and after looking up that documentation and the documentation for Write7BitEncodedInt() I found this:
The integer of the value parameter is written out seven bits at a
time, starting with the seven least-significant bits. The high bit of
a byte indicates whether there are more bytes to be written after this
one. If value will fit in seven bits, it takes only one byte of space.
If value will not fit in seven bits, the high bit is set on the first
byte and written out. value is then shifted by seven bits and the next
byte is written. This process is repeated until the entire integer has
been written.
Also, Ralf found this link that better displays what's going on.
Unless they specifically say 'int' or 'Int32', they just mean an integer as in a whole number.
By '7 bits at time', they mean that it implements 7-bit length encoding, which seems a bit confusing at first but is actually rather straightforward. Here are some example values and how they are written out using 7-bit length encoding:
/*
decimal value binary value -> enc byte 1 enc byte 2 enc byte 3
85 00000000 00000000 01010101 -> 01010101 n/a n/a
1,365 00000000 00000101 01010101 -> 11010101 00001010 n/a
349,525 00000101 01010101 01010101 -> 11010101 10101010 00010101
*/
The table above uses big endian for no other reason than I simply had to pick one and it's what I'm most familiar with. The way 7-bit length encoding works, it is little endian by it's very nature.
Note that 85 writes out to 1 byte, 1,365 writes out to 2 bytes, and 349,525 writes out to 3 bytes.
Here's the same table using letters to show how each value's bits were used in the written output (dashes are zero-value bits, and the 0s and 1s are what's added by the encoding mechanism to indicate if a subsequent byte is to be written/read)...
/*
decimal value binary value -> enc byte 1 enc byte 2 enc byte 3
85 -------- -------- -AAAAAAA -> 0AAAAAAA n/a n/a
1,365 -------- -----BBB AAAAAAAA -> 1AAAAAAA 0---BBBA n/a
349,525 -----CCC BBBBBBBB AAAAAAAA -> 1AAAAAAA 1BBBBBBA 0--CCCBB
*/
So values in the range of 0 to 2^7-1 (127) will write out as 1 byte, values of 2^7 (128) to 2^14-1 (16,383) will use 2 bytes, 2^14 (16,384) to 2^21-1 (2,097,151) will take 3 bytes, and so on and so forth.
I'm trying to understand the following:
If I am declaring 64 bytes as the array length (buffer). When I convert to a base 64 string, it says the length is 88. Shouldn't the length only be 64, since I am passing in 64 bytes? I could be totally misunderstanding how this actual works. If so, could you please explain.
//Generate a cryptographic random number
RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
// Create byte array
byte[] buffer = new byte[64];
// Get random bytes
rng.GetBytes(buffer);
// This line gives me 88 as a result.
// Shouldn't it give me 64 as declared above?
throw new Exception(Convert.ToBase64String(buffer).Length.ToString());
// Return a Base64 string representation of the random number
return Convert.ToBase64String(buffer);
No, base-64 encoding uses a whole byte to represent six bits of the data being encoded. The lost two bits is the price of using only alphanumeric, plus and slash as your symbols (basically, excluding the numbers representing not visible or special characters in plain ASCII/UTF-8 encoding). The result that you are getting is (64*4/3) rounded up to the nearest 4-byte boundary.
Base64 encoding converts 3 octets into 4 encoded characters; therefore
(64/3)*4 ≈ (22*4) = 88 bytes.
Read here.
Shouldn't the length only be 64, since I am passing in 64 bytes?
No. You are passing 64 tokens in Base256 notation. Base64 has less information per token, so it needs more tokens. 88 sounds about right.
I had problems with Umlauts in ASCII so I encode my Stream as UTF-8 now, which works, but it brings up a problem. I normally read 4 Bytes before ARTIST to determine the length of ARTIST=WHOEVER using
UTF8Encoding enc = new UTF8Encoding();
string response = enc.GetString(message, 0, bytesRead);
int posArtist = response.IndexOf("ARTIST");
BitConverter.ToInt32(message, posArtist - 4);
This works for ASCII perfectly.
The hex-editor examples are just to illustrate that reading the length doesn't work anymore like with ASCII
Here is an example-screenshot from a hex-editor:
"ARTIST=M.A.N.D.Y. vs. Booka Shade" Length = 21
However that doesn't work for the UTF8-encoded stream.
Here is a screenshot:
"ARTIST=Paulseq" Length = E but in the picture its 2E.
What am I doing wrong here?
your data is wrong - you actually have the character '\0' in the data where there should be binary zeroes
The problem lies in how you created this data, not in the reading of it
It is an utter mystery how you got 21 out of the ASCII data. The shaded byte is in hex, its real value is 33. There's no way you can get 21 out of BitConverter.ToInt32, that requires bytes values (in hex) 15 00 00 00.
This must have worked by accident but no idea what that accident might look like. Post more code, including the code that writes this.
My guess is that you are mixing tools. That is a binary stream. It should be read with a BinaryReader and written with a BinaryWriter. When writing text, use Encoder.GetBytes to get the raw bytes to write, and when reading use Encoder.GetString on the raw bytes read. BinaryWriter/Reader have methods for values (like lengths) directly.
Only the strings should be UTF-8 encoded/decoded. If you're passing other (non-string) values in binary, the encoders they will destroy them.
When I am encrypting 23 using MD5 encryption I am getting 37693cfc748049e45d87b8c7d8b9aacd this 32-character long string which will always be static for 23.
I want the same kind of mechanism but that should generate 18 or less (like: 122ff1e4883358b6) characters long string instead 32.
How I can do that in C#, is there any shorter version of MD5 in c#??
I like #RichieHindle's answer. However, if you're interested in losing fewer bits of fidelity (and thereby decreasing the risk of collisions), you could take the 128 bit value returned by the MD5 Hash, and encode it using ASCII85 (also known as Base85 encoding), instead of a hexadecimal-based encoding. This will give you the whole hash in 20 bytes (which is more than you wanted, but you could chop 2 bytes off, resulting in much less loss than removing 14 of the 32 bytes you'd get using hex encoding).
Edit: Prashant says 20 characters is close enough, and asked for sample code:
After obtaining the MD5 hash from the MD5.ComputeHash call, you can use Jeff Atwood's ASCII85 encoder:
MD5 m = MD5.Create();
byte[] hash = m.ComputeHash(System.Text.Encoding.ASCII.GetBytes("23"));
Ascii85 encoder = new Ascii85();
encoder.EnforceMarks = false;
string hash85 = encoder.Encode(hash);
Console.Out.WriteLine(hash85);
Yields
2ebDPFFZsD?&,r1fX\$,
so you can just use hash85. The encoder.EnforceMarks makes sure that the encoding doesn't include some typical prefix and suffixes that are associated with ASCII85.
You can just take as much of the MD5 hash as you need, and throw the rest away. All the bits have equal value, so there's no difference between doing that and using some hashing algorithm that natively produces fewer bits.
(If you're doing this for security reasons, remember that fewer bits makes hashes easier to crack, regardless of the algorithm. Even outside of security applications, fewer bits increase the risk of collisions. Also bear in mind that MD5 is relative insecure these days - SHA-1 or SHA-2 are considered more secure.)
MD5 always creates a 128 Bit Hash.
Other smaller Hashtypes (taken from Wikipedia)
Fowler-Noll-Vo hash function (32, 64, 128, 256, 512, or 1024 bits)
Jenkins hash function (32 bits)
MurmurHash (32 or 64 bits)
Pearson hashing (8 bits)
But remember hash collisions
I wouldn't use a hash function if you want to be able to map the result back to its original value without collisions.
If your aim is to turn a small decimal number into a long obfuscated string, just invent some mapping algorithm and encode the result with zBase32 or similar.
public string Obfuscate(long x)
{
return ToZBase32(BitConverter.GetBytes(x * 63498398L));
}
public long Deobfuscate(string x)
{
return BitConverter.ToInt64(FromZBase32(x)) / 63498398L;
}
23 gets encoded to "gmuyaiayyyyyy". (63498398 chosen by fair dice roll.)
Use FVNHash - http://www.codeproject.com/KB/security/FnvHash.aspx
You can set the length of your hash, do not use it for security reasons.
shortest useful hash algorithm would be md5 . generates 16 bytes=128 bit hash. if you use base 64 encoding ...that is 6 useful bits per byte/char.
u should be able to reduce the md5 to 22 characters (ascii). what you have is hex version where 2 bytes represent one actual byte
(leaving the trailing padding introduced by b64)
with an added advantage of using the same for legal filenames. (ofcourse u will have to substitute the default / and + characters with any other symbol which does not clash with file naming convention of your os.
base64 (by replacing / and +) ensures your hash doesnot mess up the url with special characters which may mean something else to your webserver
ASCII85 adds characters which are difficult to deal with when using as filenames and in urls
md5 ('This string will be hashed')
'37aa3296c523f6c5a7fd2102a9155dcc' (hex) (32 bytes)
raw md5 ('This string will be hashed')
[55, 170, 50, 150, 197, 35, 246, 197, 167, 253, 33, 2, 169, 21, 93, 204] = (16 Bytes)
base64 of raw md5 string
N6oylsUj9sWn_SECqRVdzA==
My Final Hash
N6oylsUj9sWn_SECqRVdzA this is actually the complete md5 in 22 ascii characters
([you can strip the two trailing = there will always be two for md5-add them later when decoding. also replace + and / characters in b64 with any other i prefer -(dash) and _ (underscore) ]
this 32-character long string are number from hexadecimal : 0-f
you can make it shorter by converting its hexadecimal value to radix of 36 : 0-z