Spec-Zone .ru
спецификации, руководства, описания, API
Содержание документации

Единственный Вход в систему Используя Kerberos в Java

Mayank Upadhyay
Поршень Marti

КРАТКИЙ ОБЗОР

Существенное улучшение к архитектуре безопасности Java является возможностью достигнуть единственного входа в систему, используя Версию 5 Kerberos в следующем выпуске Java Standard Edition (J2SE). Единственное решение входа в систему позволяет пользователям аутентифицировать себя только однажды к информации о доступе о любой из нескольких систем. Это делается, используя JAAS для аутентификации и авторизации и GSS-API Java, чтобы установить безопасный контекст для передачи с равноправным приложением. Наш фокус находится на Kerberos V5 как базовый механизм безопасности для единственного входа в систему, хотя другие механизмы безопасности могут быть добавлены в будущем.

ВВЕДЕНИЕ

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

В этой газете мы обсуждаем, как использовать единственный вход в систему, основанный на Kerberos протокол V5. Мы используем Службу Аутентификации и авторизации Java (JAAS), чтобы аутентифицировать принципал к Kerberos и получить учетные данные, которые удостоверяют его личность. Мы показываем, как реализация Sun модуля входа в систему Kerberos может быть сделана считать учетные данные из существующего кэша на платформах, которые содержат собственную поддержку Kerberos. Мы тогда используем Java Универсальный API Службы безопасности (GSS-API Java), чтобы аутентифицировать к удаленной коллеге, используя ранее полученные учетные данные Kerberos. Мы также показываем, как делегировать учетные данные Kerberos для единственного входа в систему в многоуровневой среде.

KERBEROS V5

Kerberos V5 является протоколом аутентификации сети доверенной третьей стороны, разработанным, чтобы обеспечить аутентификацию strong, используя криптографию секретного ключа. При использовании Kerberos V5 пароль пользователя никогда не отправляется по сети, не даже в зашифрованной форме, кроме во время Kerberos администрирование V5. Kerberos был разработан в середине 1980-ых как часть Проекта MIT Афина. Полное описание Kerberos протокол V5 выходит за рамки этой бумаги. Для получения дополнительной информации по Kerberos протокол V5, пожалуйста, обратитесь к [1] и [2]

Kerberos V5 является зрелым протоколом и был широко развернут. Это доступно на Солярисе как ШОВ и на Windows 2000 и нескольких других платформах. Свободная ссылочная реализация в C доступна от MIT. По этим причинам мы выбрали Kerberos V5 как базовая технология для единственного входа в систему в J2SE.

СЛУЖБА АУТЕНТИФИКАЦИИ И АВТОРИЗАЦИИ JAVA (JAAS)

До недавнего времени Java 2 архитектуры безопасности исключительно определенные полномочия источником кода и сертификатов с открытым ключом, соответствующих подписывающим лицам кода. Однако, в многопользовательской среде это является требуемым, чтобы далее определить полномочия, основанные на аутентифицируемых идентификационных данных пользователя, выполняющего код.

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

Аутентификация и авторизация

Платформа JAAS может быть разделена на два компонента: компонент аутентификации и компонент авторизации.

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

Компонент авторизации JAAS добавляет существующую платформу безопасности Java, обеспечивая средства ограничить выполняющийся код Java от выполнения чувствительных задач, в зависимости от его codesource и в зависимости от того, кто выполняет код.

Сменная и Наращиваемая Платформа

Платформа аутентификации JAAS основана на Сменном Модуле аутентификации (ПЭМ) [3, 4]. Аутентификация JAAS выполняется сменным способом, разрешающим системным администраторам добавить соответствующие модули аутентификации. Это разрешает приложениям Java оставаться независимыми от базовых технологий аутентификации, и новые или обновленные технологии аутентификации могут быть легко сконфигурированы, не требуя модификаций к приложению непосредственно.

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

Предмет

JAAS использует термин Согласно, обращаются к любому объекту, который является источником запроса, чтобы получить доступ к ресурсам. Предметом может быть пользователь или служба. Так как у объекта может быть много имен или принципалов Предмет использования JAAS как дополнительный уровень абстракции, которая обрабатывает многократные имена на объект. Таким образом Предмет состоит из ряда принципалов. Нет никаких ограничений на основные имена.

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

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

У Предмета class есть методы, чтобы получить принципалы, общедоступные учетные данные и частные учетные данные, связанные с этим.

Пожалуйста, отметьте, что различные полномочия могут требоваться для операций на этих классах. Например AuthPermission ("modifyPrincipals") может быть обязан изменять основной набор Предмета. Подобные полномочия обязаны изменять общедоступные учетные данные, частные учетные данные и получать текущий Предмет.

doAs и doAsPrivileged

Java 2 осуществляет средства управления доступом времени выполнения через java.lang. SecurityManager. С SecurityManager консультируются в любое время, секретные операции предпринимаются. SecurityManager делегирует эту ответственность перед java.security. AccessController. AccessController получает текущее изображение AccessControlContext и проверяет, что у этого есть достаточное разрешение, чтобы сделать работу, которую требуют.

JAAS обеспечивает два метода doAs и doAsPrivileged, который может использоваться, чтобы связать аутентифицируемый Предмет с AccessControlContext динамически.

doAs метод связывает Предмет с контекстом управления доступом текущего потока, и последующие проверки управления доступом осуществляются на основе выполняемого кода и Предмет, выполняющий это.

public static Object doAs(final Subject subject,
                          final  PrivilegedAction action)

public static Object doAs(final Subject subject,
                          final  PrivilegedExceptionAction action)
    throws  PrivilegedActionException;

Обе формы doAs метода сначала связывают указанный предмет с AccessControlContext текущего потока, и затем выполняют действие. Это достигает эффекта выполнения действия как Предмет. Первый метод может бросить исключения на этапе выполнения, но у нормального выполнения есть он возвращающий Объект из выполнения () метод его параметра действия. Второй метод ведет себя так же за исключением того, что он может бросить проверенный PrivilegedActionException от своего выполнения () метод. AuthPermission ("doAs") обязан вызывать doAs методы.

Следующие методы также выполняют код как конкретную тему:

public static Object doAsPrivileged(final Subject subject,
                                    final  PrivilegedAction action,
                                    final  AccessControlContext  acc);

public static Object doAsPrivileged(final Subject subject,
                                    final PrivilegedExceptionAction action,
                                    final  AccessControlContext acc)
   throws PrivilegedActionException;

doAsPrivileged метод ведет себя точно как doAs, за исключением того, что он позволяет вызывающей стороне определять контекст управления доступом. Таким образом это эффективно выбрасывает текущий AccessControlContext, и решения об авторизации будут основаны на AccessControlContext, в котором передают.

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

public static Subject getSubject(final AccessControlContext acc);

LoginContext

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

Чтобы поддерживать наращиваемую природу LoginModules, LoginContext выполняет аутентификацию в двух фазах. В первой фазе или фазе входа в систему, это вызывает, каждый сконфигурировал LoginModule, чтобы делать попытку аутентификации. Если все необходимые LoginModules успешно выполняются, то LoginContext вводит вторую фазу, где это вызывает каждый LoginModule снова, чтобы формально фиксировать процесс аутентификации. Во время этой фазы Предмет заполняется с аутентифицируемыми принципалами и их учетными данными. Если любой из фазовых сбоев, то LoginContext вызывает каждый сконфигурированный модуль, чтобы прервать всю попытку аутентификации. Каждый LoginModule тогда очищает любое соответствующее состояние, связанное с попыткой аутентификации.

У LoginContext есть четыре конструктора, которые могут использоваться, чтобы инстанцировать его. Все они требуют, чтобы имя записи конфигурации было передано. Кроме того, Предмет и/или CallbackHandler можно также передать конструкторам.

Обратные вызовы

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

LoginContext позволяет приложению определять callbackhandler что базовое использование модулей входа в систему, чтобы взаимодействовать с пользователями. Есть два обработчика обратного вызова, доступные в Мерлине (J2SE 1.4) - один основанный на командной строке и другом основанном на GUI.

LoginModules

Sun обеспечивает реализацию UnixLoginModule, NTLoginModule, JNDILoginModule, KeyStoreLoginModule и Krb5LoginModule в Мерлине. Смарт-карта базируемый модуль входа в систему JAAS доступна от GemPlus [5].

Модуль Входа в систему Kerberos

class com.sun.security.auth.module.Krb5LoginModule является реализацией Sun модуля входа в систему для протокола версии 5 Kerberos. После успешной аутентификации Билет Выдачи билетов (TGT) сохранен в частном наборе учетных данных Предмета, и принципал Kerberos сохранен в основном наборе Предмета.

Основанный на определенных конфигурируемых опциях, Krb5LoginModule может также использовать существующий кэш учетных данных, такой как собственный кэш в операционной системе, чтобы получить TGT и/или использовать keytab файл, содержащий секретный ключ, чтобы неявно аутентифицировать принципал. И Солярис и платформы Windows 2000 содержат кэш учетных данных, который Krb5LoginModule может использовать для того, чтобы выбрать TGT. Солярис также содержит keytab файл в масштабе всей системы, который Krb5LoginModule может использовать для того, чтобы выбрать секретный ключ. На всех платформах Krb5LoginModule поддерживает опции, чтобы установить путь к файлу к кэшу билета или keytab предпочтительному файлу. Это полезно, когда сторонняя поддержка Kerberos устанавливается, и интеграция Java требуется. Пожалуйста, консультируйтесь с документацией для Krb5LoginModule, чтобы узнать об этих опциях. В отсутствие собственного кэша или keytab, пользователь будет запрошен пароль и TGT, полученный из KDC.

Рисунок 1 обеспечивает демонстрационную запись конфигурации входа в систему JAAS для клиентского приложения. В этом примере Krb5LoginModule будет использовать собственный кэш билета, чтобы получить доступное TGT в этом. Аутентифицируемые идентификационные данные будут идентификационными данными принципала Kerberos, которому принадлежит TGT.

SampleClient {
    com.sun.security.auth.module.Krb5LoginModule required useTicketCache=true
};
             Figure 1. Sample client  configuration entry

Рисунок 2 обеспечивает демонстрационную запись конфигурации входа в систему для серверного приложения. С этой конфигурацией секретный ключ от keytab используется, чтобы аутентифицировать принципал "nfs/bar.example.com" и и TGT, полученный из Kerberos, KDC и секретный ключ сохранены в частном наборе учетных данных Предмета. Сохраненный ключ может использоваться позже, чтобы проверить билета службы, отправленного клиентом (См. раздел по GSS-API Java.)

SampleServer {
     com.sun.security.auth.module.Krb5LoginModule
         required useKeyTab=true storeKey=true principal="nfs/bar.example.com"
};

             Figure 2. Sample server configuration entry

В клиентском примере кода, показанном в рисунке 3, запись конфигурации "SampleClient" будет использоваться LoginContext. TextCallbackHandler class будет использоваться, чтобы запросить пользователя пароль Kerberos. Как только пользователь вошел в систему, Предмет будет заполнен с именем Принципала Kerberos и TGT. После того пользователь может выполнить код, используя Subject.doAs, передающий в Предмете, полученном из LoginContext.

LoginContext lc = null;

try {
        lc = new LoginContext("SampleClient", new TextCallbackHandler());
        // attempt authentication
        lc.login();
} catch (LoginException le) {
    ...
}

 // Now try to execute ClientAction as the authenticated Subject

 Subject mySubject = lc.getSubject();
 PrivilegedAction action = new ClientAction();
 Subject.doAs(mySubject, action);

            Figure 3. Sample client code

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

Рисунок 4 показывает серверный пример кода. Это подобно клиентскому коду, показанному в рисунке 4 за исключением имени записи приложения и PrivilegedAction.

LoginContext lc = null;

try {
        lc = new LoginContext("SampleServer", new TextCallbackHandler());
        // attempt authentication
        lc.login();
} catch (LoginException le) {
   ...
}

// Now try to execute ServerAction as the authenticated Subject

Subject mySubject = lc.getSubject();
PrivilegedAction action = new ServerAction();
Subject.doAs(mySubject, action);

            Figure 4. Sample server code

Классы Kerberos

Чтобы позволить другим поставщикам обеспечить свою собственную реализацию модуля входа в систему Kerberos, которая может использоваться с GSS-API Java, три стандартных класса Kerberos были представлены в javax.security.auth.kerberos пакете. Они - KerberosPrincipal для принципалов Kerberos, KerberosKey в течение длительного срока секретный ключ Kerberos и KerberosTicket для билетов Kerberos. Все реализации модуля входа в систему Kerberos должны использовать эти классы, чтобы сохранить принципалы, ключи и билеты в Предмете.

Авторизация

После успешной аутентификации Предмета средства управления доступом могут быть осуществлены основанные на принципалах, связанных с аутентифицируемым Предметом. Принципал JAAS базируемые средства управления доступом увеличивает средства управления доступом CodeSource Java 2. Полномочия, предоставленные Предмету, конфигурируются в Политике, которая является абстрактным class для того, чтобы представить политику управления доступом в масштабе всей системы. Sun обеспечивает файл базируемая реализация Политики class. class Политики является провайдером, базируемым так, чтобы другие могли обеспечить свою собственную реализацию политики.

JAVA УНИВЕРСАЛЬНЫЙ ПРИКЛАДНОЙ ПРОГРАММНЫЙ ИНТЕРФЕЙС СЛУЖБЫ БЕЗОПАСНОСТИ (GSS-API Java)

Универсальный API Службы безопасности (GSS-API)

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

API, описанный в независимой от языка форме в RFC 2743 [6], размещает следующие службы безопасности: аутентификация, конфиденциальность сообщения и целостность, упорядочивание защищенных сообщений, воспроизводит обнаружение, и учетную делегацию. Базовая технология безопасности или "механизм безопасности" быть используемым, имеет выбор поддержки того или большего количества этих функций вне существенного одного пути аутентификация. Сноска 1

Есть, главным образом, два стандартных механизма безопасности, которые определил IETF: Kerberos V5 [6] и Простой Механизм С открытым ключом (SPKM) [8].

API разрабатывается так, что, реализация может поддерживать многократные механизмы одновременно, давая приложение возможность выбрать один во времени выполнения. Механизмы идентифицируются посредством (OID) уникального объектного идентификатора, которые регистрируются в IANA. Например, Kerberos механизм V5 идентифицируется OID {международная организация по стандартизации (1) комитет-член (2) Соединенные Штаты (840) mit (113554) infosys (1) gssapi (2) krb5 (2)}.

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

GSS-API мультимеханизма

Рисунок 5. Реализация GSS-API мультимеханизма

GSS-API Java

API Java для Универсальной Службы безопасности был также определен в IETF и документируется в RFC 2853 [10]. Sun преследует стандартизацию этого API при Процессе Сообщества Java (JCP) [11] и планирует поставить ссылочную реализацию с Мерлином. Поскольку JCP просто подтверждает этот внешне определенный API, IETF, присвоенное пространство имен пакета "org.ietf.jgss" будет сохранено в Мерлине.

Реализация Sun GSS-API Java, первоначально поставит с поддержкой Kerberos механизм V5 только. Поддержка механизма V5 Kerberos обязательна для всех реализаций GSS-API Java в Java SE, хотя они свободны поддерживать дополнительные механизмы. В будущем выпуске будет добавлен Интерфейс Поставщика услуг (SPI) так, чтобы новые механизмы могли быть сконфигурированы статически или даже во времени выполнения. Даже теперь ссылочная реализация в Мерлине будет модульной и поддерживать частный SPI провайдера, который будет преобразован в общественность когда стандартизировано.

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

Реализация значения по умолчанию GSSManager, который поставит с Java SE, получается следующим образом:

GSSManager manager = GSSManager.getInstance();

GSSManager может использоваться, чтобы сконфигурировать новых провайдеров, и перечислять все механизмы уже представляют. GSSManager также служит фабрикой class для трех важных интерфейсов: GSSName, GSSCredential, и GSSContext. Эти интерфейсы описываются ниже с методами, чтобы инстанцировать их реализаций. Для полной спецификации API читатели относятся в [9] и [11].

Большинство звонков в GSS-API Java бросает GSSException, которые инкапсулируют проблемы, которые происходят и в пределах платформы GSS-API, и в пределах провайдеров механизма.

Интерфейс GSSName

Этот интерфейс представляет объект в целях GSS-API Java. Реализация этого интерфейса инстанцируют следующим образом:

GSSName GSSManager.createName(String name, Oid nameType)
    throws GSSException

Например:

GSSName clientName = manager.createName("duke", GSSName.NT_USER_NAME);

Этот вызов возвращает GSSName, который представляет пользовательский принципал "герцог" в механизме независимый уровень. Внутренне, предполагается, что каждый поддерживаемый механизм отобразит универсальное представление пользователя к большему количеству механизма определенная форма. Например провайдер механизма V5 Kerberos мог бы отобразить это имя к duke@EXAMPLE.COM, где EXAMPLE.COM локальная область Kerberos. Точно так же открытый ключ базируемый провайдер механизма мог бы отобразить это имя к Отличительному имени X.509.

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

Пример:

GSSName serverName = manager.createName("nfs@bar.example.com",
                                         GSSName.NT_HOSTBASED_SERVICE);

Механизм V5 Kerberos отобразил бы это имя к Kerberos определенная форма nfs/bar.example.com@EXAMPLE.COM, где EXAMPLE.COM область принципала. Этот принципал представляет nfs службы, работающую на хост-машине bar.example.com.

Реализацией Sun интерфейса GSSName является контейнерный class. Контейнерный class лениво просит, чтобы отдельные провайдеры выполнили свое отображение, когда их механизм используется и затем хранит каждый отображенный элемент в ряде принципалов. В этом отношении реализация GSSName подобна основному набору, сохраненному в Предмете. Это может даже содержать те же самые элементы, которые находятся в основном наборе Предмета, но его использование ограничивается контексту GSS-API Java.

Элемент имени, сохраненный Sun Kerberos провайдер V5, является экземпляром подкласса javax.security.auth.kerberos.KerberosPrincipal.

Интерфейс GSSCredential

Этот интерфейс инкапсулирует учетные данные, принадлежавшие одному объекту. Как GSSName, этот интерфейс также является контейнером мультимеханизма.

Его реализация инстанцируют следующим образом:

GSSCredential createCredential(GSSName name,
                               int lifetime,
                               Oid[] desiredMechs,
                               int usage)

    throws GSSException

Вот пример этого запроса к стороне клиента:

GSSCredential clientCreds =
               manager.createCredential(clientName,
                                        8*3600,
                                        desiredMechs,
                                        GSSCredential.INITIATE_ONLY);

GSSManager вызывает провайдеров механизмов, перечисленных в desiredMechs для учетных данных, которые принадлежат имени клиента GSSName. Дополнительно, это вводит ограничение, что учетные данные должны быть видом, который может инициировать исходящие запросы (то есть, клиентские учетные данные), и запрашивает время жизни 8 часов для этого. Возвращенный объект содержит элементы от подмножества desiredMechs, который имел некоторые учетные данные в наличии, чтобы удовлетворить это критерии. Элемент, сохраненный Kerberos, механизм V5 является экземпляром подкласса javax.security.auth.kerberos.KerberosTicket содержащий TGT, который принадлежит пользователю.

Учетный сбор на стороне сервера происходит следующим образом:

GSSCredential serverCreds =
          manager.createCredential(serverName,
                                   GSSCredential.INDEFINITE_LIFETIME,
                                   desiredMechs,
                                   GSSCredential.ACCEPT_ONLY);

Поведение подобно клиентскому случаю, за исключением того, что вид учетных данных, которые требуют, является тем, который может принять входящие запросы (то есть, учетные данные сервера). Кроме того серверы обычно долговечны и любят запрашивать более длинное время жизни на учетные данные, такие как INDEFINITE_LIFETIME, показанный здесь. Kerberos сохраненный элемент механизма V5 является экземпляром подкласса javax.security.auth.kerberos. KerberosKey, содержащий секретный ключ сервера.

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

Интерфейс GSSContext

GSSContext является интерфейсом, реализация которого предоставляет службам безопасности для двух коллег.

На стороне клиента реализация GSSContext получается со следующим вызовом API:

GSSContext GSSManager.createContext(GSSName peer,
                                    Oid mech,
                                    GSSCredential clientCreds,
                                    int lifetime)
    throws GSSException

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

На стороне сервера GSSContext получается следующим образом:

GSSContext GSSManager.createContext(GSSCredential serverCreds)
    throws GSSException

Это возвращает инициализированный контекст защиты на стороне получателя. В этой точке это не знает имя коллеги (клиент), который отправит запрос установления контекста или даже базовый механизм, который будет использоваться. Однако, если входящий запрос не будет для принципала службы, представленного учетными данными serverCreds, или у базового механизма, который требует сторона клиента, нет учетного элемента в serverCreds, то запрос перестанет работать.

Прежде, чем GSSContext может использоваться для его служб безопасности, он должен быть установлен с обменом маркерами между двумя коллегами. Каждый звонок в методы установления контекста генерирует непрозрачный маркер, который приложение должно так или иначе отправить его коллеге, используя канал связи его выбора.

Клиент использует следующий вызов API, чтобы установить контекст:

byte[] GSSContext.initSecContext(byte[] inToken,
                                 int offset,
                                 int len)

   throws GSSException

Сервер использует следующий вызов:

byte[] acceptSecContext(byte[] inToken,
                        int offset,
                        int len)

   throws GSSException

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

Число циклов обработки маркеров GSS-API, требуемых аутентифицировать коллеги, изменяется с механизма на механизм и также меняется в зависимости от характеристик такой как или взаимная аутентификация, или односторонняя аутентификация требуется. Таким образом каждая сторона приложения должна продолжать вызывать методы установления контекста в цикле, пока процесс не полон.

В случае Kerberos механизм V5 есть не больше, чем один цикл обработки маркеров во время установления контекста. Клиент сначала отправляет маркер, сгенерированный его initSecContext () содержащий сообщение [2] AP-REQ Kerberos. Чтобы генерировать сообщение AP-REQ, провайдер Kerberos получает билет службы для целевого сервера, используя TGT клиента. Билет службы шифруется с долгосрочным секретным ключом сервера и инкапсулируется как часть сообщения AP-REQ. После того, как сервер получает этот маркер, его передают к acceptSecContext () метод, который дешифрует билет службы и аутентифицирует клиент. Если бы взаимную аутентификацию не требовали, то и контексты стороны клиента и сервера были бы установлены, и сторона сервера acceptSecContext () не генерирует вывода.

Однако, если бы взаимная аутентификация была включена, то acceptSecContext сервера () генерировал бы выходной маркер, содержащий ЧЛЕНА ПАЛАТЫ ПРЕДСТАВИТЕЛЕЙ AP Kerberos [2] сообщение. Этот маркер должны были бы отослать назад к клиенту для того, чтобы обработать его initSecContext (), прежде, чем клиентский контекст будет установлен.

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

Вот class, показывающий, как сторона клиента приложения была бы кодирована. Это - ClientAction class, который выполнялся, используя doAs метод в рисунке 3:

class ClientAction implements PrivilegedAction {

    public Object run() {
        ...
        ...
        try {
            GSSManager manager = GSSManager.getInstance();
            GSSName clientName =
                manager.createName("duke", GSSName.NT_USER_NAME);
            GSSCredential clientCreds =
                manager.createCredential(clientName,
                                          8*3600,
                                          desiredMechs,
                                          GSSCredential.INITIATE_ONLY);
            GSSName peerName =
                manager.createName("nfs@bar.example.com",
                                    GSSName.NT_HOSTBASED_SERVICE);
            GSSContext secContext =
                manager.createContext(peerName,
                                      krb5Oid,
                                      clientCreds,
                                      GSSContext.DEFAULT_LIFETIME);
            secContext.requestMutualAuth(true);

            // The first input token is ignored
            byte[] inToken = new byte[0];

            byte[] outToken = null;

            boolean established = false;

           // Loop while the context is still not established
           while (!established) {
               outToken =
                   secContext.initSecContext(inToken, 0, inToken.length);

               // Send a token to the peer if one was generated
               if (outToken != null)
                  sendToken(outToken);

               if (!secContext.isEstablished()) {
                  inToken = readToken();
               else
                  established = true;
            }
        } catch (GSSException e) {
             ....
        }
        ...
        ...
    }
}


        Figure 6. Sample client using Java GSS-API

Соответствующий раздел кода стороны сервера, выполняющей ServerAction class из рисунка 5, следующие:

class ServerAction implelemts PrivilegedAction {

    public Object run() {
        ...
        ...
        try {
            GSSManager manager = GSSManager.getInstance();
            GSSName serverName =
                manager.createName("nfs@bar.example.com",
                                    GSSName.NT_HOSTBASED_SERVICE);
            GSSCredential serverCreds =
             manager.createCredential(serverName,
                                       GSSCredential.INDEFINITE_LIFETIME,
                                       desiredMechs,
                                       GSSCredential.ACCEPT_ONLY);
            GSSContext secContext = manager.createContext(serverCreds);

            byte[] inToken = null;
            byte[] outToken = null;

            // Loop while the context is still not established
            while (!secContext.isEstablished()) {
                 inToken = readToken();
                 outToken =
                     secContext.acceptSecContext(inToken, 0, inToken.length);

                  // Send a token to the peer if one was generated
                  if (outToken != null)
                      sendToken(outToken);
            }
        } catch (GSSException e) {
          ...
        }
        ...
        ...
   }
}

             Figure 7. Sample server using Java GSS-API

Защита сообщения

Как только контекст защиты устанавливается, он может использоваться для защиты сообщения. GSS-API Java обеспечивает и целостность сообщения и конфиденциальность сообщения. Два вызова, которые включают этому, следующие:

byte[] GSSContext.wrap(byte[] clearText,
                       int offset,
                       int len,
                       MessageProp properties)
    throws GSSException
и
byte[] unwrap(byte[] inToken,
              int offset,
              int len,
              MessageProp properties)
    throws GSSException

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

Учетная Делегация

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

Учетная делегация в многоуровневой среде

Рисунок 8. Учетная Делегация

Клиент запрашивает учетную делегацию до создания первого звонка initSecContext ():

void GSSContext.requestCredDeleg(boolean state)
    throws GSSException
устанавливая состояние в истину.

Сервер получает делегированные учетные данные после установления контекста:

GSSCredential GSSContext.getDelegCred() throws GSSException

Сервер может тогда передать этот GSSCredential к GSSManager.createContext () симулирующий быть клиентом.

В случае Kerberos механизм V5 делегированные учетные данные являются переданным TGT, который инкапсулируется как часть первого маркера, отправленного от клиента к серверу. Используя этот TGT, сервер может получить билет службы от имени клиента для любой другой службы.

УЧЕТНАЯ МОДЕЛЬ СБОРА ЗНАЧЕНИЯ ПО УМОЛЧАНИЮ

Ранее мы обсуждали, как приложение использует GSSManager.createCredential () метод, чтобы заполнить объект GSSCredential с механизмом определенные учетные данные. Следующие два подраздела сосредоточатся, как механизмы GSS-API Java получают эти учетные данные. Механизмы самостоятельно не выполняют пользовательский вход в систему. Вместо этого вход в систему выполняется до использования GSS-API Java, и учетные данные, как предполагается, сохранены в некотором кэше, о котором знает провайдер механизма. GSSManager.createCredential () метод просто получает ссылки на те учетные данные и возвращает их в GSS-центральном контейнере, GSSCredential.

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

У этой модели есть преимущество, что учетное управление просто и предсказуемо с точки зрения приложения. Приложение, учитывая правильные полномочия, может произвести чистку учетных данных в Предмете или возобновить их использующий стандартный API Java. Если бы это произвело чистку учетных данных, то это убедилось бы, что механизм GSS-API Java перестанет работать, или если бы это возобновило время базируемые учетные данные, то это убедилось бы, что механизм успешно выполнился бы.

Вот последовательность событий, относящихся к учетному сбору, когда Kerberos механизм V5 используется клиентским приложением 3 в цифрах и 6:

  1. Приложение вызывает вход в систему JAAS, который поочередно вызывает сконфигурированный Krb5LoginModule
  2. Krb5LoginModule получает TGT (KerberosTicket) для пользователя или от KDC или от существующего кэша билета, и хранит этот TGT в частном наборе учетных данных Предмета
  3. Приложение получает заполненный Предмет, затем вызывает Subject.doAs/doAsPrivileged, который помещает этот Предмет в контекст управления доступом выполнения потока ClientAction
  4. ClientAction вызывает метод GSSManager.createCredential, передавая это Kerberos OID V5 в desiredMechs.
  5. GSSManager.createCredential вызывает Kerberos провайдер GSS-API V5, прося учетные данные Kerberos для того, чтобы инициировать контексты защиты.
  6. Провайдер Kerberos получает Предмет из текущего контекста управления доступом, и перерывает его частный учетный набор для допустимого KerberosTicket, который представляет TGT для пользователя.
  7. KerberosTicket возвращается к GSSManager, который хранит его в контейнерном экземпляре GSSCredential, который будет возвращен к вызывающей стороне.

На стороне сервера, когда вход в систему Kerberos успешен в шаге 2, Krb5LoginModule, хранит KerberosKey для сервера в Предмете в дополнение к KerberosTicket. Позже KerberosKey получается в шагах 5 - 7 и используется, чтобы дешифровать билет службы, который отправляет клиент.

ИСКЛЮЧЕНИЯ TO МОДЕЛЬ

Учетная модель сбора значения по умолчанию для GSS-API Java требует, чтобы учетные данные присутствовали в текущем Предмете. Как правило, учетные данные помещаются туда после входа в систему JAAS приложением.

Могли бы быть случаи, где приложение хочет использовать учетные данные Kerberos снаружи Предмета. Рекомендуется, чтобы такие учетные данные были считаны как часть начального входа в систему JAAS, или конфигурируя Krb5LoginModule, чтобы считать их, или при записи пользовательского модуля входа в систему, который читает их. Однако, некоторые приложения могли бы иметь, ограничивает, это или препятствует тому, чтобы они использовали JAAS до вызова GSS-API Java, или вынуждает их использовать некоторого провайдера механизма Kerberos, который не получает учетные данные от текущего Предмета.

Чтобы разместить такие случаи, все еще сохраняя стандартную модель для других, системное свойство javax.security.auth.useSubjectCredsOnly было добавлено. Это системное свойство служит булевым, где значение истины требует, чтобы стандартная учетная модель сбора сопровождалась, и значение лжи разрешает провайдеру использовать любой кэш этого выбор. Значение по умолчанию этого свойства (когда это не устанавливается), как будет предполагаться, будет истиной.

Если нет никаких допустимых учетных данных Kerberos в текущем Предмете, и это свойство является истиной, то механизм Kerberos бросает GSSException. Установка этого свойства ко лжи не обязательно означает, что провайдер должен использовать кэш кроме текущего Предмета, это только дает провайдеру широту, чтобы сделать так, если это желает.

Провайдер Sun для Kerberos механизм GSS-API V5 всегда получает учетные данные из Предмета. Если нет никаких допустимых учетных данных в текущем Предмете, и это свойство устанавливается в ложь, то провайдер пытается получить новые учетные данные из временного Предмета, вызывая вход в систему JAAS непосредственно. Это использует текстовый обработчик обратного вызова для ввода/вывода с пользователем, и запись конфигурации JAAS, идентифицированную "другим" для списка модулей и опций, чтобы использовать. Сноска 2

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

ИНТЕГРАЦИЯ ВЕБ-БРАУЗЕРА

Важный class приложений, которые должны быть в состоянии извлечь выгоду из Java единственный вход в систему, является апплетами. Для этого обсуждения мы предполагаем, что браузер, у JRE есть все необходимые пакеты или плагин Java, используется с Мерлином ДЖРОМ, установленным пользователем.

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

У хорошей модели, чтобы решить эту проблему должен был бы быть браузер (или плагин Java) выполняют вход в систему JAAS однажды при запуске. Это обеспечило бы Предмет, который мог всегда связываться с контекстом управления доступом всякий раз, когда любой код Java был выполнен. В результате код апплета не должен был бы выполнить вход в систему JAAS до использования GSS-API Java, и пользовательский вход в систему произойдет только однажды.

В отсутствие этой функциональности входа в систему в браузере (или плагин Java), апплеты могут все еще избежать иметь необходимость выполнить вход в систему JAAS непосредственно. Чтобы сделать так, апплеты должны были бы установить javax.security.auth.useSubjectCredsOnly системное свойство в ложь и использовать провайдера механизма GSS-API, который способен к получению учетных данных из источников кроме текущего Предмета. При использовании Sun JRE с Sun провайдер GSS-API Kerberos ожидайте, что механизм выполнит вход в систему JAAS, чтобы получить новые учетные данные как объяснено в предыдущем разделе. Апплет deployer должен был бы только гарантировать, что соответствующие модули и опции перечисляются в записи "другой" в конфигурации JAAS, используемой JRE. Это спасает разработчика апплета от вызова в API JAAS непосредственно, но это не останавливает повторный вход в систему JAAS, который мог бы произойти с каждым апплетом пользовательские выполнения. Однако, конфигурируя модули входа в систему, чтобы считать существующий ранее собственный кэш, deployer может и скрыть вход в систему от пользователя, и минимизировать издержки в многократных входах в систему. (См., как это делается для записи конфигурации JAAS "SampleClient" в рисунке 1.)

УГРОЗЫ БЕЗОПАСНОСТИ

Удобство единственного входа в систему также представляет новые риски. Что происходит, если злонамеренный пользователь получает доступ к Вашему необслуживаемому рабочему столу от того, где он или она может запустить апплеты как Вы? Что происходит, если злонамеренные апплеты входят в систему как Вы к службам, к которым они не предполагаются?

Для прежнего у нас нет никакого решения, кроме как предостеречь Вас против отъезда Вашей разблокированной рабочей станции! Для последнего у нас есть много проверок полномочий на месте.

Чтобы иллюстрировать некоторые детали модели полномочий рассматривают пример, где Ваш браузер выполнил вход в систему JAAS во время запуска и связал Предмет со всеми апплетами, которые работают в этом.

Предмет защищается от апплетов жулика посредством javax.security.auth. AuthPermission class. Это разрешение проверяется всякий раз, когда код пытается получить ссылку на Предмет, связанный с любым контекстом управления доступом.

Даже если апплету предоставили доступ к Предмету, это нуждается javax.security.auth.PrivateCredentialPermission, чтобы фактически считать чувствительные частные учетные данные, сохраненные в этом.

Другие виды проверок должны быть сделаны провайдерами механизма GSS-API Java, поскольку они читают учетные данные и устанавливают контексты защиты от имени владельца учетных данных. Чтобы поддерживать Kerberos механизм V5, два новых класса полномочий были добавлены с пакетом javax.security.auth.kerberos:

ServicePermission(String servicePrinicipal, String action)
DelegationPermission(String principals)

Поскольку новые механизмы GSS-API стандартизируются для Java SE, больше пакетов будет добавлено, которые содержат соответствующие классы полномочий для провайдеров тех механизмов.

Проверки разрешения механизма GSS-API Kerberos имеют место в следующих моментах в выполнении программы:

Учетный Сбор

GSSManager.createCredential () метод получает механизм определенные учетные элементы из кэша, такие как текущий Предмет и хранит их в контейнере GSSCredential. Разрешение апплетов получить GSSCredential свободно, даже если они не могут использовать их, чтобы сделать много, является нежелательным. Выполнение так информация об утечках о существовании пользователя и принципалов службы. Таким образом, прежде, чем приложение может получить GSSCredential с любыми учетными элементами Kerberos в этом, проверка ServicePermission осуществляется.

На стороне клиента успешный сбор GSSCredential подразумевает, что к TGT получили доступ от кэша. Таким образом следующий ServicePermission проверяется:

ServicePermission("krbtgt/EXAMPLE.COM@EXAMPLE.COM", "initiate");

Принципал службы krbtgt/EXAMPLE.COM@EXAMPLE.COM представляет службу выдачи билетов (TGS) в области Kerberos EXAMPLE.COM, и действие "новичок" предполагает, что к билету к этой службе получают доступ. Принципал службы TGS будет всегда использоваться в этой проверке разрешения во время клиентского учетного сбора.

На стороне сервера успешный сбор GSSCredential подразумевает, что к секретному ключу получили доступ от кэша. Таким образом следующий ServicePermission проверяется:

ServicePermission("nfs/bar.example.com@EXAMPLE.COM", "accept");

Здесь принципал службы nfs/bar.example.com представляет принципал службы Kerberos, и действие "принимают", предполагает, что секретный ключ для этой службы требуют.

Установление контекста

Апплет, у которого есть полномочия, чтобы связаться с определенным сервером скажем сервер LDAP, не должен вместо этого связаться с различным сервером, таким как сервер FTP. Конечно, апплет мог бы быть ограничен от выполнения так со справкой SocketPermission. Однако, возможно использовать ServicePermission, чтобы ограничить это от аутентификации использования Ваших идентификационных данных, даже если сетевое соединение было разрешено.

Когда провайдер механизма Kerberos собирается инициировать установление контекста, это проверяет ServicePermission:

ServicePermission("ftp@EXAMPLE.COM", "initiate");

Эта проверка препятствует тому, чтобы несанкционированный код получил и использовал билет службы Kerberos для принципала ftp@EXAMPLE.COM.

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

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

Учетная Делегация

У апплета, у которого есть разрешение, чтобы установить контекст защиты с сервером от Вашего имени также, есть возможность запросить что Ваши учетные данные быть делегированным к тому серверу. Но не все серверы доверяются до такой степени, что Ваши учетные данные могут быть делегированы им. Таким образом, прежде, чем провайдер Kerberos получает делегированные учетные данные, чтобы передаться к коллеге, он проверяет следующее разрешение:

DelegationPermission(" \"ftp@EXAMPLE.COM\" \"krbtgt/EXAMPLE.COM@EXAMPLE.COM\" ");

Это разрешение позволяет принципалу службы Kerberos ftp@EXAMPLE.COM получать переданный TGT (представленный службой выдачи билетов krbtgt/EXAMPLE.COM@EXAMPLE.COM).Footnote 3

ЗАКЛЮЧЕНИЯ

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

Модуль входа в систему Kerberos для JAAS способен к чтению собственных кэшей так, чтобы пользователи не аутентифицировали себя вне настольного входа в систему на платформах та поддержка Kerberos. Кроме того, Kerberos, механизм V5 для GSS-API Java позволяет учетным данным быть делегированными, который включает единственному входу в систему в многоуровневых средах.

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

ПОДТВЕРЖДЕНИЯ

Мы благодарим Гари Эллисона, Чарли Лая, и Джеффа Низеуонджера для их содействия на каждом этапе Kerberos единственный проект входа в систему. JAAS 1.0 был реализован Чарли как дополнительный пакет для пустельги (J2SE 1.3). Гари способствовал разработке модели полномочий для механизма GSS-API Java Kerberos. Мы благодарны Бобу Шеифлеру для его обратной связи при интегрировании JAAS 1.0 в Мерлина и Тиму Блэкмену для реализаций KeyStoreLoginModule и CallbackHandler. Мы также благодарим Брюса Рича, Тони Надалина, Томаса Оуузу и Янни Занга для их комментариев и предложений. Мы благодарим Мэри Дэджефорд за документацию и учебные руководства. Sriramulu Lakkaraju, Стюарт Ки и Шитэл Шизод внесенные тесты для проектов. Максин Эрланд оказанная поддержка управления для проекта.

ССЫЛКИ

  1. Neuman, Клиффорд и Тсо, Теодор (1994). Kerberos: Служба Аутентификации для Компьютерных Сетей, Связи IEEE, объем 39 страниц 33-38
  2. J.Kohl и C.Neuman. Служба Аутентификации Сети Kerberos (V5) Инженерная группа по развитию интернета, Запрос сентября 1993 на Комментарии 1510
  3. V. Самар и К. Лай. Создание Служб Входа в систему, Независимых от Authentication Technologies. В Продолжениях Конференции Разработчика SunSoft, март 1996.
  4. X/Open Единственная Служба Входа в систему (XSSO) - Сменная Аутентификация. Предварительная Спецификация P702, Open Group, июнь 1997. http://www.opengroup.org
  5. Модуль Входа в систему Смарт-карты для Службы Аутентификации и авторизации Java. http://www.gemplus.com/techno/smartjaas/index.html
  6. J. Линн. Универсальный Прикладной программный интерфейс Службы безопасности, Версия 2. Инженерная группа по развитию интернета, Запрос января 2000 на Комментарии 2743
  7. J. Линн. Механизм GSS-API Версии 5 Kerberos. Инженерная группа по развитию интернета, Запрос июня 1996 на Комментарии 1964
  8. C.Adams. Простой Механизм GSS-API С открытым ключом (SPKM). Инженерная группа по развитию интернета, Запрос октября 1996 на Комментарии 2025
  9. J. Kabat и M.Upadhyay. Универсальная Версия 2 API Службы безопасности: Привязка Java. Инженерная группа по развитию интернета, Запрос января 1997 на Комментарии 2853
  10. JSR 000072 Универсальных API Служб безопасности
  11. Платформа Java, Standard Edition Спецификация API

Сноска 1 GSS-API механизм Kerberos выполняет аутентификацию клиента в минимуме.

Сноска 2 Фактически это сначала пытается использовать запись конфигурации JAAS "com.sun.security.jgss.initiate" для клиента и "com.sun.security.jgss.accept" для сервера и возвращается к записи для "другого", если эти записи отсутствуют. Это дает системным администраторам некоторый дополнительный контроль над его поведением.

Сноска 3, которую использование двух основных имен в этом разрешении учитывает более прекрасный, гранулировала делегация, такая как билеты прокси для определенных служб в отличие от карт-бланша переданный TGT. Даже при том, что GSS-API не учитывает билеты прокси, другой API, такие как JSSE мог бы поддерживать эту идею в некоторый момент в будущем.


Oracle и/или его филиалы Авторское право © 1993, 2012, Oracle и/или его филиалы. Все права защищены.
Свяжитесь с Нами