C# TLS 1.1 Implementation - c#
For some time I've been a bit desperately trying to implement a TLS 1.1 in my application. The reason behind this is the usage of SocketType.Raw sockets, so no SslStream or other higher-level classes are available to me.
So far I'm stuck at Finished message in TLS handshake protocol - keep on receiving bad_record_mac(20) in server response. Cipher suite is 0x0005 - TLS_RSA_WITH_RC4_128_SHA.
Here's some sample code on what's happening:
byte[] client_random, server_random = new byte[28];
byte[] pre_master_secret, master_secret;
byte[] client_write_MAC_secret, server_write_MAC_secret, client_write_key, server_write_key;
byte[] handshake_messages, verify_data;
RSACryptoServiceProvider rsa;
List<X509Certificate2> certificates = new List<X509Certificate2>();
//certificates are added
rsa = (RSACryptoServiceProvider)certificates.First().PublicKey.Key;
private byte[] ClientKeyExchange()
{
pre_master_secret = new byte[48];
(new Random()).NextBytes(pre_master_secret);
//version 0302 for TLS 1.1
pre_master_secret[0] = 3;
pre_master_secret[1] = 2;
byte[] cryptedData = rsa.Encrypt(pre_master_secret, false);
//"1603020086"
string tmp_string = "100000820080" + Utils.BitConverter.ToString(cryptedData).Replace("-", "");
AddHandShakeData(Utils.BitConverter.StringToByteArray(tmp_string));
tmp_string =
"1603020086"
+ tmp_string
+ "140302000101" //Cipher Change Spec
+ "";
return Utils.BitConverter.StringToByteArray(tmp_string);
}
private void ComputeMasterSecret()
{
byte[] label = Utils.BitConverter.StringToByteArray(Utils.BitConverter.ConvertStringToHex("master secret", Encoding.ASCII));
byte[] seed = new byte[client_random.Length + server_random.Length];
Buffer.BlockCopy(client_random, 0, seed, 0, client_random.Length);
Buffer.BlockCopy(server_random, 0, seed, client_random.Length, server_random.Length);
master_secret = PRF(pre_master_secret, label, seed, 48);
}
private void ComputeKeys()
{
byte[] label = Utils.BitConverter.StringToByteArray(Utils.BitConverter.ConvertStringToHex("key expansion", Encoding.ASCII));
byte[] seed = new byte[client_random.Length + server_random.Length];
Buffer.BlockCopy(client_random, 0, seed, 0, client_random.Length);
Buffer.BlockCopy(server_random, 0, seed, client_random.Length, server_random.Length);
byte[] key_material = PRF(master_secret, label, seed, 72);
client_write_MAC_secret = new byte[20];
Buffer.BlockCopy(key_material, 0, client_write_MAC_secret, 0, 20);
server_write_MAC_secret = new byte[20];
Buffer.BlockCopy(key_material, 20, server_write_MAC_secret, 0, 20);
client_write_key = new byte[16];
Buffer.BlockCopy(key_material, 40, client_write_key, 0, 16);
server_write_key = new byte[16];
Buffer.BlockCopy(key_material, 56, server_write_key, 0, 16);
}
private void ComputeVerifyData()
{
byte[] label = Utils.BitConverter.StringToByteArray(Utils.BitConverter.ConvertStringToHex("client finished", Encoding.ASCII));
SHA1 sha1 = SHA1.Create();
MD5 md5 = MD5.Create();
md5.ComputeHash(handshake_messages);
sha1.ComputeHash(handshake_messages);
byte[] seed = new byte[md5.HashSize / 8 + sha1.HashSize / 8];
Buffer.BlockCopy(md5.Hash, 0, seed, 0, md5.HashSize / 8);
Buffer.BlockCopy(sha1.Hash, 0, seed, md5.HashSize / 8, sha1.HashSize / 8);
verify_data = PRF(master_secret, label, seed, 12);
}
private byte[] PRF(byte[] secret, byte[] label, byte[] seed, int output_size)
{
int md5_iterations = (int)Math.Ceiling((double)output_size / 16),
sha1_iterations = (int)Math.Ceiling((double)output_size / 20);
byte[] md5_data = new byte[output_size],
sha1_data = new byte[output_size];
//особое колдунство для нечетного числа
byte[] secret_1 = new byte[(int)Math.Ceiling((double)secret.Length / 2)],
secret_2 = new byte[(int)Math.Ceiling((double)secret.Length / 2)];
Buffer.BlockCopy(secret, 0, secret_1, 0, secret_1.Length);
Buffer.BlockCopy(secret, secret.Length / 2, secret_2, 0, secret_2.Length);
byte[] A = new byte[label.Length + seed.Length];
Buffer.BlockCopy(label, 0, A, 0, label.Length);
Buffer.BlockCopy(seed, 0, A, label.Length, seed.Length);
byte[] tmp = new byte[md5_iterations * 16];
//A(1) ?
//A = P_MD5(secret_1, A);
for (int i = 0; i < md5_iterations; i++)
{
A = P_MD5(secret_1, A);
Buffer.BlockCopy(A, 0, tmp, i * A.Length, A.Length);
}
Buffer.BlockCopy(tmp, 0, md5_data, 0, md5_data.Length); //output_size = md5_data.Length
tmp = new byte[sha1_iterations * 20];
//does it have to start with A(1) ?
//A = P_SHA1(secret_2, A);
for (int i = 0; i < sha1_iterations; i++)
{
A = P_SHA1(secret_2, A);
Buffer.BlockCopy(A, 0, tmp, i * A.Length, A.Length);
}
Buffer.BlockCopy(tmp, 0, sha1_data, 0, sha1_data.Length); //output_size = sha1_data.Length
for (int i = 0; i < output_size; i++)
md5_data[i] = (byte)(md5_data[i] ^ sha1_data[i]);
return md5_data;
}
private byte[] P_MD5(byte[] secret /*это ключ?*/, byte[] seed /*это дата?*/)
{
HMACMD5 HMD5 = new HMACMD5(secret);
HMD5.ComputeHash(seed);
return HMD5.Hash;
}
private byte[] P_SHA1(byte[] secret /*это ключ?*/, byte[] seed /*это дата?*/)
{
HMACSHA1 HSHA1 = new HMACSHA1(secret);
HSHA1.ComputeHash(seed);
return HSHA1.Hash;
}
private byte[] MAC(byte[] secret, byte[] data)
{
byte[] secret_64 = new byte[64];
Buffer.BlockCopy(secret, 0, secret_64, 0, secret.Length);
for (int i = 0; i < 64; i++)
secret_64[i] = (byte)(secret_64[i] ^ (byte)54);
byte[] xor_output_data = new byte[64 + data.Length];
Buffer.BlockCopy(secret_64, 0, xor_output_data, 0, 64);
Buffer.BlockCopy(data, 0, xor_output_data, 64, data.Length);
SHA1 sha1 = SHA1.Create();
sha1.ComputeHash(xor_output_data);
secret_64 = new byte[64];
Buffer.BlockCopy(secret, 0, secret_64, 0, secret.Length);
for (int i = 0; i < 64; i++)
secret_64[i] = (byte)(secret_64[i] ^ (byte)92);
xor_output_data = new byte[64 + sha1.HashSize / 8];
Buffer.BlockCopy(secret_64, 0, xor_output_data, 0, 64);
Buffer.BlockCopy(sha1.Hash, 0, xor_output_data, 64, sha1.HashSize / 8);
sha1.ComputeHash(xor_output_data);
return sha1.Hash;
}
public void RC4(ref Byte[] bytes, Byte[] key)
{
Byte[] s = new Byte[128];
Byte[] k = new Byte[128];
Byte temp;
int i, j;
for (i = 0; i < 128; i++)
{
s[i] = (Byte)i;
k[i] = key[i % key.GetLength(0)];
}
j = 0;
for (i = 0; i < 128; i++)
{
j = (j + s[i] + k[i]) % 128;
temp = s[i];
s[i] = s[j];
s[j] = temp;
}
i = j = 0;
for (int x = 0; x < bytes.GetLength(0); x++)
{
i = (i + 1) % 128;
j = (j + s[i]) % 128;
temp = s[i];
s[i] = s[j];
s[j] = temp;
int t = (s[i] + s[j]) % 128;
bytes[x] ^= s[t];
}
}
Obviously, reading such a bunch of code ain't being the best way to spend your time, so I'd like to add some questions that, hopefully, can help a lot:
HMAC_MD5 and HMAC_SHA1 - in .Net implementation they take key and they take input byte[] to compute hash from. According to RFC4346:
First, we define a data expansion function, P_hash(secret, data)
which uses a single hash function to expand a secret and seed into an
arbitrary quantity of output:
P_hash(secret, seed) = HMAC_hash(secret, A(1) + seed) +
HMAC_hash(secret, A(2) + seed) +
HMAC_hash(secret, A(3) + seed) + ...
Where + indicates concatenation.
A() is defined as:
A(0) = seed
A(i) = HMAC_hash(secret, A(i-1))
What is seed? Is it data we compute hash from? Secret = key, so far I understand. Also, do we start P_hash with A(1)?
Thanks in advance!
Seed is Client_random + Server_random. You need to preserve them from preceding steps of the handshake, the client_hello and server_hello.
A(0) = seed
A(i) = HMAC_hash(secret,A(i-1)) for i>0
The output of the A() function consists of A(1), A(2), A(3)...
Related
How to write to a .wav file using a generated wave, complex[] array?
I would like to know how to write to a .wav file, I have written the following code which supposedly writes data to the file. But when I try to play the sound file it says the file is corrupt / empty. try { SaveFileDialog save = new SaveFileDialog(); save.Filter = "Wave File (*.wav)|*.wav;"; if (save.ShowDialog() != System.Windows.Forms.DialogResult.OK) return; using (FileStream FS = new FileStream(save.FileName, FileMode.Open, FileAccess.Write)) { BinaryWriter wr = new BinaryWriter(FS); int subchunk1Size = 16; short audioFormat = 1; short bitsPerSample = 64; short numChannels = 2; int sampleRate = Convert.ToInt32(samplingRateBox.Text); int byteRate = sampleRate * numChannels * (bitsPerSample / 8); short blockAlign = (short)(numChannels * (bitsPerSample / 8)); int numSamples = Convert.ToInt32(numberOfsamplesBox.Text); int subChunk2Size = numSamples * numChannels * (bitsPerSample / 8); int chunkSize = 4 + (8 + subchunk1Size) + (8 + subChunk2Size); wr.Write(getBytes("RIFF")); wr.Write(chunkSize); wr.Write(getBytes("WAVE")); wr.Write(getBytes("fmt")); wr.Write((byte)32); wr.Write(subchunk1Size); wr.Write(audioFormat); wr.Write(numChannels); wr.Write(sampleRate); wr.Write(byteRate); wr.Write(blockAlign); wr.Write(bitsPerSample); wr.Write(getBytes("data")); wr.Write(subChunk2Size); double[] primArray = new double[samples.Length]; byte[] byteArray = new byte[samples.Length * 8]; for (int i = 0; i < samples.Length; i++) { primArray[i] = Convert.ToDouble(samples[i].Real); } byteArray = doubleToBytes(primArray); for (int i = 0; i < samples.Length; i++) { wr.Write(byteArray[i]); } for (int i = 0; i < samples.Length; i++) { primArray[i] = Convert.ToDouble(samples[i].Imaginary); } byteArray = doubleToBytes(primArray); for (int i = 0; i < samples.Length; i++) { wr.Write(byteArray[i]); } wr.Close(); wr.Dispose(); } } catch (Exception e) { MessageBox.Show(e.Message); } As you can see I have tried converting from Complex to double, plus the header. The sampling rate and number of samples all come from textboxes. Plus I am assuming the bit depth is that of a double.
I have also tried using this method, however the length of the audio file is 0. This used the NAudio library. NAudio.Wave.WaveFileWriter waveWriter = null; WaveIn wi = new WaveIn(); double[] primArray = new double[samples.Length]; for (int i = 0; i < samples.Length; i++) { primArray[i] = Convert.ToDouble(samples[i].Real); } SaveFileDialog save = new SaveFileDialog(); save.Filter = "Wave File (*.wav)|*.wav;"; if (save.ShowDialog() != System.Windows.Forms.DialogResult.OK) return; wi = new NAudio.Wave.WaveIn(); wi.WaveFormat = new WaveFormat(samplingRate,1); waveWriter = new WaveFileWriter(save.FileName, wi.WaveFormat); byte[] byteArray = new byte[samples.Length*8]; byteArray = doubleToBytes(primArray); waveWriter.Write(byteArray, 0, byteArray.Length);
ARC4 encryption not working correctly server side
I have a socket.io client which sends data to each other where encryption is based on ARC4. I tried multiple different scenarios but it keeps failing to decrypt anything and I'm not sure why. The class: ARC4_New public class ARC4_New { private int i; private int j; private byte[] bytes; public const int POOLSIZE = 256; public ARC4_New() { bytes = new byte[POOLSIZE]; } public ARC4_New(byte[] key) { bytes = new byte[POOLSIZE]; this.Initialize(key); } public void Initialize(byte[] key) { this.i = 0; this.j = 0; for (i = 0; i < POOLSIZE; ++i) { this.bytes[i] = (byte)i; } for (i = 0; i < POOLSIZE; ++i) { j = (j + bytes[i] + key[i % key.Length]) & (POOLSIZE - 1); this.Swap(i, j); } this.i = 0; this.j = 0; } private void Swap(int a, int b) { byte t = this.bytes[a]; this.bytes[a] = this.bytes[b]; this.bytes[b] = t; } public byte Next() { this.i = ++this.i & (POOLSIZE - 1); this.j = (this.j + this.bytes[i]) & (POOLSIZE - 1); this.Swap(i, j); return this.bytes[(this.bytes[i] + this.bytes[j]) & 255]; } public void Encrypt(ref byte[] src) { for (int k = 0; k < src.Length; k++) { src[k] ^= this.Next(); } } public void Decrypt(ref byte[] src) { this.Encrypt(ref src); } } public System.Numerics.BigInteger RandomInteger(int bitSize) { var integerData = new byte[bitSize / 8]; _numberGenerator.NextBytes(integerData); integerData[integerData.Length - 1] &= 0x7f; return new System.Numerics.BigInteger(integerData); } My script which generates a key: System.Numerics.BigInteger DHPrivate = RandomInteger(256); System.Numerics.BigInteger DHPrimal = RandomInteger(256); System.Numerics.BigInteger DHGenerated = RandomInteger(256); if (DHGenerated > DHPrimal) { System.Numerics.BigInteger tempG = DHGenerated; DHGenerated= DHPrimal; DHPrimal = tempG; } Then with those values I generate a public key: System.Numerics.BigInteger DHPublic = System.Numerics.BigInteger.ModPow(DHGenerated, DHPrivate, DHPrimal); Then I encrypt this key: string pkey = EncryptY(CalculatePublic, DHPublic); (Additional code for the encryption below) protected virtual string EncryptY(Func<System.Numerics.BigInteger, System.Numerics.BigInteger> calculator, System.Numerics.BigInteger value) { byte[] valueData = Encoding.UTF8.GetBytes(value.ToString()); valueData = PKCSPad(valueData); Array.Reverse(valueData); var paddedInteger = new System.Numerics.BigInteger(valueData); System.Numerics.BigInteger calculatedInteger = calculator(paddedInteger); byte[] paddedData = calculatedInteger.ToByteArray(); Array.Reverse(paddedData); string encryptedValue = Utils.Converter.BytesToHexString(paddedData).ToLower(); return encryptedValue.StartsWith("00") ? encryptedValue.Substring(2) : encryptedValue; } protected virtual byte[] PKCSPad(byte[] data) { var buffer = new byte[128 - 1]; int dataStartPos = (buffer.Length - data.Length); buffer[0] = (byte)Padding; Buffer.BlockCopy(data, 0, buffer, dataStartPos, data.Length); int paddingEndPos = (dataStartPos - 1); bool isRandom = (Padding == PKCSPadding.RandomByte); for (int i = 1; i < paddingEndPos; i++) { buffer[i] = (byte)(isRandom ? _numberGenerator.Next(1, 256) : byte.MaxValue); } return buffer; } After all that I sent the string PKEY to the server. And after decrypting the string, the server gets the public key which is for example: 127458393 When I connect both my client and server using: 127458393 Like: BigInteger key = System.Numerics.BigInteger.Parse("127458393"); client = new ARC4_New(PrimalDing.ToByteArray()); My client sends a string like: client.Encrypt(BYTE_HERE); And my server reads it like: client.Decrypt(BYTE_HERE); But it fails, and gets a random unreadable string. What am I doing wrong here?
I managed to fix the issue For some reason, my server was and is reversing the bytes i used in the ARC4 client.. So i simple reverse it now as a hotfix System.Numerics.BigInteger temp = System.Numerics.BigInteger.Parse(textBox1.Text); client = new ARC4_New(temp.ToByteArray().Reverse().ToArray());
encryption , String to Byte conversion
I have a string and i changed it to hex values , so i want to store them in the Byte array , but it gives me error of "Input String is not correct format". here is my code : byte[] PlainText = new byte[16]; byte[] MasterKey = new byte[16]; string input = "Hello"; char[] values = input.ToCharArray(); int i =0; foreach (char letter in values) { int value = Convert.ToInt32(letter); string hexout = String.Format("{0:X}", value); PlainText[i++] = Convert.ToByte(hexout); }
Change your intial code byte[] PlainText = new byte[16]; byte[] MasterKey = new byte[16]; string input = "Hello"; char[] values = input.ToCharArray(); int i =0; string hexout=string.empty; foreach (char letter in values) { int value = Convert.ToInt32(letter); hexout+= String.Format("{0:X}", value); } plaintext=StringToByteArray(hexout); for converting hex to byte array public static byte[] StringToByteArray(String hex) { int NumberChars = hex.Length; byte[] bytes = new byte[NumberChars / 2]; for (int i = 0; i < NumberChars; i += 2) bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16); return bytes; } or For parsing long string public static byte[] StringToByteArray(String hex) { int NumberChars = hex.Length/2; byte[] bytes = new byte[NumberChars]; using (var sr = new StringReader(hex)) { for (int i = 0; i < NumberChars; i++) bytes[i] = Convert.ToByte(new string(new char[2]{(char)sr.Read(), (char)sr.Read()}), 16); } return bytes; }
var bytes=System.Text.Encoding.UTF8.GetBytes(yourString);
C# Compress Triple Byte Array
I currently got this script, which compresses byte arrays. But I need it rewritten, so it can compress triple byte arrays [,,] Thanks! public static byte[] Compress(byte[] buffer) { MemoryStream ms = new MemoryStream(); GZipStream zip = new GZipStream(ms, CompressionMode.Compress, true); zip.Write(buffer, 0, buffer.Length); zip.Close(); ms.Position = 0; MemoryStream outStream = new MemoryStream(); byte[] compressed = new byte[ms.Length]; ms.Read(compressed, 0, compressed.Length); byte[] gzBuffer = new byte[compressed.Length + 4]; Buffer.BlockCopy(compressed, 0, gzBuffer, 4, compressed.Length); Buffer.BlockCopy(BitConverter.GetBytes(buffer.Length), 0, gzBuffer, 0, 4); return gzBuffer; } public static byte[] Decompress(byte[] gzBuffer) { MemoryStream ms = new MemoryStream(); int msgLength = BitConverter.ToInt32(gzBuffer, 0); ms.Write(gzBuffer, 4, gzBuffer.Length - 4); byte[] buffer = new byte[msgLength]; ms.Position = 0; GZipStream zip = new GZipStream(ms, CompressionMode.Decompress); zip.Read(buffer, 0, buffer.Length); return buffer; }
Update: I rewrote the code, it is running much faster now and the code is cleaner. Just tested it with some random data (see end of this post). The Compression method: public static byte[] Compress(byte[, ,] uncompressed) { if (uncompressed == null) throw new ArgumentNullException("uncompressed", "The given array is null!"); if (uncompressed.LongLength > (long)int.MaxValue) throw new ArgumentException("The given array is to large!"); using (MemoryStream ms = new MemoryStream()) using (GZipStream gzs = new GZipStream(ms, CompressionMode.Compress)) { // Save sizes of the dimensions for (int dim = 0; dim < 3; dim++) gzs.Write(BitConverter.GetBytes( uncompressed.GetLength(dim)), 0, sizeof(int)); // Convert byte[,,] to byte[] by just blockcopying it // I know, some pointer-magic/unmanaged cast wouldnt // have to copy it, but its cleaner this way... byte[] data = new byte[uncompressed.Length]; Buffer.BlockCopy(uncompressed, 0, data, 0, uncompressed.Length); // Write the data to the stream to compress it gzs.Write(data, 0, data.Length); gzs.Close(); // Get the compressed byte array back return ms.ToArray(); } } The Decompression method: public static byte[, ,] Decompress(byte[] compressed) { if (compressed == null) throw new ArgumentNullException("compressed", "Data to decompress cant be null!"); using (MemoryStream ms = new MemoryStream(compressed)) using (GZipStream gzs = new GZipStream(ms, CompressionMode.Decompress)) { // Read the header and restore sizes of dimensions byte[] dimheader = new byte[sizeof(int) * 3]; gzs.Read(dimheader, 0, dimheader.Length); int[] dims = new int[3]; for (int j = 0; j < 3; j++) dims[j] = BitConverter.ToInt32(dimheader, sizeof(int) * j); // Read the data into a buffer byte[] data = new byte[dims[0] * dims[1] * dims[2]]; gzs.Read(data, 0, data.Length); // Copy the buffer to the three-dimensional array byte[, ,] uncompressed = new byte[dims[0], dims[1], dims[2]]; Buffer.BlockCopy(data, 0, uncompressed, 0, data.Length); return uncompressed; } } The test code: Random rnd = new Random(); // Create a new randomly big array, fill it with random data byte[, ,] uncomp = new byte[rnd.Next(70, 100), rnd.Next(70, 100), rnd.Next(70, 100)]; for (int x = 0; x < uncomp.GetLength(0); x++) for (int y = 0; y < uncomp.GetLength(1); y++) for (int z = 0; z < uncomp.GetLength(2); z++) uncomp[x, y, z] = (byte)rnd.Next(30, 35); // Compress and Uncompress again Stopwatch compTime = new Stopwatch(), uncompTime = new Stopwatch(); compTime.Start(); byte[] comp = Compress(uncomp); compTime.Stop(); uncompTime.Start(); byte[, ,] uncompagain = Decompress(comp); uncompTime.Stop(); // Assert all dimension lengths and contents are equal for (int j = 0; j < 3; j++) Debug.Assert(uncomp.GetLength(j) == uncompagain.GetLength(j)); for (int x = 0; x < uncomp.GetLength(0); x++) for (int y = 0; y < uncomp.GetLength(1); y++) for (int z = 0; z < uncomp.GetLength(2); z++) Debug.Assert(uncomp[x, y, z] == uncompagain[x, y, z]); Console.WriteLine(string.Format("Compression: {0}ms, " + "Decompression: {1}ms, Ratio: {2}% ({3}/{4} bytes)", compTime.ElapsedMilliseconds, uncompTime.ElapsedMilliseconds, (int)((double)comp.LongLength / (double)uncomp.LongLength * 100), comp.LongLength, uncomp.LongLength)); Output, for example: Compression: 77ms, Decompression: 23ms, Ratio: 41% (191882/461538 bytes)
How to do RLE (run length encoding) in C# on a byte array?
I am trying to XOR two bitmap files (their byte arrays) to produce a byte array that can be used to change image A into image B or vice versa. I am sending this over the network so I would like to do some basic compression before this happens. Is there a way to do RLE (run length encoding) in C# (using a built-in, or fast reliable 3rd party library) on a byte array for this purpose? Notes: If you are going to suggest an alternative to my approach please keep in mind that the decompression and transformation on the remote machine has to be as quick and efficient as possible.
I usually use GZipStream. It's fast enough and works fine. class Compressor { public static byte[] compress(byte[] buffer) { MemoryStream ms = new MemoryStream(); GZipStream zip = new GZipStream(ms, CompressionMode.Compress, true); zip.Write(buffer, 0, buffer.Length); zip.Close(); ms.Position = 0; byte[] compressed = new byte[ms.Length]; ms.Read(compressed, 0, compressed.Length); byte[] gzBuffer = new byte[compressed.Length + 4]; Buffer.BlockCopy(compressed, 0, gzBuffer, 4, compressed.Length); Buffer.BlockCopy(BitConverter.GetBytes(buffer.Length), 0, gzBuffer, 0, 4); return gzBuffer; } public static byte[] decompress(byte[] gzBuffer) { MemoryStream ms = new MemoryStream(); int msgLength = BitConverter.ToInt32(gzBuffer, 0); ms.Write(gzBuffer, 4, gzBuffer.Length - 4); byte[] buffer = new byte[msgLength]; ms.Position = 0; GZipStream zip = new GZipStream(ms, CompressionMode.Decompress); zip.Read(buffer, 0, buffer.Length); return buffer; } }
Here are two methods of RLE string packing/unpacking. The best result will be seen on a big text. public string PackText() { try { StringBuilder str1 = new StringBuilder(Text); StringBuilder str = new StringBuilder(); char ch; int i, k, j; for (i = 0; i < str1.Length; ) // from 0 to length of unpackedtext { ch = str1[i]; // get current char from str1 k = 0; //count the number of repeated characters if (i == str1.Length - 1) // If this is the last character { str.Append(ch); break; //exit the loop } if (str1[i + 1] == ch) //if current symbol is next { for (j = i; j < str1.Length; j++) //packing the characters { if (str1[j] == ch) //if current symbol is next { if (k == 9) break; //the maximum number of characters packed 9, //or there might be problems with unpacking is not packed numeric characters k++; } else break; } i = j; } else if ("0123456789".Contains(ch)) //if this digit and it is not repeated, then it must be escaped, //so when unpacking to understand that this is not the number of repeated characters { k = 1; i++; } else i++; if (k != 0) str.AppendFormat("{0}{1}", k, ch); //forming packed string else str.Append(ch); } return str.ToString(); } catch { return null; } } public string UnpackText() { try { StringBuilder str1 = new StringBuilder(Text); StringBuilder str = new StringBuilder(); char ch; char symb = 'a'; int s = 0; int i, j; for (i = 0; i < str1.Length; ) // from 0 to length of packedtext { ch = str1[i]; // get current char from str1 s = 0; if ("123456789".Contains(ch)) //if this digit { if (i == str1.Length - 1) // If this is the last character { symb = ch; s = 1; i++; } else { symb = str1[i + 1]; // get packed symbol i += 2; s = Convert.ToInt32(ch) - 48; //get the number of repetitions } } else { s = 0; i++; } if (s > 0) { for (j = 0; j < s; j++) // write the decompressed symbol str.Append(symb); } else str.Append(ch); } return str.ToString(); } catch { return null; } }
You can use the RLE encoding/decoding tool from CodePlex: http://rle.codeplex.com/ ( C# ) You may build as .net dll and its lib