6

我正在使用基于 Windows 域登录的 SSO 的 Web 应用程序,为此我选择验证 Kerberos 票证。但现在我面临一个我找不到解决方案的问题。我设法无例外地验证票证,但是当我尝试获取用户名时,NullPointerException会抛出,因为用户名是null并且我不知道问题出在哪里。

如果我在验证期间没有收到任何异常,为什么用户名为空?

我如何获得用户名: String clientName = gssContext.getSrcName().toString();

我基于此创建我的客户:

使用 GSSManager 验证 Kerberos 票证

如何通过 GSS-API 获取 kerberos 服务票据?

http://docs.oracle.com/javase/7/docs/technotes/guides/security/jgss/single-signon.html

更新1:

我如何设置内容,只需在此处复制粘贴表单https://stackoverflow.com/a/25450862/1646082

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.servicePrincipal, 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...");
System.out.println( new String (Base64.encodeBase64( gssContext.acceptSecContext(this.kerberosTicket, 0,
        this.kerberosTicket.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();

更新 2:

如果我基于此https://spring.io/blog/2009/09/28/spring-security-kerberos-spnego-extension设置弹簧安全性,我也会遇到同样的错误:

java.lang.NullPointerException at org.springframework.security.extensions.kerberos.SunJaasKerberosTicketValidator$KerberosValidateAction.run(SunJaasKerberosTicketValidator.java:136) at org.springframework.security.extensions.kerberos.SunJaasKerberosTicketValidator$KerberosValidateAction.run(SunJaasKerberosTicketValidator.java:12 ) 在 java.security.AccessController.doPrivileged(Native Method) 在 javax.security.auth.Subject.doAs(Subject.java:422)

private static class KerberosValidateAction implements PrivilegedExceptionAction<String> {
    byte[] kerberosTicket;

    public KerberosValidateAction(byte[] kerberosTicket) {
        this.kerberosTicket = kerberosTicket;
    }

    @Override
    public String run() throws Exception {
        GSSContext context = GSSManager.getInstance().createContext((GSSCredential) null);
        context.acceptSecContext(kerberosTicket, 0, kerberosTicket.length);
        String user = context.getSrcName().toString(); // ERROR!
        context.dispose();
        return user;
    }

}

更新 3:

还尝试按照此处的建议将 Java 版本从 1.8 更改为 1.7使用 Kerberos 进行域身份验证失败。没有结果。

更新 4:

首先。不要使用 Java 1.8 b40 和 b45,它们都坏了。并且不要在本地PC上测试它,它不起作用(我不知道为什么)。

在更改最新(b65)Java 版本后,我得到了关于加密的异常(找不到适当类型的密钥来解密 AP REP - AES256 ...)。我已经通过 Java 1.8 的 Java Cryptography Extension (JCE) 修复了这个问题,并重新创建了 keytab,/crypto AES256-SHA1毕竟我遇到了异常:

GSSException:在 sun.security.jgss.GSSContextImpl.acceptSecContext(Unknown Source) 在 sun.security.jgss.krb5.Krb5Context.acceptSecContext(Unknown Source) 的 GSS-API 级别未指定故障(机制级别:校验和失败) .jgss.GSSContextImpl.acceptSecContext(Unknown Source) at GssServer$GssServerAction.run(GssServer.java:159) ... 4 更多原因:KrbException: Checksum failed at sun.security.krb5.internal.crypto.ArcFourHmacEType.decrypt(未知来源)在 sun.security.krb5.internal.crypto.ArcFourHmacEType.decrypt(未知来源)在 sun.security.krb5.EncryptedData.decrypt(未知来源)在 sun.security.krb5.KrbApReq.authenticate(未知来源)在sun.security.krb5.KrbApReq.(Unknown Source) at sun.security.jgss.krb5.InitSecContextToken.(Unknown Source) ... 8 更多原因:java.security。GeneralSecurityException: 校验和在 sun.security.krb5.internal.crypto.dk.ArcFourCrypto.decrypt(Unknown Source) at sun.security.krb5.internal.crypto.ArcFourHmac.decrypt(Unknown Source) 处失败 ... 14 更多

我尝试了本教程和其他方法来创建 keytabfile,但我仍然没有解决方案。

4

2 回答 2

2

我在实现我的 GSSAPI 套接字演示时遇到了同样的Checksum failed错误,该演示是对 Oracle GSSAPI教程代码的修改。我在注册到 FreeIPA kerberos 领域的 Linux 机器上执行我的代码。我使用了krb5.conf我的 Linux 系统的 vanilla 文件。门票没有限制etype

...
[libdefaults]
  default_realm = AUTHDEMO.IT
  dns_lookup_realm = true
  dns_lookup_kdc = true
  rdns = false
  ticket_lifetime = 24h
  forwardable = true
  udp_preference_limit = 0 
...

FreeIPA 领域默认使用类型 18 票证 (AES-256)。

关于我的应用程序,它配置了以下策略文件:

grant CodeBase "file:./app.jar" {
        permission java.security.AllPermission;
};

grant CodeBase "file:./app.jar" 
    Principal javax.security.auth.kerberos.KerberosPrincipal 
        "servicename@AUTHDEMO.IT" {

    permission java.net.SocketPermission "*", "accept";

    permission javax.security.auth.kerberos.ServicePermission
        "servicename@AUTHDEMO.IT", "accept";
};

执行应用程序时,我在接受者端收到此错误:

GSSException:在 GSS-API 级别未指定故障(机制级别:不支持/启用具有 HMAC SHA1-96 的加密类型 AES256CTS 模式)

在我的情况下,错误出现在 GSS 接受方。在我的应用程序中,我以编程方式生成 Jaas 配置(我将在此称为DConfig)并且我不使用配置文件。我发现第一个解决方案是使用配置文件而不是 DConfig 并且问题消失了,它工作正常。临时解决方案,Jaas Config 文件:

DemoServer {
  com.sun.security.auth.module.Krb5LoginModule required
   principal="servicename@AUTHDEMO.IT"
   storeKey=true
   debug=true; #not mandatory
};

使用此配置,接受方没有问题,应用程序能够检查服务票证的有效性并接受连接。

我问自己……为什么?

我检查了使用两种配置获得的主题的差异。在工作案例中,使用配置文件,主题在私有凭证中包含密码哈希凭证和主体 TGT 票证。使用 DConfig,我获得了一个只有密码哈希的主题,但私有凭据中没有主体 TGT 票证。

我的修复

DConfig 包含与配置文件相同的设置,其他选项是Krb5LoginModule默认值的副本,起初我看不出错误行为的原因。

设置isInitiator = true,进入接受方 DConfig,解决了这个问题。`isInitiator = true 已强制将 TGT 票证持久化到主题中。

使用此解决方法,错误已消失,系统 krb5.conf 没有任何更改。

我的分是......在 Jaas 登录后,让我们检查您的主题私人凭据是否缺少凭据(您需要将服务主体 TGT 放入您的接受方主题!),以防万一也尝试设置isInitiator = true为接受方。

问候

于 2017-08-16T11:32:57.510 回答
1

当您尝试获取 SrcName 时,似乎上下文尚未完全建立。这似乎是 ScrName 为空的原因。根据https://www-01.ibm.com/support/knowledgecenter/SSYKE2_7.0.0/com.ibm.java.security.api.doc/jgss/org/ietf/jgss/GSSContext.html,acceptSecContext () 生成一个令牌,如果它不为空,则应将此令牌发送给对等方。在调用 acceptSecContext() 之后,您应该检查 isEstablished() 是否返回 false。如果是这样,

如果此方法返回 false,则表明需要来自其对等方的令牌才能继续上下文建立阶段。true 返回值表示上下文的本地端已建立。如果令牌是由 GSS-API 生成的,这可能仍需要向对等方发送令牌。在上下文建立阶段,可以调用 isProtReady() 方法来确定上下文是否可以用于每个消息的操作。这允许应用程序对未完全建立的上下文使用按消息操作。

教程http://www.cs.mun.ca/java-api-1.5/guide/security/jgss/tutorials/BasicClientServer.html中更详细地解释了相同的内容:

acceptSecContext 方法可以反过来返回一个令牌。如果是这样,接受者应该将该令牌发送给发起者,然后发起者应该再次调用 initSecContext 并将这个令牌传递给它。每次 initSecContext 或 acceptSecContext 返回一个令牌时,调用该方法的应用程序应将令牌发送给其对等方,并且该对等方应将令牌传递给其适当的方法(acceptSecContext 或 initSecContext)。这种情况一直持续到上下文完全建立(当上下文的 isEstablished 方法返回 true 时就是这种情况)。

于 2015-12-16T09:51:31.100 回答