Skip to content

Commit 47d1968

Browse files
authored
[ssl] simplify ssl plugin (#323)
1 parent 5155174 commit 47d1968

File tree

6 files changed

+76
-73
lines changed

6 files changed

+76
-73
lines changed

avaje-jex-ssl/src/main/java/io/avaje/jex/ssl/SslPlugin.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@
22

33
import java.util.function.Consumer;
44

5+
import javax.net.ssl.SSLContext;
6+
57
import io.avaje.jex.spi.JexPlugin;
8+
import io.avaje.jex.ssl.core.DSslPlugin;
69

710
/**
811
* Plugin that Configures Jex with SSL and mTLS.
@@ -30,4 +33,7 @@ public sealed interface SslPlugin extends JexPlugin permits DSslPlugin {
3033
static SslPlugin create(Consumer<SslConfig> consumer) {
3134
return new DSslPlugin(consumer);
3235
}
36+
37+
/** The configured SSLContext */
38+
SSLContext sslContext();
3339
}

avaje-jex-ssl/src/main/java/io/avaje/jex/ssl/DSslConfig.java renamed to avaje-jex-ssl/src/main/java/io/avaje/jex/ssl/core/DSslConfig.java

Lines changed: 17 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package io.avaje.jex.ssl;
1+
package io.avaje.jex.ssl.core;
22

33
import java.io.ByteArrayInputStream;
44
import java.io.IOException;
@@ -11,25 +11,19 @@
1111
import java.security.Provider;
1212
import java.util.function.Consumer;
1313

14-
import javax.net.ssl.X509ExtendedKeyManager;
15-
1614
import io.avaje.jex.spi.ClassResourceLoader;
15+
import io.avaje.jex.ssl.SslConfig;
16+
import io.avaje.jex.ssl.SslConfigException;
17+
import io.avaje.jex.ssl.TrustConfig;
1718

1819
final class DSslConfig implements SslConfig {
1920

2021
private static final String MULTIPLE_IDENTITY =
2122
"Both the certificate and key must be provided using the same method";
2223

23-
enum LoadedIdentity {
24-
KEY_MANAGER,
25-
KEY_STORE,
26-
NONE
27-
}
28-
2924
private String identityPassword;
30-
private X509ExtendedKeyManager keyManager = null;
3125
private KeyStore keyStore = null;
32-
private LoadedIdentity loadedIdentity = LoadedIdentity.NONE;
26+
private boolean loadedIdentity;
3327
private Provider securityProvider = null;
3428
private DTrustConfig trustConfig = null;
3529
private ClassResourceLoader resourceLoader = ClassResourceLoader.fromClass(SslConfig.class);
@@ -38,10 +32,6 @@ String identityPassword() {
3832
return identityPassword;
3933
}
4034

41-
X509ExtendedKeyManager keyManager() {
42-
return keyManager;
43-
}
44-
4535
KeyStore keyStore() {
4636
return keyStore;
4737
}
@@ -82,7 +72,7 @@ private InputStream loadCP(String path) {
8272
return url;
8373
}
8474

85-
LoadedIdentity loadedIdentity() {
75+
boolean loadedIdentity() {
8676
return loadedIdentity;
8777
}
8878

@@ -96,11 +86,14 @@ public void pemFromInputStream(
9686
InputStream certificateInputStream, InputStream privateKeyInputStream, String password) {
9787
try {
9888
var keyContent = new String(privateKeyInputStream.readAllBytes());
99-
setKeyManager(
89+
90+
setKeyStore(
10091
KeyStoreUtil.loadIdentityFromPem(
10192
certificateInputStream,
10293
keyContent,
10394
password != null ? password.toCharArray() : null));
95+
this.identityPassword = identityPassword != null ? identityPassword : "";
96+
10497
} catch (IOException e) {
10598
throw new SslConfigException("Failed to read PEM content from streams", e);
10699
}
@@ -113,21 +106,23 @@ public void pemFromPath(String certificatePath, String privateKeyPath, String pa
113106
var keyPath = Paths.get(privateKeyPath);
114107

115108
var keyContent = Files.readString(keyPath);
116-
setKeyManager(
109+
setKeyStore(
117110
KeyStoreUtil.loadIdentityFromPem(
118111
certContent, keyContent, password != null ? password.toCharArray() : null));
112+
this.identityPassword = identityPassword != null ? identityPassword : "";
119113
} catch (IOException e) {
120114
throw new SslConfigException("Failed to read PEM files", e);
121115
}
122116
}
123117

124118
@Override
125119
public void pemFromString(String certificateString, String privateKeyString, String password) {
126-
setKeyManager(
120+
setKeyStore(
127121
KeyStoreUtil.loadIdentityFromPem(
128122
new ByteArrayInputStream(certificateString.getBytes(StandardCharsets.UTF_8)),
129123
privateKeyString,
130124
password != null ? password.toCharArray() : null));
125+
this.identityPassword = identityPassword != null ? identityPassword : "";
131126
}
132127

133128
@Override
@@ -145,22 +140,12 @@ public void securityProvider(Provider securityProvider) {
145140
this.securityProvider = securityProvider;
146141
}
147142

148-
private void setKeyManager(X509ExtendedKeyManager keyManager) {
149-
if (loadedIdentity != LoadedIdentity.NONE) {
150-
throw new SslConfigException(MULTIPLE_IDENTITY);
151-
}
152-
if (keyManager != null) {
153-
loadedIdentity = LoadedIdentity.KEY_MANAGER;
154-
this.keyManager = keyManager;
155-
}
156-
}
157-
158143
private void setKeyStore(KeyStore keyStore) {
159-
if (loadedIdentity != LoadedIdentity.NONE) {
144+
if (loadedIdentity) {
160145
throw new SslConfigException(MULTIPLE_IDENTITY);
161146
}
162147
if (keyStore != null) {
163-
loadedIdentity = LoadedIdentity.KEY_STORE;
148+
loadedIdentity = true;
164149
this.keyStore = keyStore;
165150
}
166151
}
@@ -174,4 +159,4 @@ public void withTrustConfig(Consumer<TrustConfig> trustConfigConsumer) {
174159
trustConfig = trustConfig == null ? new DTrustConfig(this::loadCP) : trustConfig;
175160
trustConfigConsumer.accept(trustConfig);
176161
}
177-
}
162+
}
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,20 @@
1-
package io.avaje.jex.ssl;
1+
package io.avaje.jex.ssl.core;
22

33
import java.util.function.Consumer;
44

5+
import javax.net.ssl.SSLContext;
6+
57
import com.sun.net.httpserver.HttpsConfigurator;
68

79
import io.avaje.jex.Jex;
10+
import io.avaje.jex.ssl.SslConfig;
11+
import io.avaje.jex.ssl.SslPlugin;
812

9-
final class DSslPlugin implements SslPlugin {
13+
public final class DSslPlugin implements SslPlugin {
1014

1115
private final HttpsConfigurator sslConfigurator;
1216

13-
DSslPlugin(Consumer<SslConfig> consumer) {
17+
public DSslPlugin(Consumer<SslConfig> consumer) {
1418
final var config = new DSslConfig();
1519
consumer.accept(config);
1620
this.sslConfigurator = SSLConfigurator.create(config);
@@ -20,4 +24,9 @@ final class DSslPlugin implements SslPlugin {
2024
public void apply(Jex jex) {
2125
jex.config().httpsConfig(sslConfigurator);
2226
}
27+
28+
@Override
29+
public SSLContext sslContext() {
30+
return sslConfigurator.getSSLContext();
31+
}
2332
}

avaje-jex-ssl/src/main/java/io/avaje/jex/ssl/DTrustConfig.java renamed to avaje-jex-ssl/src/main/java/io/avaje/jex/ssl/core/DTrustConfig.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package io.avaje.jex.ssl;
1+
package io.avaje.jex.ssl.core;
22

33
import java.io.InputStream;
44
import java.nio.file.Files;
@@ -9,6 +9,9 @@
99
import java.util.List;
1010
import java.util.function.Function;
1111

12+
import io.avaje.jex.ssl.SslConfigException;
13+
import io.avaje.jex.ssl.TrustConfig;
14+
1215
final class DTrustConfig implements TrustConfig {
1316

1417
private final List<Certificate> certificates = new ArrayList<>();

avaje-jex-ssl/src/main/java/io/avaje/jex/ssl/KeyStoreUtil.java renamed to avaje-jex-ssl/src/main/java/io/avaje/jex/ssl/core/KeyStoreUtil.java

Lines changed: 7 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package io.avaje.jex.ssl;
1+
package io.avaje.jex.ssl.core;
22

33
import static java.util.Base64.getDecoder;
44

@@ -11,7 +11,6 @@
1111
import java.security.KeyStoreException;
1212
import java.security.NoSuchAlgorithmException;
1313
import java.security.PrivateKey;
14-
import java.security.UnrecoverableKeyException;
1514
import java.security.cert.Certificate;
1615
import java.security.cert.CertificateException;
1716
import java.security.cert.CertificateFactory;
@@ -22,8 +21,7 @@
2221
import java.util.List;
2322
import java.util.regex.Pattern;
2423

25-
import javax.net.ssl.KeyManagerFactory;
26-
import javax.net.ssl.X509ExtendedKeyManager;
24+
import io.avaje.jex.ssl.SslConfigException;
2725

2826
final class KeyStoreUtil {
2927
private static final Pattern CERT_PATTERN =
@@ -59,7 +57,8 @@ static KeyStore loadKeyStore(InputStream inputStream, char[] password) {
5957
return keyStore;
6058
}
6159

62-
throw new SslConfigException("Unable to load KeyStore - format not recognized or invalid password");
60+
throw new SslConfigException(
61+
"Unable to load KeyStore - format not recognized or invalid password");
6362
}
6463

6564
private static KeyStore tryLoadKeyStore(byte[] data, String type, char[] password) {
@@ -73,7 +72,7 @@ private static KeyStore tryLoadKeyStore(byte[] data, String type, char[] passwor
7372
}
7473
}
7574

76-
static X509ExtendedKeyManager loadIdentityFromPem(
75+
static KeyStore loadIdentityFromPem(
7776
InputStream certificateInputStream, String privateKeyContent, char[] password) {
7877
try {
7978
var certificates = parseCertificates(certificateInputStream);
@@ -86,22 +85,8 @@ static X509ExtendedKeyManager loadIdentityFromPem(
8685
var keyPassword = password != null ? password : new char[0];
8786
keyStore.setKeyEntry(alias, privateKey, keyPassword, certChain);
8887

89-
var kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
90-
kmf.init(keyStore, keyPassword);
91-
92-
for (var km : kmf.getKeyManagers()) {
93-
if (km instanceof X509ExtendedKeyManager m) {
94-
return m;
95-
}
96-
}
97-
98-
throw new SslConfigException("No X509ExtendedKeyManager found");
99-
100-
} catch (KeyStoreException
101-
| NoSuchAlgorithmException
102-
| UnrecoverableKeyException
103-
| CertificateException
104-
| IOException e) {
88+
return keyStore;
89+
} catch (KeyStoreException | NoSuchAlgorithmException | CertificateException | IOException e) {
10590
throw new SslConfigException("Failed to create KeyManager from PEM content", e);
10691
}
10792
}

avaje-jex-ssl/src/main/java/io/avaje/jex/ssl/SSLConfigurator.java renamed to avaje-jex-ssl/src/main/java/io/avaje/jex/ssl/core/SSLConfigurator.java

Lines changed: 30 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package io.avaje.jex.ssl;
1+
package io.avaje.jex.ssl.core;
22

33
import java.security.KeyStore;
44
import java.security.KeyStoreException;
@@ -17,16 +17,22 @@
1717
import com.sun.net.httpserver.HttpsConfigurator;
1818
import com.sun.net.httpserver.HttpsParameters;
1919

20-
final class SSLConfigurator extends HttpsConfigurator {
20+
import io.avaje.jex.ssl.SslConfigException;
21+
22+
public final class SSLConfigurator extends HttpsConfigurator {
2123

2224
private static final String SSL_PROTOCOL = "TLSv1.3";
2325
private static final String KEY_MANAGER_ALGORITHM = "SunX509";
2426
private static final String TRUST_MANAGER_ALGORITHM = "PKIX";
2527

2628
private final boolean clientAuth;
29+
private final KeyStore keyStore;
30+
private final String password;
2731

28-
SSLConfigurator(SSLContext context, boolean clientAuth) {
32+
SSLConfigurator(SSLContext context, DSslConfig sslConfig, boolean clientAuth) {
2933
super(context);
34+
this.keyStore = sslConfig.keyStore();
35+
this.password = sslConfig.identityPassword();
3036
this.clientAuth = clientAuth;
3137
}
3238

@@ -42,10 +48,8 @@ static SSLConfigurator create(DSslConfig sslConfig) throws SslConfigException {
4248
var sslContext = createContext(sslConfig);
4349
var keyManagers = createKeyManagers(sslConfig);
4450
var trustManagers = createTrustManagers(sslConfig);
45-
4651
sslContext.init(keyManagers, trustManagers, new SecureRandom());
47-
48-
return new SSLConfigurator(sslContext, trustManagers != null);
52+
return new SSLConfigurator(sslContext, sslConfig, trustManagers !=null);
4953
} catch (Exception e) {
5054
throw new SslConfigException("Failed to build SSLContext", e);
5155
}
@@ -60,11 +64,10 @@ private static SSLContext createContext(DSslConfig sslConfig) throws NoSuchAlgor
6064

6165
private static KeyManager[] createKeyManagers(DSslConfig sslConfig) throws SslConfigException {
6266
try {
63-
return switch (sslConfig.loadedIdentity()) {
64-
case KEY_MANAGER -> new KeyManager[] {sslConfig.keyManager()};
65-
case KEY_STORE -> createKeyManagersFromKeyStore(sslConfig);
66-
default -> throw new IllegalStateException("No SSL Identity provided");
67-
};
67+
if (sslConfig.loadedIdentity()) {
68+
return createKeyManagersFromKeyStore(sslConfig);
69+
}
70+
throw new IllegalStateException("No SSL Identity provided");
6871
} catch (Exception e) {
6972
throw new SslConfigException("Failed to create key managers", e);
7073
}
@@ -79,14 +82,16 @@ private static KeyManager[] createKeyManagersFromKeyStore(DSslConfig sslConfig)
7982
return keyManagerFactory.getKeyManagers();
8083
}
8184

82-
private static KeyManagerFactory createKeyManagerFactory(DSslConfig sslConfig) throws NoSuchAlgorithmException {
85+
private static KeyManagerFactory createKeyManagerFactory(DSslConfig sslConfig)
86+
throws NoSuchAlgorithmException {
8387
if (sslConfig.securityProvider() != null) {
8488
return KeyManagerFactory.getInstance(KEY_MANAGER_ALGORITHM, sslConfig.securityProvider());
8589
}
8690
return KeyManagerFactory.getInstance(KEY_MANAGER_ALGORITHM);
8791
}
8892

89-
private static TrustManager[] createTrustManagers(DSslConfig sslConfig) throws SslConfigException {
93+
private static TrustManager[] createTrustManagers(DSslConfig sslConfig)
94+
throws SslConfigException {
9095
var trustConfig = sslConfig.trustConfig();
9196
if (trustConfig == null) {
9297
return null; // Use system default trust managers
@@ -109,14 +114,16 @@ private static TrustManager[] createTrustManagers(DSslConfig sslConfig) throws S
109114
}
110115
}
111116

112-
private static TrustManagerFactory createTrustManagerFactory(DSslConfig sslConfig) throws NoSuchAlgorithmException {
117+
private static TrustManagerFactory createTrustManagerFactory(DSslConfig sslConfig)
118+
throws NoSuchAlgorithmException {
113119
if (sslConfig.securityProvider() != null) {
114120
return TrustManagerFactory.getInstance(TRUST_MANAGER_ALGORITHM, sslConfig.securityProvider());
115121
}
116122
return TrustManagerFactory.getInstance(TRUST_MANAGER_ALGORITHM);
117123
}
118124

119-
private static KeyStore createCombinedTrustStore(List<KeyStore> trustStores, List<Certificate> certificates) throws Exception {
125+
private static KeyStore createCombinedTrustStore(
126+
List<KeyStore> trustStores, List<Certificate> certificates) throws Exception {
120127
var combinedTrustStore = KeyStore.getInstance(KeyStore.getDefaultType());
121128
combinedTrustStore.load(null, null);
122129

@@ -148,4 +155,12 @@ private static int addCertificatesFromKeyStore(
148155
}
149156
return aliasCounter;
150157
}
158+
159+
public KeyStore keyStore() {
160+
return keyStore;
161+
}
162+
163+
public String password() {
164+
return password;
165+
}
151166
}

0 commit comments

Comments
 (0)