我在调用远程EJB的Windows机器上运行Java客户端 在Linux机器上运行的JBoss EAP / Wildfly上。 我使用Kerberos来实现SSO。 Java客户端根据Windows域验证用户 并将他在EJB调用中的身份传递给JBoss服务器。
我开始使用JAAS和内置com.sun.security.auth.module.Krb5LoginModule
。
它可以正常工作,除了一件事:用户必须输入他的用户名和密码
再次。所以,它不是真正的SSO。
问题是Windows禁止从其LSA凭据缓存中导出kerberos会话密钥。 可以通过在每台客户端计算机上设置特定的Windows注册表项来修复它 - 但这对客户来说是不可接受的。
因此,我试图寻找替代解决方案。 我了解到Windows提供的SSPI可以与Java使用的GSSAPI互操作。我使用Waffle库从客户端上的Java访问SSPI。在服务器上我继续使用JAAS,因为它在Linux上运行所以我不能在那里使用Waffle。
我还了解到我不需要实现LoginModule,而是需要SASL客户端。
所以,我看看com.sun.security.sasl.gsskerb.GssKrb5Client
是如何工作的,我正试图用Waffle重新实现它。
第一步似乎工作正常 - 我从Waffle获得SSPI安全上下文, 然后获取初始令牌并将其发送到服务器。 服务器接受令牌并使用自己的令牌进行响应。
现在问题来了。在最初的SASL客户端中,' unwrap'操作是 用于从服务器令牌中提取数据,并且包装'操作用于创建 回复令牌发送到服务器。
GSSAPI wrap / unwrap 操作应对应 SSPI EncryptMessage / DecryptMessage 根据Microsoft doc的操作。这两种方法在Waflle中不可用,但可用 在NetAccountClient library。
但是,我无法正确使用它们。如果我使用单个SECBUFFER_STREAM然后使用DecryptMessage 是成功的,但令牌的数据部分未被提取,我不知道如何确定 它开始的偏移量。
如果我按Microsoft docs的建议使用SECBUFFER_STREAM和SECBUFFER_DATA,则会收到错误消息:
com.sun.jna.platform.win32.Win32Exception: The message or signature supplied for verification has been altered
我还尝试了其他地方建议的SECBUFFER类型的其他组合,但没有成功。
知道我做错了什么吗?
要获取解包方法的源代码:
public byte[] unwrap(byte[] wrapper) throws LoginException {
Sspi.SecBuffer.ByReference inBuffer = new Sspi.SecBuffer.ByReference(Secur32Ext.SECBUFFER_STREAM, wrapper);
Sspi.SecBuffer.ByReference buffer = new Sspi.SecBuffer.ByReference();
buffer.BufferType = Sspi.SECBUFFER_DATA;
Secur32Ext.SecBufferDesc2 buffers = new Secur32Ext.SecBufferDesc2(inBuffer, buffer);
NativeLongByReference pfQOP = new NativeLongByReference();
int responseCode = Secur32Ext.INSTANCE.DecryptMessage(secCtx.getHandle(), buffers, new NativeLong(1), pfQOP);
if (responseCode != W32Errors.SEC_E_OK) {
throw handleError(responseCode);
}
byte[] data = buffer.getBytes();
return data;
}