PKMacBuilder.cs 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290
  1. #if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
  2. #pragma warning disable
  3. using System;
  4. using System.IO;
  5. using Best.HTTP.SecureProtocol.Org.BouncyCastle.Asn1;
  6. using Best.HTTP.SecureProtocol.Org.BouncyCastle.Asn1.Cmp;
  7. using Best.HTTP.SecureProtocol.Org.BouncyCastle.Asn1.Iana;
  8. using Best.HTTP.SecureProtocol.Org.BouncyCastle.Asn1.Oiw;
  9. using Best.HTTP.SecureProtocol.Org.BouncyCastle.Asn1.X509;
  10. using Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto;
  11. using Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.IO;
  12. using Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Parameters;
  13. using Best.HTTP.SecureProtocol.Org.BouncyCastle.Security;
  14. using Best.HTTP.SecureProtocol.Org.BouncyCastle.Utilities;
  15. namespace Best.HTTP.SecureProtocol.Org.BouncyCastle.Crmf
  16. {
  17. internal class PKMacStreamCalculator
  18. : IStreamCalculator<DefaultPKMacResult>
  19. {
  20. private readonly MacSink _stream;
  21. public PKMacStreamCalculator(IMac mac)
  22. {
  23. _stream = new MacSink(mac);
  24. }
  25. public Stream Stream
  26. {
  27. get { return _stream; }
  28. }
  29. public DefaultPKMacResult GetResult()
  30. {
  31. return new DefaultPKMacResult(_stream.Mac);
  32. }
  33. }
  34. internal class PKMacFactory
  35. : IMacFactory
  36. {
  37. protected readonly PbmParameter parameters;
  38. private readonly byte[] key;
  39. public PKMacFactory(byte[] key, PbmParameter parameters)
  40. {
  41. this.key = Arrays.Clone(key);
  42. this.parameters = parameters;
  43. }
  44. public virtual object AlgorithmDetails
  45. {
  46. get { return new AlgorithmIdentifier(CmpObjectIdentifiers.passwordBasedMac, parameters); }
  47. }
  48. public virtual IStreamCalculator<IBlockResult> CreateCalculator()
  49. {
  50. IMac mac = MacUtilities.GetMac(parameters.Mac.Algorithm);
  51. mac.Init(new KeyParameter(key));
  52. return new PKMacStreamCalculator(mac);
  53. }
  54. }
  55. internal class DefaultPKMacResult
  56. : IBlockResult
  57. {
  58. private readonly IMac mac;
  59. public DefaultPKMacResult(IMac mac)
  60. {
  61. this.mac = mac;
  62. }
  63. public byte[] Collect()
  64. {
  65. byte[] res = new byte[mac.GetMacSize()];
  66. mac.DoFinal(res, 0);
  67. return res;
  68. }
  69. public int Collect(byte[] sig, int sigOff)
  70. {
  71. byte[] signature = Collect();
  72. signature.CopyTo(sig, sigOff);
  73. return signature.Length;
  74. }
  75. #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
  76. public int Collect(Span<byte> destination)
  77. {
  78. byte[] result = Collect();
  79. result.CopyTo(destination);
  80. return result.Length;
  81. }
  82. #endif
  83. }
  84. public class PKMacBuilder
  85. {
  86. private AlgorithmIdentifier owf;
  87. private AlgorithmIdentifier mac;
  88. private IPKMacPrimitivesProvider provider;
  89. private SecureRandom random;
  90. private PbmParameter parameters;
  91. private int iterationCount;
  92. private int saltLength = 20;
  93. private int maxIterations;
  94. /// <summary>
  95. /// Default, IterationCount = 1000, OIW=IdSha1, Mac=HmacSHA1
  96. /// </summary>
  97. public PKMacBuilder() :
  98. this(new AlgorithmIdentifier(OiwObjectIdentifiers.IdSha1), 1000, new AlgorithmIdentifier(IanaObjectIdentifiers.HmacSha1, DerNull.Instance), new DefaultPKMacPrimitivesProvider())
  99. {
  100. }
  101. /// <summary>
  102. /// Defaults with IPKMacPrimitivesProvider
  103. /// </summary>
  104. /// <param name="provider"></param>
  105. public PKMacBuilder(IPKMacPrimitivesProvider provider) :
  106. this(new AlgorithmIdentifier(OiwObjectIdentifiers.IdSha1), 1000, new AlgorithmIdentifier(IanaObjectIdentifiers.HmacSha1, DerNull.Instance), provider)
  107. {
  108. }
  109. /// <summary>
  110. /// Create.
  111. /// </summary>
  112. /// <param name="provider">The Mac provider</param>
  113. /// <param name="digestAlgorithmIdentifier">Digest Algorithm Id</param>
  114. /// <param name="macAlgorithmIdentifier">Mac Algorithm Id</param>
  115. public PKMacBuilder(IPKMacPrimitivesProvider provider, AlgorithmIdentifier digestAlgorithmIdentifier, AlgorithmIdentifier macAlgorithmIdentifier) :
  116. this(digestAlgorithmIdentifier, 1000, macAlgorithmIdentifier, provider)
  117. {
  118. }
  119. /// <summary>
  120. /// Create a PKMAC builder enforcing a ceiling on the maximum iteration count.
  121. /// </summary>
  122. /// <param name="provider">supporting calculator</param>
  123. /// <param name="maxIterations">max allowable value for iteration count.</param>
  124. public PKMacBuilder(IPKMacPrimitivesProvider provider, int maxIterations)
  125. {
  126. this.provider = provider;
  127. this.maxIterations = maxIterations;
  128. }
  129. private PKMacBuilder(AlgorithmIdentifier digestAlgorithmIdentifier, int iterationCount, AlgorithmIdentifier macAlgorithmIdentifier, IPKMacPrimitivesProvider provider)
  130. {
  131. this.iterationCount = iterationCount;
  132. this.mac = macAlgorithmIdentifier;
  133. this.owf = digestAlgorithmIdentifier;
  134. this.provider = provider;
  135. }
  136. /**
  137. * Set the salt length in octets.
  138. *
  139. * @param saltLength length in octets of the salt to be generated.
  140. * @return the generator
  141. */
  142. public PKMacBuilder SetSaltLength(int saltLength)
  143. {
  144. if (saltLength < 8)
  145. throw new ArgumentException("salt length must be at least 8 bytes");
  146. this.saltLength = saltLength;
  147. return this;
  148. }
  149. /// <summary>
  150. /// Set the iteration count.
  151. /// </summary>
  152. /// <param name="iterationCount">the iteration count.</param>
  153. /// <returns>this</returns>
  154. /// <exception cref="ArgumentException">if iteration count is less than 100</exception>
  155. public PKMacBuilder SetIterationCount(int iterationCount)
  156. {
  157. if (iterationCount < 100)
  158. throw new ArgumentException("iteration count must be at least 100");
  159. CheckIterationCountCeiling(iterationCount);
  160. this.iterationCount = iterationCount;
  161. return this;
  162. }
  163. /// <summary>
  164. /// Set PbmParameters
  165. /// </summary>
  166. /// <param name="parameters">The parameters.</param>
  167. /// <returns>this</returns>
  168. public PKMacBuilder SetParameters(PbmParameter parameters)
  169. {
  170. CheckIterationCountCeiling(parameters.IterationCount.IntValueExact);
  171. this.parameters = parameters;
  172. return this;
  173. }
  174. /// <summary>
  175. /// The Secure random
  176. /// </summary>
  177. /// <param name="random">The random.</param>
  178. /// <returns>this</returns>
  179. public PKMacBuilder SetSecureRandom(SecureRandom random)
  180. {
  181. this.random = random;
  182. return this;
  183. }
  184. /// <summary>
  185. /// Build an IMacFactory.
  186. /// </summary>
  187. /// <param name="password">The password.</param>
  188. /// <returns>IMacFactory</returns>
  189. public IMacFactory Build(char[] password)
  190. {
  191. if (parameters != null)
  192. return GenCalculator(parameters, password);
  193. byte[] salt = new byte[saltLength];
  194. this.random = CryptoServicesRegistrar.GetSecureRandom(random);
  195. random.NextBytes(salt);
  196. return GenCalculator(new PbmParameter(salt, owf, iterationCount, mac), password);
  197. }
  198. private void CheckIterationCountCeiling(int iterationCount)
  199. {
  200. if (maxIterations > 0 && iterationCount > maxIterations)
  201. throw new ArgumentException("iteration count exceeds limit (" + iterationCount + " > " + maxIterations + ")");
  202. }
  203. private IMacFactory GenCalculator(PbmParameter parameters, char[] password)
  204. {
  205. // From RFC 4211
  206. //
  207. // 1. Generate a random salt value S
  208. //
  209. // 2. Append the salt to the pw. K = pw || salt.
  210. //
  211. // 3. Hash the value of K. K = HASH(K)
  212. //
  213. // 4. Iter = Iter - 1. If Iter is greater than zero. Goto step 3.
  214. //
  215. // 5. Compute an HMAC as documented in [HMAC].
  216. //
  217. // MAC = HASH( K XOR opad, HASH( K XOR ipad, data) )
  218. //
  219. // Where opad and ipad are defined in [HMAC].
  220. byte[] pw = Strings.ToUtf8ByteArray(password);
  221. byte[] salt = parameters.Salt.GetOctets();
  222. byte[] K = new byte[pw.Length + salt.Length];
  223. Array.Copy(pw, 0, K, 0, pw.Length);
  224. Array.Copy(salt, 0, K, pw.Length, salt.Length);
  225. IDigest digest = provider.CreateDigest(parameters.Owf);
  226. int iter = parameters.IterationCount.IntValueExact;
  227. digest.BlockUpdate(K, 0, K.Length);
  228. K = new byte[digest.GetDigestSize()];
  229. digest.DoFinal(K, 0);
  230. while (--iter > 0)
  231. {
  232. digest.BlockUpdate(K, 0, K.Length);
  233. digest.DoFinal(K, 0);
  234. }
  235. byte[] key = K;
  236. return new PKMacFactory(key, parameters);
  237. }
  238. }
  239. }
  240. #pragma warning restore
  241. #endif