«1. Обзор

В этом руководстве мы разберемся с API универсальной службы безопасности (GSS API) и как мы можем реализовать его на Java. Мы увидим, как мы можем защитить сетевое соединение с помощью GSS API в Java.

В процессе мы создадим простые клиентские и серверные компоненты, защитив их с помощью GSS API.

2. Что такое GSS API?

Итак, что на самом деле представляет собой Generic Security Service API? GSS API предоставляет общую структуру для приложений, позволяющих использовать различные механизмы безопасности, такие как Kerberos, NTLM и SPNEGO, подключаемым образом. Следовательно, это помогает приложениям напрямую отделить себя от механизмов безопасности.

Чтобы уточнить, безопасность здесь включает аутентификацию, целостность данных и конфиденциальность.

2.1. Зачем нам нужен GSS API?

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

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

2.2. Как работает GSS API?

GSS API — это механизм, основанный на токенах. Он работает путем обмена токенами безопасности между узлами. Этот обмен обычно происходит по сети, но API GSS не зависит от этих деталей.

Эти токены генерируются и обрабатываются конкретными реализациями GSS API. Синтаксис и семантика этих токенов специфичны для механизма безопасности, согласованного между узлами:

Центральная тема GSS API вращается вокруг контекста безопасности. Мы можем установить этот контекст между узлами посредством обмена токенами. Нам может понадобиться несколько обменов токенами между узлами для установления контекста.

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

3. Поддержка API GSS в Java

Java поддерживает API GSS как часть пакета «org.ietf.jgss». Название пакета может показаться странным. Это связано с тем, что привязки Java для GSS API определены в спецификации IETF. Сама спецификация не зависит от механизма безопасности.

Одним из популярных механизмов безопасности для Java GSS является Kerberos v5.

3.1. Java GSS API

Давайте попробуем разобраться в некоторых основных API, которые создают Java GSS:

    GSSContext инкапсулирует контекст безопасности GSS API и предоставляет услуги, доступные в этом контексте. GSSCredential инкапсулирует учетные данные API GSS для объекта, который необходим для установить контекст безопасности GSSName инкапсулирует основную сущность GSS API, которая обеспечивает абстракцию для различных пространств имен, используемых базовыми механизмами. другие важные классы API GSS, такие как GSSName, GSSCredential и GSSContext. Oid представляет собой универсальные идентификаторы объектов (OID), которые являются иерархическими идентификаторами, используемыми в API GSS для идентификации механизмов и форматов имен. и конфиденциальность при обмене данными ChannelBinding инкапсулирует необязательную информацию о привязке канала. n используется для повышения качества аутентификации одноранговых объектов

3.2. Java GSS Security Provider

    Хотя Java GSS определяет базовую структуру для реализации GSS API в Java, он не предоставляет реализацию. Java использует подключаемые реализации на основе провайдеров для служб безопасности, включая Java GSS.

«Может быть один или несколько таких поставщиков безопасности, зарегистрированных в архитектуре криптографии Java (JCA). Каждый поставщик безопасности может реализовать одну или несколько служб безопасности, таких как Java GSSAPI и лежащие в их основе механизмы безопасности.

Существует поставщик GSS по умолчанию, который поставляется с JDK. Однако есть и другие поставщики GSS с другими механизмами безопасности, которые мы можем использовать. Одним из таких поставщиков является IBM Java GSS. Мы должны зарегистрировать такого поставщика безопасности в JCA, чтобы иметь возможность их использовать.

Более того, при необходимости мы можем реализовать собственный провайдер безопасности с, возможно, собственными механизмами безопасности. Однако на практике это вряд ли нужно.

4. GSS API на примере

Теперь мы рассмотрим Java GSS в действии на примере. Мы создадим простое клиент-серверное приложение. Клиент чаще называется инициатором, а сервер — акцептором в GSS. Мы будем использовать Java GSS и Kerberos v5 для аутентификации.

4.1. Контекст GSS для клиента и сервера

Для начала нам нужно установить GSSContext как на стороне сервера, так и на стороне клиента приложения.

Давайте сначала посмотрим, как мы можем сделать это на стороне клиента:

Здесь происходит довольно много вещей, давайте разберем их:

Мы начнем с создания экземпляра GSSManager Затем мы используем этот экземпляр для создания GSSContext, передавая: GSSName, представляющий принципала сервера, обратите внимание на конкретное имя принципала Kerberos, здесь Oid используемого механизма, Kerberos v5, здесь учетные данные инициатора, нуль здесь означает, что учетные данные по умолчанию будут использоваться время жизни для установленного контекста Наконец, мы подготавливаем контекст для взаимной аутентификации, конфиденциальности и целостности данных

GSSManager manager = GSSManager.getInstance();
String serverPrinciple = "HTTP/[email protected]";
GSSName serverName = manager.createName(serverPrinciple, null);
Oid krb5Oid = new Oid("1.2.840.113554.1.2.2");
GSSContext clientContext = manager.createContext(
  serverName, krb5Oid, (GSSCredential)null, GSSContext.DEFAULT_LIFETIME);
clientContext.requestMutualAuth(true);
clientContext.requestConf(true);
clientContext.requestInteg(true);

Аналогично мы должны определить контекст на стороне сервера:

    Как мы видим, это намного проще чем клиентский контекст. Единственная разница здесь в том, что нам нужны учетные данные акцептора, которые мы использовали как null. Как и прежде, null означает, что будут использоваться учетные данные по умолчанию.

4.2. Аутентификация API GSS


GSSManager manager = GSSManager.getInstance();
GSSContext serverContext = manager.createContext((GSSCredential) null);

Несмотря на то, что мы создали GSSContext на стороне сервера и на стороне клиента, обратите внимание, что на данном этапе они еще не установлены.

Чтобы установить эти контексты, нам нужно обменяться маркерами, характерными для указанного механизма безопасности, то есть Kerberos v5:

Это, наконец, устанавливает контекст на обоих концах:

4.3. Безопасная связь GSS API

// On the client-side
clientToken = clientContext.initSecContext(new byte[0], 0, 0);
sendToServer(clientToken); // This is supposed to be send over the network
		
// On the server-side
serverToken = serverContext.acceptSecContext(clientToken, 0, clientToken.length);
sendToClient(serverToken); // This is supposed to be send over the network
		
// Back on the client side
clientContext.initSecContext(serverToken, 0, serverToken.length);

Теперь, когда у нас есть контекст, установленный на обоих концах, мы можем начать отправлять данные с целостностью и конфиденциальностью:

assertTrue(serverContext.isEstablished());
assertTrue(clientContext.isEstablished());

Здесь происходит несколько вещей, давайте проанализируем:

MessageProp используется клиентом для установки метода переноса и создания токена. Метод переноса также добавляет криптографический MIC данных, MIC входит в состав токена. Этот токен отправляется на сервер (возможно, по сетевому вызову). сервер снова использует MessageProp, чтобы установить метод развертывания и вернуть данные. Кроме того, метод развертывания проверяет MIC для полученных данных, обеспечивая целостность данных

// On the client-side
byte[] messageBytes = "Baeldung".getBytes();
MessageProp clientProp = new MessageProp(0, true);
byte[] clientToken = clientContext.wrap(messageBytes, 0, messageBytes.length, clientProp);
sendToClient(serverToken); // This is supposed to be send over the network
       
// On the server-side 
MessageProp serverProp = new MessageProp(0, false);
byte[] bytes = serverContext.unwrap(clientToken, 0, clientToken.length, serverProp);
String string = new String(bytes);
assertEquals("Baeldung", string);

Следовательно, клиент и сервер могут обмениваться данными с целостностью и конфиденциальностью.

    4.4. Настройка Kerberos для примера

Теперь ожидается, что механизм GSS, такой как Kerberos, будет извлекать учетные данные из существующего субъекта. Класс Subject здесь представляет собой абстракцию JAAS, представляющую сущность, такую ​​как человек или услуга. Обычно он заполняется во время аутентификации на основе JAAS.

Однако в нашем примере мы не будем напрямую использовать аутентификацию на основе JAAS. Мы позволим Kerberos получать учетные данные напрямую, в нашем случае с помощью файла keytab. Для этого существует системный параметр JVM:

Однако реализация Kerberos по умолчанию, предоставляемая Sun Microsystem, использует JAAS для обеспечения аутентификации.

«Это может показаться противоречащим тому, что мы только что обсуждали. Обратите внимание, что мы можем явно использовать JAAS в нашем приложении, которое будет заполнять тему. Или оставьте базовому механизму прямую аутентификацию, где он в любом случае использует JAAS. Следовательно, нам нужно предоставить файл конфигурации JAAS для базового механизма:

-Djavax.security.auth.useSubjectCredsOnly=false

Эта конфигурация является прямой, в которой мы определили Kerberos как необходимый модуль входа в систему как для инициатора, так и для акцептора. Кроме того, мы настроили использование соответствующих принципалов из файла keytab. Мы можем передать эту конфигурацию JAAS в JVM в качестве системного параметра:

Здесь предполагается, что у нас есть доступ к Kerberos KDC. В KDC мы настроили необходимых принципалов и получили файл keytab для использования, скажем, «example.keytab».

com.sun.security.jgss.initiate  {
  com.sun.security.auth.module.Krb5LoginModule required
  useKeyTab=true
  keyTab=example.keytab
  principal="client/localhost"
  storeKey=true;
};
com.sun.security.jgss.accept  {
  com.sun.security.auth.module.Krb5LoginModule required
  useKeyTab=true
  keyTab=example.keytab
  storeKey=true
  principal="HTTP/localhost";
};

Кроме того, нам нужен файл конфигурации Kerberos, указывающий на правильный KDC:

-Djava.security.auth.login.config=login.conf

Эта простая конфигурация определяет KDC, работающий на порту 52135, с областью по умолчанию EXAMPLE.COM. Мы можем передать это в JVM как системный параметр:

4.5. Запуск примера

[libdefaults]
default_realm = EXAMPLE.COM
udp_preference_limit = 1
[realms]
EXAMPLE.COM = {
    kdc = localhost:52135
}

Чтобы запустить пример, мы должны использовать артефакты Kerberos, обсуждавшиеся в предыдущем разделе.

-Djava.security.krb5.conf=krb5.conf

Также нам нужно передать необходимые параметры JVM:

Этого достаточно, чтобы Kerberos выполнил аутентификацию с учетными данными из keytab и GSS для установления контекстов.

5. GSS API в реальном мире

java -Djava.security.krb5.conf=krb5.conf \
  -Djavax.security.auth.useSubjectCredsOnly=false \
  -Djava.security.auth.login.config=login.conf \
  com.baeldung.jgss.JgssUnitTest

Хотя GSS API обещает решить множество проблем безопасности с помощью подключаемых механизмов, есть несколько вариантов использования, которые получили более широкое распространение:

Он широко используется в SASL в качестве механизм безопасности, особенно когда Kerberos является предпочтительным базовым механизмом. Kerberos — это широко используемый механизм аутентификации, особенно в корпоративной сети. Очень полезно использовать инфраструктуру Kerberized для аутентификации нового приложения. Следовательно, GSS API прекрасно устраняет этот пробел. Он также используется в сочетании с SPNEGO для согласования механизма безопасности, если он заранее неизвестен. В этом отношении SPNEGO в некотором смысле является псевдомеханизмом GSS API. Это широко поддерживается во всех современных браузерах, что позволяет им использовать аутентификацию на основе Kerberos.

6. GSS API в сравнении

    GSS API весьма эффективен в предоставлении сервисов безопасности приложениям в виде подключаемых модулей. Однако это не единственный способ добиться этого в Java.

Давайте разберемся, что еще может предложить Java и как они соотносятся с GSS API:

Java Secure Socket Extension (JSSE): JSSE — это набор пакетов в Java, который реализует Secure Sockets Layer (SSL) для Java. Он обеспечивает шифрование данных, аутентификацию клиента и сервера и целостность сообщений. В отличие от GSS API, для работы JSSE используется инфраструктура открытых ключей (PKI). Следовательно, GSS API работает более гибко и легко, чем JSSE. Java Simple Authentication and Security Layer (SASL): SASL — это структура аутентификации и безопасности данных для интернет-протоколов, которая отделяет их от конкретных механизмов аутентификации. По объему это похоже на GSS API. Однако Java GSS имеет ограниченную поддержку базовых механизмов безопасности через доступных поставщиков безопасности.

В целом, GSS API довольно эффективен в предоставлении услуг безопасности независимо от механизма. Однако поддержка большего количества механизмов безопасности в Java будет способствовать дальнейшему внедрению.

    7. Заключение

Подводя итог, в этом руководстве мы поняли основы GSS API как системы безопасности. Мы ознакомились с Java API для GSS и поняли, как мы можем их использовать. В процессе мы создали простые клиентские и серверные компоненты, которые выполняли взаимную аутентификацию и безопасно обменивались данными.

Кроме того, мы также увидели практическое применение GSS API и альтернативы, доступные в Java.

Как всегда, код можно найти на GitHub.

«

As always, the code can be found over on GitHub.