You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Keycloak集成EHerkenning/EIDAS时SAML消息Base64解码报错

问题描述

我们正尝试使用SAML将Keycloak(20.0.1版本)与EHerkenning/EIDAS(荷兰/欧洲企业身份系统)身份提供商集成。可以正常连接到EHerkenning/EIDAS代理并显示登录页面,但发送至Keycloak端点的响应引发异常,Keycloak提示“Invalid response from identity provider.”。

Keycloak日志中记录的错误如下:

2022-11-09 14:01:30,735 ERROR [org.keycloak.saml.common] (executor-thread-0) Error in base64 decoding saml message.: java.lang.RuntimeException: PL00062: Parser : Unknown tag:EncryptedAttribute::location=Line number = 159

Column number = 252

System Id = null

Public Id = null

Location Uri= null

CharacterOffset = 35248


at org.keycloak.saml.common.DefaultPicketLinkLogger.parserUnknownTag(DefaultPicketLinkLogger.java:359)

at org.keycloak.saml.processing.core.parsers.saml.assertion.SAMLAttributeStatementParser.processSubElement(SAMLAttributeStatementParser.java:56)

at org.keycloak.saml.processing.core.parsers.saml.assertion.SAMLAttributeStatementParser.processSubElement(SAMLAttributeStatementParser.java:31)

at org.keycloak.saml.common.parsers.AbstractStaxParser.parse(AbstractStaxParser.java:97)

at org.keycloak.saml.processing.core.parsers.saml.assertion.SAMLAssertionParser.processSubElement(SAMLAssertionParser.java:96)

at org.keycloak.saml.processing.core.parsers.saml.assertion.SAMLAssertionParser.processSubElement(SAMLAssertionParser.java:34)

at org.keycloak.saml.common.parsers.AbstractStaxParser.parse(AbstractStaxParser.java:97)

at org.keycloak.saml.processing.core.parsers.saml.protocol.SAMLResponseParser.processSubElement(SAMLResponseParser.java:77)

at org.keycloak.saml.processing.core.parsers.saml.protocol.SAMLResponseParser.processSubElement(SAMLResponseParser.java:38)

at org.keycloak.saml.common.parsers.AbstractStaxParser.parse(AbstractStaxParser.java:97)

at org.keycloak.saml.processing.core.parsers.saml.SAMLParser.parse(SAMLParser.java:123)

at org.keycloak.saml.common.parsers.AbstractParser.parse(AbstractParser.java:98)

at org.keycloak.saml.common.parsers.AbstractParser.parse(AbstractParser.java:102)

at org.keycloak.saml.processing.api.saml.v2.response.SAML2Response.getSAML2ObjectFromStream(SAML2Response.java:389)

at org.keycloak.saml.SAMLRequestParser.parseResponseDocument(SAMLRequestParser.java:104)

at org.keycloak.broker.saml.SAMLEndpoint$PostBinding.extractResponseDocument(SAMLEndpoint.java:726)

at org.keycloak.broker.saml.SAMLEndpoint$Binding.handleSamlResponse(SAMLEndpoint.java:624)

at org.keycloak.broker.saml.SAMLEndpoint$Binding.execute(SAMLEndpoint.java:276)

at org.keycloak.broker.saml.SAMLEndpoint.postBinding(SAMLEndpoint.java:187)

at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)

at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)

at java.base/java.lang.reflect.Method.invoke(Method.java:566)

at org.jboss.resteasy.core.MethodInjectorImpl.invoke(MethodInjectorImpl.java:170)

at org.jboss.resteasy.core.MethodInjectorImpl.invoke(MethodInjectorImpl.java:130)

at org.jboss.resteasy.core.ResourceMethodInvoker.internalInvokeOnTarget(ResourceMethodInvoker.java:660)

at org.jboss.resteasy.core.ResourceMethodInvoker.invokeOnTargetAfterFilter(ResourceMethodInvoker.java:524)

at org.jboss.resteasy.core.ResourceMethodInvoker.lambda$invokeOnTarget$2(ResourceMethodInvoker.java:474)

at org.jboss.resteasy.core.interception.jaxrs.PreMatchContainerRequestContext.filter(PreMatchContainerRequestContext.java:364)

at org.jboss.resteasy.core.ResourceMethodInvoker.invokeOnTarget(ResourceMethodInvoker.java:476)

at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:434)

at org.jboss.resteasy.core.ResourceLocatorInvoker.invokeOnTargetObject(ResourceLocatorInvoker.java:192)

at org.jboss.resteasy.core.ResourceLocatorInvoker.invoke(ResourceLocatorInvoker.java:152)

at org.jboss.resteasy.core.ResourceLocatorInvoker.invokeOnTargetObject(ResourceLocatorInvoker.java:183)

at org.jboss.resteasy.core.ResourceLocatorInvoker.invoke(ResourceLocatorInvoker.java:141)

at org.jboss.resteasy.core.ResourceLocatorInvoker.invoke(ResourceLocatorInvoker.java:32)

at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:492)

at org.jboss.resteasy.core.SynchronousDispatcher.lambda$invoke$4(SynchronousDispatcher.java:261)

at org.jboss.resteasy.core.SynchronousDispatcher.lambda$preprocess$0(SynchronousDispatcher.java:161)

at org.jboss.resteasy.core.interception.jaxrs.PreMatchContainerRequestContext.filter(PreMatchContainerRequestContext.java:364)

at org.jboss.resteasy.core.SynchronousDispatcher.preprocess(SynchronousDispatcher.java:164)

at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:247)

at io.quarkus.resteasy.runtime.standalone.RequestDispatcher.service(RequestDispatcher.java:73)

at io.quarkus.resteasy.runtime.standalone.VertxRequestHandler.dispatch(VertxRequestHandler.java:151)

at io.quarkus.resteasy.runtime.standalone.VertxRequestHandler.handle(VertxRequestHandler.java:82)

at io.quarkus.resteasy.runtime.standalone.VertxRequestHandler.handle(VertxRequestHandler.java:42)

at io.vertx.ext.web.impl.RouteState.handleContext(RouteState.java:1284)

at io.vertx.ext.web.impl.RoutingContextImplBase.iterateNext(RoutingContextImplBase.java:173)

at io.vertx.ext.web.impl.RoutingContextImpl.next(RoutingContextImpl.java:140)

at io.quarkus.vertx.http.runtime.StaticResourcesRecorder$2.handle(StaticResourcesRecorder.java:84)

at io.quarkus.vertx.http.runtime.StaticResourcesRecorder$2.handle(StaticResourcesRecorder.java:71)

at io.vertx.ext.web.impl.RouteState.handleContext(RouteState.java:1284)

at io.vertx.ext.web.impl.RoutingContextImplBase.iterateNext(RoutingContextImplBase.java:173)

at io.vertx.ext.web.impl.RoutingContextImpl.next(RoutingContextImpl.java:140)

at io.quarkus.vertx.http.runtime.VertxHttpRecorder$6.handle(VertxHttpRecorder.java:430)

at io.quarkus.vertx.http.runtime.VertxHttpRecorder$6.handle(VertxHttpRecorder.java:408)

at io.vertx.ext.web.impl.RouteState.handleContext(RouteState.java:1284)

at io.vertx.ext.web.impl.RoutingContextImplBase.iterateNext(RoutingContextImplBase.java:173)

at io.vertx.ext.web.impl.RoutingContextImpl.next(RoutingContextImpl.java:140)

at org.keycloak.quarkus.runtime.integration.web.QuarkusRequestFilter.lambda$createBlockingHandler$0(QuarkusRequestFilter.java:82)

at io.quarkus.vertx.core.runtime.VertxCoreRecorder$14.runWith(VertxCoreRecorder.java:564)

at org.jboss.threads.EnhancedQueueExecutor$Task.run(EnhancedQueueExecutor.java:2449)

at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1478)

at org.jboss.threads.DelegatingRunnable.run(DelegatingRunnable.java:29)

at org.jboss.threads.ThreadLocalResettingRunnable.run(ThreadLocalResettingRunnable.java:29)

at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)

at java.base/java.lang.Thread.run(Thread.java:829)

已尝试的操作:

  • 使用SAML工具解码响应,但工具也无法解码该消息。
  • 移除整个SAML响应中的换行符(包括加密的Base64哈希属性和响应本身),但所有修改都会导致Keycloak不接受新的Base64编码SAML响应,或无日志直接崩溃。
解决方案分析

从日志核心错误PL00062: Parser : Unknown tag:EncryptedAttribute可以看出,问题并非Base64解码失败,而是Keycloak的SAML解析器无法识别响应中的<EncryptedAttribute>标签,日志里的“base64解码错误”属于误导性提示。

核心原因

Keycloak 20.0.1版本的基础SAML解析逻辑未支持<EncryptedAttribute>元素——该元素属于SAML 2.0加密属性扩展,解析器遇到未定义的标签时直接抛出异常,导致整个响应处理失败。

解决步骤

  1. 调整IdP输出配置:检查EHerkenning/EIDAS代理是否支持关闭属性加密,改为返回明文属性。若能调整,这是最直接的解决方案。
  2. 升级Keycloak版本:Keycloak 21及以上版本已修复对<EncryptedAttribute>的支持问题,升级到兼容版本可直接解决解析异常。
  3. 自定义解析扩展:若无法升级或修改IdP配置,可编写Keycloak扩展,扩展SAMLAttributeStatementParser类,添加对<EncryptedAttribute>标签的处理逻辑,包括解密属性值并转换为普通SAML属性格式。
  4. 拦截修改SAML响应:在Keycloak与IdP之间添加代理,拦截SAML响应后移除<EncryptedAttribute>节点,或转换为Keycloak可识别的格式。注意此方式需确保敏感属性的传输安全,避免明文泄露。

额外调试建议

  • 启用Keycloak SAML详细日志:在配置文件中设置logger.org.keycloak.saml=DEBUG,获取XML解析的详细过程,确认<EncryptedAttribute>的具体结构和位置。
  • 抓包查看原始响应:用Wireshark或Fiddler捕获完整SAML响应,手动处理URL编码和换行符后进行Base64解码,直接查看XML结构,确认加密属性的存在形式。

内容的提问来源于stack exchange,提问作者Shwahlthy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 01:30:50