PasswordDeriveBytes.cs 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. //
  2. // PasswordDeriveBytes.cs: Handles PKCS#5 key derivation using password
  3. //
  4. // Author:
  5. // Sebastien Pouliot ([email protected])
  6. //
  7. // (C) 2002 Motus Technologies Inc. (http://www.motus.com)
  8. //
  9. using System;
  10. using System.Text;
  11. namespace System.Security.Cryptography {
  12. // Reference:
  13. // a. PKCS #5 - Password-Based Cryptography Standard
  14. // http://www.rsasecurity.com/rsalabs/pkcs/pkcs-5/index.html
  15. // b. IETF RFC2898: PKCS #5: Password-Based Cryptography Specification Version 2.0
  16. // http://www.rfc-editor.org/rfc/rfc2898.txt
  17. public class PasswordDeriveBytes : DeriveBytes {
  18. private string HashNameValue;
  19. private string PasswordValue;
  20. private byte[] SaltValue;
  21. private int IterationsValue;
  22. private HashAlgorithm hash;
  23. private int state;
  24. private byte[] output;
  25. public PasswordDeriveBytes (string strPassword, byte[] rgbSalt)
  26. {
  27. Prepare (strPassword, rgbSalt, "SHA1", 1);
  28. }
  29. [MonoTODO("Integrate with CAPI on Windows. Linux?")]
  30. public PasswordDeriveBytes (string strPassword, byte[] rgbSalt, CspParameters cspParams)
  31. {
  32. throw new NotSupportedException ("CspParameters not supported");
  33. }
  34. public PasswordDeriveBytes (string strPassword, byte[] rgbSalt, string strHashName, int iterations)
  35. {
  36. Prepare (strPassword, rgbSalt, strHashName, iterations);
  37. }
  38. [MonoTODO("Integrate with CAPI on Windows. Linux?")]
  39. public PasswordDeriveBytes (string strPassword, byte[] rgbSalt, string strHashName, int iterations, CspParameters cspParams)
  40. {
  41. throw new NotSupportedException ("CspParameters not supported");
  42. }
  43. [MonoTODO("Must release unmanaged resources on Windows (CAPI). Linux?")]
  44. ~PasswordDeriveBytes ()
  45. {
  46. // zeroize buffer
  47. if (output != null) {
  48. Array.Clear (output, 0, output.Length);
  49. output = null;
  50. }
  51. // FIXME: zeroize password - not easy as all string function
  52. // returns a string so we never have direct access to it's
  53. // content - the password :-(
  54. PasswordValue = null;
  55. }
  56. private void Prepare (string strPassword, byte[] rgbSalt, string strHashName, int iterations)
  57. {
  58. HashNameValue = strHashName;
  59. PasswordValue = strPassword;
  60. SaltValue = rgbSalt;
  61. IterationsValue = iterations;
  62. state = 0;
  63. }
  64. public string HashName {
  65. get { return HashNameValue; }
  66. set {
  67. if (state != 0)
  68. throw new CryptographicException ();
  69. HashNameValue = value;
  70. }
  71. }
  72. public int IterationCount {
  73. get { return IterationsValue; }
  74. set {
  75. if (state != 0)
  76. throw new CryptographicException ();
  77. IterationsValue = value;
  78. }
  79. }
  80. // FIXME ??? Comments are here to simulate the strange behaviour that
  81. // happens when we want to assign null to a salt ??? Do we want this
  82. // "feature" in Mono ? Vote "yes" for compatibility or "no" for
  83. // functionality!
  84. public byte[] Salt {
  85. get { return (byte[]) SaltValue.Clone (); }
  86. set {
  87. if (state != 0)
  88. throw new CryptographicException ();
  89. // if (value != null)
  90. SaltValue = (byte[]) value.Clone ();
  91. // else
  92. // value = null;
  93. }
  94. }
  95. // for compatibility with Microsoft CryptoAPI
  96. [MonoTODO("Integrate with CAPI on Windows. Linux?")]
  97. public byte[] CryptDeriveKey (string algname, string alghashname, int keySize, byte[] rgbIV)
  98. {
  99. if (keySize > 128)
  100. throw new CryptographicException ("Key Size can't be greater than 128 bits");
  101. throw new NotSupportedException ("CspParameters not supported");
  102. }
  103. // note: Key is returned - we can't zeroize it ourselve :-(
  104. [MonoTODO("Doesn't generate keys longer than HashSize")]
  105. public override byte[] GetBytes (int cb)
  106. {
  107. // must be first (before NotSupportedException) as the Hash
  108. // object is created in Reset()
  109. if (state == 0) {
  110. state = 1;
  111. // it's now impossible to change the HashName, Salt
  112. // and IterationCount
  113. Reset ();
  114. }
  115. // FIXME: This version can generate a key up to HashSize length
  116. // This is normal for PKCS#5 but MS implementation allows longer
  117. // keys (note that, in this case, longer keys aren't more secure!)
  118. if (cb > hash.HashSize)
  119. throw new NotSupportedException ("cb > HashSize");
  120. hash.Initialize ();
  121. // the initial hash (in reset) + at least one iteration
  122. int iter = Math.Max (1, IterationsValue - 1);
  123. // generate new key material
  124. for (int i = 0; i < iter; i++)
  125. output = hash.ComputeHash (output);
  126. byte[] result = new byte [cb];
  127. Array.Copy (output, 0, result, 0, cb);
  128. return result;
  129. }
  130. public override void Reset ()
  131. {
  132. // note: Reset doesn't change state
  133. byte[] password = Encoding.UTF8.GetBytes (PasswordValue);
  134. int len = password.Length;
  135. if (SaltValue != null)
  136. len += SaltValue.Length;
  137. byte[] input = new byte [len];
  138. Array.Copy (password, 0, input, 0, password.Length);
  139. // zeroize temporary password storage
  140. Array.Clear (password, 0, password.Length);
  141. if (SaltValue != null)
  142. Array.Copy (SaltValue, 0, input, password.Length, SaltValue.Length);
  143. hash = HashAlgorithm.Create (HashNameValue);
  144. output = hash.ComputeHash (input);
  145. }
  146. }
  147. }