55 */
66package 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 ;
836import javax .net .ssl .SSLContext ;
37+ import javax .net .ssl .TrustManager ;
38+ import javax .net .ssl .TrustManagerFactory ;
39+ import javax .security .auth .x500 .X500Principal ;
940
1041import io .jooby .SneakyThrows ;
1142import io .jooby .SslOptions ;
12- import io .jooby .internal .x509 .SslContext ;
1343
1444public 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}
0 commit comments