/// <summary>
/// Authenticates the client using the supplied stream.
/// </summary>
/// <param name="stream">the stream to use to authenticate the connection.</param>
/// <param name="additionalChallenge">Additional data that much match between the client and server
/// for the connection to succeed.</param>
/// <returns>
/// True if authentication succeded, false otherwise.
/// </returns>
public bool TryAuthenticateAsClient(Stream stream, byte[] additionalChallenge = null)
{
if (additionalChallenge == null)
additionalChallenge = new byte[] { };
if (additionalChallenge.Length > short.MaxValue)
throw new ArgumentOutOfRangeException("additionalChallenge","Must be less than 32767 bytes");
using (var negotiateStream = new NegotiateStream(stream, true))
{
try
{
negotiateStream.AuthenticateAsClient(m_credentials, string.Empty, ProtectionLevel.EncryptAndSign, TokenImpersonationLevel.Identification);
}
catch (Exception ex)
{
Log.Publish(MessageLevel.Info, "Security Login Failed", "Attempting an integrated security login failed", null, ex);
return false;
}
//Exchange the challenge data.
//Since NegotiateStream is already a trusted stream
//Simply writing the raw is as secure as creating a challenge response
negotiateStream.Write((short)additionalChallenge.Length);
if (additionalChallenge.Length > 0)
{
negotiateStream.Write(additionalChallenge);
}
negotiateStream.Flush();
int len = negotiateStream.ReadInt16();
if (len < 0)
{
Log.Publish(MessageLevel.Info, "Security Login Failed", "Attempting an integrated security login failed", "Challenge Length is invalid: " + len.ToString());
return false;
}
byte[] remoteChallenge;
if (len == 0)
{
remoteChallenge = new byte[0];
}
else
{
remoteChallenge = negotiateStream.ReadBytes(len);
}
if (remoteChallenge.SecureEquals(additionalChallenge))
{
return true;
}
else
{
Log.Publish(MessageLevel.Info, "Security Login Failed", "Attempting an integrated security login failed", "Challenge did not match. Potential man in the middle attack.");
return false;
}
}
}