从授权标头中的 Kerberos 票证中读取用户名

Posted

技术标签:

【中文标题】从授权标头中的 Kerberos 票证中读取用户名【英文标题】:Reading user name from Kerberos ticket in Authorization header 【发布时间】:2013-01-16 08:53:40 【问题描述】:

我想从 Authorization HTTP 标头中的 Kerberos 票证中读取用户名。我正在使用 Java。

我花了几天时间尝试通过阅读有关该主题的一堆网站来实现这一点,但未能做到这一点。 Kerberos 对我来说是新的/陌生的。

这是我所取得的成就:

当用户首次访问站点时 - 没有 Authorization 标头,服务器会以 401 + 标头响应:WWW-Authenticate=Negotiate。 各种神奇的事情发生在客户端。 用户返回一个 HTTP 请求,其中包含 Authorization 标头,其值如下:“Negotiate YHcGB...==" 将 base64 编码票证解码为字节数组。

从这里开始,这是一次穿越未知的恐怖旅程。据我所知,接下来的步骤应该是:

以用户身份登录 AD/Kerberos/服务器。 解码票证。

这就是我所拥有的:

login.conf

 ServicePrincipalLoginContext

      com.sun.security.auth.module.Krb5LoginModule 
      required 
      principal="HTTP/some.server.com@MY.DOMAIN.COM" 
      doNotPrompt=true
      useTicketCache=true
      password=mYpasSword
      debug=true;
;

JavaClass.java

String encodedTicket = authorization.substring("Negotiate ".length());
byte[] ticket = Base64.decode(encodedTicket);       

LoginContext lc = new LoginContext("ServicePrincipalLoginContext");
lc.login();
Subject serviceSubject = lc.getSubject();
Subject.doAs(serviceSubject, new ServiceTicketDecoder(ticket));

ServiceTicketDecoder.java

public String run() throws Exception 
    Oid kerberos5Oid = new Oid("1.2.840.113554.1.2.2");

    GSSManager gssManager = GSSManager.getInstance();

    String service = "krbtgt/MY.DOMAIN.COM@MY.DOMAIN.COM";
    GSSName serviceName = gssManager.createName(service, GSSName.NT_USER_NAME);

    GSSCredential serviceCredentials = gssManager.createCredential(serviceName, GSSCredential.INDEFINITE_LIFETIME, kerberos5Oid, GSSCredential.ACCEPT_ONLY);

    GSSContext gssContext = gssManager.createContext(serviceCredentials);
    gssContext.acceptSecContext(this.serviceTicket, 0, this.serviceTicket.length);

    GSSName srcName = gssContext.getSrcName();
    return srcName.toString;

JavaClass.java 中的登录正常,所以我假设 login.conf 正常。在 ServiceTicketDecoder.java 中的 "GSSCredential serviceCredentials = gssManager.createCredential(..." 上抛出以下异常:

java.security.PrivilegedActionException: GSSException: No valid credentials provided (Mechanism level: Failed to find any Kerberos Key)

我不确定这是否是正确的方法。我也不知道“字符串服务”的价值应该是什么或如何获取该信息。你能帮帮我吗?


编辑: 登录.conf

 ServicePrincipalLoginContext

      com.sun.security.auth.module.Krb5LoginModule 
      required 
      principal="HTTP/some.server.com@MY.DOMAIN.COM" 
      doNotPrompt=true
      useTicketCache=true
      keyTab="C:/server-http.keytab" 
      debug=true;
;

我收到了一个密钥表文件。显然 HTTP/some.server.com 用户的帐户已经是服务主体帐户。我现在在 lc.login() 的 JavaClass.java 上有一个问题:

javax.security.auth.login.LoginException: KDC has no support for encryption type (14)
Caused by: KrbException: KDC has no support for encryption type (14)
Caused by: KrbException: Identifier doesn't match expected value (906)

keytab 文件使用 des-cbc-md5 加密,我在 krb.conf 文件中定义了以下内容:

[libdefaults]
default_realm = MY.DOMAIN.COM
default_tkt_enctypes = des-cbc-md5
default_tgs_enctypes = des-cbc-md5

如果我将默认编码类型更改为例如aes128-cts,我得到以下异常:

javax.security.auth.login.LoginException: Do not have keys of types listed in default_tkt_enctypes available; only have keys of following type: DES CBC mode with MD5

我不明白这是怎么回事......

【问题讨论】:

我想知道“各种神奇的事情都发生在客户端”。您能否更具体地说明如何通过 http 请求从用户 pc 获取 kerberos 令牌 【参考方案1】:

Kerberos 是一个受信任的第三方安全系统:您从客户端收到的安全令牌只能由您解密,无需联系任何 Kerberos 基础架构服务器(例如 KDC)。你走在正确的轨道上;但是,您似乎缺少可指导您进行进一步研究的 Kerberos 背景知识。

实现这一点的方法是,在服务器上,您需要一个包含服务器密钥的 keytab 文件。 Kerberos 服务器(我认为是 Microsoft Windows Server)必须为您的服务创建一个服务主体帐户。管理员可以向您提供为此帐户生成的 keytab 文件,其中将包含密钥。

然后你需要配置服务器来找到这个keytab文件;它用于涉及LoginContext.login 的服务器端步骤。接受安全上下文的代码必须在您的服务器端凭据生效的 doPrivileged 代码段内执行。

【讨论】:

keytab 不应使用 DES,显然 KDC 配置为不支持它。这是服务主体帐户上的一个可配置选项,用于向后兼容不支持现代加密标准的服务器。它被称为“仅使用 DES 加密”或类似名称,应禁用。之后应该会生成一个新的 keytab。 更改密钥表的加密解决了问题,现在一切正常。谢谢!!【参考方案2】:

如果你想要的只是用户名,还有一个更简单的方法。

request.getUserPrincipal().getName()

http://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#getUserPrincipal() http://docs.oracle.com/javase/7/docs/api/java/security/Principal.html

【讨论】:

以上是关于从授权标头中的 Kerberos 票证中读取用户名的主要内容,如果未能解决你的问题,请参考以下文章

在没有用户密码的情况下生成 AD Kerberos 票证

在 SOAP Web 服务调用中将 Kerberos 票证作为参数传递

带有反应的 Kerberos 身份验证

如何使用 GSS API 更新 kerberos 服务票证

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

来自 SSPI 的 KRB_AP_REQ 票证