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."));
113 protected override void Dispose (bool disposing)
116 if (initial != null) {
117 Array.Clear (initial, 0, initial.Length);
120 // zeroize temporary password storage
121 if (password != null) {
122 Array.Clear (password, 0, password.Length);
125 base.Dispose (disposing);
128 private void Prepare (string strPassword, byte[] rgbSalt, string strHashName, int iterations)
130 if (strPassword == null)
131 throw new ArgumentNullException ("strPassword");
133 byte[] pwd = Encoding.UTF8.GetBytes (strPassword);
134 Prepare (pwd, rgbSalt, strHashName, iterations);
135 Array.Clear (pwd, 0, pwd.Length);
138 private void Prepare (byte[] password, byte[] rgbSalt, string strHashName, int iterations)
140 if (password == null)
141 throw new ArgumentNullException ("password");
143 this.password = (byte[]) password.Clone ();
147 HashName = strHashName;
148 IterationCount = iterations;
151 public string HashName {
152 get { return HashNameValue; }
155 throw new ArgumentNullException ("HashName");
157 throw new CryptographicException (
158 Locale.GetText ("Can't change this property at this stage"));
160 HashNameValue = value;
164 public int IterationCount {
165 get { return IterationsValue; }
168 throw new ArgumentOutOfRangeException ("> 0", "IterationCount");
170 throw new CryptographicException (
171 Locale.GetText ("Can't change this property at this stage"));
173 IterationsValue = value;
179 if (SaltValue == null)
181 return (byte[]) SaltValue.Clone ();
185 throw new CryptographicException (
186 Locale.GetText ("Can't change this property at this stage"));
189 SaltValue = (byte[]) value.Clone ();
195 public byte[] CryptDeriveKey (string algname, string alghashname, int keySize, byte[] rgbIV)
198 throw new CryptographicException (
199 Locale.GetText ("Key Size can't be greater than 128 bits"));
201 throw new NotSupportedException (
202 Locale.GetText ("CspParameters not supported by Mono"));
205 // note: Key is returned - we can't zeroize it ourselve :-(
206 [Obsolete ("see Rfc2898DeriveBytes for PKCS#5 v2 support")]
207 #pragma warning disable 809
208 public override byte[] GetBytes (int cb)
210 #pragma warning restore 809
213 throw new IndexOutOfRangeException ("cb");
216 // it's now impossible to change the HashName, Salt
217 // and IterationCount
222 byte[] result = new byte [cb];
224 // the initial hash (in reset) + at least one iteration
225 int iter = Math.Max (1, IterationsValue - 1);
227 // start with the PKCS5 key
228 if (output == null) {
229 // calculate the PKCS5 key
232 // generate new key material
233 for (int i = 0; i < iter - 1; i++)
234 output = hash.ComputeHash (output);
238 byte[] output2 = null;
239 if (hashnumber == 0) {
240 // last iteration on output
241 output2 = hash.ComputeHash (output);
243 else if (hashnumber < 1000) {
244 string n = Convert.ToString (hashnumber);
245 output2 = new byte [output.Length + n.Length];
246 for (int j=0; j < n.Length; j++)
247 output2 [j] = (byte)(n [j]);
248 Buffer.BlockCopy (output, 0, output2, n.Length, output.Length);
249 // don't update output
250 output2 = hash.ComputeHash (output2);
253 throw new CryptographicException (
254 Locale.GetText ("too long"));
257 int rem = output2.Length - position;
258 int l = Math.Min (cb - cpos, rem);
259 Buffer.BlockCopy (output2, position, result, cpos, l);
262 while (position >= output2.Length) {
263 position -= output2.Length;
270 public override void Reset ()
276 hash = HashAlgorithm.Create (HashNameValue);
277 if (SaltValue != null) {
278 hash.TransformBlock (password, 0, password.Length, password, 0);
279 hash.TransformFinalBlock (SaltValue, 0, SaltValue.Length);
283 initial = hash.ComputeHash (password);