Java 如何通过 GSS-API 获取 kerberos 服务票证?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/370878/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to obtain a kerberos service ticket via GSS-API?
提问by Roland Schneider
Does anyone know how to get a service ticket from the Key Distribution Center (KDC) using the Java GSS-API?
有谁知道如何使用 Java GSS-API 从密钥分发中心 (KDC) 获取服务票证?
I have a thick-client-application that first authenticates via JAAS using the Krb5LoginModule to fetch the TGT from the ticket cache (background: Windows e.g. uses a kerberos implementation and stores the ticket granting ticket in a secure memory area). From the LoginManager I get the Subject object which contains the TGT. Now I hoped when I create a specific GSSCredential object for my service, the service ticket will be put into the Subject's private credentials as well (I've read so somewhere in the web). So I have tried the following:
我有一个胖客户端应用程序,它首先使用 Krb5LoginModule 通过 JAAS 进行身份验证,以从票证缓存中获取 TGT(背景:Windows 例如使用 kerberos 实现并将票证授予票证存储在安全内存区域中)。从 LoginManager 我得到包含 TGT 的 Subject 对象。现在我希望当我为我的服务创建一个特定的 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");
}
});
Unfortunately I get a GSSException: No valid credentials provided (Mechanism level: Failed to find any Kerberos tgt).
不幸的是,我收到了一个 GSSException:没有提供有效的凭据(机制级别:找不到任何 Kerberos tgt)。
采纳答案by Roland Schneider
My understanding of getting the service ticket was wrong. I do not need to get the credentials from the service - this is not possible on the client, because the client really doesn't have a TGT for the server and therefore doesn't have the rights to get the service credentials. What's just missing here is to create a new GSSContext and to initialize it. The return value from this method contains the service ticket, if I have understood that correctly. Here is a working code example. It must be run in a PrivilegedAction on behalf of a logged in subject:
我对获取服务票的理解是错误的。我不需要从服务中获取凭据 - 这在客户端上是不可能的,因为客户端确实没有服务器的 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();
The outToken contains then contains the Service Ticket. However this is not the way the GSS-API was meant to be used. Its goal was to hide those details to the code, so it is better to establish a GSSContext using the GSS-API on both sides. Otherwise you really should know what you are doing because of potential security holes. For more information read the Sun SSO tutorial with kerberosmore carefully than I did.
outToken 包含然后包含服务票证。然而,这并不是 GSS-API 的使用方式。它的目标是将这些细节隐藏到代码中,因此最好在双方使用 GSS-API 建立 GSSContext。否则你真的应该知道你在做什么,因为潜在的安全漏洞。有关更多信息,请比我更仔细地阅读Sun SSO 与 kerberos 教程。
EDIT: Just forgot that I am using Windows XP with SP2. There is a new "feature" in this version of Windows that disallows using the TGT in the Windows RAM. You have to edit the registry to allow this. For more information have a look at the JGSS Troubleshooting pagetopic in case you experience a "KrbException: KDC has no support for encryption type (14)" like I did.
编辑:只是忘了我使用的是带有 SP2 的 Windows XP。此版本的 Windows 中有一项新“功能”,它不允许在 Windows RAM 中使用 TGT。您必须编辑注册表以允许这样做。有关更多信息,请查看JGSS 故障排除页面主题,以防您像我一样遇到“KrbException:KDC 不支持加密类型 (14)”。
回答by Olivier Faucheux
I had a lot of problems to use this code, but I have at least a solution. I post it here, perhaps it will help some of you...
我在使用这段代码时遇到了很多问题,但我至少有一个解决方案。我把它贴在这里,也许它会帮助你们中的一些人......
/**
* 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);
}
}