GSF.Communication.Radius.RadiusPacket.EncryptPassword C# (CSharp) Method

EncryptPassword() public static method

Generates an encrypted password using the RADIUS protocol specification (RFC 2285).
public static EncryptPassword ( string password, string sharedSecret, byte requestAuthenticator ) : byte[]
password string User's password.
sharedSecret string Shared secret key.
requestAuthenticator byte Request authenticator byte array.
return byte[]
        public static byte[] EncryptPassword(string password, string sharedSecret, byte[] requestAuthenticator)
        {
            // Avoiding Null Dereferences
            if (string.IsNullOrEmpty(sharedSecret))
                throw new ArgumentException("Shared secret cannot be null or empty.");

            if (string.IsNullOrEmpty(password))
                throw new ArgumentException("Password cannot be null or empty.");

            if ((object)requestAuthenticator == null)
                throw new ArgumentException("Request authenticator cannot be null.");

            // Max length of the password can be 130 according to RFC 2865. Since 128 is the closest multiple
            // of 16 (password segment length), we allow the password to be no longer than 128 characters.
            if (password.Length <= 128)
            {
                byte[] result;
                byte[] xorBytes = null;
                byte[] passwordBytes = Encoding.GetBytes(password);
                byte[] sharedSecretBytes = Encoding.GetBytes(sharedSecret);
                byte[] md5HashInputBytes = new byte[sharedSecretBytes.Length + 16];

                MD5CryptoServiceProvider md5Provider = new MD5CryptoServiceProvider();

                if (passwordBytes.Length % 16 == 0)
                {
                    // Length of password is a multiple of 16.
                    result = new byte[passwordBytes.Length];
                }
                else
                {
                    // Length of password is not a multiple of 16, so we'll take the multiple of 16 that's next
                    // closest to the password's length and leave the empty space at the end as padding.
                    result = new byte[((passwordBytes.Length / 16) * 16) + 16];
                }

                // Copy the password to the result buffer where it'll be XORed.
                Buffer.BlockCopy(passwordBytes, 0, result, 0, passwordBytes.Length);

                // For the first 16-byte segment of the password, password characters are to be XORed with the
                // MD5 hash value that's computed as follows:
                //   MD5(Shared secret key + Request authenticator)
                Buffer.BlockCopy(sharedSecretBytes, 0, md5HashInputBytes, 0, sharedSecretBytes.Length);
                Buffer.BlockCopy(requestAuthenticator, 0, md5HashInputBytes, sharedSecretBytes.Length, requestAuthenticator.Length);

                for (int i = 0; i <= result.Length - 1; i += 16)
                {
                    // Perform XOR-based encryption of the password in 16-byte segments.
                    if (i > 0)
                    {
                        // For passwords that are more than 16 characters in length, each consecutive 16-byte
                        // segment of the password is XORed with MD5 hash value that's computed as follows:
                        //   MD5(Shared secret key + XOR bytes used in the previous segment)
                        // ReSharper disable once PossibleNullReferenceException
                        Buffer.BlockCopy(xorBytes, 0, md5HashInputBytes, sharedSecretBytes.Length, xorBytes.Length);
                    }

                    xorBytes = md5Provider.ComputeHash(md5HashInputBytes);

                    // XOR the password bytes in the current segment with the XOR bytes.
                    for (int j = i; j <= (i + 16) - 1; j++)
                    {
                        result[j] = (byte)(result[j] ^ xorBytes[j]);
                    }
                }

                return result;
            }

            throw new ArgumentException("Password can be a maximum of 128 characters in length.");
        }

Usage Example

Example #1
0
        /// <summary>
        /// Authenticates the username and password against the RADIUS server.
        /// </summary>
        /// <param name="username">Username to be authenticated.</param>
        /// <param name="password">Password to be authenticated.</param>
        /// <param name="state">State value from a previous challenge response.</param>
        /// <returns>Response packet received from the server for the authentication request.</returns>
        /// <remarks>
        /// <para>
        /// The type of response packet (if any) will be one of the following:
        /// <list>
        /// <item>AccessAccept: If the authentication is successful.</item>
        /// <item>AccessReject: If the authentication is not successful.</item>
        /// <item>AccessChallenge: If the server need more information from the user.</item>
        /// </list>
        /// </para>
        /// <para>
        /// When an AccessChallenge response packet is received from the server, it contains a State attribute
        /// that must be included in the AccessRequest packet that is being sent in response to the AccessChallenge
        /// response. So if this method returns an AccessChallenge packet, then this method is to be called again
        /// with the requested information (from ReplyMessage attribute) in the password field and the value State
        /// attribute.
        /// </para>
        /// </remarks>
        public RadiusPacket Authenticate(string username, string password, byte[] state)
        {
            CheckDisposed();

            if (string.IsNullOrEmpty(username) || string.IsNullOrEmpty(password))
            {
                throw new ArgumentException("Username and Password cannot be null.");
            }

            RadiusPacket request = new RadiusPacket(PacketType.AccessRequest);

            byte[] authenticator = RadiusPacket.CreateRequestAuthenticator(m_sharedSecret);

            request.Authenticator = authenticator;
            request.Attributes.Add(new RadiusPacketAttribute(AttributeType.UserName, username));
            request.Attributes.Add(new RadiusPacketAttribute(AttributeType.UserPassword, RadiusPacket.EncryptPassword(password, m_sharedSecret, authenticator)));

            // State attribute is used when responding to a AccessChallenge response.
            if ((object)state != null)
            {
                request.Attributes.Add(new RadiusPacketAttribute(AttributeType.State, state));
            }

            return(ProcessRequest(request));
        }