我在 JBoss 下使用 spnego ( http://spnego.sourceforge.net ) 进行 kerberos 身份验证。
我需要解密 kerberos 票证以访问将包含 PAC 数据的授权数据。需要 PAC 数据来决定将哪些角色授予用户。
如何访问和解密kerberos票?我已经在网上搜索了示例,但没有努力。
我在 JBoss 下使用 spnego ( http://spnego.sourceforge.net ) 进行 kerberos 身份验证。
我需要解密 kerberos 票证以访问将包含 PAC 数据的授权数据。需要 PAC 数据来决定将哪些角色授予用户。
如何访问和解密kerberos票?我已经在网上搜索了示例,但没有努力。
这些家伙有一个完整的 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 票证,您将需要跳过一些障碍。不确定你是否需要那个。
授予
我已经成功地将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
希望这对任何人都有帮助。
__
马塞尔
如果您从以下spnegoToken
方式获取机制令牌:
byte[] mechanismToken = spnegoToken.getMechanismToken();
机制令牌通常是一个KerberosApRequest
. 有一个KerberosToken
构造函数接受一个KerberosApRequest
. 只需传入mechanismToken
字节数组和密钥即可解密内容。
我为这个问题提供了自己的解决方案:
我的解决方案基于 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。
根包含:
级别 1 包含:
第 2 级包含:
0x01 0x00
,解析为布尔值 (false)
第 3 级包含:
工单部分 - 第 4 级包含:
加密部分序列(第 5 级)包含:
问题出在 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];
}
哇自从我使用 spnego (将近一年)以来已经有一段时间了……你问了一个非常酷的问题。
我做了一些挖掘,并打算尝试运行一些我之前使用 MS-AD 的代码,但今天感觉不到:-/
希望这能给你一些见解。