16
16
17
17
package org .springframework .boot .info ;
18
18
19
- import java .io .BufferedReader ;
20
- import java .io .IOException ;
21
- import java .io .InputStreamReader ;
22
- import java .nio .charset .StandardCharsets ;
23
- import java .nio .file .Path ;
19
+ import java .time .Clock ;
20
+ import java .time .Instant ;
21
+ import java .time .ZoneId ;
24
22
import java .util .List ;
25
- import java .util .stream .Collectors ;
26
23
27
24
import org .junit .jupiter .api .Test ;
28
- import org .junit .jupiter .api .io .TempDir ;
29
25
30
26
import org .springframework .boot .info .SslInfo .BundleInfo ;
31
27
import org .springframework .boot .info .SslInfo .CertificateChainInfo ;
45
41
* Tests for {@link SslInfo}.
46
42
*
47
43
* @author Jonatan Ivanov
44
+ * @author Moritz Halbritter
48
45
*/
49
46
class SslInfoTests {
50
47
48
+ private static final Clock CLOCK = Clock .fixed (Instant .parse ("2025-06-18T13:00:00Z" ), ZoneId .of ("UTC" ));
49
+
51
50
@ Test
52
51
@ WithPackageResources ("test.p12" )
53
52
void validCertificatesShouldProvideSslInfo () {
@@ -70,8 +69,8 @@ void validCertificatesShouldProvideSslInfo() {
70
69
assertThat (cert1 .getSerialNumber ()).isNotEmpty ();
71
70
assertThat (cert1 .getVersion ()).isEqualTo ("V3" );
72
71
assertThat (cert1 .getSignatureAlgorithmName ()).isEqualTo ("SHA256withRSA" );
73
- assertThat (cert1 .getValidityStarts ()).isInThePast ( );
74
- assertThat (cert1 .getValidityEnds ()).isInTheFuture ( );
72
+ assertThat (cert1 .getValidityStarts ()).isBefore ( CLOCK . instant () );
73
+ assertThat (cert1 .getValidityEnds ()).isAfter ( CLOCK . instant () );
75
74
assertThat (cert1 .getValidity ()).isNotNull ();
76
75
assertThat (cert1 .getValidity ().getStatus ()).isSameAs (Status .VALID );
77
76
assertThat (cert1 .getValidity ().getMessage ()).isNull ();
@@ -81,8 +80,8 @@ void validCertificatesShouldProvideSslInfo() {
81
80
assertThat (cert2 .getSerialNumber ()).isNotEmpty ();
82
81
assertThat (cert2 .getVersion ()).isEqualTo ("V3" );
83
82
assertThat (cert2 .getSignatureAlgorithmName ()).isEqualTo ("SHA256withRSA" );
84
- assertThat (cert2 .getValidityStarts ()).isInThePast ( );
85
- assertThat (cert2 .getValidityEnds ()).isInTheFuture ( );
83
+ assertThat (cert2 .getValidityStarts ()).isBefore ( CLOCK . instant () );
84
+ assertThat (cert2 .getValidityEnds ()).isAfter ( CLOCK . instant () );
86
85
assertThat (cert2 .getValidity ()).isNotNull ();
87
86
assertThat (cert2 .getValidity ().getStatus ()).isSameAs (Status .VALID );
88
87
assertThat (cert2 .getValidity ().getMessage ()).isNull ();
@@ -106,8 +105,8 @@ void notYetValidCertificateShouldProvideSslInfo() {
106
105
assertThat (cert .getSerialNumber ()).isNotEmpty ();
107
106
assertThat (cert .getVersion ()).isEqualTo ("V3" );
108
107
assertThat (cert .getSignatureAlgorithmName ()).isEqualTo ("SHA256withRSA" );
109
- assertThat (cert .getValidityStarts ()).isInTheFuture ( );
110
- assertThat (cert .getValidityEnds ()).isInTheFuture ( );
108
+ assertThat (cert .getValidityStarts ()).isAfter ( CLOCK . instant () );
109
+ assertThat (cert .getValidityEnds ()).isAfter ( CLOCK . instant () );
111
110
assertThat (cert .getValidity ()).isNotNull ();
112
111
assertThat (cert .getValidity ().getStatus ()).isSameAs (Status .NOT_YET_VALID );
113
112
assertThat (cert .getValidity ().getMessage ()).startsWith ("Not valid before" );
@@ -131,19 +130,18 @@ void expiredCertificateShouldProvideSslInfo() {
131
130
assertThat (cert .getSerialNumber ()).isNotEmpty ();
132
131
assertThat (cert .getVersion ()).isEqualTo ("V3" );
133
132
assertThat (cert .getSignatureAlgorithmName ()).isEqualTo ("SHA256withRSA" );
134
- assertThat (cert .getValidityStarts ()).isInThePast ( );
135
- assertThat (cert .getValidityEnds ()).isInThePast ( );
133
+ assertThat (cert .getValidityStarts ()).isBefore ( CLOCK . instant () );
134
+ assertThat (cert .getValidityEnds ()).isBefore ( CLOCK . instant () );
136
135
assertThat (cert .getValidity ()).isNotNull ();
137
136
assertThat (cert .getValidity ().getStatus ()).isSameAs (Status .EXPIRED );
138
137
assertThat (cert .getValidity ().getMessage ()).startsWith ("Not valid after" );
139
138
}
140
139
141
140
@ Test
142
- @ WithPackageResources ({ "test.p12" , "test-not-yet-valid.p12" , "test-expired.p12" })
143
- void multipleBundlesShouldProvideSslInfo (@ TempDir Path tempDir ) throws IOException , InterruptedException {
144
- Path keyStore = createKeyStore (tempDir );
141
+ @ WithPackageResources ({ "test.p12" , "test-not-yet-valid.p12" , "test-expired.p12" , "will-expire-soon.p12" })
142
+ void multipleBundlesShouldProvideSslInfo () {
145
143
SslInfo sslInfo = createSslInfo ("classpath:test.p12" , "classpath:test-not-yet-valid.p12" ,
146
- "classpath:test-expired.p12" , keyStore . toString () );
144
+ "classpath:test-expired.p12" , "classpath:will-expire-soon.p12" );
147
145
assertThat (sslInfo .getBundles ()).hasSize (4 );
148
146
assertThat (sslInfo .getBundles ()).allSatisfy ((bundle ) -> assertThat (bundle .getName ()).startsWith ("test-" ));
149
147
List <CertificateInfo > certs = sslInfo .getBundles ()
@@ -161,22 +159,22 @@ void multipleBundlesShouldProvideSslInfo(@TempDir Path tempDir) throws IOExcepti
161
159
assertThat (cert .getValidity ()).isNotNull ();
162
160
});
163
161
assertThat (certs ).anySatisfy ((cert ) -> {
164
- assertThat (cert .getValidityStarts ()).isInThePast ( );
165
- assertThat (cert .getValidityEnds ()).isInTheFuture ( );
162
+ assertThat (cert .getValidityStarts ()).isBefore ( CLOCK . instant () );
163
+ assertThat (cert .getValidityEnds ()).isAfter ( CLOCK . instant () );
166
164
assertThat (cert .getValidity ()).isNotNull ();
167
165
assertThat (cert .getValidity ().getStatus ()).isSameAs (Status .VALID );
168
166
assertThat (cert .getValidity ().getMessage ()).isNull ();
169
167
});
170
168
assertThat (certs ).satisfiesOnlyOnce ((cert ) -> {
171
- assertThat (cert .getValidityStarts ()).isInTheFuture ( );
172
- assertThat (cert .getValidityEnds ()).isInTheFuture ( );
169
+ assertThat (cert .getValidityStarts ()).isAfter ( CLOCK . instant () );
170
+ assertThat (cert .getValidityEnds ()).isAfter ( CLOCK . instant () );
173
171
assertThat (cert .getValidity ()).isNotNull ();
174
172
assertThat (cert .getValidity ().getStatus ()).isSameAs (Status .NOT_YET_VALID );
175
173
assertThat (cert .getValidity ().getMessage ()).startsWith ("Not valid before" );
176
174
});
177
175
assertThat (certs ).satisfiesOnlyOnce ((cert ) -> {
178
- assertThat (cert .getValidityStarts ()).isInThePast ( );
179
- assertThat (cert .getValidityEnds ()).isInThePast ( );
176
+ assertThat (cert .getValidityStarts ()).isBefore ( CLOCK . instant () );
177
+ assertThat (cert .getValidityEnds ()).isBefore ( CLOCK . instant () );
180
178
assertThat (cert .getValidity ()).isNotNull ();
181
179
assertThat (cert .getValidity ().getStatus ()).isSameAs (Status .EXPIRED );
182
180
assertThat (cert .getValidity ().getMessage ()).startsWith ("Not valid after" );
@@ -187,7 +185,7 @@ void multipleBundlesShouldProvideSslInfo(@TempDir Path tempDir) throws IOExcepti
187
185
void nullKeyStore () {
188
186
DefaultSslBundleRegistry sslBundleRegistry = new DefaultSslBundleRegistry ();
189
187
sslBundleRegistry .registerBundle ("test" , SslBundle .of (SslStoreBundle .NONE , SslBundleKey .NONE ));
190
- SslInfo sslInfo = new SslInfo (sslBundleRegistry );
188
+ SslInfo sslInfo = new SslInfo (sslBundleRegistry , CLOCK );
191
189
assertThat (sslInfo .getBundles ()).hasSize (1 );
192
190
assertThat (sslInfo .getBundles ().get (0 ).getCertificateChains ()).isEmpty ();
193
191
}
@@ -199,41 +197,7 @@ private SslInfo createSslInfo(String... locations) {
199
197
SslStoreBundle sslStoreBundle = new JksSslStoreBundle (keyStoreDetails , null );
200
198
sslBundleRegistry .registerBundle ("test-%d" .formatted (i ), SslBundle .of (sslStoreBundle ));
201
199
}
202
- return new SslInfo (sslBundleRegistry );
203
- }
204
-
205
- private Path createKeyStore (Path directory ) throws IOException , InterruptedException {
206
- Path keyStore = directory .resolve ("test.p12" );
207
- Process process = createProcessBuilder (keyStore ).start ();
208
- int exitCode = process .waitFor ();
209
- if (exitCode != 0 ) {
210
- try (BufferedReader reader = new BufferedReader (
211
- new InputStreamReader (process .getInputStream (), StandardCharsets .UTF_8 ))) {
212
- String out = reader .lines ().collect (Collectors .joining ("\n " ));
213
- throw new RuntimeException ("Unexpected exit code from keytool: %d\n %s" .formatted (exitCode , out ));
214
- }
215
- }
216
- return keyStore ;
217
- }
218
-
219
- private ProcessBuilder createProcessBuilder (Path keystore ) {
220
- // @formatter:off
221
- ProcessBuilder processBuilder = new ProcessBuilder (
222
- "keytool" ,
223
- "-genkeypair" ,
224
- "-storetype" , "PKCS12" ,
225
- "-alias" , "spring-boot" ,
226
- "-keyalg" , "RSA" ,
227
- "-storepass" , "secret" ,
228
- "-keypass" , "secret" ,
229
- "-keystore" , keystore .toString (),
230
- "-dname" , "CN=localhost,OU=Spring,O=VMware,L=Palo Alto,ST=California,C=US" ,
231
- "-validity" , "1" ,
232
- "-ext" , "SAN=DNS:localhost,IP:::1,IP:127.0.0.1"
233
- );
234
- // @formatter:on
235
- processBuilder .redirectErrorStream (true );
236
- return processBuilder ;
200
+ return new SslInfo (sslBundleRegistry , CLOCK );
237
201
}
238
202
239
203
}
0 commit comments