我提供了我自己的解决方案:
我的解决方案基于 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];
}