/// <summary>
        /// Checks there's a Kerberos Ticket in current user's Windows Ticket Cache matched with the service principal name.
        /// If there's a valid ticket, then show the Ticket Information on the console.
        /// </summary>
        /// <returns></returns>
        public bool ShowCachedTicket()
        {
            try
            {
                byte[] ticket
                    = KerberosSupplementalTicketManager.GetKerberosTicketFromWindowsTicketCache(_kerberosServicePrincipalName, _logonId);
                if (ticket != null && ticket.Length > 32)
                {
                    var encode = Convert.ToBase64String(ticket);

                    AADKerberosLogger.PrintLines(2);
                    AADKerberosLogger.Save($"---Find cached Ticket: {ticket.Length} bytes");
                    AADKerberosLogger.PrintBinaryData(ticket);

                    TicketDecoder decoder = new TicketDecoder();
                    decoder.ShowApReqTicket(encode);
                    return(true);
                }

                Console.WriteLine($"There's no ticket associated with '{_kerberosServicePrincipalName}'");
            }
            catch (Win32Exception ex)
            {
                Console.WriteLine($"ERROR while finding Kerberos Ticket for '{_kerberosServicePrincipalName}': {ex.Message}");
            }
            return(false);
        }
 private void ShowAuthorizationData(string title, KrbAuthorizationData auth)
 {
     if (auth != null)
     {
         AADKerberosLogger.Save($"  {title}.Type: {auth.Type}");
         AADKerberosLogger.Save($"  {title}.Data.Length: {auth.Data.Length}");
         AADKerberosLogger.Save($"  {title}.Data.Value:");
         AADKerberosLogger.PrintBinaryData(auth.Data.ToArray());
     }
 }
        /// <summary>
        /// Shows the Kerberos Ticket included in an authentication token with KrbCred format
        /// which is used to transfer Kerberos credentials between applications.
        /// Reference:
        ///     The Unencrypted Form of Kerberos 5 KRB-CRED Message
        ///     https://tools.ietf.org/html/rfc6448
        /// </summary>
        /// <param name="message"></param>
        internal void ShowKrbCredTicket(string message)
        {
            var krbAsRepBytes = Convert.FromBase64String(message);
            var krbCred       = KrbCred.DecodeApplication(krbAsRepBytes);

            Assert.IsNotNull(krbCred);

            var credPart = krbCred.Validate();

            Assert.IsNotNull(credPart);

            AADKerberosLogger.PrintLines(2);
            AADKerberosLogger.Save("KRB-CRED Supplemental Ticket -----------------------------");
            AADKerberosLogger.Save("  ProtocolVersionNumber: " + krbCred.ProtocolVersionNumber);
            AADKerberosLogger.Save("  Message Type: " + krbCred.MessageType);
            AADKerberosLogger.Save("  # of Tickets: " + krbCred.Tickets.Length);

            for (int i = 0; i < krbCred.Tickets.Length; i++)
            {
                var ticket     = krbCred.Tickets[i];
                var ticketInfo = credPart.TicketInfo[i];

                var key = new byte[ticketInfo.Key.KeyValue.Length];
                ticketInfo.Key.KeyValue.CopyTo(key);

                AADKerberosLogger.Save("  Number: " + ticket.TicketNumber);
                AADKerberosLogger.Save("  Realm: " + ticket.Realm);
                AADKerberosLogger.Save("  SName: " + ticket.SName.FullyQualifiedName);
                ShowEncryptedDataPart("EncryptedPart", ticket.EncryptedPart);

                AADKerberosLogger.Save("  Ticket.Flags: " + ticketInfo.Flags);
                AADKerberosLogger.Save("  Ticket.Realm: " + ticketInfo.Realm);
                AADKerberosLogger.Save("  Ticket.PName: " + ticketInfo.PName.FullyQualifiedName);
                AADKerberosLogger.Save("  Ticket.SRealm: " + ticketInfo.SRealm);
                AADKerberosLogger.Save("  Ticket.SName: " + ticketInfo.SName.FullyQualifiedName);
                AADKerberosLogger.Save("  Ticket.AuthTime: " + ticketInfo.AuthTime);
                AADKerberosLogger.Save("  Ticket.StartTime: " + ticketInfo.StartTime);
                AADKerberosLogger.Save("  Ticket.EndTime: " + ticketInfo.EndTime);
                AADKerberosLogger.Save("  Ticket.RenewTill: " + ticketInfo.RenewTill);
                ShowEncrytionKey("Ticket.Key", ticketInfo.Key);

                if (ticketInfo.AuthorizationData == null)
                {
                    AADKerberosLogger.Save("  Ticket.AuthorizationData:");
                }
                else
                {
                    for (int j = 0; j < ticketInfo.AuthorizationData.Length; j++)
                    {
                        ShowAuthorizationData("Ticket.AuthorizationData", ticketInfo.AuthorizationData[j]);
                    }
                }
                AADKerberosLogger.Save("");
            }
        }
 /// <summary>
 /// Shows the account information included in the authentication result.
 /// </summary>
 /// <param name="account">The <see cref="IAccount"/> information to display.</param>
 private void ShowAccount(IAccount account)
 {
     if (account != null)
     {
         AADKerberosLogger.Save("Account Info:");
         AADKerberosLogger.Save("                 Username: "******"              Environment: " + account.Environment);
         AADKerberosLogger.Save("    HomeAccount Tenant Id: " + account.HomeAccountId.TenantId);
         AADKerberosLogger.Save("    HomeAccount Object Id: " + account.HomeAccountId.ObjectId);
         AADKerberosLogger.Save("  Home Account Identifier: " + account.HomeAccountId.Identifier);
     }
 }
        /// <summary>
        /// Acquire an authentication token with public client configuration using username/password non-interactively.
        /// </summary>
        /// <returns>The <see cref="AuthenticationResult"/> object.</returns>
        private AuthenticationResult AcquireTokenWithDeviceCodeFlow()
        {
            // 1. Setup pulic client application to get Kerberos Ticket.
            var app = PublicClientApplicationBuilder.Create(_clientId)
                      .WithTenantId(_tenantId)
                      .WithRedirectUri(_redirectUri)
                      .WithKerberosTicketClaim(_kerberosServicePrincipalName, _ticketContainer)
                      .WithLogging(LogDelegate, LogLevel.Verbose, true, true)
                      .Build();

            try
            {
                AADKerberosLogger.Save("Calling AcquireTokenWithDeviceCodeFlow() with:");
                AADKerberosLogger.Save("         Tenant Id: " + _tenantId);
                AADKerberosLogger.Save("         Client Id: " + _clientId);
                AADKerberosLogger.Save("      Redirect Uri: " + _redirectUri);
                AADKerberosLogger.Save("               spn: " + _kerberosServicePrincipalName);
                AADKerberosLogger.Save("  Ticket container: " + _ticketContainer);

                // 2. Acquire the authentication token.
                // Kerberos Ticket will be contained in Id Token or Access Token
                // according to specified ticket container parameter.
                AuthenticationResult result = app
                                              .AcquireTokenWithDeviceCode(
                    _publicAppScopes,
                    deviceCodeCallback =>
                {
                    ConsoleColor current    = Console.ForegroundColor;
                    Console.ForegroundColor = ConsoleColor.Yellow;
                    Console.WriteLine(deviceCodeCallback.Message);
                    Console.ForegroundColor = current;

                    // stop console output of logging information posted from the MSAL.
                    AADKerberosLogger.SkipLoggingToConsole = true;

                    return(Task.FromResult(0));
                })
                                              .ExecuteAsync()
                                              .GetAwaiter()
                                              .GetResult();

                AADKerberosLogger.SkipLoggingToConsole = false;
                ShowAuthenticationResult(result);
                return(result);
            }
            catch (Exception ex)
            {
                AADKerberosLogger.Save("Exception: " + ex);
                return(null);
            }
        }
 private void ShowEncrytionKey(string title, KrbEncryptionKey key)
 {
     if (key == null)
     {
         AADKerberosLogger.Save($"  {title}:");
     }
     else
     {
         AADKerberosLogger.Save($"  {title}.Usage: {key.Usage}");
         AADKerberosLogger.Save($"  {title}.EType: {key.EType}");
         AADKerberosLogger.Save($"  {title}.KeyValue.Length: {key.KeyValue.Length}");
         AADKerberosLogger.Save($"  {title}.KeyValue.Value:");
         AADKerberosLogger.PrintBinaryData(key.KeyValue.ToArray());
     }
 }
 private void ShowEncryptedDataPart(string title, KrbEncryptedData data)
 {
     if (data == null)
     {
         AADKerberosLogger.Save($"  {title}:");
     }
     else
     {
         AADKerberosLogger.Save($"  {title}.EType: " + data.EType);
         AADKerberosLogger.Save($"  {title}.KeyVersionNumber: " + data.KeyVersionNumber);
         AADKerberosLogger.Save($"  {title}.Cipher.Length: " + data.Cipher.Length);
         AADKerberosLogger.Save($"  {title}.Cipher.Value:");
         AADKerberosLogger.PrintBinaryData(data.Cipher.ToArray());
     }
 }
        /// <summary>
        /// Checks there's a valid Kerberos Ticket information within the received authentication token.
        /// If there's a valid one, show the ticket information and cache it into current user's
        /// Windows Ticket Cache so that it can be shared with other Kerberos-aware applications.
        /// </summary>
        /// <param name="result">The <see cref="AuthenticationResult"/> from token request.</param>
        private void ProcessKerberosTicket(AuthenticationResult result)
        {
            KerberosSupplementalTicket ticket;

            if (_ticketContainer == KerberosTicketContainer.IdToken)
            {
                // 1. Get the Kerberos Ticket contained in the Id Token.
                ticket = KerberosSupplementalTicketManager.FromIdToken(result.IdToken);
                if (ticket == null)
                {
                    AADKerberosLogger.Save("ERROR: There's no Kerberos Ticket information within the IdToken.");
                    return;
                }

                AADKerberosLogger.PrintLines(2);

                try
                {
                    // 2. Save the Kerberos Ticket into current user's Windows Ticket Cache.
                    KerberosSupplementalTicketManager.SaveToWindowsTicketCache(ticket, _logonId);
                    AADKerberosLogger.Save("---Kerberos Ticket cached into user's Ticket Cache\n");
                }
                catch (Win32Exception ex)
                {
                    AADKerberosLogger.Save("---Kerberos Ticket caching failed: " + ex.Message);
                }

                AADKerberosLogger.PrintLines(2);
                AADKerberosLogger.Save("KerberosSupplementalTicket {");
                AADKerberosLogger.Save("                Client Key: " + ticket.ClientKey);
                AADKerberosLogger.Save("                  Key Type: " + ticket.KeyType);
                AADKerberosLogger.Save("            Errorr Message: " + ticket.ErrorMessage);
                AADKerberosLogger.Save("                     Realm: " + ticket.Realm);
                AADKerberosLogger.Save("    Service Principal Name: " + ticket.ServicePrincipalName);
                AADKerberosLogger.Save("               Client Name: " + ticket.ClientName);
                AADKerberosLogger.Save("     KerberosMessageBuffer: " + ticket.KerberosMessageBuffer);
                AADKerberosLogger.Save("}\n");

                // shows detailed ticket information.
                TicketDecoder decoder = new TicketDecoder();
                decoder.ShowKrbCredTicket(ticket.KerberosMessageBuffer);
            }
            else
            {
                AADKerberosLogger.PrintLines(2);
                AADKerberosLogger.Save("Kerberos Ticket handling is not supported for access token.");
            }
        }
        private void ShowAuthenticationResult(AuthenticationResult result)
        {
            ConsoleColor current = Console.ForegroundColor;

            Console.ForegroundColor = ConsoleColor.Yellow;

            ShowAccount(result.Account);
            AADKerberosLogger.Save("Token Information:");
            AADKerberosLogger.Save("           Correlation Id: " + result.CorrelationId);
            AADKerberosLogger.Save("                Unique Id:" + result.UniqueId);
            AADKerberosLogger.Save("                Expres On: " + result.ExpiresOn);
            AADKerberosLogger.Save("  IsExtendedLifeTimeToken: " + result.IsExtendedLifeTimeToken);
            AADKerberosLogger.Save("       Extended Expres On: " + result.ExtendedExpiresOn);
            AADKerberosLogger.Save("             Access Token:\n" + result.AccessToken);
            AADKerberosLogger.Save("                 Id Token:\n" + result.IdToken);

            Console.ForegroundColor = current;
        }
        /// <summary>
        /// Acquire an authentication token with public client configuration using username/password non-interactively.
        ///
        /// NOTE: You have to enable public client flows for your application in the Azure Portal:
        ///     Login to the Azure Portal
        ///     Goto "App Registrations"
        ///     Select your application
        ///     Select "Authentication" under the Manage section.
        ///     Select "Yes" for the "Allow public client flows" under the Advanced Settings.
        ///     Click "Save" to save the changes.
        /// </summary>
        /// <returns>The <see cref="AuthenticationResult"/> object.</returns>
        private AuthenticationResult AcquireTokenFromPublicClientWithUserPassword()
        {
            // 1. Setup pulic client application to get Kerberos Ticket.
            var app = PublicClientApplicationBuilder.Create(_clientId)
                      .WithTenantId(_tenantId)
                      .WithRedirectUri(_redirectUri)
                      .WithKerberosTicketClaim(_kerberosServicePrincipalName, _ticketContainer)
                      .WithLogging(LogDelegate, LogLevel.Verbose, true, true)
                      .Build();

            try
            {
                AADKerberosLogger.Save("Calling AcquireTokenByUsernamePassword() with:");
                AADKerberosLogger.Save("         Tenant Id: " + _tenantId);
                AADKerberosLogger.Save("         Client Id: " + _clientId);
                AADKerberosLogger.Save("      Redirect Uri: " + _redirectUri);
                AADKerberosLogger.Save("               spn: " + _kerberosServicePrincipalName);
                AADKerberosLogger.Save("  Ticket container: " + _ticketContainer);
                AADKerberosLogger.Save("          Username: "******"Exception: " + ex);
                return(null);
            }
        }
        /// <summary>
        /// Shows the internal information of a cached Kerberos Ticket in current user's Windows Ticket Cache
        /// with KRB_AP_REQ format.
        /// Reference:
        ///     The Kerberos Network Authentication Service (V5)
        ///     https://tools.ietf.org/html/rfc4120#section-3.2.1
        /// </summary>
        /// <param name="messaage"></param>
        internal void ShowApReqTicket(string messaage)
        {
            var tokenBytes   = System.Convert.FromBase64String(messaage);
            var contextToken = MessageParser.Parse <KerberosContextToken>(tokenBytes);

            Assert.IsNotNull(contextToken);
            Assert.IsNotNull(contextToken.KrbApReq);

            var req = contextToken.KrbApReq;

            Assert.IsNotNull(req.Ticket);

            AADKerberosLogger.PrintLines(2);
            AADKerberosLogger.Save("AP-REQ Cached Ticket----------------------------------------");
            AADKerberosLogger.Save("  Protocol Version Number: " + req.ProtocolVersionNumber);
            AADKerberosLogger.Save("  MessageType: " + req.MessageType);
            AADKerberosLogger.Save("  ApOptions: " + req.ApOptions);

            AADKerberosLogger.Save("  Ticket.TicketNumber: " + req.Ticket.TicketNumber);
            AADKerberosLogger.Save("  Ticket.Realm: " + req.Ticket.Realm);
            AADKerberosLogger.Save("  Ticket.SName: " + req.Ticket.SName.FullyQualifiedName);
            ShowEncryptedDataPart("Ticket.EncryptedPart", req.Ticket.EncryptedPart);
            ShowEncryptedDataPart("Ticket.Authenticator", req.Authenticator);
        }
 /// <summary>
 /// Callback to receive logging message for internal operation of the MSAL.
 /// Show the received message to the console and save to the logging file.
 /// </summary>
 /// <param name="level">Log level of the log message to process</param>
 /// <param name="message">Pre-formatted log message</param>
 /// <param name="containsPii">Indicates if the log message contains Organizational Identifiable Information (OII)
 /// or Personally Identifiable Information (PII) nor not.</param>
 private static void LogDelegate(LogLevel level, string message, bool containsPii)
 {
     AADKerberosLogger.Save(message);
 }