Keycloak自签名证书SSL验证失败问题排查与解决咨询
同一自签名证书在Flask验证正常,但Keycloak请求时SSL验证失败的问题及解决
场景说明
Keycloak容器配置(16.1.1版本)
keycloak-m1: container_name: keycloak image: codingpuss/keycloak-silicon:16.1.1 restart: always environment: KEYCLOAK_USER: admin KEYCLOAK_PASSWORD: admin KEYCLOAK_EXTRA_ARGS="-Dkeycloak:profile:feature:upload_scripts=enabled": KC_HTTPS_CERTIFICATE_FILE: /opt/keycloak/localhostCert.pem KC_HTTPS_CERTIFICATE_KEY_FILE: /opt/keycloak/localhostKey.pem ports: - "8090:8080" - "8843:8443" networks: - myNetwork volumes: - ./keycloak-data:/tmp - ./certs-folder/local:/opt/keycloak
自签名证书生成命令
openssl req -x509 -out localhostCert.pem -keyout localhostKey.pem \ -newkey rsa:2048 -nodes -sha256 \ -subj '/CN=localhost' -extensions EXT -config <( \ printf "[dn]\nCN=localhost\n[req]\ndistinguished_name = dn\n[EXT]\nsubjectAltName=DNS:localhost\nkeyUsage=digitalSignature\nextendedKeyUsage=serverAuth")
请求Keycloak时的错误信息
requests.exceptions.SSLError: HTTPSConnectionPool(host='localhost', port=8843): Max retries exceeded with url: /auth/realms/test/protocol/openid-connect/token (Caused by SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: self signed certificate (_ssl.c:1002)')))
Python请求Keycloak的代码
if __name__ == '__main__': data = { constants.GRANT_TYPE_KEY: constants.OAUTH_PASSWORD_FLOW, constants.USERNAME_KEY: "test", constants.PASSWORD_KEY: "test", constants.CLIENT_ID_KEY: "test-client", constants.CLIENT_SECRET_KEY: "QX1hsAGiQADuegPcAXOn4mlwZUfcoksH" } session = Session() oauth_request = Request() oauth_request.method = "POST" oauth_request.url = "https://localhost:8843/auth/realms/test/protocol/openid-connect/token" oauth_request.data = data session.verify = "<path_to_the_certs_directory>" response = session.send(oauth_request.prepare()) access_token = response.json().get(constants.ACCESS_TOKEN_KEY, None) print(access_token)
对比:Flask使用同一证书的代码(验证正常)
from flask import Flask, request, jsonify import ssl app = Flask(__name__) context = ssl.SSLContext() context.load_cert_chain('localhostCert.pem', 'localhostKey.pem') @app.route('/test/', methods=['GET']) def get_test(): return jsonify(test="test") if __name__ == '__main__': app.run(host='0.0.0.0', port=5001, debug=True, ssl_context=context)
问题
为何同一证书在Flask中可正常验证,在Keycloak中却失败?如何解决该问题?
解决方案
原因分析
- Keycloak证书加载逻辑差异:Keycloak基于WildFly运行,对证书的存放格式、权限要求更严格,且可能未正确读取挂载的证书文件;而Flask直接加载证书文件,不存在额外的信任链配置要求。
- requests验证细节问题:当
session.verify指向目录时,依赖c_rehash生成的哈希符号链接匹配证书,若Keycloak返回的证书链与本地存储的证书不匹配,就会验证失败。
解决步骤
1. 检查并修复容器内证书权限
进入Keycloak容器查看证书权限:
docker exec -it keycloak ls -l /opt/keycloak/localhostCert.pem /opt/keycloak/localhostKey.pem
确保文件拥有者为jboss(Keycloak默认运行用户),否则修改权限:
docker exec -it keycloak chown jboss:jboss /opt/keycloak/localhostCert.pem /opt/keycloak/localhostKey.pem
修改后重启容器:
docker restart keycloak
2. 直接指定证书文件而非目录
修改Python请求代码,将session.verify指向证书文件路径,避免目录哈希匹配问题:
session.verify = "<path_to_localhostCert.pem>"
3. 修正Keycloak的HTTPS环境变量
Keycloak 16.x版本可能不支持KC_HTTPS_*格式的环境变量,改用旧版变量配置:
KEYCLOAK_HTTPS_CERTIFICATE_FILE: /opt/keycloak/localhostCert.pem KEYCLOAK_HTTPS_CERTIFICATE_KEY_FILE: /opt/keycloak/localhostKey.pem
更新配置后重启容器。
4. 将证书导入Keycloak的Java信任存储(可选)
若上述方法无效,将自签名证书添加到Java信任存储(Keycloak依赖Java运行):
# 进入容器 docker exec -it keycloak bash # 导入证书,默认信任存储密码为changeit keytool -importcert -file /opt/keycloak/localhostCert.pem -alias localhost -keystore /opt/keycloak/jre/lib/security/cacerts -storepass changeit -noprompt
完成后重启容器。
内容的提问来源于stack exchange,提问作者Abdel Ysf
相关产品推荐
相关产品推荐

