【问题标题】:JAAS Kerberos not adding keys from keytab as I expectJAAS Kerberos 没有像我期望的那样从 keytab 添加密钥
【发布时间】:2016-01-15 22:30:02
【问题描述】:

所以我正在尝试为 AIX 服务器(即 IBM JRE)实现 SSO/集成安全系统。它使用 Kerberos 对 AD 进行身份验证。

请记住,下面的数据已经过清理。

命令我的 AD 管理员用于在 AD 服务器上创建 keytab 文件(注意 /kvno 2)。

ktpass /princ HTTP/local.domain.com@LOCALDOMAIN.NET /mapuser PSLDAP@LOCALDOMAIN.NET /pass <PASSWORD> /crypto ALL /ptype KRB5_NT_PRINCIPAL /kvno 2 /out krb5.keytab

我的 krb5.conf 文件:

[libdefaults]
    default_realm = LOCALDOMAIN.NET
    default_keytab_name = FILE:/keytabs/krb.keytab
    default_tkt_enctypes = rc4-hmac
    default_tgs_enctypes = rc4-hmac
    dns_lookup_kdc = true
    dns_lookup_realm = true
[realms]
    LOCALDOMAIN.NET = {
            kdc = localdc08.localdomain.net:88
            kdc = otherdc08.localdomain.net:88
            admin_server = localdc08.localdomain.net:749
            master_kdc = localdc08.localdomain.net
            default_domain = LOCALDOMAIN.NET
    }
[domain_realm]
    .LOCALDOMAIN.NET = LOCALDOMAIN.NET
    LOCALDOMAIN.NET = LOCALDOMAIN.NET
    localdc08.localdomain.net = LOCALDOMAIN.NET
    localdc08.localdomain.net = LOCALDOMAIN.NET
    localdomain.net = LOCALDOMAIN.NET
    .localdomain.net = LOCALDOMAIN.NET
[logging]
    kdc = FILE:/var/krb5/log/krb5kdc.log
    admin_server = FILE:/var/krb5/log/kadmin.log
    kadmin_local = FILE:/var/krb5/log/kadmin_local.log
    default = FILE:/var/krb5/log/krb5lib.log

这是我用于 LoginModule 的 krb5Login.conf:

krbServer {
    com.ibm.security.auth.module.Krb5LoginModule required 
    credsType=acceptor 
    refreshKrb5Config=true 
    principal="HTTP/local.domain.com" 
    useKeytab="/keytabs/krb5.keytab" 
    debug=true;
};

这是我正在运行的java(由于IP无法透露整个事情)

        context = new LoginContext("krbServer");
        context.login();

        // Get server credentials
        Subject sub = context.getSubject();
        serverCred = Subject.doAs(sub, new PrivilegedExceptionAction<GSSCredential>() {
            public GSSCredential run() throws GSSException {
                // mechanism OID for SPNEGO authentication
                Oid spnegoOid = new Oid("1.3.6.1.5.5.2");

                // null name defaults to currently logged in name
                GSSCredential cred = authManager.createCredential(null,
                        GSSCredential.INDEFINITE_LIFETIME,
                        spnegoOid,
                        GSSCredential.ACCEPT_ONLY);
                return cred;
            }
        });
        context.logout();

当我调用上面的代码时,我得到以下调试输出:

Constructor With Arg: krbServer Version: 1.7.0 Home: /dev/jre
LoginContext Constructed
[JGSS_DBG_CRED]  Thread-2 JAAS config: debug=true
[JGSS_DBG_CRED]  Thread-2 JAAS config: principal=HTTP/local.domain.com
[JGSS_DBG_CRED]  Thread-2 JAAS config: credsType=accept only
[JGSS_DBG_CRED]  Thread-2 config: useDefaultCcache=false (default)
[JGSS_DBG_CRED]  Thread-2 config: useCcache=null
[JGSS_DBG_CRED]  Thread-2 config: useDefaultKeytab=false
[JGSS_DBG_CRED]  Thread-2 config: useKeytab=/keytabs/krb5.keytab
[KRB_DBG_CFG] Config:Thread-2:   ConfigFile: /etc/krb5/krb5.conf
[JGSS_DBG_CRED]  Thread-2 JAAS config: forwardable=false (default)
[JGSS_DBG_CRED]  Thread-2 JAAS config: renewable=false (default)
[JGSS_DBG_CRED]  Thread-2 JAAS config: proxiable=false (default)
[JGSS_DBG_CRED]  Thread-2 JAAS config: tryFirstPass=false (default)
[JGSS_DBG_CRED]  Thread-2 JAAS config: useFirstPass=false (default)
[JGSS_DBG_CRED]  Thread-2 JAAS config: moduleBanner=false (default)
[JGSS_DBG_CRED]  Thread-2 JAAS config: interactive login? no
[JGSS_DBG_CRED]  Thread-2 JAAS config: refreshKrb5Config = true
[KRB_DBG_CFG] Config:Thread-2:   ConfigFile: /etc/krb5/krb5.conf
[KRB_DBG_KDC] KdcComm:Thread-2:   >>> KdcAccessibility: reset
[KRB_DBG_KDC] KdcComm:Thread-2:   >>> KdcAccessibility: reset
[JGSS_DBG_CRED]  Thread-2 Try keytab for principal=HTTP/local.domain.com
[KRB_DBG_KTAB] KeyTab:Thread-2Loading the keytab file ...   >>> KeyTab: load() entry length: 73
[KRB_DBG_KTAB] KeyTableInputStream:Thread-2:   >>> KeyTabInputStream, readName(): LOCALDOMAIN.NET
[KRB_DBG_KTAB] KeyTableInputStream:Thread-2:   >>> KeyTabInputStream, readName(): HTTP
[KRB_DBG_KTAB] KeyTableInputStream:Thread-2:   >>> KeyTabInputStream, readName(): local.domain.com
[KRB_DBG_KDC] EncryptionKey:Thread-2:   >>> EncryptionKey: config default key type is rc4-hmac
[KRB_DBG_KTAB] KeyTab:Thread-2:   Added key: 23  version: 2
[KRB_DBG_KTAB] KeyTab:Thread-2:   Ordering keys wrt default_tkt_enctypes list
[JGSS_DBG_CRED]  Thread-2 No Kerberos creds in keytab for principal HTTP/local.domain.com
[JGSS_DBG_CRED]  Thread-2 Login successful
[JGSS_DBG_CRED]  Thread-2 kprincipal : HTTP/local.domain.com@LOCALDOMAIN.NET
[JGSS_DBG_CRED]  Thread-2 HTTP/local.domain.com@LOCALDOMAIN.NET added to Subject
[JGSS_DBG_CRED]  Thread-2 Attempting to add KeyTab to Subject for HTTP/local.domain.com@LOCALDOMAIN.NET
[JGSS_DBG_CRED]  Thread-2 find keys for HTTP/local.domain.com@LOCALDOMAIN.NET
[KRB_DBG_KTAB] KeyTab:Thread-2:   Added key: 23  version: 2
[KRB_DBG_KTAB] KeyTab:Thread-2:   Ordering keys wrt default_tkt_enctypes list
[JGSS_DBG_CRED]  Thread-2 No keys to add to Subject for HTTP/local.domain.com@LOCALDOMAIN.NET
LoginContext login() method executed
LoginContext getSubject() method executed
Subject doAs() method executed, serverCred Name: default Lifetime: 2147483647
[JGSS_DBG_CRED]  Thread-2 KeyTab is removed from subject
[JGSS_DBG_CRED]  Thread-2 KerberosKey Kerberos Principal HTTP/local.domain.com@LOCALDOMAIN.NETKey Version 2key EncryptionKey: keyType=23 keyBytes (hex dump)=
0000: <MASKED>

当我打电话时

        public String validate(String encToken) {
        byte[] token = Base64.decode(encToken);

        GSSContext authContext;
        try {
            authContext = authManager.createContext(serverCred);
            authContext.acceptSecContext(token, 0, token.length);
            if (authContext.isEstablished()) {
                return authContext.getSrcName().toString();
            }
        } catch (GSSException e) {
            // fall through to the return
        }

        return null;
    }
}

我发现在我的令牌上调用的“acceptSecContext”命令返回了一个值。我一直认为 acceptSecContext 只返回一个需要传递回发起者的值。然而,发起者并不期望得到回应。此外(更重要的是),.isEstablished() 方法返回 false。

所以我的问题是

1) 上面的设置有什么问题吗?
2) 为什么当我为上下文对象调用 login() 方法时会发生这种情况?

[JGSS_DBG_CRED]  Thread-2 Attempting to add KeyTab to Subject for HTTP/local.domain.com@LOCALDOMAIN.NET
    [JGSS_DBG_CRED]  Thread-2 find keys for HTTP/local.domain.com@LOCALDOMAIN.NET
    [KRB_DBG_KTAB] KeyTab:Thread-2:   Added key: 23  version: 2
    [KRB_DBG_KTAB] KeyTab:Thread-2:   Ordering keys wrt default_tkt_enctypes list
    [JGSS_DBG_CRED]  Thread-2 No keys to add to Subject for HTTP/local.domain.com@LOCALDOMAIN.NET

如果它找到密钥 23 版本 2,为什么它会说“没有要添加到主体@域的主题的密钥?为什么它没有添加它找到的密钥?kvno=2 有问题吗?

3) 我已经进行了详尽的搜索,但无法确定如何解析 acceptSecContext 的输出以找出返回值是什么。我收到的返回值(base-64 编码)是oQcwBaADCgEC

编辑:更新。 acceptSecContext 十六进制值的返回值是: 0xA1 0x07 0x30 0x05 0xA0 0x03 0x0A 0x01 0x02

从以下站点 (https://msdn.microsoft.com/en-us/library/ms995330.aspx#http-sso-2_topic2) 看来,第一个十六进制值 (A1) 对应于 NegTokenTarg。这就说得通了。

下一个八位字节应该是长度(如果长度需要更多八位字节,则最高位为 1)。由于最高位为 0,因此长度为 7 个八位字节。退房。

下一个八位字节(0x30)表示一个构造序列,下一个八位字节是序列长度(0x05); 5 个八位字节,签出。

然后我们有 0xA0, 0x03, 0x0A, 0x01 表示序列元素 0 (negResult)。

最后的八位字节 (0x02) 是 ENUMERATED 值,即“拒绝”。

所以我的令牌被拒绝了。我如何弄清楚“为什么”?我想我需要让 AD 团队了解他们到底发生了什么。

【问题讨论】:

    标签: active-directory kerberos aix jaas gssapi


    【解决方案1】:

    您是否尝试过手动测试使用 kinit 和 SPN 提供的密钥表?同样在您的 jaas.conf 中,您可能会使用 useKeyTab=true 和 keyTab="keytab_filename"。但这可能是您的 IBM JDK 特有的。

    【讨论】:

    • 我不熟悉那个 Microsoft ktpass.exe 实用程序 -- 密钥表中使用的实际“主体”是什么,HTTP/local.domain.com@LOCALDOMAIN.NETPSLDAP@LOCALDOMAIN.NET??尝试使用 Unix 命令klist -k /keytabs/krb5.keytab -e 来显示主体及其密钥(根据您的配置,无论如何只有“rc4-hmac”加密有用)。
    • @samson principal 是 /princ 之后的那个
    • useKeyTab=true 和 keyTab="keytab_filename" 是特定于太阳的。 IBM 需要 useKeytab="keytab_filename"。这也太费劲了。
    • 这是 klist 的输出,如上所述:Number of entries: 1 [1] principal: HTTP/local.domain.com@LOCALDOMAIN.NET KVNO: 2
    • 并且至少一键使用“rc4-hmac”以符合您的[libdefaults] conf?
    【解决方案2】:

    我已经为遇到类似问题的其他人发现了这个问题。

    事实证明 OID 不起作用。

    对我的 java 代码进行以下更改以检索服务器凭据修复了问题:

        serverCred = Subject.doAs(sub, new PrivilegedExceptionAction<GSSCredential>() {
            public GSSCredential run() throws GSSException {
                // mechanism OID for SPNEGO authentication
                Oid spnegoOid = new Oid("1.3.6.1.5.5.2");
    
                // null name defaults to currently logged in name
                GSSCredential cred = authManager.createCredential(null,
                        GSSCredential.INDEFINITE_LIFETIME,
                        spnegoOid,
                        GSSCredential.ACCEPT_ONLY);
                cred.add(null, 
                        GSSCredential.INDEFINITE_LIFETIME,
                        GSSCredential.INDEFINITE_LIFETIME,
                        new Oid("1.2.840.113554.1.2.2"),
                        GSSCredential.ACCEPT_ONLY);
                return cred;
            }
    

    我认为这与我没有来回协商任何事情(发起者和接受者)这一事实有关,因此 SPNEGO 从来没有真正有机会告诉发起者它更喜欢什么机制以及可用的机制,但我是给人的印象是 SPNEGO Oid 可以适应许多不同的机制,所以我不太清楚这个工作的原因,但它确实有效。

    附录:经过进一步研究,我能够找到对这个问题的模糊参考,原因是“AIX 中 GSSCredential 实现中的一些功能”。所以你有它。

    【讨论】:

      猜你喜欢
      • 2016-12-03
      • 1970-01-01
      • 2019-05-08
      • 1970-01-01
      • 1970-01-01
      • 2013-09-26
      • 2019-12-04
      • 1970-01-01
      相关资源
      最近更新 更多