java - SPNEGO身份验证可通过自定义Java客户端运行,但不能通过Web浏览器运行

标签 java kerberos jaas spnego gssapi

我在通过SPNEGO从Web浏览器(Internet Explorer 11)到自定义Java应用程序服务器提供的Web服务的身份验证时遇到问题。

我可以使用自定义Java客户端应用程序将SPNEGO成功验证到同一Application Server。

定制Java客户端和应用程序服务器的实现细节可以在下面找到。

我怀疑来自Web浏览器的SPNEGO无法正常工作,因为:

a)Internet Explorer中的 token 是否为有效的SPNEGO token ?

Web浏览器提供的GSSAPI token 与Java客户端提供的GSSAPI token 不同,并且可能不是有效的SPNEGO / Kerberos token 。 Java客户端提供以“Negotiate YIMMQA ...”(确定)开头的授权标头,而Web浏览器提供以“Negotiate oYIMRz ...”开头的授权标头(可能不正确)。

和/或

b)服务器主体名称的格式

由于历史原因,Application Server正在使用实际上是Microsoft Active Directory用户主体(格式=“user @ DOMAIN”)的服务主体名称运行,而我强烈怀疑Web浏览器SPNEGO实现使用所请求的URL来构建服务。主体名称。确实,这正是我的自定义Java客户端在Linux上针对Linux后端运行时所做的事情。

实现细节:

Java应用程序服务器在Windows Server 2012上运行。Kerberos / SPNEGO实现是纯Java JAAS + GSSAPI。

Java客户端可在Windows(7/10)上运行,并且可以配置为使用Java SSPI(通过Waffle)或JAAS + GSSAPI。两种实现都创建服务器接受的GSS token 。

生成的GSS / SPNEGO token 在Web服务请求(客户端)和响应(服务器)的标头中传输。

服务器正在使用Oids“1.3.6.1.5.5.2”(SPNEGO)和“1.2.840.113554.1.2.2”(Kerberos)。

使用自定义Java客户端进行测试(OK):

服务器能够通过一次握手来验证Java客户端。 Java客户端直接使用带有“Negotiate YIMMQA ...”的授权标头直接调用Web服务。在服务器上对Base64进行解码之后,gssapiData的长度为3140字节,并且对acceptSecContext()的调用成功。

如果我将gssapiData从此调用转换为字符串,并在其中搜索任何人类可读的内容,那么一开始我会找到“EXAMPLE.COM”和“user-DEV”。看起来像服务器正在使用的SPN,即Active Directory用户主体(“user-Dev@EXAMPLE.COM”)。

使用Internet Explorer 11进行测试(不确定):

来自浏览器的第一个调用的授权标题为空。我的服务器提示“协商”->确定。

来自浏览器的第二个调用具有一个授权标头,其开头为“协商YH4GBis ...”。 Base64解码后,gssapiData的长度为128个字节。显然,其中不包含服务凭单。

如果将gssapiData转换为字符串,则在中间找到字符“NTLMSSP”。我猜浏览器建议使用NTLM。我的服务器拒绝此呼叫。

来自浏览器的第三个调用具有一个以“Negotiate oYIMRz ...”开头的授权标头。对Base64进行解码后,gssapiData的长度为3147字节(非常接近Java客户端的长度)。

但是,当我的服务器对此执行一个acceptSecContext()时,它将引发错误。 “GSS异常:检测到 token 有缺陷(机制级别:GSS标头未找到正确的标签)。 ->不好。

这向我表明该 token 无效,或者我使用了错误的Oids来读取它。

如果我将gssapiData从此调用转换为字符串,则一开始我会找到“HTTP”和“APPSERVER.example.com”。这看起来像是以URL为基础构建的Kerberos服务主体名称(SPN)。 —>这向我建议我的Application Server应该以SPN的格式运行,例如“HTTP / APPSERVER.example.com”或“HTTP/appserver.example.com@EXAMPLE.COM”(第二种是该格式)我的Linux / FreeIPA配置使用)。

附带说明:在这个问题的焦点所在的Windows平台上,我没有创建/更改SPN或别名的权限,也没有尝试使用其他Web浏览器的权限。在我的Linux开发环境中,它可能会提供其他输入。 。 。

最佳答案

快速回答

需要两个修复程序:

1)Internet Explorer(IE)基于URL构建服务主体名称(SPN)。例如https://appserver.example.com/foo生成SPN“HTTP / APPSERVER.example.com”。

因此,必须在Active Directory中以上述格式设置适当的服务主体名称,作为应用程序服务器使用的用户主体名称(UPN)的别名。



2)来自Internet Explorer的 token 是有效的SPNEGO token ,但服务器上的GSS API不接受。

但是,通过对传入 token 进行一些简单的字符串操作,可以提取Kerbeos token ,GSS将接受该 token 并成功进行身份验证。

更长的答案

1)服务主体名称...

在此发布此问题之后,我们将2个SPN设置为服务器的UPN用户-Dev@EXAMPLE.COM的别名HTTP / APPSERVER.example.com和HTTP / APPSERVER。

服务器将继续使用UPN用户-Dev@EXAMPLE.COM运行。 (我最初认为服务器必须使用新的SPN之一运行是错误的。)

我的Java客户端现在可以使用新的SPN来获取Kerberos服务票证并创建已由我的服务器成功认证的 token 。

但是,来自Internet Explorer的 token 将继续被拒绝。

2)来自Internet Explorer的 token 与我的Java客户端...

我的Java客户端中的 token 开始如下:

协商YIIMdwYGKwYBBQUCoI...。

Base64解码,并表示为十六进制字节:

60 82 0C 77 06 06 2B 06 01 05 05 02 A0………

其中06 06 2B 06 01 05 05 02是SPNEGO OID 1.3.6.1.5.5.2。

Internet Explorer的 token 开始如下:

协商oYIMPjCCDDqgAwoBAaKCDDEEggwtYIIMKQYJKoZIhvcSAQIC

Base64解码,并表示为十六进制字节:

A1 82 0C 3E 30 82 0C 3A A0 03 0A 01 01 A2 82 0C 31 04 82 0C 2D 60 82 0C 29 06 09 2A 86 48 86 F7 12 01 02 02…。

这是Spnego NegTokenTarg,因为它以“A1”开头。但是,java类sun.security.jgss.GSSHeader将拒绝所有不以“60”开头的GSS token 。

逐字节检查IE NegTokenTarg显示,在前21个字节之后,我有一系列字节与应用程序中的 token 非常接近:

60 82 0C 29 06 09 2A 86 48 86 F7 12 01 02 02 ....

其中06 09 2A 86 48 86 F7 12 01 02 02是Kerberos OID 1.2.840.113554.1.2.2

如果我通过丢弃原始 token 的前21个字节来提取此 token (或提供偏移量为21的gssContext.acceptSecContext(gssapiData,offset,gssapiData.length)),则GSS API能够读取新 token 并提取用户主体,从而验证来自Internet Explorer的请求。

下面的代码示例使用base64最终编码的授权字符串的字符串操作来实现相同功能:

String auth =req.headers("Authorization");
if ( auth != null && auth.startsWith("Negotiate ")) {
    //smells like an SPNEGO request, so get the token from the http headers
    String authBody = auth.substring("Negotiate ".length());
    if (authBody.startsWith("oY")) {
        // This is a NegTokenTarg from IE, which GSS API does not properly handle.
        // However if we chop of the first (28) chars, we find a Kerberos Token starting with "60 82 0C" that GSS can handle.            
        authBody=authBody.substring(authBody.indexOf("YI", 2));
     }

     try {                 
         byte gssapiData[] = Base64.getDecoder().decode(authBody);               
         gssContext = initGSSContext(MyUtils.SPNEGOOID, MyUtils.KRB5OID);
         byte token[] = gssContext.acceptSecContext(gssapiData, offset, gssapiData.length);


         ..etc.

总之,我认为我们有

a)Java GSS API的一个弱点:GSS不直接接受作为NegTokenTarg的SPNEGO token 。

要么

b)Internet Explorer和我的服务器之间的相互作用,导致IE发送NegTokenTarg,这不是GSS API期望的。

IE-服务器互动是:

1)来自IE的请求(无需协商)

2)拒绝我的服务器,进行协商

3)来自IE的第二个请求,带有协商标头+ token ,看起来像NTLM,而不是Kerberos。 ->这可能是导致问题的路由原因。

4)使用协商+ SPNEGO token 从我的服务器拒绝

5)来自IE的第三个请求,带有协商标头+ SPNEGO NegTokenTarg

背景信息:

我的应用程序服务器使用Java JAAS + GSS来实现Kerberos / Spnego功能。我的自定义客户端可以使用Java JAAS + GSS或Microsoft SSPI + Waffle。

我发现此Microsoft文档对理解SPNEGO token 的格式非常有帮助。

https://msdn.microsoft.com/en-us/library/ms995330.aspx

并且该博客了解如何“处理”负十进制字节。 (数字-128到-1转换为128到255)。

http://sketchytech.blogspot.com/2015/11/bytes-for-beginners-representation-of.html

由于我的目标最终用户的公司标准浏览器是Internet Explorer,没有使用“更好”功能的现实选择,而在谷歌搜索时,我遇到了Chromium和Firefox的SPN处理代码。链接如下。 Chromium代码包含大量注释,并链接到知识库文章和SME博客。

Chrome 码

https://cs.chromium.org/chromium/src/net/http/http_auth_handler_negotiate.cc?type=cs&l=142

FireFox代码

https://dxr.mozilla.org/mozilla-central/source/extensions/auth/nsAuthSSPI.cpp#98

关于java - SPNEGO身份验证可通过自定义Java客户端运行,但不能通过Web浏览器运行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51409652/

相关文章:

java - 尝试创建 Web 服务客户端时找不到类异常

java - 为什么 middle.next 设置为 null?

java - null是java中的一个类吗?

java - ActiveMQ授权

java - JAAS - 无法将 Kerberos 票证保存到缓存文件,并且无法从头开始创建缓存......和其他细节

java - 如何修复 "Error running ' 应用' : Default Activity not found"

linux - 在 Ubuntu 16.04 上无人值守安装 krb5-user

postgresql - 我如何判断 postgres 是否安装了 kerberos?

java - Java8 中的 Kerberos/SPNEGO 服务器端身份验证更改

java - j_security_check - 可以为表单提交传递额外的参数吗?