2 // PasswordDeriveBytes.cs: Handles PKCS#5 key derivation using password
5 // Sebastien Pouliot (sebastien@ximian.com)
7 // (C) 2002, 2003 Motus Technologies Inc. (http://www.motus.com)
8 // Copyright (C) 2004-2007 Novell, Inc (http://www.novell.com)
10 // Permission is hereby granted, free of charge, to any person obtaining
11 // a copy of this software and associated documentation files (the
12 // "Software"), to deal in the Software without restriction, including
13 // without limitation the rights to use, copy, modify, merge, publish,
14 // distribute, sublicense, and/or sell copies of the Software, and to
15 // permit persons to whom the Software is furnished to do so, subject to
16 // the following conditions:
18 // The above copyright notice and this permission notice shall be
19 // included in all copies or substantial portions of the Software.
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
25 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
27 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 using System.Globalization;
31 using System.Runtime.InteropServices;
34 namespace System.Security.Cryptography {
37 // a. PKCS #5 - Password-Based Cryptography Standard
38 // http://www.rsasecurity.com/rsalabs/pkcs/pkcs-5/index.html
39 // b. IETF RFC2898: PKCS #5: Password-Based Cryptography Specification Version 2.0
40 // http://www.rfc-editor.org/rfc/rfc2898.txt
43 public class PasswordDeriveBytes : DeriveBytes {
45 private string HashNameValue;
46 private byte[] SaltValue;
47 private int IterationsValue;
49 private HashAlgorithm hash;
51 private byte[] password;
52 private byte[] initial;
53 private byte[] output;
55 private int hashnumber;
57 public PasswordDeriveBytes (string strPassword, byte[] rgbSalt)
59 Prepare (strPassword, rgbSalt, "SHA1", 100);
62 public PasswordDeriveBytes (string strPassword, byte[] rgbSalt, CspParameters cspParams)
64 Prepare (strPassword, rgbSalt, "SHA1", 100);
65 if (cspParams != null) {
66 throw new NotSupportedException (
67 Locale.GetText ("CspParameters not supported by Mono for PasswordDeriveBytes."));
71 public PasswordDeriveBytes (string strPassword, byte[] rgbSalt, string strHashName, int iterations)
73 Prepare (strPassword, rgbSalt, strHashName, iterations);
76 public PasswordDeriveBytes (string strPassword, byte[] rgbSalt, string strHashName, int iterations, CspParameters cspParams)
78 Prepare (strPassword, rgbSalt, strHashName, iterations);
79 if (cspParams != null) {
80 throw new NotSupportedException (
81 Locale.GetText ("CspParameters not supported by Mono for PasswordDeriveBytes."));
85 public PasswordDeriveBytes (byte[] password, byte[] salt)
87 Prepare (password, salt, "SHA1", 100);
90 public PasswordDeriveBytes (byte[] password, byte[] salt, CspParameters cspParams)
92 Prepare (password, salt, "SHA1", 100);
93 if (cspParams != null) {
94 throw new NotSupportedException (
95 Locale.GetText ("CspParameters not supported by Mono for PasswordDeriveBytes."));
99 public PasswordDeriveBytes (byte[] password, byte[] salt, string hashName, int iterations)
101 Prepare (password, salt, hashName, iterations);
104 public PasswordDeriveBytes (byte[] password, byte[] salt, string hashName, int iterations, CspParameters cspParams)
106 Prepare (password, salt, hashName, iterations);
107 if (cspParams != null) {
108 throw new NotSupportedException (
109 Locale.GetText ("CspParameters not supported by Mono for PasswordDeriveBytes."));
114 protected override void Dispose (bool disposing)
117 if (initial != null) {
118 Array.Clear (initial, 0, initial.Length);
121 // zeroize temporary password storage
122 if (password != null) {
123 Array.Clear (password, 0, password.Length);
126 base.Dispose (disposing);
130 private void Prepare (string strPassword, byte[] rgbSalt, string strHashName, int iterations)
132 if (strPassword == null)
133 throw new ArgumentNullException ("strPassword");
135 byte[] pwd = Encoding.UTF8.GetBytes (strPassword);
136 Prepare (pwd, rgbSalt, strHashName, iterations);
137 Array.Clear (pwd, 0, pwd.Length);
140 private void Prepare (byte[] password, byte[] rgbSalt, string strHashName, int iterations)
142 if (password == null)
143 throw new ArgumentNullException ("password");
145 this.password = (byte[]) password.Clone ();
149 HashName = strHashName;
150 IterationCount = iterations;
153 public string HashName {
154 get { return HashNameValue; }
157 throw new ArgumentNullException ("HashName");
159 throw new CryptographicException (
160 Locale.GetText ("Can't change this property at this stage"));
162 HashNameValue = value;
166 public int IterationCount {
167 get { return IterationsValue; }
170 throw new ArgumentOutOfRangeException ("> 0", "IterationCount");
172 throw new CryptographicException (
173 Locale.GetText ("Can't change this property at this stage"));
175 IterationsValue = value;
181 if (SaltValue == null)
183 return (byte[]) SaltValue.Clone ();
187 throw new CryptographicException (
188 Locale.GetText ("Can't change this property at this stage"));
191 SaltValue = (byte[]) value.Clone ();
197 public byte[] CryptDeriveKey (string algname, string alghashname, int keySize, byte[] rgbIV)
200 throw new CryptographicException (
201 Locale.GetText ("Key Size can't be greater than 128 bits"));
203 throw new NotSupportedException (
204 Locale.GetText ("CspParameters not supported by Mono"));
207 // note: Key is returned - we can't zeroize it ourselve :-(
208 [Obsolete ("see Rfc2898DeriveBytes for PKCS#5 v2 support")]
209 #pragma warning disable 809
210 public override byte[] GetBytes (int cb)
212 #pragma warning restore 809
215 throw new IndexOutOfRangeException ("cb");
218 // it's now impossible to change the HashName, Salt
219 // and IterationCount
224 byte[] result = new byte [cb];
226 // the initial hash (in reset) + at least one iteration
227 int iter = Math.Max (1, IterationsValue - 1);
229 // start with the PKCS5 key
230 if (output == null) {
231 // calculate the PKCS5 key
234 // generate new key material
235 for (int i = 0; i < iter - 1; i++)
236 output = hash.ComputeHash (output);
240 byte[] output2 = null;
241 if (hashnumber == 0) {
242 // last iteration on output
243 output2 = hash.ComputeHash (output);
245 else if (hashnumber < 1000) {
246 string n = Convert.ToString (hashnumber);
247 output2 = new byte [output.Length + n.Length];
248 for (int j=0; j < n.Length; j++)
249 output2 [j] = (byte)(n [j]);
250 Buffer.BlockCopy (output, 0, output2, n.Length, output.Length);
251 // don't update output
252 output2 = hash.ComputeHash (output2);
255 throw new CryptographicException (
256 Locale.GetText ("too long"));
259 int rem = output2.Length - position;
260 int l = Math.Min (cb - cpos, rem);
261 Buffer.BlockCopy (output2, position, result, cpos, l);
264 while (position >= output2.Length) {
265 position -= output2.Length;
272 public override void Reset ()
278 hash = HashAlgorithm.Create (HashNameValue);
279 if (SaltValue != null) {
280 hash.TransformBlock (password, 0, password.Length, password, 0);
281 hash.TransformFinalBlock (SaltValue, 0, SaltValue.Length);
285 initial = hash.ComputeHash (password);