Pārlūkot izejas kodu

2006-05-03 Chris Toshok <[email protected]>

	* SqlMembershipProvider.cs (GetAlg): move this here for the time
	being, as it's the only class that uses it.
	(HashAndBase64Encode): nuke.
	(EncryptAndBase64Encode): nuke.
	(Base64DecodeAndDecrypt): nuke.
	(DecryptPassword): new function.
	(EncryptPassword): new function.
	(ChangePassword): replace the switch with a call to
	EncodePassword.
	(ChangePasswordQuestionAndAnswer): same.
	(CreateUser): same.
	(ResetPassword): same.
	(ValidateUsingPassword): same.
	(ValidateUsingPasswordAnswer): same.
	(GetPassword): same, and throw MembershipPasswordException if the
	password answer is incorrect.

	* MembershipProvider.cs (InitVector): nuke this.  it's actually
	the salt from the database (for the sql provider, anyway).
	(EncodePassword): based on the password format, password, and
	salt, encode it.  Makes use of EncryptPassword.
	(DecodePassword): likewise for decoding, makes use of
	DecryptPassword.
	(DecryptPassword): revert this to throwing
	NotImplementedException, as the sql provideroverrides it to
	perform the actual decryption.
	(EncryptPassword): same.


svn path=/trunk/mcs/; revision=60240
Chris Toshok 19 gadi atpakaļ
vecāks
revīzija
06d0f2af03

+ 30 - 0
mcs/class/System.Web/System.Web.Security/ChangeLog

@@ -1,3 +1,33 @@
+2006-05-03  Chris Toshok  <[email protected]>
+
+	* SqlMembershipProvider.cs (GetAlg): move this here for the time
+	being, as it's the only class that uses it.
+	(HashAndBase64Encode): nuke.
+	(EncryptAndBase64Encode): nuke.
+	(Base64DecodeAndDecrypt): nuke.
+	(DecryptPassword): new function.
+	(EncryptPassword): new function.
+	(ChangePassword): replace the switch with a call to
+	EncodePassword.
+	(ChangePasswordQuestionAndAnswer): same.
+	(CreateUser): same.
+	(ResetPassword): same.
+	(ValidateUsingPassword): same.
+	(ValidateUsingPasswordAnswer): same.
+	(GetPassword): same, and throw MembershipPasswordException if the
+	password answer is incorrect.
+
+	* MembershipProvider.cs (InitVector): nuke this.  it's actually
+	the salt from the database (for the sql provider, anyway).
+	(EncodePassword): based on the password format, password, and
+	salt, encode it.  Makes use of EncryptPassword.
+	(DecodePassword): likewise for decoding, makes use of
+	DecryptPassword.
+	(DecryptPassword): revert this to throwing
+	NotImplementedException, as the sql provideroverrides it to
+	perform the actual decryption.
+	(EncryptPassword): same.
+
 2006-05-02  Chris Toshok  <[email protected]>
 
 	* SqlMembershipProvider.cs: 85% complete, maybe more.  The major

+ 53 - 42
mcs/class/System.Web/System.Web.Security/MembershipProvider.cs

@@ -32,6 +32,7 @@
 using System.Configuration.Provider;
 using System.Web.Configuration;
 using System.Security.Cryptography;
+using System.Text;
 
 namespace System.Web.Security
 {
@@ -78,61 +79,71 @@ namespace System.Web.Security
 
 		protected virtual byte[] DecryptPassword (byte[] encodedPassword)
 		{
-			byte[] decryptionKey;
-			SymmetricAlgorithm alg = GetAlg (out decryptionKey);
-
-			ICryptoTransform decryptor = alg.CreateDecryptor (decryptionKey, InitVector);
-
-			return decryptor.TransformFinalBlock (encodedPassword, 0, encodedPassword.Length);
+			throw new NotImplementedException ();
 		}
 
 		protected virtual byte[] EncryptPassword (byte[] password)
 		{
-			byte[] decryptionKey;
-			SymmetricAlgorithm alg = GetAlg (out decryptionKey);
-
-			ICryptoTransform encryptor = alg.CreateEncryptor (decryptionKey, InitVector);
-
-			return encryptor.TransformFinalBlock (password, 0, password.Length);
+			throw new NotImplementedException ();
 		}
-		
+
 		public event MembershipValidatePasswordEventHandler ValidatingPassword;
 
-		SymmetricAlgorithm GetAlg (out byte[] decryptionKey)
+		internal string EncodePassword (string password, MembershipPasswordFormat passwordFormat, string salt)
 		{
-			MachineKeySection section = (MachineKeySection)WebConfigurationManager.GetSection ("system.web/machineKey");
-
-			if (section.DecryptionKey.StartsWith ("AutoGenerate"))
-				throw new ProviderException ("You must explicitly specify a decryption key in the <machineKey> section when using encrypted passwords.");
+			byte[] password_bytes;
+			byte[] salt_bytes;
+
+			switch (passwordFormat) {
+			case MembershipPasswordFormat.Clear:
+				return password;
+			case MembershipPasswordFormat.Hashed:
+				password_bytes = Encoding.Unicode.GetBytes (password);
+				salt_bytes = Convert.FromBase64String (salt);
+
+				byte[] hashBytes = new byte[salt_bytes.Length + password_bytes.Length];
+
+				Buffer.BlockCopy (salt_bytes, 0, hashBytes, 0, salt_bytes.Length);
+				Buffer.BlockCopy (password_bytes, 0, hashBytes, salt_bytes.Length, password_bytes.Length);
+
+				MembershipSection section = (MembershipSection)WebConfigurationManager.GetSection ("system.web/membership");
+				string alg_type = section.HashAlgorithmType;
+				if (alg_type == "") {
+					MachineKeySection keysection = (MachineKeySection)WebConfigurationManager.GetSection ("system.web/machineKey");
+					alg_type = keysection.Validation.ToString ();
+				}
+				using (HashAlgorithm hash = HashAlgorithm.Create (alg_type)) {
+					hash.TransformFinalBlock (hashBytes, 0, hashBytes.Length);
+					return Convert.ToBase64String (hash.Hash);
+				}
+			case MembershipPasswordFormat.Encrypted:
+				password_bytes = Encoding.Unicode.GetBytes (password);
+				salt_bytes = Convert.FromBase64String (salt);
 
-			string alg_type = section.Decryption;
-			if (alg_type == "Auto")
-				alg_type = "AES";
+				byte[] buf = new byte[password_bytes.Length + salt_bytes.Length];
 
-			SymmetricAlgorithm alg = null;
-			if (alg_type == "AES")
-				alg = Rijndael.Create ();
-			else if (alg_type == "3DES")
-				alg = TripleDES.Create ();
-			else
-				throw new ProviderException (String.Format ("Unsupported decryption attribute '{0}' in <machineKey> configuration section", alg_type));
+				Array.Copy (salt_bytes, 0, buf, 0, salt_bytes.Length);
+				Array.Copy (password_bytes, 0, buf, salt_bytes.Length, password_bytes.Length);
 
-			decryptionKey = section.DecryptionKey192Bits;
-			return alg;
+				return Convert.ToBase64String (EncryptPassword (buf));
+			default:
+				/* not reached.. */
+				return null;
+			}
 		}
 
-		byte[] init_vector;
-		byte[] InitVector {
-			get {
-				if (init_vector == null) {
-					/* come up with an init_vector for encryption algorithms */
-					// IV is 8 bytes long for 3DES
-					init_vector = new byte[8];
-					for (int i = 0; i < 8; i++)
-						init_vector [i] = (byte) ApplicationName [i % ApplicationName.Length];
-				}
-
-				return init_vector;
+		internal string DecodePassword (string password, MembershipPasswordFormat passwordFormat)
+		{
+			switch (passwordFormat) {
+			case MembershipPasswordFormat.Clear:
+				return password;
+			case MembershipPasswordFormat.Hashed:
+				throw new ProviderException ("Hashed passwords cannot be decoded.");
+			case MembershipPasswordFormat.Encrypted:
+				return Encoding.Unicode.GetString (DecryptPassword (Convert.FromBase64String (password)));
+			default:
+				/* not reached.. */
+				return null;
 			}
 		}
 	}

+ 68 - 110
mcs/class/System.Web/System.Web.Security/SqlMembershipProvider.cs

@@ -101,35 +101,60 @@ namespace System.Web.Security {
 				throw new ArgumentException (String.Format ("invalid format for {0}", pName));
 		}
 
-		string HashAndBase64Encode (string s, byte[] salt)
+		SymmetricAlgorithm GetAlg (out byte[] decryptionKey)
 		{
-			byte[] tmp = Encoding.Unicode.GetBytes (s);
+			MachineKeySection section = (MachineKeySection)WebConfigurationManager.GetSection ("system.web/machineKey");
 
-			byte[] hashBytes = new byte[salt.Length + tmp.Length];
+			if (section.DecryptionKey.StartsWith ("AutoGenerate"))
+				throw new ProviderException ("You must explicitly specify a decryption key in the <machineKey> section when using encrypted passwords.");
 
-			Buffer.BlockCopy (salt, 0, hashBytes, 0, salt.Length);
-			Buffer.BlockCopy (tmp, 0, hashBytes, salt.Length, tmp.Length);
+			string alg_type = section.Decryption;
+			if (alg_type == "Auto")
+				alg_type = "AES";
 
-			MembershipSection section = (MembershipSection)WebConfigurationManager.GetSection ("system.web/membership");
-			string alg_type = section.HashAlgorithmType;
-			if (alg_type == "") {
-				MachineKeySection keysection = (MachineKeySection)WebConfigurationManager.GetSection ("system.web/machineKey");
-				alg_type = keysection.Validation.ToString ();
-			}
-			using (HashAlgorithm hash = HashAlgorithm.Create (alg_type)) {
-				hash.TransformFinalBlock (hashBytes, 0, hashBytes.Length);
-				return Convert.ToBase64String (hash.Hash);
-			}
-		}
+			SymmetricAlgorithm alg = null;
+			if (alg_type == "AES")
+				alg = Rijndael.Create ();
+			else if (alg_type == "3DES")
+				alg = TripleDES.Create ();
+			else
+				throw new ProviderException (String.Format ("Unsupported decryption attribute '{0}' in <machineKey> configuration section", alg_type));
 
-		string EncryptAndBase64Encode (string s)
-		{
-			return Convert.ToBase64String (EncryptPassword (Encoding.UTF8.GetBytes (s)));
+			decryptionKey = section.DecryptionKey192Bits;
+			return alg;
 		}
 
-		string Base64DecodeAndDecrypt (string s)
-		{
-			return Encoding.UTF8.GetString (DecryptPassword (Convert.FromBase64String (s)));
+                protected override byte[] DecryptPassword (byte[] encodedPassword)
+                {
+			byte[] decryptionKey;
+
+			using (SymmetricAlgorithm alg = GetAlg (out decryptionKey)) {
+				alg.Key = decryptionKey;
+
+				using (ICryptoTransform decryptor = alg.CreateDecryptor ()) {
+
+					byte[] buf = decryptor.TransformFinalBlock (encodedPassword, 0, encodedPassword.Length);
+					byte[] rv = new byte[buf.Length - SALT_BYTES];
+
+					Array.Copy (buf, 16, rv, 0, buf.Length - 16);
+					return rv;
+				}
+			}
+                }
+
+                protected override byte[] EncryptPassword (byte[] password)
+                {
+			byte[] decryptionKey;
+			byte[] iv = new byte[SALT_BYTES];
+
+			Array.Copy (password, 0, iv, 0, SALT_BYTES);
+			Array.Clear (password, 0, SALT_BYTES);
+
+			using (SymmetricAlgorithm alg = GetAlg (out decryptionKey)) {
+				using (ICryptoTransform encryptor = alg.CreateEncryptor (decryptionKey, iv)) {
+					return encryptor.TransformFinalBlock (password, 0, password.Length);
+				}
+			}
 		}
 
 		public override bool ChangePassword (string username, string oldPwd, string newPwd)
@@ -162,19 +187,7 @@ namespace System.Web.Security {
 
 					EmitValidatingPassword (username, newPwd, false);
 
-					string db_password = newPwd;
-
-					switch (passwordFormat) {
-					case MembershipPasswordFormat.Hashed:
-						byte[] salt = Convert.FromBase64String (db_salt);
-						db_password = HashAndBase64Encode (db_password, salt);
-						break;
-					case MembershipPasswordFormat.Encrypted:
-						db_password = EncryptAndBase64Encode (db_password);
-						break;
-					case MembershipPasswordFormat.Clear:
-						break;
-					}
+					string db_password = EncodePassword (newPwd, passwordFormat, db_salt);
 
 					DateTime now = DateTime.Now.ToUniversalTime ();
 
@@ -249,19 +262,7 @@ UPDATE m
 				bool valid = ValidateUsingPassword (trans, username, password, out passwordFormat, out db_salt);
 				if (valid) {
 
-					string db_passwordAnswer = newPwdAnswer;
-
-					switch (passwordFormat) {
-					case MembershipPasswordFormat.Hashed:
-						byte[] salt = Convert.FromBase64String (db_salt);
-						db_passwordAnswer = HashAndBase64Encode (db_passwordAnswer, salt);
-						break;
-					case MembershipPasswordFormat.Encrypted:
-						db_passwordAnswer = EncryptAndBase64Encode (db_passwordAnswer);
-						break;
-					case MembershipPasswordFormat.Clear:
-						break;
-					}
+					string db_passwordAnswer = EncodePassword (newPwdAnswer, passwordFormat, db_salt);
 
 					commandText = @"
 UPDATE m
@@ -357,23 +358,13 @@ UPDATE m
 			string passwordSalt = "";
 
 			RandomNumberGenerator rng = RandomNumberGenerator.Create ();
+			byte[] salt = new byte[SALT_BYTES];
+			rng.GetBytes (salt);
+			passwordSalt = Convert.ToBase64String (salt);
 
-			switch (PasswordFormat) {
-			case MembershipPasswordFormat.Hashed:
-				byte[] salt = new byte[SALT_BYTES];
-				rng.GetBytes (salt);
-				passwordSalt = Convert.ToBase64String (salt);
-				password = HashAndBase64Encode (password, salt);
-				if (RequiresQuestionAndAnswer)
-					pwdAnswer = HashAndBase64Encode (pwdAnswer, salt);
-				break;
-			case MembershipPasswordFormat.Encrypted:
-				password = EncryptAndBase64Encode (password);
-				break;
-			case MembershipPasswordFormat.Clear:
-			default:
-				break;
-			}
+			password = EncodePassword (password, PasswordFormat, passwordSalt);
+			if (RequiresQuestionAndAnswer)
+				pwdAnswer = EncodePassword (pwdAnswer, PasswordFormat, passwordSalt);
 
 			/* make sure the hashed/encrypted password and
 			 * answer are still under 128 characters. */
@@ -833,7 +824,6 @@ SELECT COUNT (*)
 			return (int)command.ExecuteScalar ();
 		}
 		
-		[MonoTODO ("more details here")]
 		public override string GetPassword (string username, string answer)
 		{
 			if (!enablePasswordRetrieval)
@@ -889,21 +879,19 @@ SELECT m.Password
 					password = reader.GetString (0);
 					reader.Close();
 
-					/* decrypt the password */
-					switch (passwordFormat) {
-					case MembershipPasswordFormat.Hashed:
-						throw new NotSupportedException ("can't retrieve hashed passwords");
-					case MembershipPasswordFormat.Encrypted:
-						password = Base64DecodeAndDecrypt (password);
-						break;
-					case MembershipPasswordFormat.Clear:
-						break;
-					}
+					password = DecodePassword (password, passwordFormat);
+				}
+				else {
+					throw new MembershipPasswordException ("The password-answer supplied is wrong.");
 				}
 
 				trans.Commit ();
 				return password;
 			}
+			catch (MembershipPasswordException) {
+				trans.Commit ();
+				throw;
+			}
 			catch {
 				trans.Rollback ();
 				throw;
@@ -1198,23 +1186,13 @@ SELECT u.UserName
 				if (ValidateUsingPasswordAnswer (trans, user.UserName, answer, out db_passwordFormat, out db_salt)) {
 
 					newPassword = GeneratePassword ();
-					string db_password = newPassword;
+					string db_password;
 
 					EmitValidatingPassword (username, newPassword, false);
 
 					/* otherwise update the user's password in the db */
 
-					switch (db_passwordFormat) {
-					case MembershipPasswordFormat.Hashed:
-						byte[] salt = Convert.FromBase64String (db_salt);
-						db_password = HashAndBase64Encode (newPassword, salt);
-						break;
-					case MembershipPasswordFormat.Encrypted:
-						db_password = EncryptAndBase64Encode (newPassword);
-						break;
-					case MembershipPasswordFormat.Clear:
-						break;
-					}
+					db_password = EncodePassword (newPassword, db_passwordFormat, db_salt);
 
 					commandText = @"
 UPDATE m
@@ -1439,9 +1417,7 @@ UPDATE dbo.aspnet_Users
 
 				return valid;
 			}
-			catch (Exception e) {
-				Console.WriteLine (e);
-
+			catch {
 				trans.Rollback ();
 
 				throw;
@@ -1608,16 +1584,7 @@ SELECT m.Password, m.PasswordFormat, m.PasswordSalt
 			reader.Close();
 
 			/* do the actual validation */
-			switch (passwordFormat) {
-			case MembershipPasswordFormat.Hashed:
-				password = HashAndBase64Encode (password, Convert.FromBase64String (salt));
-				break;
-			case MembershipPasswordFormat.Encrypted:
-				password = EncryptAndBase64Encode (password);
-				break;
-			case MembershipPasswordFormat.Clear:
-				break;
-			}
+			password = EncodePassword (password, passwordFormat, salt);
 
 			bool valid = (password == db_password);
 
@@ -1660,16 +1627,7 @@ SELECT m.PasswordAnswer, m.PasswordFormat, m.PasswordSalt
 			reader.Close();
 
 			/* do the actual password answer check */
-			switch (passwordFormat) {
-			case MembershipPasswordFormat.Hashed:
-				answer = HashAndBase64Encode (answer, Convert.FromBase64String (salt));
-				break;
-			case MembershipPasswordFormat.Encrypted:
-				answer = EncryptAndBase64Encode (answer);
-				break;
-			case MembershipPasswordFormat.Clear:
-				break;
-			}
+			answer = EncodePassword (answer, passwordFormat, salt);
 
 			if (answer.Length > 128)
 				throw new ArgumentException (String.Format ("password answer hashed to longer than 128 characters"));