TransferCodec.cs 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. using System;
  2. using System.IO;
  3. using System.IO.Compression;
  4. using System.Linq;
  5. using System.Security.Cryptography;
  6. using System.Security.Policy;
  7. namespace UdPunching
  8. {
  9. public static class TransferCodec
  10. {
  11. //Transfer format
  12. // [0...............15] GUID Sender peer id
  13. // [ gz if compressable
  14. // [15....15+BlockSize] data encrypt by receiverPublicKey
  15. // [16+BlockSize...END] signature of [encrypted data] of senderPrivateKey
  16. // ]
  17. private const int RsaKeySize = 4096;
  18. private const int BlockSize = RsaKeySize / 8;
  19. private static readonly RSAEncryptionPadding EncryptionPadding = RSAEncryptionPadding.OaepSHA512;
  20. private static readonly SHA512Cng SignatureHasher = new SHA512Cng();
  21. private static readonly HashAlgorithmName SignatureHashAlgorithm = HashAlgorithmName.SHA512;
  22. private static readonly RSASignaturePadding SignaturePadding = RSASignaturePadding.Pkcs1;
  23. public static RSACng LoadKey(string path)
  24. {
  25. var rsa = new RSACng();
  26. rsa.FromXmlString(File.ReadAllText(path));
  27. if (RsaKeySize == rsa.KeySize) return rsa;
  28. rsa.Dispose();
  29. throw new ArgumentException("invalid key size");
  30. }
  31. public static Guid ReadId(byte[] receivedData)
  32. {
  33. return new Guid(receivedData.Take(16).ToArray());
  34. }
  35. public static byte[] Encode(RSACng senderPrivateKey, RSACng receiverPublicKey, Guid senderId, byte[] data)
  36. {
  37. byte[] pass1;
  38. byte[] signature;
  39. {
  40. pass1 = receiverPublicKey.Encrypt(data, EncryptionPadding);// encode
  41. var hash = SignatureHasher.ComputeHash(pass1);
  42. using (RSA rsaCng = new RSACng())
  43. {
  44. rsaCng.ImportParameters(senderPrivateKey.ExportParameters(true));
  45. signature = rsaCng.SignHash(hash, SignatureHashAlgorithm, SignaturePadding); // signature
  46. }
  47. }
  48. var toCompress = new byte[BlockSize * 2];
  49. pass1.WriteTo(toCompress);
  50. signature.WriteTo(toCompress, BlockSize);
  51. var compressed = Compress(toCompress);
  52. var buf = new byte[16 + compressed.Length];
  53. var ptr = -1;
  54. ptr += senderId.WriteTo(buf, ++ptr);
  55. ptr += compressed.WriteTo(buf, ++ptr);
  56. return buf;
  57. }
  58. public static byte[] DecodeData(RSACng receiverPrivateKey, RSACng senderPublicKey, byte[] data)
  59. {
  60. var pass2 = Decompress(data, 16); // skip sender id
  61. pass2.ReadBytes(BlockSize, out var enc);
  62. pass2.ReadBytes(BlockSize, out var signature, BlockSize);
  63. using (RSA rsaCng = new RSACng())
  64. {
  65. rsaCng.ImportParameters(senderPublicKey.ExportParameters(false));
  66. var hash = SignatureHasher.ComputeHash(enc);
  67. if (false == rsaCng.VerifyHash(hash, signature, SignatureHashAlgorithm, SignaturePadding))
  68. {
  69. throw new InvalidDataException("Signature verify fail");
  70. }
  71. }
  72. var decodeData = receiverPrivateKey.Decrypt(enc, EncryptionPadding); //decode
  73. return decodeData;
  74. }
  75. private static byte[] Compress(byte[] data)
  76. {
  77. using (var rawIn = new MemoryStream(data))
  78. using (var gzOut = new MemoryStream())
  79. using (var gZipStream = new GZipStream(gzOut, CompressionLevel.Optimal))
  80. using (var finalOut = new MemoryStream())
  81. {
  82. rawIn.CopyTo(gZipStream);
  83. gZipStream.Close();
  84. var compressed = gzOut.ToArray();
  85. if (compressed.Length >= data.Length)
  86. {
  87. finalOut.WriteByte(0);
  88. finalOut.Write(data, 0, data.Length);
  89. }
  90. else
  91. {
  92. finalOut.WriteByte(1);
  93. finalOut.Write(compressed, 0, compressed.Length);
  94. }
  95. return finalOut.ToArray();
  96. }
  97. }
  98. private static byte[] Decompress(byte[] data, int startFrom = 0)
  99. {
  100. using (var rawIn = new MemoryStream(data))
  101. using (var finalOut = new MemoryStream())
  102. {
  103. rawIn.Position = startFrom;
  104. if (rawIn.ReadByte() == 1)
  105. {
  106. using (var gZipStream = new GZipStream(rawIn, CompressionMode.Decompress))
  107. {
  108. gZipStream.CopyTo(finalOut);
  109. }
  110. }
  111. else
  112. {
  113. rawIn.CopyTo(finalOut);
  114. }
  115. return finalOut.ToArray();
  116. }
  117. }
  118. }
  119. }