123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137 |
- using System;
- using System.IO;
- using System.IO.Compression;
- using System.Linq;
- using System.Security.Cryptography;
- using System.Security.Policy;
- namespace UdPunching
- {
- public static class TransferCodec
- {
- //Transfer format
- // [0...............15] GUID Sender peer id
- // [ gz if compressable
- // [15....15+BlockSize] data encrypt by receiverPublicKey
- // [16+BlockSize...END] signature of [encrypted data] of senderPrivateKey
- // ]
- private const int RsaKeySize = 4096;
- private const int BlockSize = RsaKeySize / 8;
- private static readonly RSAEncryptionPadding EncryptionPadding = RSAEncryptionPadding.OaepSHA512;
- private static readonly SHA512Cng SignatureHasher = new SHA512Cng();
- private static readonly HashAlgorithmName SignatureHashAlgorithm = HashAlgorithmName.SHA512;
- private static readonly RSASignaturePadding SignaturePadding = RSASignaturePadding.Pkcs1;
- public static RSACng LoadKey(string path)
- {
- var rsa = new RSACng();
- rsa.FromXmlString(File.ReadAllText(path));
- if (RsaKeySize == rsa.KeySize) return rsa;
- rsa.Dispose();
- throw new ArgumentException("invalid key size");
- }
- public static Guid ReadId(byte[] receivedData)
- {
- return new Guid(receivedData.Take(16).ToArray());
- }
- public static byte[] Encode(RSACng senderPrivateKey, RSACng receiverPublicKey, Guid senderId, byte[] data)
- {
- byte[] pass1;
- byte[] signature;
- {
- pass1 = receiverPublicKey.Encrypt(data, EncryptionPadding);// encode
- var hash = SignatureHasher.ComputeHash(pass1);
- using (RSA rsaCng = new RSACng())
- {
- rsaCng.ImportParameters(senderPrivateKey.ExportParameters(true));
- signature = rsaCng.SignHash(hash, SignatureHashAlgorithm, SignaturePadding); // signature
- }
- }
- var toCompress = new byte[BlockSize * 2];
- pass1.WriteTo(toCompress);
- signature.WriteTo(toCompress, BlockSize);
- var compressed = Compress(toCompress);
- var buf = new byte[16 + compressed.Length];
- var ptr = -1;
- ptr += senderId.WriteTo(buf, ++ptr);
- ptr += compressed.WriteTo(buf, ++ptr);
- return buf;
- }
- public static byte[] DecodeData(RSACng receiverPrivateKey, RSACng senderPublicKey, byte[] data)
- {
- var pass2 = Decompress(data, 16); // skip sender id
- pass2.ReadBytes(BlockSize, out var enc);
- pass2.ReadBytes(BlockSize, out var signature, BlockSize);
- using (RSA rsaCng = new RSACng())
- {
- rsaCng.ImportParameters(senderPublicKey.ExportParameters(false));
- var hash = SignatureHasher.ComputeHash(enc);
- if (false == rsaCng.VerifyHash(hash, signature, SignatureHashAlgorithm, SignaturePadding))
- {
- throw new InvalidDataException("Signature verify fail");
- }
- }
- var decodeData = receiverPrivateKey.Decrypt(enc, EncryptionPadding); //decode
- return decodeData;
- }
- private static byte[] Compress(byte[] data)
- {
- using (var rawIn = new MemoryStream(data))
- using (var gzOut = new MemoryStream())
- using (var gZipStream = new GZipStream(gzOut, CompressionLevel.Optimal))
- using (var finalOut = new MemoryStream())
- {
- rawIn.CopyTo(gZipStream);
- gZipStream.Close();
- var compressed = gzOut.ToArray();
- if (compressed.Length >= data.Length)
- {
- finalOut.WriteByte(0);
- finalOut.Write(data, 0, data.Length);
- }
- else
- {
- finalOut.WriteByte(1);
- finalOut.Write(compressed, 0, compressed.Length);
- }
- return finalOut.ToArray();
- }
- }
- private static byte[] Decompress(byte[] data, int startFrom = 0)
- {
- using (var rawIn = new MemoryStream(data))
- using (var finalOut = new MemoryStream())
- {
- rawIn.Position = startFrom;
- if (rawIn.ReadByte() == 1)
- {
- using (var gZipStream = new GZipStream(rawIn, CompressionMode.Decompress))
- {
- gZipStream.CopyTo(finalOut);
- }
- }
- else
- {
- rawIn.CopyTo(finalOut);
- }
- return finalOut.ToArray();
- }
- }
- }
- }
|