gpt4 book ai didi

java - 使用 GSSManager 验证 Kerberos 票证

转载 作者:塔克拉玛干 更新时间:2023-11-03 03:04:02 25 4
gpt4 key购买 nike

我有以下代码:

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();
}
}

当然失败了。这是异常(exception):
GSSException: Defective token detected (Mechanism level: GSSHeader did not find the right tag)
我不知道我该怎么做才能解决这个问题。老实说,我不太了解 Kerberos。

我通过发送带有适当标题的 401 WWW-Authenticate 获得了这张票以“协商”为值。浏览器立即再次发出相同的请求 authorization包含此票证的 header 。

我希望我可以验证票证并确定用户是谁。

我需要 key 表文件吗?如果是这样,我将在什么凭据下运行它?我正在尝试使用 Kerberos 票证对网站进行身份验证。凭据是来自 IIS 的凭据吗?

我错过了什么?

更新 1
从 Michael-O 的回复中,我做了更多的谷歌搜索,发现 this article ,这让我找到了 this article .

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 上失败了出现此错误:
GSSException: No valid credentials provided (Mechanism level: Failed to find any Kerberos credentails)

这是全票: YIGCBgYrBgEFBQKgeDB2oDAwLgYKKwYBBAGCNwICCgYJKoZIgvcSAQICBgkqhkiG9xIBAgIGCisGAQQBgjcCAh6iQgRATlRMTVNTUAABAAAAl7II4g4ADgAyAAAACgAKACgAAAAGAbEdAAAAD0xBUFRPUC0yNDVMSUZFQUNDT1VOVExMQw==

最佳答案

从 Java 验证 SPNEGO 票证是一个有点复杂的过程。这是一个简短的概述,但请记住,该过程可能存在大量陷阱。您确实需要了解 Active Directory、Kerberos、SPNEGO 和 JAAS 如何成功诊断问题。

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

步骤 1 - 获取 Kerberos Keytab

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

在 Active Directory 方面,KDC 是域 Controller ,共享 secret 只是拥有 SPN 的帐户的纯文本密码。 SPN 可以由 AD 中的计算机或用户对象拥有。

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

  • 在 AD 中创建一个密码不会过期的非特权服务帐户,例如SVC_HTTP_MYSERVER 带密码 ReallyLongRandomPass
  • 使用windows将服务SPN绑定(bind)到账号setspn公用事业。最佳实践是为主机的短名称和 FQDN 定义多个 SPN:
    setspn -U -S HTTP/myserver@MYDOMAIN SVC_HTTP_MYSERVER
    setspn -U -S HTTP/myserver.my-domain.com@MYDOMAIN SVC_HTTP_MYSERVER
  • 使用 Java 的 ktab 为帐户生成 key 表公用事业。
    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

  • 如果您尝试对绑定(bind)到计算机帐户或您无法控制的用户帐户的预先存在的 SPN 进行身份验证,则上述方法将不起作用。您需要从 ActiveDirectory 本身提取 key 表。 Wireshark Kerberos Page对此有一些很好的指示。

    第 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 作为接受者。这是一个假设我们上面创建的 key 表在 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<String, String> options = new HashMap<String, String>();
    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 ctx = new LoginContext("doesn't matter", subject, null, 
    getJaasKrbValidationCfg("HTTP/myserver.my-domain.com@MYDOMAIN", "MYDOMAIN",
    new File("C:/path/to/my.ktab")));

    第 4 步 - 接受票

    这有点随意,但总体思路是定义一个 PriviledgedAction,它使用票证执行 SPNEGO 协议(protocol)。请注意,此示例不检查 SPNEGO 协议(protocol)是否完整。例如,如果客户端请求服务器身份验证,您将需要返回由 acceptSecContext() 生成的 token 。在 HTTP 响应中的身份验证 header 中。
    public class Krb5TicketValidateAction implements PrivilegedExceptionAction<String> {
    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;
    }

    然后要验证票证,您将执行以下操作。假设 ticket包含来自身份验证 header 的已经 base-64 解码的票证。 spn应该来自 Host如果格式为 HTTP/<HOST>@<REALM>,则 HTTP 请求中的 header .例如。如果 Host标题是 myserver.my-domain.com然后 spn应该是 HTTP/myserver.my-domain.com@MYDOMAIN .
    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<Principal> principals = new HashSet<Principal>();
    principals.add(principal);

    // define the subject to execute our secure action as
    Subject subject = new Subject(false, principals, new HashSet<Object>(),
    new HashSet<Object>());

    // 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;
    }

    关于java - 使用 GSSManager 验证 Kerberos 票证,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25289231/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com