使用 Spnego 解密 kerberos 票证

发布于 2024-10-09 05:36:48 字数 251 浏览 9 评论 0原文

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

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

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

I'm using spnego ( http://spnego.sourceforge.net ) for kerberos authentication under JBoss.

I need to decrypt kerberos ticket to access the authorization-data which will containt PAC data. The PAC data is needed to decide which roles are to be granted to user.

How to access and decrypt kerberos ticket? I've searched net for examples, but without effort.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

毁我热情 2024-10-16 05:36:48

这些人有一个完整的 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 票证,则需要跳过一些步骤。不确定你是否需要那个。

授予

These guys have a full PAC decoding implementation:

http://jaaslounge.sourceforge.net/

You can use the token parser like this:

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

You're going to need to jump though some hoops if you want to decrypt the underlying Kerberos ticket. Not sure if you need that.

Grant

深海不蓝 2024-10-16 05:36:48

我已经成功地将 http://spnego.sourceforge.net 中的 servlet 过滤器与 < a href="http://jaaslounge.sourceforge.net/" rel="noreferrer">http://jaaslounge.sourceforge.net/ 无需使用 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

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

__
马塞尔

I have successfully used the servlet filter from http://spnego.sourceforge.net in combination with the PAC parser from http://jaaslounge.sourceforge.net/ without the need to do something explicitly with DER/ASN.1 parsers :

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

I've also written a new HttpFilter (forked from spnego.sf.net): spnego-pac, that discloses the LogonInfo through the getUserPrincipal().

An example project demonstrating the above code in full can be found here:

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

The spnego-pac filter (used in the above example) can be found here:

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

Hope this is helpful to anyone.

__
Marcel

故事还在继续 2024-10-16 05:36:48

如果您从 spnegoToken 获取机制令牌,如下所示:

byte[] mechanismToken = spnegoToken.getMechanismToken(); 

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

If you get the mechanism token from the spnegoToken like this:

byte[] mechanismToken = spnegoToken.getMechanismToken(); 

The mechanism token is usually a KerberosApRequest. There is a KerberosToken constructor which takes a KerberosApRequest. Simply pass in the mechanismToken byte array along with the key to decrypt the contents.

开始看清了 2024-10-16 05:36:48

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

我的解决方案基于 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,解析为布尔值 (false)
  • DERApplicationSpecific - DERSequence 容器 - 级别 3

级别 3 包含:

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

票证部分 - 第 4 级包含:

  • 票证版本 - 应为 5
  • 票证领域 - 用户进行身份验证的领域的名称
  • DER 服务器名称的序列。每个服务器名称都是由 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];
}

I provide my own solution to the problem:

I've based my solution on BouncyCastle library (for parsing parts of token) and JaasLounge (for decrypting encrypted part of token). Unfortunatelly, the code for decoding whole spnego token from JaasLounge failed for my requirements. I had to write it myself.

I've decoded ticket part by part, firstly constructing DERObjects from byte[] array:

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

The untag() is my helper function, to remove DERTaggedObject wrapping

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

For extracting sequence of DERObject from given DERObject I've written another helper function:

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

At the end, when I've got DEROctetStream, that contained encrypted part, I've just used KerberosEncData:

KerberosEncData encData = new KerberosEncData(decrypted, matchingKey);

The byte sequence we receive from client browser will be parsed into single DERApplicationSpecific
which is ticket root - level 0.

The root contains:

  • DERObjectIdentifier - SPNEGO OID
  • DERSequence - level 1

Level 1 contains:

  • SEQUENCE of DERObjectIdentifier - mech types
  • DEROctetString - wrapped DERApplicationSepecific - level 2

Level 2 contains:

  • DERObjectIndentifier - Kerberos OID
  • KRB5_AP_REQ tag 0x01 0x00, parsed as boolean (false)
  • DERApplicationSpecific - container of DERSequence - level 3

Level 3 contains:

  • version number - should be 5
  • message type - 14 (AP_REQ)
  • AP options (DERBITString)
  • DERApplicationSpecific - wrapped DERSequence with ticket part
  • DERSeqeuence with additional ticket part - not processed

Ticket part - level 4 contains:

  • Ticket version - should be 5
  • Ticket realm - the name of the realm in which user is authenticated
  • DERSequence of server names. Each server name is DERSequence of 2 strings:
    server name and instance name

  • DERSequence with encrypted part

Encrypted part sequence (level 5) contains:

  • Used algorithm number
    • 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
  • Key version number
  • Encrypted part (DEROctetStream)

The problem was with DERBoolean constructor, that throw ArrayIndexOutOfBoundException, when sequence 0x01 0x00 was found. I had to change that constructor:

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];
}
鸠书 2024-10-16 05:36:48

哇,自从我使用 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

Hopefully that can give you some insight.

Wow been a while since I've used spnego (nearly a year) ... You're asking a very cool question.

I did a little digging and was going to try and run up some code I had from a while back that was working with MS-AD but just not feeling it today :-/

Anyway, I found this link through google:
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

Hopefully that can give you some insight.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文