16
16
17
17
package org .springframework .boot .ssl .pem ;
18
18
19
+ import java .io .IOException ;
19
20
import java .security .KeyStore ;
20
21
import java .security .KeyStoreException ;
22
+ import java .security .NoSuchAlgorithmException ;
21
23
import java .security .PrivateKey ;
24
+ import java .security .cert .CertificateException ;
22
25
import java .security .cert .X509Certificate ;
23
26
24
27
import org .springframework .boot .ssl .SslStoreBundle ;
28
+ import org .springframework .boot .ssl .pem .KeyVerifier .Result ;
25
29
import org .springframework .util .Assert ;
26
30
import org .springframework .util .StringUtils ;
27
31
@@ -71,8 +75,24 @@ public PemSslStoreBundle(PemSslStoreDetails keyStoreDetails, PemSslStoreDetails
71
75
*/
72
76
public PemSslStoreBundle (PemSslStoreDetails keyStoreDetails , PemSslStoreDetails trustStoreDetails , String keyAlias ,
73
77
String keyPassword ) {
74
- this .keyStore = createKeyStore ("key" , keyStoreDetails , keyAlias , keyPassword );
75
- this .trustStore = createKeyStore ("trust" , trustStoreDetails , keyAlias , keyPassword );
78
+ this (keyStoreDetails , trustStoreDetails , keyAlias , keyPassword , false );
79
+ }
80
+
81
+ /**
82
+ * Create a new {@link PemSslStoreBundle} instance.
83
+ * @param keyStoreDetails the key store details
84
+ * @param trustStoreDetails the trust store details
85
+ * @param keyAlias the key alias to use or {@code null} to use a default alias
86
+ * @param keyPassword the password to use for the key
87
+ * @param verifyKeys whether to verify that the private key matches the public key
88
+ * @since 3.2.0
89
+ */
90
+ public PemSslStoreBundle (PemSslStoreDetails keyStoreDetails , PemSslStoreDetails trustStoreDetails , String keyAlias ,
91
+ String keyPassword , boolean verifyKeys ) {
92
+ this .keyStore = createKeyStore ("key" , keyStoreDetails , (keyAlias != null ) ? keyAlias : DEFAULT_KEY_ALIAS ,
93
+ keyPassword , verifyKeys );
94
+ this .trustStore = createKeyStore ("trust" , trustStoreDetails , (keyAlias != null ) ? keyAlias : DEFAULT_KEY_ALIAS ,
95
+ keyPassword , verifyKeys );
76
96
}
77
97
78
98
@ Override
@@ -90,38 +110,74 @@ public KeyStore getTrustStore() {
90
110
return this .trustStore ;
91
111
}
92
112
93
- private KeyStore createKeyStore (String name , PemSslStoreDetails details , String alias , String keyPassword ) {
113
+ private static KeyStore createKeyStore (String name , PemSslStoreDetails details , String keyAlias , String keyPassword ,
114
+ boolean verifyKeys ) {
94
115
if (details == null || details .isEmpty ()) {
95
116
return null ;
96
117
}
97
118
try {
98
119
Assert .notNull (details .certificate (), "Certificate content must not be null" );
99
- String type = (!StringUtils .hasText (details .type ())) ? KeyStore .getDefaultType () : details .type ();
100
- KeyStore store = KeyStore .getInstance (type );
101
- store .load (null );
102
- String certificateContent = PemContent .load (details .certificate ());
103
- String privateKeyContent = PemContent .load (details .privateKey ());
104
- X509Certificate [] certificates = PemCertificateParser .parse (certificateContent );
105
- PrivateKey privateKey = PemPrivateKeyParser .parse (privateKeyContent , details .privateKeyPassword ());
106
- addCertificates (store , certificates , privateKey , (alias != null ) ? alias : DEFAULT_KEY_ALIAS , keyPassword );
120
+ KeyStore store = createKeyStore (details );
121
+ X509Certificate [] certificates = loadCertificates (details );
122
+ PrivateKey privateKey = loadPrivateKey (details );
123
+ if (privateKey != null ) {
124
+ if (verifyKeys ) {
125
+ verifyKeys (privateKey , certificates );
126
+ }
127
+ addPrivateKey (store , privateKey , keyAlias , keyPassword , certificates );
128
+ }
129
+ else {
130
+ addCertificates (store , certificates , keyAlias );
131
+ }
107
132
return store ;
108
133
}
109
134
catch (Exception ex ) {
110
135
throw new IllegalStateException ("Unable to create %s store: %s" .formatted (name , ex .getMessage ()), ex );
111
136
}
112
137
}
113
138
114
- private void addCertificates (KeyStore keyStore , X509Certificate [] certificates , PrivateKey privateKey , String alias ,
115
- String keyPassword ) throws KeyStoreException {
116
- if (privateKey != null ) {
117
- keyStore .setKeyEntry (alias , privateKey , (keyPassword != null ) ? keyPassword .toCharArray () : null ,
118
- certificates );
119
- }
120
- else {
121
- for (int index = 0 ; index < certificates .length ; index ++) {
122
- keyStore .setCertificateEntry (alias + "-" + index , certificates [index ]);
139
+ private static void verifyKeys (PrivateKey privateKey , X509Certificate [] certificates ) {
140
+ KeyVerifier keyVerifier = new KeyVerifier ();
141
+ // Key should match one of the certificates
142
+ for (X509Certificate certificate : certificates ) {
143
+ Result result = keyVerifier .matches (privateKey , certificate .getPublicKey ());
144
+ if (result == Result .YES ) {
145
+ return ;
123
146
}
124
147
}
148
+ throw new IllegalStateException ("Private key matches none of the certificates" );
149
+ }
150
+
151
+ private static PrivateKey loadPrivateKey (PemSslStoreDetails details ) {
152
+ String privateKeyContent = PemContent .load (details .privateKey ());
153
+ return PemPrivateKeyParser .parse (privateKeyContent , details .privateKeyPassword ());
154
+ }
155
+
156
+ private static X509Certificate [] loadCertificates (PemSslStoreDetails details ) {
157
+ String certificateContent = PemContent .load (details .certificate ());
158
+ X509Certificate [] certificates = PemCertificateParser .parse (certificateContent );
159
+ Assert .state (certificates != null && certificates .length > 0 , "Loaded certificates are empty" );
160
+ return certificates ;
161
+ }
162
+
163
+ private static KeyStore createKeyStore (PemSslStoreDetails details )
164
+ throws KeyStoreException , IOException , NoSuchAlgorithmException , CertificateException {
165
+ String type = StringUtils .hasText (details .type ()) ? details .type () : KeyStore .getDefaultType ();
166
+ KeyStore store = KeyStore .getInstance (type );
167
+ store .load (null );
168
+ return store ;
169
+ }
170
+
171
+ private static void addPrivateKey (KeyStore keyStore , PrivateKey privateKey , String alias , String keyPassword ,
172
+ X509Certificate [] certificates ) throws KeyStoreException {
173
+ keyStore .setKeyEntry (alias , privateKey , (keyPassword != null ) ? keyPassword .toCharArray () : null , certificates );
174
+ }
175
+
176
+ private static void addCertificates (KeyStore keyStore , X509Certificate [] certificates , String alias )
177
+ throws KeyStoreException {
178
+ for (int index = 0 ; index < certificates .length ; index ++) {
179
+ keyStore .setCertificateEntry (alias + "-" + index , certificates [index ]);
180
+ }
125
181
}
126
182
127
183
}
0 commit comments