예제 #1
0
        static void ComputeNtlmV2Session(string password, byte[] challenge, out byte[] lm, out byte[] ntlm)
        {
            var nonce = new byte[8];

            using (var rng = RandomNumberGenerator.Create())
                rng.GetBytes(nonce);

            var sessionNonce = new byte[challenge.Length + 8];

            challenge.CopyTo(sessionNonce, 0);
            nonce.CopyTo(sessionNonce, challenge.Length);

            lm = new byte[24];
            nonce.CopyTo(lm, 0);

            using (var md5 = MD5.Create()) {
                var hash         = md5.ComputeHash(sessionNonce);
                var newChallenge = new byte[8];

                Array.Copy(hash, newChallenge, 8);

                ntlm = ComputeNtlm(password, newChallenge);

                // clean up
                Array.Clear(newChallenge, 0, newChallenge.Length);
                Array.Clear(hash, 0, hash.Length);
            }

            // clean up
            Array.Clear(sessionNonce, 0, sessionNonce.Length);
            Array.Clear(nonce, 0, nonce.Length);
        }
예제 #2
0
        /// <summary>
        /// Computes the MD5 checksum of the content.
        /// </summary>
        /// <remarks>
        /// Computes the MD5 checksum of the MIME content in its canonical
        /// format and then base64-encodes the result.
        /// </remarks>
        /// <returns>The md5sum of the content.</returns>
        /// <exception cref="System.InvalidOperationException">
        /// The <see cref="ContentObject"/> is <c>null</c>.
        /// </exception>
        public string ComputeContentMd5()
        {
            if (ContentObject == null)
            {
                throw new InvalidOperationException("Cannot compute Md5 checksum without a ContentObject.");
            }

            using (var stream = ContentObject.Open()) {
                byte[] checksum;

                using (var filtered = new FilteredStream(stream)) {
                    if (ContentType.IsMimeType("text", "*"))
                    {
                        filtered.Add(new Unix2DosFilter());
                    }

                    using (var md5 = MD5.Create())
                        checksum = md5.ComputeHash(filtered);
                }

                var base64 = new Base64Encoder(true);
                var digest = new byte[base64.EstimateOutputLength(checksum.Length)];
                int n      = base64.Flush(checksum, 0, checksum.Length, digest);

                return(Encoding.ASCII.GetString(digest, 0, n));
            }
        }
예제 #3
0
        static SaslMechanism()
        {
            try {
                using (var md5 = MD5.Create())
                    md5supported = true;
            } catch {
                md5supported = false;
            }

            // Note: It's probably arguable that NTLM is more secure than SCRAM but the odds of a server supporting both is probably low.
            var supported = new List <string> {
                "SCRAM-SHA-512",
                "SCRAM-SHA-256",
                "SCRAM-SHA-1",
                "NTLM"
            };

            if (md5supported)
            {
                supported.Add("DIGEST-MD5");
                supported.Add("CRAM-MD5");
            }
            supported.Add("PLAIN");
            supported.Add("LOGIN");

            RankedAuthenticationMechanisms = supported.ToArray();
        }
예제 #4
0
 static SaslMechanism()
 {
     try {
         using (var md5 = MD5.Create())
             md5supported = true;
     } catch {
         md5supported = false;
     }
 }
예제 #5
0
        public string ComputeHash(string password, bool client)
        {
            string text, a1, a2;

            byte[] buf, digest;

            // compute A1
            text = string.Format("{0}:{1}:{2}", UserName, Realm, password);
            buf  = Encoding.UTF8.GetBytes(text);
            using (var md5 = MD5.Create())
                digest = md5.ComputeHash(buf);

            using (var md5 = MD5.Create()) {
                md5.TransformBlock(digest, 0, digest.Length, null, 0);
                text = string.Format(":{0}:{1}", Nonce, CNonce);
                if (!string.IsNullOrEmpty(AuthZid))
                {
                    text += ":" + AuthZid;
                }
                buf = Encoding.ASCII.GetBytes(text);
                md5.TransformFinalBlock(buf, 0, buf.Length);
                a1 = HexEncode(md5.Hash);
            }

            // compute A2
            text  = client ? "AUTHENTICATE:" : ":";
            text += DigestUri;

            if (Qop == "auth-int" || Qop == "auth-conf")
            {
                text += ":00000000000000000000000000000000";
            }

            buf = Encoding.ASCII.GetBytes(text);
            using (var md5 = MD5.Create())
                digest = md5.ComputeHash(buf);
            a2 = HexEncode(digest);

            // compute KD
            text = string.Format("{0}:{1}:{2:x8}:{3}:{4}:{5}", a1, Nonce, Nc, CNonce, Qop, a2);
            buf  = Encoding.ASCII.GetBytes(text);
            using (var md5 = MD5.Create())
                digest = md5.ComputeHash(buf);

            return(HexEncode(digest));
        }
		public string ComputeHash (string password, bool client)
		{
			string text, a1, a2;
			byte[] buf, digest;

			// compute A1
			text = string.Format ("{0}:{1}:{2}", UserName, Realm, password);
			buf = Encoding.UTF8.GetBytes (text);
			using (var md5 = new MD5 ())
				digest = md5.ComputeHash (buf);

			using (var md5 = new MD5 ()) {
				md5.TransformBlock (digest, 0, digest.Length, null, 0);
				text = string.Format (":{0}:{1}", Nonce, CNonce);
				if (!string.IsNullOrEmpty (AuthZid))
					text += ":" + AuthZid;
				buf = Encoding.ASCII.GetBytes (text);
				md5.TransformFinalBlock (buf, 0, buf.Length);
				a1 = HexEncode (md5.Hash);
			}

			// compute A2
			text = client ? "AUTHENTICATE:" : ":";
			text += DigestUri;

			if (Qop == "auth-int" || Qop == "auth-conf")
				text += ":00000000000000000000000000000000";

			buf = Encoding.ASCII.GetBytes (text);
			using (var md5 = new MD5 ())
				digest = md5.ComputeHash (buf);
			a2 = HexEncode (digest);

			// compute KD
			text = string.Format ("{0}:{1}:{2:x8}:{3}:{4}:{5}", a1, Nonce, Nc, CNonce, Qop, a2);
			buf = Encoding.ASCII.GetBytes (text);
			using (var md5 = new MD5 ())
				digest = md5.ComputeHash (buf);

			return HexEncode (digest);
		}
예제 #7
0
        /// <summary>
        /// Parses the server's challenge token and returns the next challenge response.
        /// </summary>
        /// <remarks>
        /// Parses the server's challenge token and returns the next challenge response.
        /// </remarks>
        /// <returns>The next challenge response.</returns>
        /// <param name="token">The server's challenge token.</param>
        /// <param name="startIndex">The index into the token specifying where the server's challenge begins.</param>
        /// <param name="length">The length of the server's challenge.</param>
        /// <exception cref="System.InvalidOperationException">
        /// The SASL mechanism is already authenticated.
        /// </exception>
        /// <exception cref="System.NotSupportedException">
        /// The SASL mechanism does not support SASL-IR.
        /// </exception>
        /// <exception cref="SaslException">
        /// An error has occurred while parsing the server's challenge token.
        /// </exception>
        protected override byte[] Challenge(byte[] token, int startIndex, int length)
        {
            if (IsAuthenticated)
            {
                throw new InvalidOperationException();
            }

            if (token == null)
            {
                throw new NotSupportedException("CRAM-MD5 does not support SASL-IR.");
            }

            var userName = Encoding.UTF8.GetBytes(Credentials.UserName);
            var password = Encoding.UTF8.GetBytes(Credentials.Password);
            var ipad     = new byte[64];
            var opad     = new byte[64];

            byte[] digest;

            if (password.Length > 64)
            {
                byte[] checksum;

                using (var md5 = MD5.Create())
                    checksum = md5.ComputeHash(password);

                Array.Copy(checksum, ipad, checksum.Length);
                Array.Copy(checksum, opad, checksum.Length);
            }
            else
            {
                Array.Copy(password, ipad, password.Length);
                Array.Copy(password, opad, password.Length);
            }

            Array.Clear(password, 0, password.Length);

            for (int i = 0; i < 64; i++)
            {
                ipad[i] ^= 0x36;
                opad[i] ^= 0x5c;
            }

            using (var md5 = MD5.Create()) {
                md5.TransformBlock(ipad, 0, ipad.Length, null, 0);
                md5.TransformFinalBlock(token, startIndex, length);
                digest = md5.Hash;
            }

            using (var md5 = MD5.Create()) {
                md5.TransformBlock(opad, 0, opad.Length, null, 0);
                md5.TransformFinalBlock(digest, 0, digest.Length);
                digest = md5.Hash;
            }

            var buffer = new byte[userName.Length + 1 + (digest.Length * 2)];
            int offset = 0;

            for (int i = 0; i < userName.Length; i++)
            {
                buffer[offset++] = userName[i];
            }
            buffer[offset++] = 0x20;
            for (int i = 0; i < digest.Length; i++)
            {
                byte c = digest[i];

                buffer[offset++] = HexAlphabet[(c >> 4) & 0x0f];
                buffer[offset++] = HexAlphabet[c & 0x0f];
            }

            IsAuthenticated = true;

            return(buffer);
        }
예제 #8
0
		/// <summary>
		/// Parses the server's challenge token and returns the next challenge response.
		/// </summary>
		/// <remarks>
		/// Parses the server's challenge token and returns the next challenge response.
		/// </remarks>
		/// <returns>The next challenge response.</returns>
		/// <param name="token">The server's challenge token.</param>
		/// <param name="startIndex">The index into the token specifying where the server's challenge begins.</param>
		/// <param name="length">The length of the server's challenge.</param>
		/// <exception cref="System.InvalidOperationException">
		/// The SASL mechanism is already authenticated.
		/// </exception>
		/// <exception cref="System.NotSupportedException">
		/// The SASL mechanism does not support SASL-IR.
		/// </exception>
		/// <exception cref="SaslException">
		/// An error has occurred while parsing the server's challenge token.
		/// </exception>
		protected override byte[] Challenge (byte[] token, int startIndex, int length)
		{
			if (IsAuthenticated)
				throw new InvalidOperationException ();

			if (token == null)
				throw new NotSupportedException ("CRAM-MD5 does not support SASL-IR.");

			var cred = Credentials.GetCredential (Uri, MechanismName);
			var userName = Encoding.UTF8.GetBytes (cred.UserName);
			var password = Encoding.UTF8.GetBytes (cred.Password);
			var ipad = new byte[64];
			var opad = new byte[64];
			byte[] digest;

			if (password.Length > 64) {
				byte[] checksum;

				using (var md5 = new MD5 ())
					checksum = md5.ComputeHash (password);

				Array.Copy (checksum, ipad, checksum.Length);
				Array.Copy (checksum, opad, checksum.Length);
			} else {
				Array.Copy (password, ipad, password.Length);
				Array.Copy (password, opad, password.Length);
			}

			for (int i = 0; i < 64; i++) {
				ipad[i] ^= 0x36;
				opad[i] ^= 0x5c;
			}

			using (var md5 = new MD5 ()) {
				md5.TransformBlock (ipad, 0, ipad.Length, null, 0);
				md5.TransformFinalBlock (token, startIndex, length);
				digest = md5.Hash;
			}

			using (var md5 = new MD5 ()) {
				md5.TransformBlock (opad, 0, opad.Length, null, 0);
				md5.TransformFinalBlock (digest, 0, digest.Length);
				digest = md5.Hash;
			}

			var buffer = new byte[userName.Length + 1 + (digest.Length * 2)];
			int offset = 0;

			for (int i = 0; i < userName.Length; i++)
				buffer[offset++] = userName[i];
			buffer[offset++] = 0x20;
			for (int i = 0; i < digest.Length; i++) {
				byte c = digest[i];

				buffer[offset++] = HexAlphabet[(c >> 4) & 0x0f];
				buffer[offset++] = HexAlphabet[c & 0x0f];
			}

			IsAuthenticated = true;

			return buffer;
		}
예제 #9
0
        /// <summary>
        /// Authenticates using the supplied credentials.
        /// </summary>
        /// <remarks>
        /// <para>If the POP3 server supports the APOP authentication mechanism,
        /// then APOP is used.</para>
        /// <para>If the APOP authentication mechanism is not supported and the
        /// server supports one or more SASL authentication mechanisms, then
        /// the SASL mechanisms that both the client and server support are tried
        /// in order of greatest security to weakest security. Once a SASL
        /// authentication mechanism is found that both client and server support,
        /// the credentials are used to authenticate.</para>
        /// <para>If the server does not support SASL or if no common SASL mechanisms
        /// can be found, then the USER and PASS commands are used as a fallback.</para>
        /// </remarks>
        /// <param name="credentials">The user's credentials.</param>
        /// <param name="cancellationToken">A cancellation token.</param>
        /// <exception cref="System.ArgumentNullException">
        /// <paramref name="credentials"/> is <c>null</c>.
        /// </exception>
        /// <exception cref="System.ObjectDisposedException">
        /// The <see cref="Pop3Client"/> has been disposed.
        /// </exception>
        /// <exception cref="System.InvalidOperationException">
        /// The <see cref="Pop3Client"/> is not connected or is already authenticated.
        /// </exception>
        /// <exception cref="System.OperationCanceledException">
        /// The operation was canceled via the cancellation token.
        /// </exception>
        /// <exception cref="MailKit.Security.AuthenticationException">
        /// Authentication using the supplied credentials has failed.
        /// </exception>
        /// <exception cref="MailKit.Security.SaslException">
        /// A SASL authentication error occurred.
        /// </exception>
        /// <exception cref="System.IO.IOException">
        /// An I/O error occurred.
        /// </exception>
        /// <exception cref="Pop3CommandException">
        /// A POP3 command failed.
        /// </exception>
        /// <exception cref="Pop3ProtocolException">
        /// An POP3 protocol error occurred.
        /// </exception>
        public void Authenticate(ICredentials credentials, CancellationToken cancellationToken = default (CancellationToken))
        {
            CheckDisposed ();

            if (!IsConnected)
                throw new InvalidOperationException ("The Pop3Client must be connected before you can authenticate.");

            if (engine.State == Pop3EngineState.Transaction)
                throw new InvalidOperationException ("The Pop3Client is already authenticated.");

            if (credentials == null)
                throw new ArgumentNullException ("credentials");

            var uri = new Uri ("pop://" + host);
            NetworkCredential cred;
            string challenge;
            Pop3Command pc;

            if ((engine.Capabilities & Pop3Capabilities.Apop) != 0) {
                cred = credentials.GetCredential (uri, "APOP");
                challenge = engine.ApopToken + cred.Password;
                var md5sum = new StringBuilder ();
                byte[] digest;

                using (var md5 = new MD5 ()) {
                    digest = md5.ComputeHash (Encoding.UTF8.GetBytes (challenge));
                }

                for (int i = 0; i < digest.Length; i++)
                    md5sum.Append (digest[i].ToString ("x2"));

                try {
                    SendCommand (cancellationToken, "APOP {0} {1}", cred.UserName, md5sum);
                    engine.State = Pop3EngineState.Transaction;
                } catch (Pop3CommandException) {
                }

                if (engine.State == Pop3EngineState.Transaction) {
                    engine.QueryCapabilities (cancellationToken);
                    ProbeCapabilities (cancellationToken);
                    return;
                }
            }

            if ((engine.Capabilities & Pop3Capabilities.Sasl) != 0) {
                foreach (var authmech in SaslMechanism.AuthMechanismRank) {
                    if (!engine.AuthenticationMechanisms.Contains (authmech))
                        continue;

                    var sasl = SaslMechanism.Create (authmech, uri, credentials);

                    cancellationToken.ThrowIfCancellationRequested ();

                    pc = engine.QueueCommand (cancellationToken, (pop3, cmd, text) => {
                        while (!sasl.IsAuthenticated && cmd.Status == Pop3CommandStatus.Continue) {
                            challenge = sasl.Challenge (text);

                            var buf = Encoding.ASCII.GetBytes (challenge + "\r\n");
                            pop3.Stream.Write (buf, 0, buf.Length);

                            var response = pop3.ReadLine (cmd.CancellationToken);

                            cmd.Status = Pop3Engine.GetCommandStatus (response, out text);
                            if (cmd.Status == Pop3CommandStatus.ProtocolError)
                                throw new Pop3ProtocolException (string.Format ("Unexpected response from server: {0}", response));
                        }
                    }, "AUTH {0}", authmech);

                    while (engine.Iterate () < pc.Id) {
                        // continue processing commands
                    }

                    if (pc.Status == Pop3CommandStatus.Error)
                        continue;

                    if (pc.Status != Pop3CommandStatus.Ok)
                        throw CreatePop3Exception (pc);

                    if (pc.Exception != null)
                        throw pc.Exception;

                    engine.State = Pop3EngineState.Transaction;
                    engine.QueryCapabilities (cancellationToken);
                    ProbeCapabilities (cancellationToken);
                    return;
                }
            }

            // fall back to the classic USER & PASS commands...
            cred = credentials.GetCredential (uri, "DEFAULT");

            try {
                SendCommand (cancellationToken, "USER {0}", cred.UserName);
                SendCommand (cancellationToken, "PASS {0}", cred.Password);
            } catch (Pop3CommandException) {
                throw new AuthenticationException ();
            }

            engine.State = Pop3EngineState.Transaction;
            engine.QueryCapabilities (cancellationToken);
            ProbeCapabilities (cancellationToken);
        }
예제 #10
0
		/// <summary>
		/// Computes the MD5 checksum of the content.
		/// </summary>
		/// <remarks>
		/// Computes the MD5 checksum of the MIME content in its canonical
		/// format and then base64-encodes the result.
		/// </remarks>
		/// <returns>The md5sum of the content.</returns>
		/// <exception cref="System.InvalidOperationException">
		/// The <see cref="ContentObject"/> is <c>null</c>.
		/// </exception>
		public string ComputeContentMd5 ()
		{
			if (ContentObject == null)
				throw new InvalidOperationException ("Cannot compute Md5 checksum without a ContentObject.");

			using (var stream = ContentObject.Open ()) {
				byte[] checksum;

				using (var filtered = new FilteredStream (stream)) {
					if (ContentType.Matches ("text", "*"))
						filtered.Add (new Unix2DosFilter ());

					using (var md5 = new MD5 ())
						checksum = md5.ComputeHash (filtered);
				}

				var base64 = new Base64Encoder (true);
				var digest = new byte[base64.EstimateOutputLength (checksum.Length)];
				int n = base64.Flush (checksum, 0, checksum.Length, digest);

				return Encoding.ASCII.GetString (digest, 0, n);
			}
		}
예제 #11
0
        public string ComputeHash(string password, bool client)
        {
            using (var checksum = new MD5 ()) {
                byte[] buf, digest;
                string text, a1, a2;

                // compute A1
                text = string.Format ("{0}:{1}:{2}", UserName, Realm, password);
                buf = Encoding.UTF8.GetBytes (text);
                checksum.Initialize ();
                digest = checksum.ComputeHash (buf);

                text = string.Format ("{0}:{1}:{2}", HexEncode (digest), Nonce, CNonce);
                buf = Encoding.UTF8.GetBytes (text);
                checksum.Initialize ();
                digest = checksum.ComputeHash (buf);
                a1 = HexEncode (digest);

                // compute A2
                text = client ? "AUTHENTICATE:" : ":";
                text += DigestUri;

                if (Qop == "auth-int" || Qop == "auth-conf")
                    text += ":00000000000000000000000000000000";

                buf = Encoding.ASCII.GetBytes (text);
                checksum.Initialize ();
                digest = checksum.ComputeHash (buf);
                a2 = HexEncode (digest);

                // compute KD
                text = string.Format ("{0}:{1}:{2:8X}:{3}:{4}:{5}", a1, Nonce, Nc, CNonce, Qop, a2);
                buf = Encoding.ASCII.GetBytes (text);
                checksum.Initialize ();
                digest = checksum.ComputeHash (buf);

                return HexEncode (digest);
            }
        }
예제 #12
0
        /// <summary>
        /// Parses the server's challenge token and returns the next challenge response.
        /// </summary>
        /// <returns>The next challenge response.</returns>
        /// <param name="token">The server's challenge token.</param>
        /// <param name="startIndex">The index into the token specifying where the server's challenge begins.</param>
        /// <param name="length">The length of the server's challenge.</param>
        /// <exception cref="SaslException">
        /// An error has occurred while parsing the server's challenge token.
        /// </exception>
        protected override byte[] Challenge(byte[] token, int startIndex, int length)
        {
            if (IsAuthenticated)
                throw new InvalidOperationException ();

            if (token == null)
                return null;

            var cred = Credentials.GetCredential (Uri, MechanismName);
            var userName = Encoding.UTF8.GetBytes (cred.UserName);
            var password = Encoding.UTF8.GetBytes (cred.Password);

            using (var md5 = new MD5 ()) {
                var ipad = new byte[64];
                var opad = new byte[64];
                byte[] buffer;
                byte[] digest;
                int offset;

                if (password.Length > 64) {
                    var checksum = md5.ComputeHash (password);
                    Array.Copy (checksum, ipad, checksum.Length);
                    Array.Copy (checksum, opad, checksum.Length);
                } else {
                    Array.Copy (password, ipad, password.Length);
                    Array.Copy (password, opad, password.Length);
                }

                for (int i = 0; i < 64; i++) {
                    ipad[i] ^= 0x36;
                    opad[i] ^= 0x5c;
                }

                buffer = new byte[ipad.Length + length];
                offset = 0;

                for (int i = 0; i < ipad.Length; i++)
                    buffer[offset++] = ipad[i];
                for (int i = 0; i < length; i++)
                    buffer[offset++] = token[startIndex + i];

                md5.Initialize ();
                digest = md5.ComputeHash (buffer);

                buffer = new byte[opad.Length + digest.Length];
                offset = 0;

                for (int i = 0; i < opad.Length; i++)
                    buffer[offset++] = opad[i];
                for (int i = 0; i < digest.Length; i++)
                    buffer[offset++] = digest[i];

                md5.Initialize ();
                digest = md5.ComputeHash (buffer);

                buffer = new byte[userName.Length + 1 + (digest.Length * 2)];
                offset = 0;

                for (int i = 0; i < userName.Length; i++)
                    buffer[offset++] = userName[i];
                buffer[offset++] = 0x20;
                for (int i = 0; i < digest.Length; i++) {
                    byte c = digest[i];

                    buffer[offset++] = HexAlphabet[(c >> 4) & 0x0f];
                    buffer[offset++] = HexAlphabet[c & 0x0f];
                }

                IsAuthenticated = true;

                return buffer;
            }
        }