【发布时间】:2020-01-29 01:06:17
【问题描述】:
我想在我的 spring boot (tomcat) 应用程序中动态添加/替换 SSL 证书,而无需重新启动它。我还有很长的路要走,但目前我遇到了 javax.crypto.BadPaddingException 并且不知道为什么。
这就是我想要做的。
首先,我定义自己的TomcatServletWebServerFactory,以便设置SslStoreProvider。
@Component
public class PathWatchingTomcatFactory extends TomcatServletWebServerFactory {
public PathWatchingTomcatFactory(PathWatchingSslStoreProvider pathWatchingSslStoreProvider) {
setSslStoreProvider(pathWatchingSslStoreProvider);
}
}
我的PathWatchingSslStoreProvider 提供了一个PathMatchingKeyStore。
@Component
public class PathWatchingSslStoreProvider implements SslStoreProvider {
private final PathWatchingKeyStore pathWatchingKeyStore;
public PathWatchingSslStoreProvider(PathWatchingKeyStore pathWatchingKeyStore) {
this.pathWatchingKeyStore = pathWatchingKeyStore;
}
@Override
public KeyStore getKeyStore() throws Exception {
return pathWatchingKeyStore;
}
}
PathWatchingKeyStore 似乎只需要为它提供服务提供者接口。
@Component
public class PathWatchingKeyStore extends KeyStore {
protected PathWatchingKeyStore(
PathWatchingKeyStoreSpi pathWatchingKeyStoreSpi,
DynamicProvider provider)
{
super(pathWatchingKeyStoreSpi, provider, KeyStore.getDefaultType());
initialize();
}
private void initialize() {
// Loading a keystore marks it internally as initialized and only
// initialized keystores work properly. Unfortunately
// nobody initializes this keystore. So we have to do it
// ourselves.
//
// Internally the keystore will delegate loading to the
// KeyStoreSpi, which, in our case is the PathWatchingKeyStoreSpi.
try {
load(null, null);
}
catch (Exception e) {
e.printStackTrace();
}
}
}
现在,在启动时,将加载密钥库。因为我提供了一个 SslStoreProvider,所以我的密钥库将由 SslStoreProviderUrlStreamHandlerFactory 加载,方法是请求我的 PathWatchingKeyStoreSpi 将其密钥库存储到 ByteArrayOutputStream 中,其内容最终复制到用于加载内部使用的密钥库的 InputStream 中。
在下面的代码 sn-p 中,您可以看到我如何尝试编写已经存在的密钥库的内容。现在完全没有动态。我只想看看 spring boot 应用程序是否在所有这些自定义类都到位的情况下启动。但事实并非如此。
@Component
public class PathWatchingKeyStoreSpi extends KeyStoreSpi {
private static final Logger LOGGER = LoggerFactory.getLogger(PathWatchingKeyStoreSpi.class);
private final Path keyStoreLocation;
public PathWatchingKeyStoreSpi(@Value("${server.ssl.key-store}") Path keyStoreLocation) {
super();
this.keyStoreLocation = keyStoreLocation;
}
@Override
public void engineStore(OutputStream stream, char[] password) throws IOException, NoSuchAlgorithmException, CertificateException {
try {
final KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(new FileInputStream(keyStoreLocation.toString()), "secret".toCharArray());
// Password must be empty because the SslConnectorCustomizer sets the keystore
// password used by the tomcat to the empty string if the SslStoreProvider
// returns a keystore. And because that is what we wanted to do in the first place,
// providing a dynamic keystore, this is what we have to do.
keyStore.store(stream, "".toCharArray());
}
catch (Exception e) {
e.printStackTrace();
}
}
}
我可以看到密钥库已加载,但一旦 SSLUtilBase 尝试从该存储中读取密钥,它就会抛出 BadPaddingException:
Caused by: javax.crypto.BadPaddingException: Given final block not properly padded. Such issues can arise if a bad key is used during decryption.
at java.base/com.sun.crypto.provider.CipherCore.unpad(CipherCore.java:975) ~[na:na]
at java.base/com.sun.crypto.provider.CipherCore.fillOutputBuffer(CipherCore.java:1056) ~[na:na]
at java.base/com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:853) ~[na:na]
at java.base/com.sun.crypto.provider.PKCS12PBECipherCore.implDoFinal(PKCS12PBECipherCore.java:408) ~[na:na]
at java.base/com.sun.crypto.provider.PKCS12PBECipherCore$PBEWithSHA1AndDESede.engineDoFinal(PKCS12PBECipherCore.java:440) ~[na:na]
at java.base/javax.crypto.Cipher.doFinal(Cipher.java:2202) ~[na:na]
at java.base/sun.security.pkcs12.PKCS12KeyStore.lambda$engineGetKey$0(PKCS12KeyStore.java:406) ~[na:na]
at java.base/sun.security.pkcs12.PKCS12KeyStore$RetryWithZero.run(PKCS12KeyStore.java:302) ~[na:na]
at java.base/sun.security.pkcs12.PKCS12KeyStore.engineGetKey(PKCS12KeyStore.java:400) ~[na:na]
... 25 common frames omitted
我创建了我在这里使用的静态密钥库,如下所示:
keytool -genkey -alias tomcat -keyalg RSA
首先,我要解决我的问题的方向是否有希望?还是我完全错了?我首先尝试只注入我自己的X509ExtendedKeyManager。我可以在调试器中看到它是密钥管理器,它被要求为传入请求提供证书,但尽管如此,tomcat 端点似乎是用密钥库初始化的,而没有涉及到管理器。
有没有人尝试过为使用 tomcat 作为servelt 容器的 Spring Boot 应用程序实现和使用动态密钥库/信任库?
欢迎任何帮助。 托比亚斯
【问题讨论】:
标签: spring-boot ssl tomcat keystore tomcat9