ToBase64Transform.cs 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. //
  2. // System.Security.Cryptography.ToBase64Transform
  3. //
  4. // Author:
  5. // Sergey Chaban ([email protected])
  6. //
  7. using System;
  8. using System.Security.Cryptography;
  9. namespace System.Security.Cryptography {
  10. public class ToBase64Transform : ICryptoTransform {
  11. /// <summary>
  12. /// Default constructor.
  13. /// </summary>
  14. public ToBase64Transform ()
  15. {
  16. }
  17. /// <summary>
  18. /// </summary>
  19. public bool CanTransformMultipleBlocks {
  20. get {
  21. return false;
  22. }
  23. }
  24. /// <summary>
  25. /// Returns the input block size for the Base64 encoder.
  26. /// </summary>
  27. /// <remarks>
  28. /// The returned value is always 3.
  29. /// </remarks>
  30. public int InputBlockSize {
  31. get {
  32. return 3;
  33. }
  34. }
  35. /// <summary>
  36. /// Returns the output block size for the Base64 encoder.
  37. /// </summary>
  38. /// <remarks>
  39. /// The value returned by this property is always 4.
  40. /// </remarks>
  41. public int OutputBlockSize {
  42. get {
  43. return 4;
  44. }
  45. }
  46. void System.IDisposable.Dispose ()
  47. {
  48. }
  49. /// <summary>
  50. /// </summary>
  51. public int TransformBlock (byte [] inputBuffer,
  52. int inputOffset,
  53. int inputCount,
  54. byte [] outputBuffer,
  55. int outputOffset)
  56. {
  57. if (inputCount != this.InputBlockSize)
  58. throw new CryptographicException();
  59. byte [] lookup = Base64Table.EncodeTable;
  60. int b1 = inputBuffer [inputOffset];
  61. int b2 = inputBuffer [inputOffset + 1];
  62. int b3 = inputBuffer [inputOffset + 2];
  63. outputBuffer [outputOffset] = lookup [b1 >> 2];
  64. outputBuffer [outputOffset+1] = lookup [((b1 << 4) & 0x30) | (b2 >> 4)];
  65. outputBuffer [outputOffset+2] = lookup [((b2 << 2) & 0x3c) | (b3 >> 6)];
  66. outputBuffer [outputOffset+3] = lookup [b3 & 0x3f];
  67. return this.OutputBlockSize;
  68. }
  69. // LAMESPEC: It's not clear from Beta2 docs what should be
  70. // happening here if inputCount > InputBlockSize.
  71. // It just "Converts the specified region of the specified
  72. // byte array" and that's all.
  73. // Beta2 implementation throws some strange (and undocumented)
  74. // exception in such case. The exception is thrown by
  75. // System.Convert and not the method itself.
  76. // Anyhow, this implementation just encodes blocks of any size,
  77. // like any usual Base64 encoder.
  78. /// <summary>
  79. /// </summary>
  80. public byte [] TransformFinalBlock (byte [] inputBuffer,
  81. int inputOffset,
  82. int inputCount)
  83. {
  84. int blockLen = this.InputBlockSize;
  85. int outLen = this.OutputBlockSize;
  86. int fullBlocks = inputCount / blockLen;
  87. int tail = inputCount % blockLen;
  88. byte [] res = new byte [(inputCount != 0)
  89. ? ((inputCount + 2) / blockLen) * outLen
  90. : 0];
  91. int outputOffset = 0;
  92. for (int i = 0; i < fullBlocks; i++) {
  93. TransformBlock (inputBuffer, inputOffset,
  94. blockLen, res, outputOffset);
  95. inputOffset += blockLen;
  96. outputOffset += outLen;
  97. }
  98. byte [] lookup = Base64Table.EncodeTable;
  99. int b1,b2;
  100. // When fewer than 24 input bits are available
  101. // in an input group, zero bits are added
  102. // (on the right) to form an integral number of
  103. // 6-bit groups.
  104. switch (tail) {
  105. case 0:
  106. break;
  107. case 1:
  108. b1 = inputBuffer [inputOffset];
  109. res [outputOffset] = lookup [b1 >> 2];
  110. res [outputOffset+1] = lookup [(b1 << 4) & 0x30];
  111. // padding
  112. res [outputOffset+2] = (byte)'=';
  113. res [outputOffset+3] = (byte)'=';
  114. break;
  115. case 2:
  116. b1 = inputBuffer [inputOffset];
  117. b2 = inputBuffer [inputOffset + 1];
  118. res [outputOffset] = lookup [b1 >> 2];
  119. res [outputOffset+1] = lookup [((b1 << 4) & 0x30) | (b2 >> 4)];
  120. res [outputOffset+2] = lookup [(b2 << 2) & 0x3c];
  121. // one-byte padding
  122. res [outputOffset+3] = (byte)'=';
  123. break;
  124. default:
  125. break;
  126. }
  127. return res;
  128. }
  129. } // ToBase64Transform
  130. [MonoTODO ("Put me in a separate file")]
  131. internal sealed class Base64Table {
  132. // This is the Base64 alphabet as described in RFC 2045
  133. // (Table 1, page 25).
  134. private static string ALPHABET =
  135. "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
  136. private static byte[] encodeTable;
  137. private static byte[] decodeTable;
  138. static Base64Table ()
  139. {
  140. int len = ALPHABET.Length;
  141. encodeTable = new byte [len];
  142. for (int i=0; i < len; i++) {
  143. encodeTable [i] = (byte) ALPHABET [i];
  144. }
  145. decodeTable = new byte [1 + (int)'z'];
  146. for (int i=0; i < decodeTable.Length; i++) {
  147. decodeTable [i] = Byte.MaxValue;
  148. }
  149. for (int i=0; i < len; i++) {
  150. char ch = ALPHABET [i];
  151. decodeTable [(int)ch] = (byte) i;
  152. }
  153. }
  154. private Base64Table ()
  155. {
  156. // Never instantiated.
  157. }
  158. internal static byte [] EncodeTable {
  159. get {
  160. return encodeTable;
  161. }
  162. }
  163. internal static byte [] DecodeTable {
  164. get {
  165. return decodeTable;
  166. }
  167. }
  168. } // Base64Table
  169. } // System.Security.Cryptography