/Mono.Security.Cryptography/CryptoService.cs

http://github.com/jbevain/cecil · C# · 204 lines · 142 code · 43 blank · 19 comment · 12 complexity · ce8a7ac234de78552d317d1c37ee1cb4 MD5 · raw file

  1. //
  2. // Author:
  3. // Jb Evain (jbevain@gmail.com)
  4. //
  5. // Copyright (c) 2008 - 2015 Jb Evain
  6. // Copyright (c) 2008 - 2011 Novell, Inc.
  7. //
  8. // Licensed under the MIT/X11 license.
  9. //
  10. using System;
  11. using System.IO;
  12. using System.Reflection;
  13. using System.Security.Cryptography;
  14. using System.Runtime.Serialization;
  15. using Mono.Security.Cryptography;
  16. using Mono.Cecil.PE;
  17. namespace Mono.Cecil {
  18. // Most of this code has been adapted
  19. // from Jeroen Frijters' fantastic work
  20. // in IKVM.Reflection.Emit. Thanks!
  21. static class CryptoService {
  22. public static byte [] GetPublicKey (WriterParameters parameters)
  23. {
  24. using (var rsa = parameters.CreateRSA ()) {
  25. var cspBlob = CryptoConvert.ToCapiPublicKeyBlob (rsa);
  26. var publicKey = new byte [12 + cspBlob.Length];
  27. Buffer.BlockCopy (cspBlob, 0, publicKey, 12, cspBlob.Length);
  28. // The first 12 bytes are documented at:
  29. // http://msdn.microsoft.com/library/en-us/cprefadd/html/grfungethashfromfile.asp
  30. // ALG_ID - Signature
  31. publicKey [1] = 36;
  32. // ALG_ID - Hash
  33. publicKey [4] = 4;
  34. publicKey [5] = 128;
  35. // Length of Public Key (in bytes)
  36. publicKey [8] = (byte) (cspBlob.Length >> 0);
  37. publicKey [9] = (byte) (cspBlob.Length >> 8);
  38. publicKey [10] = (byte) (cspBlob.Length >> 16);
  39. publicKey [11] = (byte) (cspBlob.Length >> 24);
  40. return publicKey;
  41. }
  42. }
  43. public static void StrongName (Stream stream, ImageWriter writer, WriterParameters parameters)
  44. {
  45. int strong_name_pointer;
  46. var strong_name = CreateStrongName (parameters, HashStream (stream, writer, out strong_name_pointer));
  47. PatchStrongName (stream, strong_name_pointer, strong_name);
  48. }
  49. static void PatchStrongName (Stream stream, int strong_name_pointer, byte [] strong_name)
  50. {
  51. stream.Seek (strong_name_pointer, SeekOrigin.Begin);
  52. stream.Write (strong_name, 0, strong_name.Length);
  53. }
  54. static byte [] CreateStrongName (WriterParameters parameters, byte [] hash)
  55. {
  56. const string hash_algo = "SHA1";
  57. using (var rsa = parameters.CreateRSA ()) {
  58. var formatter = new RSAPKCS1SignatureFormatter (rsa);
  59. formatter.SetHashAlgorithm (hash_algo);
  60. byte [] signature = formatter.CreateSignature (hash);
  61. Array.Reverse (signature);
  62. return signature;
  63. }
  64. }
  65. static byte [] HashStream (Stream stream, ImageWriter writer, out int strong_name_pointer)
  66. {
  67. const int buffer_size = 8192;
  68. var text = writer.text;
  69. var header_size = (int) writer.GetHeaderSize ();
  70. var text_section_pointer = (int) text.PointerToRawData;
  71. var strong_name_directory = writer.GetStrongNameSignatureDirectory ();
  72. if (strong_name_directory.Size == 0)
  73. throw new InvalidOperationException ();
  74. strong_name_pointer = (int) (text_section_pointer
  75. + (strong_name_directory.VirtualAddress - text.VirtualAddress));
  76. var strong_name_length = (int) strong_name_directory.Size;
  77. var sha1 = new SHA1Managed ();
  78. var buffer = new byte [buffer_size];
  79. using (var crypto_stream = new CryptoStream (Stream.Null, sha1, CryptoStreamMode.Write)) {
  80. stream.Seek (0, SeekOrigin.Begin);
  81. CopyStreamChunk (stream, crypto_stream, buffer, header_size);
  82. stream.Seek (text_section_pointer, SeekOrigin.Begin);
  83. CopyStreamChunk (stream, crypto_stream, buffer, (int) strong_name_pointer - text_section_pointer);
  84. stream.Seek (strong_name_length, SeekOrigin.Current);
  85. CopyStreamChunk (stream, crypto_stream, buffer, (int) (stream.Length - (strong_name_pointer + strong_name_length)));
  86. }
  87. return sha1.Hash;
  88. }
  89. static void CopyStreamChunk (Stream stream, Stream dest_stream, byte [] buffer, int length)
  90. {
  91. while (length > 0) {
  92. int read = stream.Read (buffer, 0, System.Math.Min (buffer.Length, length));
  93. dest_stream.Write (buffer, 0, read);
  94. length -= read;
  95. }
  96. }
  97. public static byte [] ComputeHash (string file)
  98. {
  99. if (!File.Exists (file))
  100. return Empty<byte>.Array;
  101. using (var stream = new FileStream (file, FileMode.Open, FileAccess.Read, FileShare.Read))
  102. return ComputeHash (stream);
  103. }
  104. public static byte [] ComputeHash (Stream stream)
  105. {
  106. const int buffer_size = 8192;
  107. var sha1 = new SHA1Managed ();
  108. var buffer = new byte [buffer_size];
  109. using (var crypto_stream = new CryptoStream (Stream.Null, sha1, CryptoStreamMode.Write))
  110. CopyStreamChunk (stream, crypto_stream, buffer, (int) stream.Length);
  111. return sha1.Hash;
  112. }
  113. public static byte [] ComputeHash (params ByteBuffer [] buffers)
  114. {
  115. var sha1 = new SHA1Managed ();
  116. using (var crypto_stream = new CryptoStream (Stream.Null, sha1, CryptoStreamMode.Write)) {
  117. for (int i = 0; i < buffers.Length; i++) {
  118. crypto_stream.Write (buffers [i].buffer, 0, buffers [i].length);
  119. }
  120. }
  121. return sha1.Hash;
  122. }
  123. public static Guid ComputeGuid (byte [] hash)
  124. {
  125. // From corefx/src/System.Reflection.Metadata/src/System/Reflection/Metadata/BlobContentId.cs
  126. var guid = new byte [16];
  127. Buffer.BlockCopy (hash, 0, guid, 0, 16);
  128. // modify the guid data so it decodes to the form of a "random" guid ala rfc4122
  129. guid [7] = (byte) ((guid [7] & 0x0f) | (4 << 4));
  130. guid [8] = (byte) ((guid [8] & 0x3f) | (2 << 6));
  131. return new Guid (guid);
  132. }
  133. }
  134. static partial class Mixin {
  135. public static RSA CreateRSA (this WriterParameters writer_parameters)
  136. {
  137. byte [] key;
  138. string key_container;
  139. if (writer_parameters.StrongNameKeyBlob != null)
  140. return CryptoConvert.FromCapiKeyBlob (writer_parameters.StrongNameKeyBlob);
  141. if (writer_parameters.StrongNameKeyContainer != null)
  142. key_container = writer_parameters.StrongNameKeyContainer;
  143. else if (!TryGetKeyContainer (writer_parameters.StrongNameKeyPair, out key, out key_container))
  144. return CryptoConvert.FromCapiKeyBlob (key);
  145. var parameters = new CspParameters {
  146. Flags = CspProviderFlags.UseMachineKeyStore,
  147. KeyContainerName = key_container,
  148. KeyNumber = 2,
  149. };
  150. return new RSACryptoServiceProvider (parameters);
  151. }
  152. static bool TryGetKeyContainer (ISerializable key_pair, out byte [] key, out string key_container)
  153. {
  154. var info = new SerializationInfo (typeof (StrongNameKeyPair), new FormatterConverter ());
  155. key_pair.GetObjectData (info, new StreamingContext ());
  156. key = (byte []) info.GetValue ("_keyPairArray", typeof (byte []));
  157. key_container = info.GetString ("_keyPairContainer");
  158. return key_container != null;
  159. }
  160. }
  161. }