首页
/ Keycloak FIPS 140-2 实战指南:从启用 fips 特性到 Strict 模式的完整落地方案

Keycloak FIPS 140-2 实战指南:从启用 fips 特性到 Strict 模式的完整落地方案

2026-09-05 17:14:44作者:廉皓灿Ida

本文基于 Keycloak 仓库中 docs/fips.md 及其指向的完整指南 fips.adoc 编写,覆盖在 FIPS 140-2 合规环境下部署 Keycloak 服务器所需的全部环节:FIPS 系统的启用与验证、BouncyCastle FIPS(BCFIPS)库的部署、pkcs12bcfks 两种 KeyStore 的生成命令、--features=fips--fips-mode=strict 的启动参数、严格模式下的密码学限制(密码长度、RSA 位数、HMAC 密钥、JWE 算法)、CLI 工具与容器化部署的配套操作,以及从非 FIPS 环境迁移的注意事项。结合仓库中 FIPS1402Provider.java 的源码与 SecurityOptions.java 的参数定义,你将获得“可复制的命令 + 可验证的源码依据”两方面的完整方案,并能按 HOW-TO-RUN.md 中的方法在 FIPS 环境运行单元/集成测试。

1. 文档定位:docs/fips.md 与新版 FIPS 指南的关系

docs/fips.md 本身是一个简短的说明文件,其内容为两部分:

  1. 声明该文档已过时(outdated),并指向当前有效的 FIPS 140-2 集成指南 fips.adoc
  2. 说明如何在 FIPS 环境下运行单元/集成测试,指引读者参阅 HOW-TO-RUN.md 中的 FIPS 章节。

因此,仓库中 FIPS 集成的“权威说明”位于 docs/guides/server/fips.adoc(即官方文档中的 "FIPS 140-2 support" 指南),而 docs/fips.md 保留下来主要是为了指引测试执行。本文以 fips.adoc 的主体内容为核心骨架,并结合 crypto/fips1402 模块源码进行深化。

2. 前置条件:FIPS 启用的系统

联邦信息处理标准 FIPS 140-2 是美国政府用于审批密码模块的计算机安全标准。Keycloak 支持以 FIPS 140-2 合规模式运行,此时所有密码功能只使用 FIPS 批准的算法。

系统要求:Keycloak 必须运行在已启用 FIPS 140-2 的系统上,通常是安装时即启用 FIPS 的 RHEL 或 Fedora。系统处于 FIPS 模式后,底层的 OpenJDK 也会进入 FIPS 模式,只使用 FIPS 启用的安全提供器。

版本建议(重要):Keycloak 的 FIPS 140-2 集成在 OpenJDK 25 上经过测试,但底层 BouncyCastle FIPS 库并未针对 OpenJDK 25 官方验证,因此合规场景仍建议在 OpenJDK 21 上运行 Keycloak

检查与启用系统 FIPS 模式

检查当前系统是否处于 FIPS 模式:

fips-mode-setup --check

若系统尚未启用,可以启用(官方建议自安装起即处于 FIPS 模式,而非事后开启):

fips-mode-setup --enable

从源码可以印证 Keycloak 自身如何探测宿主机 FIPS 状态:FIPS1402Provider.java 中的 isSystemFipsEnabled() 方法读取内核文件 /proc/sys/crypto/fips_enabled,并检查第一个 Java 安全提供器是否为 SunPKCS11(-NSS)?-FIPS 模式匹配(注释说明 Java 25 下不再依赖专门的类,而是直接检查该文件),两者都满足才返回 enabled,否则返回 disabled/unknown

3. 部署 BouncyCastle FIPS 库

Keycloak 内部大量使用 BouncyCastle 库,但随发行版分发的默认 BouncyCastle 版本并非 FIPS 合规;BouncyCastle 同时提供经过 FIPS 验证的版本,而 Keycloak 因无法为其提供官方支持,故不随附 BCFIPS 位(bits)。要运行 FIPS 合规模式,需自行下载 BouncyCastle-FIPS 位并加入发行版;执行 FIPS 模式时,Keycloak 会改用 BCFIPS 位替代默认 BouncyCastle 位,从而实现合规。

需要的 BCFIPS 组件及版本

到 BouncyCastle 官方下载页获取 FIPS 库后,加入发行版的 KEYCLOAK_HOME/providers 目录,且版本必须与 Keycloak 所依赖的 BouncyCastle 兼容。仓库中声明了所需的四个 BCFIPS 组件(版本取自根 pom.xml 中的属性定义,指南中通过模板变量引用):

组件 根 pom 属性名
bc-fips bouncycastle.bcfips.version
bctls-fips bouncycastle.bctls-fips.version
bcpkix-fips bouncycastle.pkixfips.version
bcutil-fips bouncycastle.bcutilfips.version

源码印证:KC 提供器的注册流程

FIPS1402Provider 是 Keycloak 的 CryptoProvider 实现,其构造器(约 L88–L116)完成以下关键动作:

  1. BCFIPS 提供器已注册在 Java 安全文件中则复用,否则 new BouncyCastleFipsProvider()
  2. 注册一系列 FIPS 专用算法提供器:A128KWRSA1_5/RSA-OAEP/RSA-OAEP-256(JWE 密钥加密)、ECDH-ES 家族等;
  3. SecureRandom.getInstanceStrong() 不可用(如 RHEL 8 + OpenJDK 17 的已知问题),回退设置 securerandom.strongAlgorithms 后再插入 BCFIPS 提供器(见 checkSecureRandom,约 L337–L363);
  4. fips:BCFIPS 标识创建 BouncyCastleJsseProvider 并插入到安全提供器第 2 位,同时按 BCJSSE 实际提供的服务修正 ssl.KeyManagerFactory.algorithm / ssl.TrustManagerFactory.algorithm(约 L375–L401);
  5. 输出启动日志——这正是指南中用于验证模式的核心依据:
FIPS1402Provider created: KC(BCFIPS version 2.0102 Approved Mode, FIPS-JVM: enabled)

日志中是否带 Approved Mode 字样、FIPS-JVMenabled 还是 disabled,直接反映 BCFIPS 是否运行在批准模式以及 JVM 是否检测到系统级 FIPS。

4. 生成 KeyStore:PKCS12 与 BCFKS 两种方式

Keycloak 服务器 SSL 可以使用 pkcs12bcfks 两种类型的 KeyStore,二者的适用场景不同。

4.1 PKCS12 KeyStore

p12pkcs12)KeyStore/TrustStore 在 BCFIPS 的**非批准模式(non-approved mode)**下工作良好。示例:在 RHEL 9 + OpenJDK 21 上用标准方式生成:

keytool -genkeypair -sigalg SHA512withRSA -keyalg RSA -storepass passwordpassword \
  -keystore $KEYCLOAK_HOME/conf/server.keystore \
  -alias localhost \
  -dname CN=localhost -keypass passwordpassword

需要注意的限制:

  • FIPS 模式下 pkcs12 不能管理秘密(对称)密钥——这是 BCFIPS 提供器对 pkcs12 类型的限制;
  • 系统处于 FIPS 模式时,默认的 java.security 文件已改为 FIPS 启用的安全提供器,无需额外配置;
  • 可以用 keytool 直接在 PKCS12 中存放 PBE(基于口令加密)密钥,因此它非常适合 Keycloak 的 KeyStore Vault 以及 KeyStore 配置源(Config Source)场景,参见 vault.adocconfiguration.adoc

4.2 BCFKS KeyStore

bcfks 类型必须借助 BouncyCastle FIPS 库与一个自定义安全文件来生成。

第一步,创建辅助安全文件(例如 /tmp/kc.keystore-create.java.security),内容只需:

securerandom.strongAlgorithms=PKCS11:SunPKCS11-NSS-FIPS

第二步,执行生成命令:

keytool -keystore $KEYCLOAK_HOME/conf/server.keystore \
  -storetype bcfks \
  -providername BCFIPS \
  -providerclass org.bouncycastle.jcajce.provider.BouncyCastleFipsProvider \
  -provider org.bouncycastle.jcajce.provider.BouncyCastleFipsProvider \
  -providerpath $KEYCLOAK_HOME/providers/bc-fips-*.jar \
  -alias localhost \
  -genkeypair -sigalg SHA512withRSA -keyalg RSA -storepass passwordpassword \
  -dname CN=localhost -keypass passwordpassword \
  -J-Djava.security.properties=/tmp/kc.keystore-create.java.security

警告:自签名证书仅用于演示,生产环境请替换为正式证书。

之后对 bcfks 类型 KeyStore/TrustStore 的任何其他操作,也需要类似的一组参数。

5. 以 FIPS 模式启动服务器

使用 BCFIPS 非批准模式启动 Keycloak:

kc.sh start --features=fips \
  --hostname=localhost \
  --https-key-store-password=passwordpassword \
  --log-level=INFO,org.keycloak.common.crypto:TRACE,org.keycloak.crypto:TRACE

其中 org.keycloak.common.crypto:TRACE,org.keycloak.crypto:TRACE 用于在日志中确认所用安全提供器;一切正常后,生产环境可关闭 TRACE 日志。

注意:非批准模式下默认 KeyStore 类型(及默认 TrustStore 类型)是 PKCS12。若你按上文生成了 BCFKS KeyStore,还必须附加 --https-key-store-type=bcfks;若使用 TrustStore 也可能需要对应的类型参数。

6. Strict 模式(fips-mode=strict)及其限制

fips-mode 选项在启用 fips 特性时自动默认为 non-strict(即 BCFIPS 非批准模式)。更安全的做法是使用:

--features=fips --fips-mode=strict

此时 BouncyCastle FIPS 运行在批准模式(approved mode),对密码与安全算法施加更严格的约束。从源码看,SecurityOptions.javaFIPS_MODE 选项的定义与指南完全一致:当 fips 特性禁用时默认 disabled,启用 fips 特性时默认 non-strict,取值仅限 non-strictstrict

启动后可通过日志确认进入了批准模式——KC 提供器应带有 Approved Mode 说明:

FIPS1402Provider created: KC(BCFIPS version 2.0102 Approved Mode, FIPS-JVM: enabled)

Strict 模式下的 KeyStore 注意:默认 KeyStore/TrustStore 类型变为 BCFKS。若要使用其他类型,必须显式指定 --https-key-store-type(TrustStore 同理)。

6.1 Strict 模式的密码学限制清单

  • KeyStore 类型pkcs12 在 strict 模式下可能不可用(须改用 bcfks 等);且 jkspkcs12 在 strict 模式下不受 Keycloak 支持——典型场景如管理控制台为 OIDC/SAML 客户端导入或生成 KeyStore、为 Realm Keys 配置 java-keystore 提供器等;

  • 用户密码长度:必须 ≥ 14 个字符。Keycloak 默认使用基于 PBKDF2 的密码编码;BCFIPS 批准模式要求 PBKDF2 的口令至少 112 位(等效 14 字符)。若需允许更短密码,可将 SPI password-hashing 下提供器 pbkdf2-sha512 的属性 max-padding-length 设为 14,在校验该算法生成的哈希时做额外填充(对已存储的旧密码向后兼容):

    --spi-password-hashing--pbkdf2-sha512--max-padding-length=14
    

    该选项不破坏 FIPS 合规。更长的口令本身也是良好实践——现代浏览器自动生成的口令即超过 14 字符。若不想使用该选项,可在 Realm 密码策略中直接要求密码 ≥ 14 字符。

    补充:若从 Keycloak 24 之前版本迁移,或显式覆盖过默认哈希算法,部分用户可能使用旧算法 pbkdf2-sha256。为保证这些(可能短于 14 字符的)密码在 BCFIPS 批准模式下仍可登录,可考虑追加 --spi-password-hashing--pbkdf2-sha256--max-padding-length=14

  • RSA 密钥位数:1024 位 RSA 不可用,最小为 2048 位。适用于 Realm 自身的密钥(管理控制台 Keys 选项卡的 Realm Keys),也包括客户端密钥与 IDP 密钥;

  • HMAC 密钥长度HMAC-SHA-XXX 密钥至少 112 位(等效 14 字符)。例如使用 Signed JWT with Client Secret(OIDC 记法 client-secret-jwt)认证的 OIDC 客户端,其 client secret 应至少 14 字符;建议使用 Keycloak 服务端生成的 client secret,它天然满足该要求;

  • JWE 算法:bc-fips 1.0.2.4 处理了 PKCS#1 v1.5 RSA 加密过渡期结束的问题,因此 strict 模式默认不允许 JWE 算法 RSA1_5(BC 暂提供系统属性 -Dorg.bouncycastle.rsa.allow_pkcs15_enc=true 作为向后兼容开关)。RSA-OAEPRSA-OAEP-256 仍可用。源码侧对应实现为 FIPSRsaKeyEncryptionJWEAlgorithmProvider,在 FIPS1402Provider.java 中以 FipsRSA.WRAP_PKCS1v1_5 / WRAP_OAEP / WRAP_OAEP + SHA-256 三种变体注册。

7. 其他系统级限制:SAML 与 Kerberos 的安全提供器

  • SAML:需要 XMLDSig 安全提供器。FIPS 启用的 RHEL 9 + OpenJDK 21(以及较新的 OpenJDK 17)中,java.security 默认可能已启用 XMLDSig;但较老的 OpenJDK 17 可能默认未启用,SAML 将无法工作。可手动在 JAVA_HOME/conf/security/java.security 的 FIPS 提供器列表中追加(注意序号要顺延,如已有 6 个 fips.provider.N 条目则写 7):

    fips.provider.7=XMLDSig
    

    该提供器本身是 FIPS 合规的,OpenJDK 21 及更新版本的 OpenJDK 17 已默认加入。若不想改动 JDK 内的 java.security 文件,可创建仅含上述单条属性的自定义安全文件(如 kc.java.security),再以 JVM 参数挂载:

    -Djava.security.properties=/location/to/your/file/kc.java.security
    
  • Kerberos/SPNEGOSunJGSS 安全提供器尚未完全 FIPS 合规,若追求 FIPS 合规不建议加入提供器列表。在 FIPS 平台运行且 SunJGSS 不可用时,Keycloak 的 KERBEROS 特性默认禁用。

8. 在 FIPS 主机上运行 CLI(kcadm / kcreg)

客户端注册 CLI(kcreg.sh/kcreg.bat)与管理 CLI(kcadm.sh/kcadm.bat)同样需要改用 BCFIPS 依赖而非普通 BouncyCastle。做法是把对应 jar 复制进 CLI 的 lib 目录即可——CLI 检测到 BCFIPS jar 存在时会自动优先使用:

cp $KEYCLOAK_HOME/providers/bc-fips-*.jar $KEYCLOAK_HOME/bin/client/lib/
cp $KEYCLOAK_HOME/providers/bctls-fips-*.jar $KEYCLOAK_HOME/bin/client/lib/
cp $KEYCLOAK_HOME/providers/bcutil-fips-*.jar $KEYCLOAK_HOME/bin/client/lib/

注意:若 CLI 需要操作 BCFKS 类型的 TrustStore/KeyStore,由于它不是 Java 默认 keystore 类型,可能在默认配置下出现问题。可在执行 kcadm/kcreg 前通过 JVM 安全属性将其设为默认(Unix 示例):

echo "keystore.type=bcfks
fips.keystore.type=bcfks" > /tmp/kcadm.java.security
export KC_OPTS="-Djava.security.properties=/tmp/kcadm.java.security"

9. 容器化部署 Keycloak FIPS 模式

要在容器中运行 FIPS 模式的 Keycloak,宿主机必须处于 FIPS 模式,容器会从宿主机"继承"FIPS 模式(详见 RHEL 安全加固文档中"在容器中启用 FIPS 模式"一节)。Keycloak 容器镜像从 FIPS 宿主机执行时会自动进入 FIPS 模式,但仍需确保容器内使用 BCFIPS jar(而非普通 BC jar)以及正确的启动参数。最佳实践是自行构建镜像(参考 containers.adoc 的构建方式)并做 FIPS 定制。

在构建目录下创建 files 子目录,放入:

  • 上述 BCFIPS jar 文件;
  • 自定义 KeyStore 文件,例如命名 keycloak-fips.keystore.bcfks
  • 安全文件 kc.java.security(含 SAML 所需 XMLDSig 提供器;OpenJDK 21 或更新版 OpenJDK 17 可不提供)。

然后创建如下 Containerfile{containerlabel} 为镜像版本标签):

FROM quay.io/keycloak/keycloak:{containerlabel} as builder

ADD files /tmp/files/

WORKDIR /opt/keycloak
RUN cp /tmp/files/*.jar /opt/keycloak/providers/
RUN cp /tmp/files/keycloak-fips.keystore.* /opt/keycloak/conf/server.keystore
RUN cp /tmp/files/kc.java.security /opt/keycloak/conf/

RUN /opt/keycloak/bin/kc.sh build --features=fips --fips-mode=strict

FROM quay.io/keycloak/keycloak:{containerlabel}
COPY --from=builder /opt/keycloak/ /opt/keycloak/

ENTRYPOINT ["/opt/keycloak/bin/kc.sh"]

构建出优化后的 FIPS 镜像后,按容器启动指南的方式启动,并在启动参数中携带上文所述选项。

10. 从非 FIPS 环境迁移到 FIPS 环境

此前在非 FIPS 环境运行 Keycloak 的数据可以整体迁移到 FIPS 环境,但需满足前述各节限制,重点包括:

  • 密码哈希算法(关键):自 Keycloak 25 起默认哈希算法为 argon2,而 argon2 不受 FIPS 140-2 支持——用 argon2 存储密码的用户切换到 FIPS 环境后无法登录。若计划迁移,建议尽早(在任何用户创建之前)为 Realm 设置密码策略并覆盖默认算法为 FIPS 合规的 pbkdf2-sha512;若用户已是 argon2 密码,迁移后引导用户通过"忘记密码"/密码重置邮件重置密码;
  • KeyStore 类型:确保所有依赖 KeyStore 的功能仅使用当前模式(strict / non-strict)支持的类型;
  • Kerberos:Kerberos 认证可能不可用。迁移到 FIPS 环境时,Kerberos 认证器会被自动置为 DISABLED;建议先移除 Realm 中的 Kerberos 用户存储提供器,并关闭 LDAP 提供器的 Kerberos 相关功能。

切换到 FIPS strict 模式前还需额外确认:

  • 所有依赖密钥的功能(Realm 密钥、客户端密钥等)均使用 ≥ 2048 位的 RSA 密钥;
  • 依赖 Signed JWT with Client Secret 的客户端,其 secret 至少 14 字符(理想是服务端生成的 secret);
  • 口令长度限制如前所述:短口令用户需启动时设置 PBKDF2 提供器的 max-padding-length=14,或要求所有用户首次在新环境登录时重置密码(例如通过"忘记密码"链接)。

11. 在非 FIPS 系统上运行 FIPS 模式(不受官方支持)

Keycloak 在 FIPS 启用的 RHEL 8 系统与 ubi8 镜像上经过支持并测试,同样支持 RHEL 9(及 ubi9 镜像)。在非 RHEL 兼容平台或非 FIPS 启用的平台上运行时,FIPS 合规性无法严格保证,也不在官方支持范围内。

若受限只能在此类系统上运行,至少可以更新 java.security 文件中配置的安全提供器:这并不构成 FIPS 合规,但配置会更接近合规状态。做法是提供仅覆盖安全提供器列表的自定义安全文件(方法同第 7 节),推荐提供器清单可参考 OpenJDK 21 的 RHEL FIPS 配置文档。启动 Keycloak 后检查服务器日志确认使用了正确的安全提供器——需按第 5 节的启动命令为 crypto 相关包开启 TRACE 日志。

12. 在 FIPS 环境中运行单元/集成测试

docs/fips.md 指到的测试说明位于 HOW-TO-RUN.md 的 "FIPS 140-2 testing" 一节(约 L616–L664),对应测试代码集中在 crypto/fips1402 模块(含 pom.xmlsrc/test 下的 FIPS1402*Test 系列测试类)。

单元测试

mvn clean install -f crypto/fips1402

以 BouncyCastle 批准模式(对所用密码算法更严格)运行单元测试:

mvn clean install -f crypto/fips1402 -Dorg.bouncycastle.fips.approved_only=true

集成测试

在启用 FIPS 的平台(FIPS 启用的 OpenJDK 21)上,对启用了 FIPS 140-2 集成的 Quarkus 版 Keycloak 服务器运行测试:

mvn -B -f testsuite/integration-arquillian/pom.xml \
  clean install \
  -Pauth-server-quarkus,auth-server-fips140-2 \
  -Dcom.redhat.fips=false

-Dcom.redhat.fips=false 使测试套本身运行在 FIPS 禁用的 JVM 中;关键是 Keycloak 服务器自身运行在 FIPS 启用的 JVM 上。服务器启动日志中应出现类似:

DEBUG [org.keycloak.common.crypto.CryptoIntegration] (main) Using the crypto provider: org.keycloak.crypto.fips.FIPS1402Provider
TRACE [org.keycloak.common.crypto.CryptoIntegration] (main) Java security providers: [
 KC(BCFIPS version 1.000203, FIPS-JVM: enabled) version 1.0 - class org.keycloak.crypto.fips.KeycloakFipsSecurityProvider,
 BCFIPS version 1.000203 - class org.bouncycastle.jcajce.provider.BouncyCastleFipsProvider,
 BCJSSE version 1.001202 - class org.bouncycastle.jsse.provider.BouncyCastleJsseProvider,
]

BCFIPS 批准模式下的集成测试

追加以下属性:

-Dauth.server.fips.mode=strict \
-Dauth.server.supported.keystore.types=BCFKS \
-Dauth.server.keystore.type=bcfks \
-Dauth.server.supported.rsa.key.sizes=2048,3072,4096

日志中 KeycloakFipsSecurityProvider 应提及 "Approved mode",例如:

KC(BCFIPS version 1.000203 Approved Mode, FIPS-JVM: enabled) version 1.0 - class org.keycloak.crypto.fips.KeycloakFipsSecurityProvider

另外需注意:HOW-TO-RUN.md 在嵌入式(embedded)模式测试的已知限制中明确列出 "FIPS tests not working",因此 FIPS 测试需在全分布(full-distribution)方式下运行,而非嵌入式模式。

13. 小结:一份可核对的 FIPS 落地清单

环节 关键操作 / 参数
系统 RHEL/Fedora 安装时启用 FIPS;fips-mode-setup --check 验证;建议 OpenJDK 21
依赖 下载 bc-fips / bctls-fips / bcpkix-fips / bcutil-fips 放入 KEYCLOAK_HOME/providers
KeyStore 非 strict 用 pkcs12(keytool 直接生成,不能存对称密钥);strict 用 bcfks(需 BCFIPS 提供器 + securerandom.strongAlgorithms 安全文件)
启动 --features=fips(默认 non-strict);批准模式追加 --fips-mode=strict 并显式 --https-key-store-type=bcfks;调试开 crypto TRACE 日志
Strict 限制 密码 ≥14 字符(或 --spi-password-hashing--pbkdf2-sha512--max-padding-length=14);RSA ≥2048;HMAC 密钥 ≥112 位;JWE RSA1_5 默认禁用
SAML/Kerberos SAML 需 XMLDSig 提供器;SunJGSS 未完全合规,KERBEROS 特性默认禁用
CLI 复制 BCFIPS jar 到 bin/client/lib;bcfks 场景用 KC_OPTS 注入安全属性
容器 宿主机 FIPS 模式,镜像内放 BCFIPS jar + kc.sh build --features=fips --fips-mode=strict
迁移 argon2 密码无法登录,需重置或迁移前改为 pbkdf2-sha512;清理 Kerberos 组件;核对密钥与 secret 长度
测试 mvn clean install -f crypto/fips1402;集成测试 -Pauth-server-quarkus,auth-server-fips140-2 -Dcom.redhat.fips=false
登录后查看全文
热门项目推荐
相关项目推荐