/// <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)); } }
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)); }
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); }
/// <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; }
/// <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); }
/// <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); } }
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); } }
/// <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); }
/// <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; } }