Аутентификации клиент-сервер с помощью SSPI?

Я работаю над клиент-серверным приложением, и я хочу, чтобы клиент аутентифицировался на сервере, используя учетные данные пользователя, но я не хочу, чтобы пользователь вводил свое имя пользователя и пароль. Я, конечно, не хочу нести ответственность за безопасное обращение с паролями. Мне нужно только, чтобы пользователь доказал мне, что они те, за кого себя выдают, и тогда мой сервер сможет выполнять команды, как ему заблагорассудится.

мои пользователи являются частью домена, и поэтому Я хочу иметь возможность использовать учетные данные для входа, которые они создали при входе в систему.

Я не использую какие-либо веб-службы, и я не хочу. Я контролирую как клиентское, так и серверное программное обеспечение, и оба написаны на чистом C# и используют хорошие сокеты для выполнения работы.

Я бы предпочел сделать это с чистым C#/.Net, но я открыт для использования небезопасного C# и pinvokes для win32 API, если это означает, что я выполню эту работу.

Я прочитал немного о SSPI в windows, но я вроде как чувствую себя в темноте, так как этот вид разработки приложений является новым для меня.

кто-нибудь знает, как это сделать? SSPI-это путь? Как использовать SSPI из C#? Есть ли собственный способ .Net, чтобы мой код оставался переносимым?

4 ответов


обновление:

SSPI является правильным подходом для этого. API не слишком сложен в использовании, но требует, чтобы проект приличного размера был обернут в C#.

в процессе исследования необходимых бит для решения этого вопроса я написал проект для предоставления SSPI .Сеть. Ниже я описываю основы взаимодействия с API Windows SSPI, чтобы любой мог воспроизвести мои результаты. Если вы хотите использовать SSPI в .Net, я могу предложить вам использовать созданный мной проект чтобы решить эту проблему:

NSspi-интерфейс .Net для API SSPI

SSPI предоставляет вам необработанные байтовые массивы, содержащие маркеры аутентификации, которые вы затем решаете, как передавать - будь то через сокет с двоичными отформатированными сообщениями, пользовательский XML-канал, .NET Remoting, некоторую форму WCF, heck, даже последовательный порт. Тебе решать, как с ними обращаться. С помощью SSPI сервер может аутентифицировать клиентов, надежно идентифицировать клиента и даже выполнять основные процедуры обработки сообщений, такие как шифрование/подписание с использованием контекста безопасности, установленного с клиентом.

API SSPI документирован здесь:обзор API SSPI

в частности, взгляните на следующие функции:

  • AcquireCredentialsHandle
    • получает дескриптор для некоторой формы учетных данных (например, вход текущего пользователя). Используется серверами и клиенты.
  • InitializeSecurityContext
    • используется клиентами для установления контекста безопасности с сервером.
  • AcceptSecurityContext
    • используется серверами для установления контекста безопасности с клиентом.

типичный рабочий процесс заключается в том, что каждая сторона инициализирует свои учетные данные с помощью AcquireCredentialsHandle. Этот затем начинается и продолжается цикл аутентификации следующим образом:

  • клиент вызывает InitializeSecurityContext, не предоставляя входных маркеров, который возвращает выходные маркеры в виде массива байтов. ISC возвращает 'ContinueNeeded', чтобы указать, что цикл проверки подлинности не завершен.
  • клиент отправляет токены на сервер любым способом, который он пожелает.
  • сервер подает полученные маркеры в качестве входных данных в AcceptSecurityContext и производит свои собственные выходные токены. ASC также возвращает 'ContinueNeeded', чтобы указать, что аутентификация цикл не завершен.
  • затем сервер отправляет свои выходные токены клиенту.
  • клиент предоставляет маркеры серверов в качестве входных данных для InitializeSecurityContext, который возвращает новые выходные маркеры.
  • клиент отправляет свои новые выходные токены на сервер.
  • ...

этот цикл продолжается до тех пор, пока клиент не увидит InitializeSecurityContext возвращает "OK", и сервер видит AcceptSecurityContext возвращает "OK". Каждая функция может возвращать " OK " и по-прежнему предоставлять выходной токен (как указано ненулевым возвратом), чтобы указать, что она все еще должна отправлять данные другой стороне. Именно так клиент знает, что его половина выполнена, но сервер все еще неполон; и наоборот, если сервер завершается до клиента. Какая сторона завершается первой (возвращает 'OK') зависит от используемого конкретного пакета безопасности под капотом SSPI, и любой потребитель SSPI должен знать об этом.

приведенной выше информации должно быть достаточно для любого, кто взаимодействует с системой SSPI, чтобы обеспечить "встроенную аутентификацию Windows" в своем приложении и реплицировать мои результаты.

ниже приведен мой предыдущий ответ, поскольку я узнал, как вызвать API SSPI.


Я забыл об этом вопросе и случайно вернулся к этой проблеме несколько дней назад на прихоть. Мне нужно решить эту проблему через год или два хотя бы :)

это возможно в .Net, и в настоящее время я разрабатываю оболочку .Net SSPI, которую я намерен опубликовать.

я основываю свою работу на каком-то образцы SSPI из Microsoft я нашел.

образец содержит управляемую сборку C++ / CLI, которая реализует необходимые части API SSPI (в папке Microsoft\Samples\Security\SSPI\SSPI извлечено из REMSSPI.файл EXE.) Затем у них есть два UIs, клиент приложение и серверное приложение, написанные на C#, которые используют этот API для выполнения аутентификации SSPI.

UIs используют Средство удаленного взаимодействия .Net, чтобы связать все это вместе, но если я правильно понимаю API SSPI, единственная информация, которую клиент и сервер должны обмениваться, состоит из байтов [], содержащих данные токена контекста безопасности, которые могут быть легко интегрированы в любую инфраструктуру связи, которую вы хотите; в моем случае двоичный протокол моего собственного дизайн.

некоторые заметки о том, как заставить образец работать - у них есть источник библиотеки "SSPI", который лучше всего компилируется под VS 2005, хотя я получил его для работы под 2008; 2010 или выше потребуется некоторая доработка, поскольку они используют устаревшие языковые конструкции. Вам также может потребоваться изменить файлы заголовков, которые являются частью вашего SDK платформы, потому что они используют назначения указателей const для unconst переменных, и я не знаю лучшего способа сделать компилятор счастливым (я никогда раньше использовался C++ / CLI).

они включают скомпилированную dll SSPI в папке Microsoft\Samples\Security\SSPI\bin. Чтобы заставить двоичные файлы клиента / сервера работать,вы должны скопировать эту dll в их каталог bin, иначе разрешение сборки fail.

Итак:

  • Go здесь для загрузки REMSSPI.exe образец самораспаковывающегося zip.
  • извлеките REMSSPI.файл EXE (дважды..)
  • Microsoft\Samples\Security\SSPI\
    • bin\ - содержит скомпилированную dll Microsoft.Samples.Security.SSPI.dll
    • SSPI\ - содержит источник в dll
    • Sample\ - содержит исходный код пользовательского интерфейса
      • bin\ - содержит образцы пользовательского интерфейса сборки. Скопируйте SSPI.dll файл здесь и запустить ControlPanel.Client.exe и ControlPanel.Server.exe

Никола прав; нет .NET-родного способа выполнить то, что вы делаете (по крайней мере, не используя поддержку низкоуровневых .NET-сокетов). Вы можете, конечно, нырнуть под крышки, чтобы сделать некоторую черную магию взаимодействия, но если и клиент, и сервер находятся под вашим контролем, вы можете рассмотреть вопрос о перемещении вверх по стеку немного и использовании API более высокого уровня, такого как WCF, который имеет встроенную проверку подлинности Windows.net-native.

основанный на вашем вопросе и в описанной среде вы сможете использовать NetTcpBinding, который предлагает высокую производительность, а также сантехнику для потока аутентификации/идентификации, который вы ищете (он также предлагает довольно чистый способ обработки авторизации с помощью класса ServiceAuthorizationManager). Не зная особенностей вашего приложения / сервиса, я не могу предоставить " как " реализовать то, что вы хотите сделать, но я могу указать вам на docs, которые имеют достаточно простой образец.


Я пошел совершенно неправильно с WindowsIdentity (что хорошо для авторизация) потому что я забыл, что WCF обрабатывает много вещей с файлами конфигурации, безопасностью конечных точек и безопасностью сообщений/транспорта.

вы пробовали с NegotiateStream ? Данный пример, кажется, лучше соответствует вашим потребностям : он использует Kerberos для аутентификации, прежде чем разрешать чтение/запись. С помощью CredentialCache.DefaultNetworkCredentials следует избегать запроса пароля.


когда-либо пытались работать с WindowsIdentity ?
Pure C# / .Net, serializable и GetCurrent() возвращает исполняющую учетную запись.


Маркер Безопасности
Пользователь доставляет в приложение набор утверждений поросенок вместе с ее просьбой. В веб-службе эти утверждения переносится в защитный заголовок конверта SOAP. В браузерное веб-приложение, утверждения поступают через HTTP-сообщение от браузер пользователя, и может быть позже кэшируется в файле cookie, если сеанс желателен. Независимо от того, как они поступают, они должны быть сериализованы каким-то образом, и именно здесь появляются маркеры безопасности. Маркер безопасности является сериализованным набором утверждений, который имеет цифровую подпись выдавшего власть. Подпись важна – она дает вам уверенность, что пользователь не просто составил кучу претензий и отправил их вам. В ситуациях низкой безопасности, когда криптография не нужна или желаемый, вы можете использовать unsigned токены, но это не сценарий, я сосредоточимся на этой статье. Одной из основных функций WIF является возможность создания и чтения маркеров безопасности. WIF и базовых сантехника в .NET Framework обрабатывает все криптографические тяжелые поднимающ, и представляет ваше применение с набором претензий которые вы могу читать.

цитата из Windows Identity Foudation WhitePaper

ваш основной вопрос был :

" Is существует чистый способ проверки подлинности пользователей C# / .NET с использованием их учетных данных?"

WindowsIdentity" является " токеном проверки подлинности, выданным вашим контроллером домена, и мне кажется, что лучший подход на данный момент.


Я знал очень мало о WindowsIdentity, когда я впервые опубликовал, но я также чувствовал, что это поможет с вашей проблемой и ограничениями. Я много читал и, наконец, пришел к этому страница.
Введение WIF вполне понятен, WindowsIdentity-это новый набор .NET framework, предназначенный для проблем безопасности на основе Windows/ролей.
SSPI, Kerberos являются частями всего процесса проверки подлинности Windows, маркер входа, полученный пользователем / машиной / процессом, предоставляется контроллером домена и не может быть получен "просто" путем создания экземпляра нового объекта WindowsIdentity. Если такой вид незаконного создания экземпляра существовал, вся модель безопасности Windows (Домены, UAC и т. д.) должен быть мертвый.

здесь (очень!) небольшая консольная программа, которая выдает исключение, если вы не являетесь частью "BUILTIN\Administrateurs" (измените имя группы в соответствии с вашими потребностями). Когда "Запуск от имени администратора", программа завершается без ошибок.
Существует очень большой набор разрешений, и каждое требование основано на утверждении (является ли идентификатор memeber xxx ?)

using System;
using System.Security;
using System.Security.Permissions;
using System.Security.Principal;

namespace WindowsIdentityTest
{
    class Program
    {
        [PrincipalPermission(SecurityAction.Demand, Authenticated = true)]
        static string SomeServerAction()
        { return "Authenticated users can access"; }

        [PrincipalPermission(SecurityAction.Demand, Role = "BUILTIN\Administrateurs")]
        static string SomeCriticalServerAction()
        { return "Only Admins can access"; }

        static void Main(string[] args)
        {
            //This allows to perform security checks against the current Identity.   
            AppDomain.CurrentDomain.SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal);

            try
            {
                Console.WriteLine(SomeServerAction());
                Console.WriteLine(SomeCriticalServerAction());

            }
            catch (SecurityException sec)
            {
                Console.WriteLine(string.Format("{0} : {1}\n------------\n{2}"
                    , sec.GetType()
                    , sec.Message
                    , sec.StackTrace));
            }
            catch (Exception ex)
            {
                Console.WriteLine("This shall not appen.");
            }
            Console.WriteLine("Press enter to quit.");
            Console.ReadLine();
        }
    }
}

надеюсь, это поможет.