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
45 public class PasswordDeriveBytes : DeriveBytes {
47 private string HashNameValue;
48 private byte[] SaltValue;
49 private int IterationsValue;
51 private HashAlgorithm hash;
53 private byte[] password;
54 private byte[] initial;
55 private byte[] output;
57 private int hashnumber;
59 public PasswordDeriveBytes (string strPassword, byte[] rgbSalt)
61 Prepare (strPassword, rgbSalt, "SHA1", 100);
64 public PasswordDeriveBytes (string strPassword, byte[] rgbSalt, CspParameters cspParams)
66 Prepare (strPassword, rgbSalt, "SHA1", 100);
67 if (cspParams != null) {
68 throw new NotSupportedException (
69 Locale.GetText ("CspParameters not supported by Mono for PasswordDeriveBytes."));
73 public PasswordDeriveBytes (string strPassword, byte[] rgbSalt, string strHashName, int iterations)
75 Prepare (strPassword, rgbSalt, strHashName, iterations);
78 public PasswordDeriveBytes (string strPassword, byte[] rgbSalt, string strHashName, int iterations, CspParameters cspParams)
80 Prepare (strPassword, rgbSalt, strHashName, iterations);
81 if (cspParams != null) {
82 throw new NotSupportedException (
83 Locale.GetText ("CspParameters not supported by Mono for PasswordDeriveBytes."));
88 public PasswordDeriveBytes (byte[] password, byte[] salt)
90 Prepare (password, salt, "SHA1", 100);
93 public PasswordDeriveBytes (byte[] password, byte[] salt, CspParameters cspParams)
95 Prepare (password, salt, "SHA1", 100);
96 if (cspParams != null) {
97 throw new NotSupportedException (
98 Locale.GetText ("CspParameters not supported by Mono for PasswordDeriveBytes."));
102 public PasswordDeriveBytes (byte[] password, byte[] salt, string hashName, int iterations)
104 Prepare (password, salt, hashName, iterations);
107 public PasswordDeriveBytes (byte[] password, byte[] salt, string hashName, int iterations, CspParameters cspParams)
109 Prepare (password, salt, hashName, iterations);
110 if (cspParams != null) {
111 throw new NotSupportedException (
112 Locale.GetText ("CspParameters not supported by Mono for PasswordDeriveBytes."));
117 ~PasswordDeriveBytes ()
120 if (initial != null) {
121 Array.Clear (initial, 0, initial.Length);
124 // zeroize temporary password storage
125 Array.Clear (password, 0, password.Length);
129 private void Prepare (string strPassword, byte[] rgbSalt, string strHashName, int iterations)
131 if (strPassword == null)
132 throw new ArgumentNullException ("strPassword");
134 byte[] pwd = Encoding.UTF8.GetBytes (strPassword);
135 Prepare (pwd, rgbSalt, strHashName, iterations);
136 Array.Clear (pwd, 0, pwd.Length);
139 private void Prepare (byte[] password, byte[] rgbSalt, string strHashName, int iterations)
141 if (password == null)
142 throw new ArgumentNullException ("password");
144 this.password = (byte[]) password.Clone ();
148 HashName = strHashName;
149 IterationCount = iterations;
153 private void Prepare (string strPassword, byte[] rgbSalt, string strHashName, int iterations)
155 if (strPassword == null)
158 password = Encoding.UTF8.GetBytes (strPassword);
163 SaltValue = (byte[]) rgbSalt.Clone ();
165 HashName = strHashName;
166 IterationCount = iterations;
171 public string HashName {
172 get { return HashNameValue; }
175 throw new ArgumentNullException ("HashName");
177 throw new CryptographicException (
178 Locale.GetText ("Can't change this property at this stage"));
180 HashNameValue = value;
184 public int IterationCount {
185 get { return IterationsValue; }
188 throw new ArgumentOutOfRangeException ("> 0", "IterationCount");
190 throw new CryptographicException (
191 Locale.GetText ("Can't change this property at this stage"));
193 IterationsValue = value;
199 if (SaltValue == null)
201 return (byte[]) SaltValue.Clone ();
205 throw new CryptographicException (
206 Locale.GetText ("Can't change this property at this stage"));
210 SaltValue = (byte[]) value.Clone ();
214 // this will cause a NullReferenceException if set to null (like 1.0/1.1)
215 SaltValue = (byte[]) value.Clone ();
220 public byte[] CryptDeriveKey (string algname, string alghashname, int keySize, byte[] rgbIV)
223 throw new CryptographicException (
224 Locale.GetText ("Key Size can't be greater than 128 bits"));
226 throw new NotSupportedException (
227 Locale.GetText ("CspParameters not supported by Mono"));
230 // note: Key is returned - we can't zeroize it ourselve :-(
232 [Obsolete ("see Rfc2898DeriveBytes for PKCS#5 v2 support")]
234 public override byte[] GetBytes (int cb)
237 // 1.0/1.1 was a little late at throwing the argument exception ;-)
238 if (password == null)
239 throw new ArgumentNullException ("Password");
242 throw new IndexOutOfRangeException ("cb");
245 // it's now impossible to change the HashName, Salt
246 // and IterationCount
251 byte[] result = new byte [cb];
253 // the initial hash (in reset) + at least one iteration
254 int iter = Math.Max (1, IterationsValue - 1);
256 // start with the PKCS5 key
257 if (output == null) {
258 // calculate the PKCS5 key
261 // generate new key material
262 for (int i = 0; i < iter - 1; i++)
263 output = hash.ComputeHash (output);
267 byte[] output2 = null;
268 if (hashnumber == 0) {
269 // last iteration on output
270 output2 = hash.ComputeHash (output);
272 else if (hashnumber < 1000) {
273 string n = Convert.ToString (hashnumber);
274 output2 = new byte [output.Length + n.Length];
275 for (int j=0; j < n.Length; j++)
276 output2 [j] = (byte)(n [j]);
277 Buffer.BlockCopy (output, 0, output2, n.Length, output.Length);
278 // don't update output
279 output2 = hash.ComputeHash (output2);
282 throw new CryptographicException (
283 Locale.GetText ("too long"));
286 int rem = output2.Length - position;
287 int l = Math.Min (cb - cpos, rem);
288 Buffer.BlockCopy (output2, position, result, cpos, l);
291 while (position >= output2.Length) {
292 position -= output2.Length;
299 public override void Reset ()
304 // note: Reset doesn't change state
309 hash = HashAlgorithm.Create (HashNameValue);
310 if (SaltValue != null) {
311 hash.TransformBlock (password, 0, password.Length, password, 0);
312 hash.TransformFinalBlock (SaltValue, 0, SaltValue.Length);
316 initial = hash.ComputeHash (password);