Skip to content

Commit 58dc252

Browse files
committed
refactor(ssl): remove legacy Netty abstractions from SslX509Provider
The previous X.509 implementation relied on an overly complex and mostly unused inheritance tree (JdkSslServerContext -> JdkSslContext -> SslContext) originally ported from Netty. This introduced dead code, including redundant cipher and protocol calculations that were never applied to the SSLEngine. This commit simplifies the architecture to match the clean, native-Java approach used in SslPkcs12Provider: - Deleted SslContext, JdkSslContext, and JdkSslServerContext. - Inlined the essential KeyStore, TrustManager, and KeySpec builder logic directly into SslX509Provider. - Retained PemReader as a standalone utility for PEM-to-DER parsing. This removes significant technical debt, eliminates dead branches, and unifies the SSL instantiation patterns across the framework. fixes #3932
1 parent 253ec9b commit 58dc252

7 files changed

Lines changed: 314 additions & 472 deletions

File tree

jooby/src/main/java/io/jooby/internal/x509/PemReader.java renamed to jooby/src/main/java/io/jooby/internal/PemReader.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* Apache License Version 2.0 https://jooby.io/LICENSE.txt
44
* Copyright 2014 Edgar Espina
55
*/
6-
package io.jooby.internal.x509;
6+
package io.jooby.internal;
77

88
import java.io.IOException;
99
import java.io.InputStream;
@@ -17,8 +17,6 @@
1717
import java.util.regex.Matcher;
1818
import java.util.regex.Pattern;
1919

20-
import io.jooby.internal.IOUtils;
21-
2220
/**
2321
* Reads a PEM file and converts it into a list of DERs.
2422
*

jooby/src/main/java/io/jooby/internal/SslX509Provider.java

Lines changed: 148 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,44 @@
55
*/
66
package io.jooby.internal;
77

8+
import java.io.ByteArrayInputStream;
9+
import java.io.IOException;
10+
import java.io.InputStream;
11+
import java.nio.ByteBuffer;
12+
import java.security.InvalidAlgorithmParameterException;
13+
import java.security.InvalidKeyException;
14+
import java.security.KeyException;
15+
import java.security.KeyFactory;
16+
import java.security.KeyStore;
17+
import java.security.KeyStoreException;
18+
import java.security.NoSuchAlgorithmException;
19+
import java.security.PrivateKey;
20+
import java.security.cert.Certificate;
21+
import java.security.cert.CertificateException;
22+
import java.security.cert.CertificateFactory;
23+
import java.security.cert.X509Certificate;
24+
import java.security.spec.InvalidKeySpecException;
25+
import java.security.spec.PKCS8EncodedKeySpec;
26+
import java.util.ArrayList;
27+
import java.util.List;
28+
29+
import javax.crypto.Cipher;
30+
import javax.crypto.EncryptedPrivateKeyInfo;
31+
import javax.crypto.NoSuchPaddingException;
32+
import javax.crypto.SecretKey;
33+
import javax.crypto.SecretKeyFactory;
34+
import javax.crypto.spec.PBEKeySpec;
35+
import javax.net.ssl.KeyManagerFactory;
836
import javax.net.ssl.SSLContext;
37+
import javax.net.ssl.TrustManager;
38+
import javax.net.ssl.TrustManagerFactory;
39+
import javax.security.auth.x500.X500Principal;
940

1041
import io.jooby.SneakyThrows;
1142
import io.jooby.SslOptions;
12-
import io.jooby.internal.x509.SslContext;
1343

1444
public class SslX509Provider implements SslContextProvider {
45+
1546
@Override
1647
public boolean supports(String type) {
1748
return SslOptions.X509.equalsIgnoreCase(type);
@@ -20,19 +51,124 @@ public boolean supports(String type) {
2051
@Override
2152
public SSLContext create(ClassLoader loader, String provider, SslOptions options) {
2253
try (options) {
23-
SslContext sslContext =
24-
SslContext.newServerContextInternal(
25-
provider,
26-
options.getTrustCert(),
27-
options.getCert(),
28-
options.getPrivateKey(),
29-
null,
30-
0,
31-
0);
32-
33-
return sslContext.context();
54+
char[] password = toCharArray(options.getPassword());
55+
56+
var store = buildKeyStore(options.getCert(), options.getPrivateKey(), password);
57+
var kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
58+
kmf.init(store, password);
59+
60+
TrustManager[] tms = null;
61+
if (options.getTrustCert() != null) {
62+
TrustManagerFactory tmf = buildTrustManagerFactory(options.getTrustCert());
63+
tms = tmf.getTrustManagers();
64+
}
65+
66+
SSLContext context =
67+
provider == null
68+
? SSLContext.getInstance("TLS")
69+
: SSLContext.getInstance("TLS", provider);
70+
71+
context.init(kmf.getKeyManagers(), tms, null);
72+
73+
return context;
3474
} catch (Exception x) {
3575
throw SneakyThrows.propagate(x);
3676
}
3777
}
78+
79+
private KeyStore buildKeyStore(
80+
final InputStream certChainFile, final InputStream keyFile, final char[] keyPasswordChars)
81+
throws KeyStoreException,
82+
NoSuchAlgorithmException,
83+
NoSuchPaddingException,
84+
InvalidKeySpecException,
85+
InvalidAlgorithmParameterException,
86+
CertificateException,
87+
KeyException,
88+
IOException {
89+
90+
ByteBuffer encodedKeyBuf = PemReader.readPrivateKey(keyFile);
91+
byte[] encodedKey = encodedKeyBuf.array();
92+
93+
PKCS8EncodedKeySpec encodedKeySpec = generateKeySpec(keyPasswordChars, encodedKey);
94+
95+
PrivateKey key;
96+
try {
97+
key = KeyFactory.getInstance("RSA").generatePrivate(encodedKeySpec);
98+
} catch (InvalidKeySpecException ignore) {
99+
try {
100+
key = KeyFactory.getInstance("DSA").generatePrivate(encodedKeySpec);
101+
} catch (InvalidKeySpecException ignore2) {
102+
try {
103+
key = KeyFactory.getInstance("EC").generatePrivate(encodedKeySpec);
104+
} catch (InvalidKeySpecException e) {
105+
throw new InvalidKeySpecException("Neither RSA, DSA nor EC worked", e);
106+
}
107+
}
108+
}
109+
110+
CertificateFactory cf = CertificateFactory.getInstance("X.509");
111+
List<ByteBuffer> certs = PemReader.readCertificates(certChainFile);
112+
List<Certificate> certChain = new ArrayList<>(certs.size());
113+
114+
for (ByteBuffer buf : certs) {
115+
certChain.add(cf.generateCertificate(new ByteArrayInputStream(buf.array())));
116+
}
117+
118+
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
119+
ks.load(null, null);
120+
ks.setKeyEntry("key", key, keyPasswordChars, certChain.toArray(new Certificate[0]));
121+
return ks;
122+
}
123+
124+
private TrustManagerFactory buildTrustManagerFactory(final InputStream certChainFile)
125+
throws NoSuchAlgorithmException, CertificateException, KeyStoreException, IOException {
126+
127+
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
128+
ks.load(null, null);
129+
CertificateFactory cf = CertificateFactory.getInstance("X.509");
130+
131+
List<ByteBuffer> certs = PemReader.readCertificates(certChainFile);
132+
133+
for (ByteBuffer buf : certs) {
134+
X509Certificate cert =
135+
(X509Certificate) cf.generateCertificate(new ByteArrayInputStream(buf.array()));
136+
X500Principal principal = cert.getSubjectX500Principal();
137+
ks.setCertificateEntry(principal.getName("RFC2253"), cert);
138+
}
139+
140+
TrustManagerFactory trustManagerFactory =
141+
TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
142+
trustManagerFactory.init(ks);
143+
144+
return trustManagerFactory;
145+
}
146+
147+
private PKCS8EncodedKeySpec generateKeySpec(final char[] password, final byte[] key)
148+
throws IOException,
149+
NoSuchAlgorithmException,
150+
NoSuchPaddingException,
151+
InvalidKeySpecException,
152+
InvalidKeyException,
153+
InvalidAlgorithmParameterException {
154+
155+
if (password == null || password.length == 0) {
156+
return new PKCS8EncodedKeySpec(key);
157+
}
158+
159+
EncryptedPrivateKeyInfo encryptedPrivateKeyInfo = new EncryptedPrivateKeyInfo(key);
160+
SecretKeyFactory keyFactory =
161+
SecretKeyFactory.getInstance(encryptedPrivateKeyInfo.getAlgName());
162+
PBEKeySpec pbeKeySpec = new PBEKeySpec(password);
163+
SecretKey pbeKey = keyFactory.generateSecret(pbeKeySpec);
164+
165+
Cipher cipher = Cipher.getInstance(encryptedPrivateKeyInfo.getAlgName());
166+
cipher.init(Cipher.DECRYPT_MODE, pbeKey, encryptedPrivateKeyInfo.getAlgParameters());
167+
168+
return encryptedPrivateKeyInfo.getKeySpec(cipher);
169+
}
170+
171+
private char[] toCharArray(String password) {
172+
return password == null ? null : password.toCharArray();
173+
}
38174
}

jooby/src/main/java/io/jooby/internal/x509/JdkSslContext.java

Lines changed: 0 additions & 180 deletions
This file was deleted.

0 commit comments

Comments
 (0)