使用GSSManagervalidationKerberos票证

我有以下代码:

public static void main(String args[]){ try { //String ticket = "Negotiate YIGCBg...=="; //byte[] kerberosTicket = ticket.getBytes(); byte[] kerberosTicket = Base64.decode("YIGCBg...=="); GSSContext context = GSSManager.getInstance().createContext((GSSCredential) null); context.acceptSecContext(kerberosTicket, 0, kerberosTicket.length); String user = context.getSrcName().toString(); context.dispose(); } catch (GSSException e) { e.printStackTrace(); } catch (Base64DecodingException e) { e.printStackTrace(); } } 

当然它失败了。 这是例外:

GSSException: Defective token detected (Mechanism level: GSSHeader did not find the right tag)

我不知道我应该做些什么来解决这个问题。 老实说,我真的不懂Kerberos。

我通过发送带有相应标题WWW-Authenticate的401并以’Negotiate’作为值来获得此票。 浏览器立即使用包含此票证的authorization标题再次发出相同的请求。

我希望我能validation票证并确定用户是谁。

我需要keytab文件吗? 如果是这样,我会在哪个凭据下运行? 我正在尝试将Kerberos票证用于网站的身份validation。 凭据是否是IIS的凭据?

我错过了什么?


更新1从Michael-O的回复中,我做了一些谷歌搜索并找到了这篇文章 ,这让我看到了这篇文章 。

table 3 ,我找到了1.3.6.1.5.5.2 SPNEGO

我现在已经按照第一篇文章中的示例添加了我的凭据。 这是我的代码:

 public static void main(String args[]){ try { Oid mechOid = new Oid("1.3.6.1.5.5.2"); GSSManager manager = GSSManager.getInstance(); GSSCredential myCred = manager.createCredential(null, GSSCredential.DEFAULT_LIFETIME, mechOid, GSSCredential.ACCEPT_ONLY); GSSContext context = manager.createContext(myCred); byte[] ticket = Base64.decode("YIGCBg...=="); context.acceptSecContext(ticket, 0, ticket.length); String user = context.getSrcName().toString(); context.dispose(); } catch (GSSException e) { e.printStackTrace(); } catch (Base64DecodingException e) { e.printStackTrace(); } } 

但是现在代码在createCredential上失败并createCredential此错误:

 GSSException: No valid credentials provided (Mechanism level: Failed to find any Kerberos credentails) 

这里就是整个票: YIGCBgYrBgEFBQKgeDB2oDAwLgYKKwYBBAGCNwICCgYJKoZIgvcSAQICBgkqhkiG9xIBAgIGCisGAQQBgjcCAh6iQgRATlRMTVNTUAABAAAAl7II4g4ADgAyAAAACgAKACgAAAAGAbEdAAAAD0xBUFRPUC0yNDVMSUZFQUNDT1VOVExMQw==

从JavavalidationSPNEGO票证是一个有点复杂的过程。 这是一个简短的概述,但请记住,这个过程可能会有很多陷阱。 您真的需要了解Active Directory,Kerberos,SPNEGO和JAAS如何运行以成功诊断问题。

在开始之前,请确保您知道Windows域的kerberos域名。 出于这个答案的目的,我会假设它是MYDOMAIN 。 您可以通过从cmd窗口运行echo %userdnsdomain%来获取领域名称。 请注意,kerberos 区分大小写 ,并且领域几乎总是ALL CAPS。

步骤1 – 获取Kerberos Keytab

为了使kerberos客户端访问服务,它会请求代表该服务的服务主体名称[SPN]的票证。 SPN通常源自机器名称和正在访问的服务类型(例如HTTP/www.my-domain.com )。 为了validation特定SPN​​的kerberos票证,您必须拥有一个keytab文件,其中包含Kerberos域控制器[KDC]票证授予票证[TGT]服务和服务提供商(您)都知道的共享密码。

就Active Directory而言,KDC是域控制器,共享密钥只是拥有SPN帐户的纯文本密码。 SPN可以由AD中的计算机或用户对象拥有。

如果要定义服务,在AD中设置SPN的最简单方法是设置基于用户的SPN,如下所示:

  1. 在AD中创建一个未被过期的服务帐户,其密码不会过期,例如SVC_HTTP_MYSERVER,密码为ReallyLongRandomPass
  2. 使用windows setspn实用程序将服务SPN绑定到帐户。 最佳做法是为主机的短名称和FQDN定义多个SPN:

     setspn -U -S HTTP/myserver@MYDOMAIN SVC_HTTP_MYSERVER setspn -U -S HTTP/myserver.my-domain.com@MYDOMAIN SVC_HTTP_MYSERVER 
  3. 使用Java的ktab实用程序为帐户生成密钥表。

     ktab -k FILE:http_myserver.ktab -a HTTP/myserver@MYDOMAIN ReallyLongRandomPass ktab -k FILE:http_myserver.ktab -a HTTP/myserver.my-domain.com@MYDOMAIN ReallyLongRandomPass 

如果您尝试validation绑定到计算机帐户或您无法控制的用户帐户的预先存在的SPN,则上述操作无效。 您需要从ActiveDirectory本身提取密钥表。 Wireshark Kerberos页面有一些很好的指针。

第2步 – 设置您的krb5.conf

%JAVA_HOME%/jre/lib/security创建一个描述您的域的krb5.conf。 确保您在此处定义的领域与您为SPN设置的领域相匹配。 如果不将文件放在JVM目录中,可以通过在命令行上设置-Djava.security.krb5.conf=C:\path\to\krb5.conf来指向它。

例:

 [libdefaults] default_realm = MYDOMAIN [realms] MYDOMAIN = { kdc = dc1.my-domain.com default_domain = my-domain.com } [domain_realm] .my-domain.com = MYDOMAIN my-domain.com = MYDOMAIN 

第3步 – 设置JAAS login.conf

您的JAAS login.conf应定义一个登录配置,将Krb5LoginModule设置为接受器。 这是一个假设我们在上面创建的keytab在C:\http_myserver.ktab 。 通过在命令行上设置-Djava.security.auth.login.config=C:\path\to\login.conf指向JASS配置文件。

 http_myserver_mydomain { com.sun.security.auth.module.Krb5LoginModule required principal="HTTP/myserver.my-domain.com@MYDOMAIN" doNotPrompt="true" useKeyTab="true" keyTab="C:/http_myserver.ktab" storeKey="true" isInitiator="false"; }; 

或者,您可以在运行时生成JAAS配置,如下所示:

 public static Configuration getJaasKrb5TicketCfg( final String principal, final String realm, final File keytab) { return new Configuration() { @Override public AppConfigurationEntry[] getAppConfigurationEntry(String name) { Map options = new HashMap(); options.put("principal", principal); options.put("keyTab", keytab.getAbsolutePath()); options.put("doNotPrompt", "true"); options.put("useKeyTab", "true"); options.put("storeKey", "true"); options.put("isInitiator", "false"); return new AppConfigurationEntry[] { new AppConfigurationEntry( "com.sun.security.auth.module.Krb5LoginModule", LoginModuleControlFlag.REQUIRED, options) }; } }; } 

您可以为此配置创建一个LoginContext ,如下所示:

 LoginContext ctx = new LoginContext("doesn't matter", subject, null, getJaasKrbValidationCfg("HTTP/myserver.my-domain.com@MYDOMAIN", "MYDOMAIN", new File("C:/path/to/my.ktab"))); 

第4步 – 接受机票

这是一个小小的袖口,但一般的想法是定义一个使用票证执行SPNEGO协议的PriviledgedAction。 请注意,此示例不检查SPNEGO协议是否完整。 例如,如果客户端请求服务器身份validation,则需要在HTTP响应的身份validation标头中返回acceptSecContext()生成的令牌。

 public class Krb5TicketValidateAction implements PrivilegedExceptionAction { public Krb5TicketValidateAction(byte[] ticket, String spn) { this.ticket = ticket; this.spn = spn; } @Override public String run() throws Exception { final Oid spnegoOid = new Oid("1.3.6.1.5.5.2"); GSSManager gssmgr = GSSManager.getInstance(); // tell the GSSManager the Kerberos name of the service GSSName serviceName = gssmgr.createName(this.spn, GSSName.NT_USER_NAME); // get the service's credentials. note that this run() method was called by Subject.doAs(), // so the service's credentials (Service Principal Name and password) are already // available in the Subject GSSCredential serviceCredentials = gssmgr.createCredential(serviceName, GSSCredential.INDEFINITE_LIFETIME, spnegoOid, GSSCredential.ACCEPT_ONLY); // create a security context for decrypting the service ticket GSSContext gssContext = gssmgr.createContext(serviceCredentials); // decrypt the service ticket System.out.println("Entering accpetSecContext..."); gssContext.acceptSecContext(this.ticket, 0, this.ticket.length); // get the client name from the decrypted service ticket // note that Active Directory created the service ticket, so we can trust it String clientName = gssContext.getSrcName().toString(); // clean up the context gssContext.dispose(); // return the authenticated client name return clientName; } private final byte[] ticket; private final String spn; } 

然后要validation票证,您将执行以下操作。 假设该ticket包含来自认证头的已经基本64解码的票证。 如果格式为HTTP/@则应从HTTP请求中的Host头派生spn 。 例如,如果Host标头是myserver.my-domain.com那么spn应该是HTTP/myserver.my-domain.com@MYDOMAIN myserver.my-domain.com

 public boolean isTicketValid(String spn, byte[] ticket) { LoginContext ctx = null; try { // this is the name from login.conf. This could also be a parameter String ctxName = "http_myserver_mydomain"; // define the principal who will validate the ticket Principal principal = new KerberosPrincipal(spn, KerberosPrincipal.KRB_NT_SRV_INST); Set principals = new HashSet(); principals.add(principal); // define the subject to execute our secure action as Subject subject = new Subject(false, principals, new HashSet(), new HashSet()); // login the subject ctx = new LoginContext("http_myserver_mydomain", subject); ctx.login(); // create a validator for the ticket and execute it Krb5TicketValidateAction validateAction = new Krb5TicketValidateAction(ticket, spn); String username = Subject.doAs(subject, validateAction); System.out.println("Validated service ticket for user " + username + " to access service " + spn ); return true; } catch(PriviledgedActionException e ) { System.out.println("Invalid ticket for " + spn + ": " + e); } catch(LoginException e) { System.out.println("Error creating validation LoginContext for " + spn + ": " + e); } finally { try { if(ctx!=null) { ctx.logout(); } } catch(LoginException e) { /* noop */ } } return false; } 

这不是Kerberos票证,而是SPNEGO票证。 您的上下文有错误的机制。

编辑:虽然,您现在拥有正确的机制,但您的客户端正在向您发送GSL-API无法处理的NTLM令牌。 使用Base 64令牌,解码为原始字节并显示ASCII字符。 如果它以NTLMSSP启动,它将无法正常工作,并且您已经破坏了Kerberos设置。

编辑2:这是你的票:

 60 81 82 06 06 2B 06 01 05 05 02 A0 78 30 76 A0 30 30 2E 06 `..+..... x0v 00.. 0A 2B 06 01 04 01 82 37 02 02 0A 06 09 2A 86 48 82 F7 12 01 .+....7.....*H÷.. 02 02 06 09 2A 86 48 86 F7 12 01 02 02 06 0A 2B 06 01 04 01 ....*H÷......+.... 82 37 02 02 1E A2 42 04 40 4E 54 4C 4D 53 53 50 00 01 00 00 7...¢B.@NTLMSSP.... 00 97 B2 08 E2 0E 00 0E 00 32 00 00 00 0A 00 0A 00 28 00 00 .².â....2.......(.. 00 06 01 B1 1D 00 00 00 0F 4C 41 50 54 4F 50 2D 32 34 35 4C ...±.....LAPTOP-245L 49 46 45 41 43 43 4F 55 4E 54 4C 4C 43 IFEACCOUNTLLC 

这是SPNEGO令牌中的包装NTLM令牌。 这仅仅意味着Kerberos由于某些原因而失败,例如,

  • SPN未注册
  • 时钟偏移
  • 不允许使用Kerberos
  • DNS记录不正确

最好的选择是在客户端上使用Wireshark来查找根本原因。

请注意,Java不支持NTLM作为SPNEGO子机制。 NTLM仅由SSPI和Heimdal支持。

如果服务器没有注册KDC的密钥表和关联密钥,您将永远无法使用kerberos来validation故障单。

让SPNEGO工作是最棘手的,如果没有至少粗略地了解kerberos如何工作,几乎是不可能的。 尝试阅读此对话框,看看是否可以更好地理解。

http://web.mit.edu/kerberos/dialogue.html

SPNEGO需要HTTP / server.example.comforms的SPN,您需要告诉GSS库启动服务器时该密钥表的位置。