我正在寻找将 Keycloak 领域密钥存储在 HSM 中并在启动时加载到内存中的可能性。
目前我可以看到标准领域密钥“不受保护”地存储在 Keycloak 集成数据库中。我还发现了 2016 年 [1] 的讨论,建议实施加密/签名 SPI,以便在 HSM 内部进行加密/签名。据我所知,这也已实现[2]。
但是,对于使用签名访问令牌登录的情况,每次颁发令牌时都需要与 HSM 进行通信,对于更多数量的应用程序和用户来说,这会对登录过程的持续时间造成严重影响。
如果我们想规避这个问题,我们可以有一个启动过程,在启动时从 HSM 加载领域密钥,然后将它们保存在内存中。但我找不到任何有关此类功能存在或考虑的事件的信息。这个方向有什么特色吗?
[1] https://lists.jboss.org/pipermail/keycloak-dev/2016-February/006610.html
[2] 例如https://www.keycloak.org/docs-api/22.0.1/javadocs/org/keycloak/crypto/SignatureSpi.html
如果您要将加密/签名委托给 HSM 本身,那么确实,正如您自己所指出的,这会增加开销并且可能并不总是可行。
为了避免这种情况,可以在 Keycloak 第一次尝试使用 HSM 时加载密钥并将其保留在内存中以供将来登录。为此,您可以通过实现 keyprovider 来利用 Keycloak 的 key SPI。然后,如果您将其添加到 Keycloak 管理 UI 中的领域(领域设置 -> 密钥 -> 提供者选项卡),Keycloak 将在需要密钥时调用
KeyProvider#getKeysStream
。此时,您可以从任何地方加载密钥并执行您需要的任何缓存和密钥轮换策略。
自定义密钥提供程序如下所示:
import org.keycloak.component.ComponentModel;
import org.keycloak.crypto.KeyWrapper;
import org.keycloak.keys.KeyProvider;
import java.util.Collection;
import java.util.Collections;
import java.util.stream.Stream;
public class HsmKeyProvider implements KeyProvider {
private Collection<KeyWrapper> keys;
public HsmKeyProvider(ComponentModel model) {
if (model.hasNote(KeyWrapper.class.getName())) {
keys = model.getNote(KeyWrapper.class.getName());
} else {
keys = loadKeys();
model.setNote(KeyWrapper.class.getName(), keys);
}
}
@Override
public Stream<KeyWrapper> getKeysStream() {
return keys.stream();
}
protected Collection<KeyWrapper> loadKeys() {
// TODO: Load keys from HSM, wrap into KeyWrapper
return Collections.emptyList();
}
}
供应商工厂:
import org.keycloak.component.ComponentModel;
import org.keycloak.keys.KeyProviderFactory;
import org.keycloak.models.KeycloakSession;
import org.keycloak.provider.ProviderConfigProperty;
import java.util.List;
public class HsmKeyProviderFactory implements KeyProviderFactory<HsmKeyProvider> {
@Override
public HsmKeyProvider create(KeycloakSession session, ComponentModel model) {
return new HsmKeyProvider(model);
}
@Override
public String getHelpText() {
return "HSM key";
}
@Override
public List<ProviderConfigProperty> getConfigProperties() {
// TODO: You may want to have some dynamic configuration
return null;
}
@Override
public String getId() {
return "my-hsm-key-example";
}
}
有关如何开发自定义提供程序并将其部署到 Keycloak 的详细信息,请参阅 Keycloak 的服务器开发人员指南文档页面。