【问题标题】:Decrypt kerberos ticket using Spnego使用 Spnego 解密 kerberos 票证
【发布时间】:2011-05-29 08:33:58
【问题描述】:

我在 JBoss 下使用 spnego (http://spnego.sourceforge.net) 进行 kerberos 身份验证。

我需要解密 kerberos 票证才能访问包含 PAC 数据的授权数据。需要 PAC 数据来决定向用户授予哪些角色。

如何访问和解密kerberos票据?我已经在网上搜索了示例,但没有努力。

【问题讨论】:

  • 这是什么意思,“我实际上是在测试解决方案以检查它是否仅适用于单一特定格式的令牌。”什么是单一特定格式?
  • 好吧,来自 JaasLounge 的代码和来自互联网的其他示例都不起作用。他们不认识票的结构。在使用 JaasLounge 时,我收到了诸如“此处不应有 DERSequence”之类的消息。
  • 我们在看什么?这是否表明您自己的问题的解决方案?还是在尝试解决时有更多问题?
  • @eleotlecram 我已经在问题中回答了我自己的问题。这是我开始使用 SO 的冒险,现在我更正了这个反模式 - 我已将我的解决方案作为单独的答案发布。

标签: java jboss active-directory kerberos spnego


【解决方案1】:

这些家伙有完整的 PAC 解码实现:

http://jaaslounge.sourceforge.net/

您可以像这样使用令牌解析器:

HttpServletRequest request = (HttpServletRequest) req;
String header = request.getHeader("Authorization");
byte[] base64Token = header.substring(10).getBytes("UTF-8");
byte[] spnegoHeader = Base64.decode(base64Token);

SpnegoInitToken spnegoToken = new SpnegoInitToken(spnegoHeader);

如果您想解密底层的 Kerberos 票证,您将需要跳过一些障碍。不知道你是否需要。

授予

【讨论】:

  • 我已经下载了这个带有源的 jaaslounge,但是没有找到从这个 SpnegoInitToken 中提取 kerberos 票证的方法。
  • “如果你想解密底层的 Kerberos 票证,你需要跳过一些障碍。不确定你是否需要那个。” ——这就是我这么说的原因。它需要额外的代码才能获得 Kerberos 票证。我会提供后续回复。
  • 嗯,是的,你说得对,他们有完整的 PAC 解码实现。但是,他们解析整个 kerberos 令牌的代码不起作用,我不得不自己编写。在我获得加密部分并对其进行解密后,我将其传递给 KerberosEncData,由它完成剩下的工作。
【解决方案2】:

我已经成功地将来自http://spnego.sourceforge.net 的 servlet 过滤器与来自http://jaaslounge.sourceforge.net/ 的 PAC 解析器结合使用,而无需对 DER/ASN.1 解析器进行明确的操作:

/** 
 * Retrieve LogonInfo (for example, Group SID) from the PAC Authorization Data
 * from a Kerberos Ticket that was issued by Active Directory.
 */  
byte[] kerberosTokenData = gssapiData;
try {
    SpnegoToken token = SpnegoToken.parse(gssapiData);
    kerberosTokenData = token.getMechanismToken();
} catch (DecodingException dex) {
    // Chromium bug: sends a Kerberos response instead of an spnego response 
    // with a Kerberos mechanism
} catch (Exception ex) {
    log.error("", ex);
}   

try {
    Object[] keyObjs = IteratorUtils.toArray(loginContext.getSubject()
                         .getPrivateCredentials(KerberosKey.class).iterator());
    KerberosKey[] keys = new KerberosKey[keyObjs.length];
    System.arraycopy(keyObjs, 0, keys, 0, keyObjs.length);

    KerberosToken token = new KerberosToken(kerberosTokenData, keys);
    log.info("Authorizations: "); 
    for (KerberosAuthData authData : token.getTicket().getEncData()
                                             .getUserAuthorizations()) {
        if (authData instanceof KerberosPacAuthData) {
            PacSid[] groupSIDs = ((KerberosPacAuthData) authData)
                                      .getPac().getLogonInfo().getGroupSids();
            log.info("GroupSids: " + Arrays.toString(groupSIDs));
            response.getWriter().println("Found group SIDs: " + 
                Arrays.toString(groupSIDs));
        } else {
            log.info("AuthData without PAC: " + authData.toString());
        }   
    }   
} catch (Exception ex) {
    log.error("", ex);
}   

我还编写了一个新的 HttpFilter(来自 spnego.sf.net):spnego-pac,它通过 getUserPrincipal() 公开 LogonInfo。

可在此处找到完整演示上述代码的示例项目:

https://github.com/EleotleCram/jetty-spnego-demo

spnego-pac 过滤器(在上面的例子中使用)可以在这里找到:

https://github.com/EleotleCram/spnego.sf.net-fork

希望这对任何人都有帮助。

__
马塞尔

【讨论】:

    【解决方案3】:

    如果你像这样从spnegoToken 获得机制令牌:

    byte[] mechanismToken = spnegoToken.getMechanismToken(); 
    

    机制令牌通常是KerberosApRequest。有一个KerberosToken 构造函数接受KerberosApRequest。只需传入mechanismToken 字节数组以及解密内容的密钥即可。

    【讨论】:

    • 你的意思是类 sun.security.jgss.spnego.SpNegoToken?不幸的是,这个类在我正在处理的项目中没有使用,它基于 org.ietf.jgss.GSSCredential 接口。我不是手动使用它,而是使用实用程序 net.sourceforge.spnego.SpnegoAuthenticator。
    • 不,我说的是 SpnegoInitToken spnegoToken = new SpnegoInitToken(spnegoHeader);在我在上一个答案中输入的代码中。这 - org.jaaslounge.decoding.spnego.SpnegoInitToken 和 org.jaaslounge.decoding.kerberos.KerberosApRequest
    • 我已经测试过了,还是不行。 KerberosAsRequest 构造失败,因为期望的是DERSequence,而mechanicalToken 的内容是DERApplicationSpecific。
    • 您使用的是什么版本的 bouncycastle?我认为它的某些版本存在错误。您需要使用最新的(1.45):repo2.maven.org/maven2/org/bouncycastle org.bouncycastlebcprov-jdk161.45
    【解决方案4】:

    我提供了我自己的解决方案:

    我的解决方案基于 BouncyCastle 库(用于解析令牌的一部分)和 JaasLounge(用于解密令牌的加密部分)。不幸的是,从 JaasLounge 解码整个 spnego 令牌的代码未能满足我的要求。我必须自己写。

    我已经逐部分解码票证,首先从 byte[] 数组构造 DERObjects:

    private DERObject[] readDERObjects(byte[] bytes) throws IOException {
        ASN1InputStream stream = new ASN1InputStream(new ByteArrayInputStream(
            bytes));
        List<DERObject> objects = new ArrayList<DERObject>();
        DERObject curObj;
        while ((curObj = stream.readObject()) != null) {
            objects.add(untag(curObj));
        }
        return objects.toArray(new DERObject[0]);
    }
    

    untag() 是我的辅助函数,用于删除 DERTaggedObject 包装

    private DERObject untag(DERObject src) {
        if (src instanceof DERTaggedObject) {
            return ((DERTaggedObject) src).getObject();
        }
        return src;
    }
    

    为了从给定的 DERObject 中提取 DERObject 序列,我编写了另一个辅助函数:

    private DERObject[] readDERObjects(DERObject container) throws IOException {
    // do operation varying from the type of container
    if (container instanceof DERSequence) {
        // decode using enumerator
        List<DERObject> objects = new ArrayList<DERObject>();
        DERSequence seq = (DERSequence) container;
        Enumeration enumer = seq.getObjects();
        while (enumer.hasMoreElements()) {
        DERObject curObj = (DERObject) enumer.nextElement();
        objects.add(untag(curObj));
        }
        return objects.toArray(new DERObject[0]);
    }
    if (container instanceof DERApplicationSpecific) {
        DERApplicationSpecific aps = (DERApplicationSpecific) container;
        byte[] bytes = aps.getContents();
        return readDERObjects(bytes);
    }
    if (container instanceof DEROctetString) {
        DEROctetString octets = (DEROctetString) container;
        byte[] bytes = octets.getOctets();
        return readDERObjects(bytes);
    }
    throw new IllegalArgumentException("Unable to decode sequence from "+container);
    }
    

    最后,当我得到包含加密部分的DEROctetStream时,我刚刚使用了KerberosEncData:

    KerberosEncData encData = new KerberosEncData(decrypted, matchingKey);
    

    我们从客户端浏览器收到的字节序列将被解析为单个 DERApplicationSpecific 这是票根 - 级别 0。
    根包含:

    • DERObjectIdentifier - SPNEGO OID
    • DERSequence - 1 级

    第 1 级包含:

    • DERObjectIdentifier 的序列 - 机械类型
    • DEROctetString - 包装的 DERApplicationSepecific - 2 级

    第 2 级包含:

    • DERObjectIndentifier - Kerberos OID
    • KRB5_AP_REQ 标签0x01 0x00,解析为布尔值(假)
    • DERApplicationSpecific - DERSequence 容器 - 级别 3

    第 3 级包含:

    • 版本号 - 应该是 5
    • 消息类型 -​​ 14 (AP_REQ)
    • AP 选项(DERBITString)
    • DERApplicationSpecific - 使用票证部分包装 DERSequence
    • 带有附加票证部分的 DERSeqeuence - 未处理

    票证部分 - 第 4 级包含:

    • 票证版本 - 应该是 5
    • Ticket 领域 - 用户在其中进行身份验证的领域的名称
    • DERSequence 服务器名称。每个服务器名称都是 2 个字符串的 DERSequence: 服务器名称和实例名称
    • 带有加密部分的DERSequence

    加密部分序列(第 5 级)包含:

    • 使用的算法编号
      • 1, 3 - DES
      • 16 - des3-cbc-sha1-kd
      • 17 - ETYPE-AES128-CTS-HMAC-SHA1-96
      • 18 - ETYPE-AES256-CTS-HMAC-SHA1-96
      • 23 - RC4-HMAC
      • 24 - RC4-HMAC-EXP
    • 密钥版本号
    • 加密部分 (DEROctetStream)

    问题出在 DERBoolean 构造函数上,当找到序列 0x01 0x00 时会抛出 ArrayIndexOutOfBoundException。我不得不改变那个构造函数:

    public DERBoolean(
        byte[]       value)
    {
    // 2011-01-24 llech make it byte[0] proof, sequence 01 00 is KRB5_AP_REQ
    if (value.length == 0)
        this.value = 0;
    else
        this.value = value[0];
    }
    

    【讨论】:

    • 当算法编号不是 3 或 23 时,KerberosEncData(至少我知道的版本)似乎会抛出 GeneralSecurityException("Unsupported encryption type.")。您如何处理其他算法?或者你还没有遇到过这个问题?
    • 我一直在与这段代码作斗争,使用 WireShark 进行逆向工程,直到它适用于我使用的特定域。我没有遇到过这样的问题,代码是很久以前写的。但最初我已将答案放入问题中,这是错误的,我现在正在纠正它。因此,答案并不新鲜。
    【解决方案5】:

    哇,我使用 spnego 已经有一段时间了(将近一年)......你问了一个非常酷的问题。

    我进行了一些挖掘,并打算尝试运行一些我之前使用 MS-AD 的代码,但今天却感觉不到:-/

    无论如何,我通过谷歌找到了这个链接: http://www.google.com/url?sa=t&source=web&cd=1&sqi=2&ved=0CBMQFjAA&url=http%3A%2F%2Fbofriis.dk%2Ffiles%2Fms_kerberos_pac.pdf&rct=j&q=java%20kerberos%20privilege%20attribute%20certificate&ei=2FASTbaLGcP38Abk07iQDg&usg=AFQjCNHcIfQRUTxkQUvLRcgOaQksCALTHA&sig2=g8yn7ie1PbzSkE2Mfv41Bw&cad=rja

    希望这能给你一些见解。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-10-20
      • 1970-01-01
      • 2010-09-25
      • 2011-12-23
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多