Kerberos.NET
Kerberos.NET copied to clipboard
Windows Extended Protection support
Is your feature request related to a problem? Please describe. When using the library together the sample code provided to create a "Negotiate" header and then using that header with httpClient to make a request to a website, it fails if the receiving IIS has Windows Extended Protection set to required.
Describe the solution you'd like It would be great if there was a way to add channel binding information when the apReq (i think) is created.
Yeah, good feature. In theory this could be built out by an application directly by filling in the DelegationInfo
object during AP-REQ creation, but that's certainly a pain.
First of all, thanks for taking the time to answer and to point me in the right direction. I did give it a go (well, a lot of go's actually) but unfortunately I wasn't able to get it working. While this is not a support channel perhaps you can easily see where i went wrong.
By your direction I made the changes to DelegationInfo.cs
I started with declaring:
public const int ChannelBindingLength = 0x10;
public readonly byte[] cbt_zeros = new byte[] {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
public readonly byte[] tls_server_end_point = new byte[] {
0x74, 0x6c, 0x73, 0x2d, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2d, 0x65, 0x6e, 0x64, 0x2d, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x3a
};
And then I attempted to change the Encode method like below but when i add "Negotiate " + Convert.ToBase64String(ticket.EncodeGssApi().ToArray())
to the Authorization: header of a http request the binding fails.
public ReadOnlyMemory<byte> Encode()
{
using (var stream = new MemoryStream())
using (var writer = new BinaryWriter(stream))
{
X509Certificate cert = new X509Certificate("C:\\Temp\\cert\\test\\publicKey.cer");
using (var hasher = SHA256.Create())
{
var hash = hasher.ComputeHash(cert.GetRawCertData());
int cbtLength = cbt_zeros.Length + tls_server_end_point.Length + hash.Length + 4;
var cbt = new byte[cbtLength];
int pos = 0;
//first 16 bytes zeros
Array.Copy(cbt_zeros, 0, cbt, pos, cbt_zeros.Length);
//add 4 bytes of app data length (tls prefix + cert hash)
byte[] dataLength = new byte[] { (byte)(tls_server_end_point.Length + hash.Length), 0, 0, 0 };
pos += cbt_zeros.Length;
Array.Copy(dataLength, 0, cbt, pos, 4);
//add tls prefix
pos += 4;
Array.Copy(tls_server_end_point, 0, cbt, pos, tls_server_end_point.Length);
//add cert hash
pos += tls_server_end_point.Length;
Array.Copy(hash, 0, cbt, pos, hash.Length);
this.ChannelBinding = cbt;
}
if (this.ChannelBinding.Length == 0)
{
this.ChannelBinding = new byte[ChannelBindingLength];
}
writer.Write(this.ChannelBinding.Length);
writer.Write(this.ChannelBinding.ToArray());
if (this.DelegationTicket != null)
{
this.Flags |= GssContextEstablishmentFlag.GSS_C_DELEG_FLAG;
}
writer.Write((int)this.Flags);
if (this.DelegationTicket != null)
{
writer.Write((short)this.DelegationOption);
var deleg = this.DelegationTicket.EncodeApplication();
writer.Write((short)deleg.Length);
writer.Write(deleg.ToArray());
}
return stream.ToArray();
}
}
I'm just eyeballing this so I haven’t looked at the Windows side, but I believe you’re missing the MD5 hash and/or MIC to bind the data to the session.
https://www.rfc-editor.org/rfc/rfc6542.html#section-3.1
https://www.rfc-editor.org/rfc/rfc4121#section-4.1.1.2
From: Mika @.> Sent: Thursday, March 14, 2024 7:00:31 AM To: dotnet/Kerberos.NET @.> Cc: Comment @.>; Subscribed @.> Subject: Re: [dotnet/Kerberos.NET] Windows Extended Protection support (Issue #366)
First of all, thanks for taking the time to answer and to point me in the right direction. I did give it a go (well, a lot of go's actually) but unfortunately I wasn't able to get it working. While this is not a support channel perhaps you can easily see where i went wrong.
By your direction I made the changes to DelegationInfo.cs
I started with declaring:
public const int ChannelBindingLength = 0x10; public readonly byte[] cbt_zeros = new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; public readonly byte[] tls_server_end_point = new byte[] { 0x74, 0x6c, 0x73, 0x2d, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2d, 0x65, 0x6e, 0x64, 0x2d, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x3a };
And then I attempted to change the Encode method like below but when i add "Negotiate " + Convert.ToBase64String(ticket.EncodeGssApi().ToArray()) to the Authorization: header of a http request the binding fails.
public ReadOnlyMemory
using (var hasher = SHA256.Create())
{
var hash = hasher.ComputeHash(cert.GetRawCertData());
int cbtLength = cbt_zeros.Length + tls_server_end_point.Length + hash.Length + 4;
var cbt = new byte[cbtLength];
int pos = 0;
//first 16 bytes zeros
Array.Copy(cbt_zeros, 0, cbt, pos, cbt_zeros.Length);
//add 4 bytes of app data length (tls prefix + cert hash)
byte[] dataLength = new byte[] { (byte)(tls_server_end_point.Length + hash.Length), 0, 0, 0 };
pos += cbt_zeros.Length;
Array.Copy(dataLength, 0, cbt, pos, 4);
//add tls prefix
pos += 4;
Array.Copy(tls_server_end_point, 0, cbt, pos, tls_server_end_point.Length);
//add cert hash
pos += tls_server_end_point.Length;
Array.Copy(hash, 0, cbt, pos, hash.Length);
this.ChannelBinding = cbt;
}
if (this.ChannelBinding.Length == 0)
{
this.ChannelBinding = new byte[ChannelBindingLength];
}
writer.Write(this.ChannelBinding.Length);
writer.Write(this.ChannelBinding.ToArray());
if (this.DelegationTicket != null)
{
this.Flags |= GssContextEstablishmentFlag.GSS_C_DELEG_FLAG;
}
writer.Write((int)this.Flags);
if (this.DelegationTicket != null)
{
writer.Write((short)this.DelegationOption);
var deleg = this.DelegationTicket.EncodeApplication();
writer.Write((short)deleg.Length);
writer.Write(deleg.ToArray());
}
return stream.ToArray();
}
}
— Reply to this email directly, view it on GitHubhttps://github.com/dotnet/Kerberos.NET/issues/366#issuecomment-1997528881 or unsubscribehttps://github.com/notifications/unsubscribe-auth/AAJHTYI5RCJVKATAFXVFBNDYYGUQBBFKMF2HI4TJMJ2XIZLTSSBKK5TBNR2WLJDUOJ2WLJDOMFWWLO3UNBZGKYLEL5YGC4TUNFRWS4DBNZ2F6YLDORUXM2LUPGBKK5TBNR2WLJDUOJ2WLJDOMFWWLLTXMF2GG2C7MFRXI2LWNF2HTAVFOZQWY5LFUVUXG43VMWSG4YLNMWVXI2DSMVQWIX3UPFYGLAVFOZQWY5LFVE2TMNBSG4ZDGNJXURXGC3LFVFUGC427NRQWEZLMVRZXKYTKMVRXIX3UPFYGLLCJONZXKZKDN5WW2ZLOOSTHI33QNFRXHE4CUR2HS4DFVJZGK4DPONUXI33SPGSXMYLMOVS2QOBVGQ4DSMJTHCBKI5DZOBS2K2LTON2WLJLWMFWHKZNKGIYTQMRRGU2TMMZRQKSHI6LQMWSWYYLCMVWKK5TBNR2WLKJVGY2DENZSGM2TPJ3UOJUWOZ3FOKTGG4TFMF2GK. You are receiving this email because you commented on the thread.
Triage notifications on the go with GitHub Mobile for iOShttps://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675 or Androidhttps://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub.
Hi, you were absolutely right about the MD5 hash. For some reason I thought that part was only for NTLM. Thanks a ton for taking the time to help out. Hopefully this helps someone else.