如何使用Windows Java客户端保存Kerberos服务票证?

时间:2016-03-29 14:49:55

标签: java authentication kerberos jaas

我编写了一个在Windows下运行的简单Java HTTP客户端。客户端与Web服务器通信,该服务器需要通过SPNego进行Kerberos身份验证。

我遇到了两个问题:

  • 服务票证未存储在我的凭证缓存中。执行请求后,我希望在C:\Users\<user>\krb5cc_<user>下的证书缓存中看到存储Kerberos服务票证 - 我认为Java将服务票据存储在凭证缓存中是错误的吗?我想重新使用客户端A中获取的服务票据来获取客户端B中的请求(其中两个客户端都是同一台计算机上的Java应用程序)。这可能与Java有关吗?

  • 如果我在循环中运行下面的代码一百次,它只能工作n次(其中n是1到100之间的随机数)。失败的请求返回401错误消息,因为Java无法检索服务票证(请记住:由于我的应用程序不在请求之间存储服务票证,因此它会尝试从TGT获取每个请求的新服务票证) 。我已将错误消息添加到此问题的底部。

我在JDK的bin文件夹中通过kinit创建了一个TGT。以下代码段用于制作简单的GET请求:

  static void testJavaHttpKerberosAuthentication() throws IOException {
    URL obj = new URL(URI);
    HttpURLConnection con = (HttpURLConnection) obj.openConnection();
    int responseCode = con.getResponseCode();
    System.out.println("\nSending 'GET' request to URL : " + URI);
    System.out.println("Response Code : " + responseCode);

    BufferedReader in = new BufferedReader(
    new InputStreamReader(con.getInputStream()));
    String inputLine;
    StringBuffer response = new StringBuffer();
    while ((inputLine = in.readLine()) != null) {
      response.append(inputLine);
    }
    in.close();

    //print result
    System.out.println(response.toString());
  }

这是我的jaas.conf的内容(如here所述):

com.sun.security.jgss.krb5.initiate {
com.sun.security.auth.module.Krb5LoginModule required doNotPrompt=false useTicketCache=true;
};

我正在使用以下参数运行我的应用程序:

-Djava.security.auth.login.config=D:\jaas.conf
-Dsun.security.krb5.debug=true
-Djavax.security.auth.useSubjectCredsOnly=false

我不是用作krb5.ini,因为我的客户端从域配置中获取了正确的KDC。

我可以通过以下命令为我的凭据缓存生成TGT:

C:\Program Files\Java\jdk1.8.0_77\bin>kinit
Password for <user>@<domain>:
New ticket is stored in cache file C:\Users\<user>\krb5cc_<user>

最后,这是授权失败的情况下的异常和Kerberos调试输出(参考问题2)。请注意,ctime显然是错误的。我有很多不同的尝试,ctime的时间范围从1970年到2040年。有趣的是,每次请求都不会发生这种情况。

>>>KRBError:
 cTime is Wed Jun 07 12:24:03 CEST 2017 1496831043000
 sTime is Tue Mar 29 16:38:24 CEST 2016 1459262304000
 suSec is 283371
 error code is 34
 error Message is Request is a replay
 sname is HTTP/<spn>@<domain>
 msgType is 30
 KrbException: Request is a replay (34) - PROCESS_TGS

我已经尝试使用Subject.doAs使用JAAS,但这会导致同样的问题。通过浏览器访问服务器工作正常(虽然这是不可比的,因为浏览器使用Windows本机凭据缓存AFAICT)。

我会感谢你就如何调试这样的问题提出一些建议。

编辑:明确地通过KRB5CCNAME环境变量指定凭证缓存的路径,不会改变行为。似乎TGT是从凭证缓存中获得的,但服务票据不存储在那里。

3 个答案:

答案 0 :(得分:0)

关于缓存&gt;&gt;看起来您没有指定系统上的默认缓存(参见env变量KRB5CCNAME),因此Java和kinit恢复为硬编码默认值。这不是默认的......

  • 您的kinit版本明确使用Linux标准,即FILE:
  • Java通常使用Windows标准,即由MIT-Kerberos-for-Windows服务管理的API:

可能的解决方法:使用Windows上的Kerberos UI创建TGT,或通过设置KRB5CCNAME强制Java使用文件缓存。

参考: MIT Kerberos documentation,尤其是关于硬编码默认的最后一个链接

~~~~~~~

关于随机时间值&gt;&gt;我不知道。

答案 1 :(得分:0)

关于偶尔出现的随机时间值:我们发现在udp_preference_limit = 1中设置krb5.ini可以解决问题。这有效地告诉Kerberos总是首先尝试使用TCP来发送包。显然,切换到UDP时会出现问题(不确定UDP是问题还是在协议之间切换)。

答案 2 :(得分:0)

JAAS不会将票证持久存储到缓存中,您必须使用kinit或通过代码以编程方式调用kinit代码。 我在这个问题here上写了一个问题/答案。