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 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.
31 using System
.Globalization
;
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
42 public class PasswordDeriveBytes
: DeriveBytes
{
44 private string HashNameValue
;
45 private byte[] SaltValue
;
46 private int IterationsValue
;
48 private HashAlgorithm hash
;
50 private byte[] password
;
51 private byte[] initial
;
52 private byte[] output
;
54 private int hashnumber
;
56 public PasswordDeriveBytes (string strPassword
, byte[] rgbSalt
)
58 Prepare (strPassword
, rgbSalt
, "SHA1", 100);
61 public PasswordDeriveBytes (string strPassword
, byte[] rgbSalt
, CspParameters cspParams
)
63 Prepare (strPassword
, rgbSalt
, "SHA1", 100);
64 if (cspParams
!= null) {
65 throw new NotSupportedException (
66 Locale
.GetText ("CspParameters not supported by Mono for PasswordDeriveBytes."));
70 public PasswordDeriveBytes (string strPassword
, byte[] rgbSalt
, string strHashName
, int iterations
)
72 Prepare (strPassword
, rgbSalt
, strHashName
, iterations
);
75 public PasswordDeriveBytes (string strPassword
, byte[] rgbSalt
, string strHashName
, int iterations
, CspParameters cspParams
)
77 Prepare (strPassword
, rgbSalt
, strHashName
, iterations
);
78 if (cspParams
!= null) {
79 throw new NotSupportedException (
80 Locale
.GetText ("CspParameters not supported by Mono for PasswordDeriveBytes."));
84 ~
PasswordDeriveBytes ()
87 if (initial
!= null) {
88 Array
.Clear (initial
, 0, initial
.Length
);
91 // zeroize temporary password storage
92 Array
.Clear (password
, 0, password
.Length
);
95 private void Prepare (string strPassword
, byte[] rgbSalt
, string strHashName
, int iterations
)
98 if (strPassword
== null)
99 throw new ArgumentNullException ("strPassword");
100 password
= Encoding
.UTF8
.GetBytes (strPassword
);
104 if (strPassword
== null)
107 password
= Encoding
.UTF8
.GetBytes (strPassword
);
112 SaltValue
= (byte[]) rgbSalt
.Clone ();
115 HashName
= strHashName
;
116 IterationCount
= iterations
;
120 public string HashName
{
121 get { return HashNameValue; }
124 throw new ArgumentNullException ("HashName");
126 throw new CryptographicException (
127 Locale
.GetText ("Can't change this property at this stage"));
129 HashNameValue
= value;
133 public int IterationCount
{
134 get { return IterationsValue; }
137 throw new ArgumentOutOfRangeException ("> 0", "IterationCount");
139 throw new CryptographicException (
140 Locale
.GetText ("Can't change this property at this stage"));
142 IterationsValue
= value;
148 if (SaltValue
== null)
150 return (byte[]) SaltValue
.Clone ();
154 throw new CryptographicException (
155 Locale
.GetText ("Can't change this property at this stage"));
159 SaltValue
= (byte[]) value.Clone ();
163 // this will cause a NullReferenceException if set to null (like 1.0/1.1)
164 SaltValue
= (byte[]) value.Clone ();
169 public byte[] CryptDeriveKey (string algname
, string alghashname
, int keySize
, byte[] rgbIV
)
172 throw new CryptographicException (
173 Locale
.GetText ("Key Size can't be greater than 128 bits"));
175 throw new NotSupportedException (
176 Locale
.GetText ("CspParameters not supported by Mono"));
179 // note: Key is returned - we can't zeroize it ourselve :-(
180 public override byte[] GetBytes (int cb
)
183 // 1.0/1.1 was a little late at throwing the argument exception ;-)
184 if (password
== null)
185 throw new ArgumentNullException ("Password");
188 throw new IndexOutOfRangeException ("cb");
192 // it's now impossible to change the HashName, Salt
193 // and IterationCount
197 byte[] result
= new byte [cb
];
199 // the initial hash (in reset) + at least one iteration
200 int iter
= Math
.Max (1, IterationsValue
- 1);
202 // start with the PKCS5 key
203 if (output
== null) {
204 // calculate the PKCS5 key
207 // generate new key material
208 for (int i
= 0; i
< iter
- 1; i
++)
209 output
= hash
.ComputeHash (output
);
213 byte[] output2
= null;
214 if (hashnumber
== 0) {
215 // last iteration on output
216 output2
= hash
.ComputeHash (output
);
218 else if (hashnumber
< 1000) {
219 string n
= Convert
.ToString (hashnumber
);
220 output2
= new byte [output
.Length
+ n
.Length
];
221 for (int j
=0; j
< n
.Length
; j
++)
222 output2
[j
] = (byte)(n
[j
]);
223 Buffer
.BlockCopy (output
, 0, output2
, n
.Length
, output
.Length
);
224 // don't update output
225 output2
= hash
.ComputeHash (output2
);
228 throw new CryptographicException (
229 Locale
.GetText ("too long"));
232 int rem
= output2
.Length
- position
;
233 int l
= Math
.Min (cb
- cpos
, rem
);
234 Buffer
.BlockCopy (output2
, position
, result
, cpos
, l
);
237 while (position
>= output2
.Length
) {
238 position
-= output2
.Length
;
245 public override void Reset ()
247 // note: Reset doesn't change state
251 hash
= HashAlgorithm
.Create (HashNameValue
);
252 if (SaltValue
!= null) {
253 hash
.TransformBlock (password
, 0, password
.Length
, password
, 0);
254 hash
.TransformFinalBlock (SaltValue
, 0, SaltValue
.Length
);
258 initial
= hash
.ComputeHash (password
);