Кто-нибудь знает, как получить сервисный билет в Центре распространения ключей (KDC) с помощью Java GSS-API?
У меня есть толстое клиентское приложение, которое сначала аутентифицируется через JAAS с использованием Krb5LoginModule для извлечения TGT из кеша билетов (фон: Windows, например, использует реализацию Kerberos и сохраняет билет выдачи билетов в защищенной области памяти). Из LoginManager я получаю объект Subject, содержащий TGT. Теперь я надеялся, что когда я создам конкретный объект GSSCredential для своей службы, билет службы также будет помещен в личные учетные данные Субъекта (я читал об этом где-то в Интернете). Итак, я пробовал следующее:
// Exception handling ommitted
LoginContext lc = new LoginContext("HelloEjbClient", new DialogCallbackHandler());
lc.login()
Subject.doAs(lc.getSubject(), new PrivilegedAction() {
public Object run() {
GSSManager manager = GSSManager.getInstance();
GSSName clientName = manager.createName("clientUser", GSSName.NT_USER_NAME);
GSSCredential clientCreds = manager.createCredential(clientName, 8 * 3600, createKerberosOid(), GSSCredential.INITIATE_ONLY);
GSSName serverName = manager.createName("myService@localhost", GSSName.NT_HOSTBASED_SERVICE);
manager.createCredential(serverName, GSSCredential.INDEFINITE_LIFETIME, createKerberosOid(), GSSCredential.INITIATE_ONLY);
return null;
}
private Oid createKerberosOid() {
return new Oid("1.2.840.113554.1.2.2");
}
});
К сожалению, я получаю исключение GSSException: действительные учетные данные не указаны (уровень механизма: не удалось найти какой-либо протокол Kerberos).




Мое понимание получения служебного билета было неправильным. Мне не нужно получать учетные данные от службы - это невозможно на клиенте, потому что у клиента действительно нет TGT для сервера и, следовательно, у него нет прав на получение учетных данных службы. Чего здесь просто не хватает, так это создания нового GSSContext и его инициализации. Возвращаемое значение этого метода содержит билет службы, если я правильно это понял. Вот пример рабочего кода. Он должен быть запущен в PrivilegedAction от имени вошедшего в систему субъекта:
GSSManager manager = GSSManager.getInstance();
GSSName clientName = manager.createName("clientUser", GSSName.NT_USER_NAME);
GSSCredential clientCred = manager.createCredential(clientName,
8 * 3600,
createKerberosOid(),
GSSCredential.INITIATE_ONLY);
GSSName serverName = manager.createName("http@server", GSSName.NT_HOSTBASED_SERVICE);
GSSContext context = manager.createContext(serverName,
createKerberosOid(),
clientCred,
GSSContext.DEFAULT_LIFETIME);
context.requestMutualAuth(true);
context.requestConf(false);
context.requestInteg(true);
byte[] outToken = context.initSecContext(new byte[0], 0, 0);
System.out.println(new BASE64Encoder().encode(outToken));
context.dispose();
Затем outToken содержит служебный билет. Однако это не способ использования GSS-API. Его цель состояла в том, чтобы скрыть эти детали от кода, поэтому лучше создать GSSContext, используя GSS-API с обеих сторон. В противном случае вам действительно следует знать, что вы делаете, из-за потенциальных дыр в безопасности. Для получения дополнительной информации прочтите Учебник Sun SSO с Kerberos внимательнее, чем я.
Обновлено: Просто забыл, что я использую Windows XP с SP2. В этой версии Windows есть новая «функция», которая запрещает использование TGT в оперативной памяти Windows. Вы должны отредактировать реестр, чтобы разрешить это. Для получения дополнительной информации просмотрите тему Страница устранения неполадок JGSS, если вы столкнетесь с «KrbException: KDC не поддерживает тип шифрования (14)», как это сделал я.
проверьте свою ссылку на учебник Sun SSO с Kerberos и взгляните на рисунок 6. Это цикл, о котором я писал.
Думаю, я не включил цикл, потому что не мог понять, что должны делать методы readToken () и sendToken (...).
в случае, если HTTP readToken запрашивает HTTP-ответ и извлекает токен, sendToken выполняет обратное.
Может ли это создание токена использоваться с двумя пользователями в Active Directory, то есть user1 в качестве клиента, выполняющего вызов http api, передавая этот токен пользователю user2 в качестве http-сервера. Если я правильно понимаю, сервер сможет расшифровать этот токен.
У меня было много проблем с использованием этого кода, но у меня есть хоть какое-то решение. Я размещаю это здесь, возможно, это поможет некоторым из вас ...
/**
* Tool to retrieve a kerberos ticket. This one will not be stored in the windows ticket cache.
*/
public final class KerberosTicketRetriever
{
private final static Oid KERB_V5_OID;
private final static Oid KRB5_PRINCIPAL_NAME_OID;
static {
try
{
KERB_V5_OID = new Oid("1.2.840.113554.1.2.2");
KRB5_PRINCIPAL_NAME_OID = new Oid("1.2.840.113554.1.2.2.1");
} catch (final GSSException ex)
{
throw new Error(ex);
}
}
/**
* Not to be instanciated
*/
private KerberosTicketRetriever() {};
/**
*
*/
private static class TicketCreatorAction implements PrivilegedAction
{
final String userPrincipal;
final String applicationPrincipal;
private StringBuffer outputBuffer;
/**
*
* @param userPrincipal p.ex. <tt>[email protected]</tt>
* @param applicationPrincipal p.ex. <tt>HTTP/webserver.myfirm.com</tt>
*/
private TicketCreatorAction(final String userPrincipal, final String applicationPrincipal)
{
this.userPrincipal = userPrincipal;
this.applicationPrincipal = applicationPrincipal;
}
private void setOutputBuffer(final StringBuffer newOutputBuffer)
{
outputBuffer = newOutputBuffer;
}
/**
* Only calls {@link #createTicket()}
* @return <tt>null</tt>
*/
public Object run()
{
try
{
createTicket();
}
catch (final GSSException ex)
{
throw new Error(ex);
}
return null;
}
/**
*
* @throws GSSException
*/
private void createTicket () throws GSSException
{
final GSSManager manager = GSSManager.getInstance();
final GSSName clientName = manager.createName(userPrincipal, KRB5_PRINCIPAL_NAME_OID);
final GSSCredential clientCred = manager.createCredential(clientName,
8 * 3600,
KERB_V5_OID,
GSSCredential.INITIATE_ONLY);
final GSSName serverName = manager.createName(applicationPrincipal, KRB5_PRINCIPAL_NAME_OID);
final GSSContext context = manager.createContext(serverName,
KERB_V5_OID,
clientCred,
GSSContext.DEFAULT_LIFETIME);
context.requestMutualAuth(true);
context.requestConf(false);
context.requestInteg(true);
final byte[] outToken = context.initSecContext(new byte[0], 0, 0);
if (outputBuffer !=null)
{
outputBuffer.append(String.format("Src Name: %s\n", context.getSrcName()));
outputBuffer.append(String.format("Target : %s\n", context.getTargName()));
outputBuffer.append(new BASE64Encoder().encode(outToken));
outputBuffer.append("\n");
}
context.dispose();
}
}
/**
*
* @param realm p.ex. <tt>MYFIRM.COM</tt>
* @param kdc p.ex. <tt>kerbserver.myfirm.com</tt>
* @param applicationPrincipal cf. {@link #TicketCreatorAction(String, String)}
* @throws GSSException
* @throws LoginException
*/
static public String retrieveTicket(
final String realm,
final String kdc,
final String applicationPrincipal)
throws GSSException, LoginException
{
// create the jass-config-file
final File jaasConfFile;
try
{
jaasConfFile = File.createTempFile("jaas.conf", null);
final PrintStream bos = new PrintStream(new FileOutputStream(jaasConfFile));
bos.print(String.format(
"Krb5LoginContext { com.sun.security.auth.module.Krb5LoginModule required refreshKrb5Config=true useTicketCache=true debug=true ; };"
));
bos.close();
jaasConfFile.deleteOnExit();
}
catch (final IOException ex)
{
throw new IOError(ex);
}
// set the properties
System.setProperty("java.security.krb5.realm", realm);
System.setProperty("java.security.krb5.kdc", kdc);
System.setProperty("java.security.auth.login.config",jaasConfFile.getAbsolutePath());
// get the Subject(), i.e. the current user under Windows
final Subject subject = new Subject();
final LoginContext lc = new LoginContext("Krb5LoginContext", subject, new DialogCallbackHandler());
lc.login();
// extract our principal
final Set<Principal> principalSet = subject.getPrincipals();
if (principalSet.size() != 1)
throw new AssertionError("No or several principals: " + principalSet);
final Principal userPrincipal = principalSet.iterator().next();
// now try to execute the SampleAction as the authenticated Subject
// action.run() without doAsPrivileged leads to
// No valid credentials provided (Mechanism level: Failed to find any Kerberos tgt)
final TicketCreatorAction action = new TicketCreatorAction(userPrincipal.getName(), applicationPrincipal);
final StringBuffer outputBuffer = new StringBuffer();
action.setOutputBuffer(outputBuffer);
Subject.doAsPrivileged(lc.getSubject(), action, null);
return outputBuffer.toString();
}
public static void main (final String args[]) throws Throwable
{
final String ticket = retrieveTicket("MYFIRM.COM", "kerbserver", "HTTP/webserver.myfirm.com");
System.out.println(ticket);
}
}
@Michael: Спасибо за ваш вклад. Как видите, я написал это довольно давно, поэтому я не помню точных деталей, но я почти уверен, что это было просто в качестве примера. Как вы думаете, что можно сделать, чтобы улучшить этот ответ?