有谁知道如何使用Java GSS-API从密钥分发中心(KDC)获取服务票证?
我有一个胖客户端应用程序,首先通过JAAS使用Krb5LoginModule从票证缓存中获取TGT进行身份验证(后台:Windows例如使用kerberos实现并将票证授予票证存储在安全的内存区域).从LoginManager我得到包含TGT的Subject对象.现在我希望当我为我的服务创建一个特定的GSSCredential对象时,服务票证也将被放入Subject的私人凭证中(我已经在网上的某处读过).所以我尝试了以下方法:
// 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).
我对获取服务票证的理解是错误的.我不需要从服务获取凭据 - 这在客户端上是不可能的,因为客户端实际上没有服务器的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的使用方式.它的目标是将这些细节隐藏到代码中,因此最好使用两侧的GSS-API建立GSSContext.否则你真的应该知道你在做什么,因为潜在的安全漏洞.有关更多信息,请阅读使用kerberos的Sun SSO教程,比我更仔细.
编辑:忘记我使用Windows XP SP2.此版本的Windows中有一个新的"功能",它不允许在Windows RAM中使用TGT.您必须编辑注册表才能允许此操作.有关更多信息,请查看JGSS疑难解答页面主题,以防您遇到"KrbException:KDC不支持加密类型(14)",就像我一样.
使用此代码时遇到了很多问题,但我至少有一个解决方案.我在这里发布,也许它会帮助你们中的一些人......
/** * 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. MuelleHA@MYFIRM.COM * @param applicationPrincipal p.ex. HTTP/webserver.myfirm.com */ 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 null */ 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. MYFIRM.COM * @param kdc p.ex. kerbserver.myfirm.com * @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 SetprincipalSet = 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); } }