Skip to content

Commit 849bb28

Browse files
committed
8372661: Add a null-safe static factory method to "jdk.test.lib.net.SimpleSSLContext"
Backport-of: 95ef1c5dbc468f6ff0a93050fe53d4936c5ab100
1 parent ab29567 commit 849bb28

1 file changed

Lines changed: 107 additions & 67 deletions

File tree

Lines changed: 107 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2005, 2022, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -23,69 +23,100 @@
2323

2424
package jdk.test.lib.net;
2525

26+
import java.nio.file.Files;
27+
import java.nio.file.Path;
2628
import java.util.*;
2729
import java.io.*;
2830
import java.security.*;
29-
import java.security.cert.*;
30-
import java.util.function.Supplier;
3131
import javax.net.ssl.*;
3232

3333
/**
34-
* Creates a simple usable SSLContext for SSLSocketFactory
35-
* or a HttpsServer using either a given keystore or a default
36-
* one in the test tree.
37-
*
38-
* Using this class with a security manager requires the following
39-
* permissions to be granted:
40-
*
41-
* permission "java.util.PropertyPermission" "test.src.path", "read";
42-
* permission java.io.FilePermission "/path/to/test/lib/jdk/test/lib/testkeys", "read";
43-
* The exact path above depends on the location of the test.
34+
* Utility for creating a simple usable {@link SSLContext} for testing purposes.
4435
*/
45-
public class SimpleSSLContext {
36+
public final class SimpleSSLContext {
37+
38+
private static final String DEFAULT_PROTOCOL = "TLS";
39+
40+
private static final String DEFAULT_KEY_STORE_FILE_REL_PATH = "jdk/test/lib/net/testkeys";
4641

47-
SSLContext ssl;
42+
private final SSLContext ssl;
43+
44+
// Made `public` for backward compatibility
45+
public SimpleSSLContext() throws IOException {
46+
this.ssl = findSSLContext(DEFAULT_KEY_STORE_FILE_REL_PATH, DEFAULT_PROTOCOL);
47+
}
48+
49+
// Kept for backward compatibility
50+
public SimpleSSLContext(String keyStoreFileRelPath) throws IOException {
51+
this.ssl = findSSLContext(Objects.requireNonNull(keyStoreFileRelPath), DEFAULT_PROTOCOL);
52+
}
4853

4954
/**
50-
* loads default keystore from SimpleSSLContext
51-
* source directory
55+
* {@return a new {@link SSLContext} instance by searching for a key store
56+
* file path, and loading the first found one}
57+
*
58+
* @throws RuntimeException if no key store file can be found or the found
59+
* one cannot be loaded
5260
*/
53-
public SimpleSSLContext() throws IOException {
54-
this(() -> "TLS");
61+
public static SSLContext findSSLContext() throws IOException {
62+
return findSSLContext(DEFAULT_PROTOCOL);
63+
}
64+
65+
/**
66+
* {@return a new {@link SSLContext} instance by searching for a key store
67+
* file path, and loading the first found one}
68+
*
69+
* @param protocol an {@link SSLContext} protocol
70+
*
71+
* @throws NullPointerException if {@code protocol} is null
72+
* @throws RuntimeException if no key store file can be found or the found
73+
* one cannot be loaded
74+
*/
75+
public static SSLContext findSSLContext(String protocol) throws IOException {
76+
Objects.requireNonNull(protocol);
77+
return findSSLContext(DEFAULT_KEY_STORE_FILE_REL_PATH, protocol);
5578
}
5679

80+
/**
81+
* {@return a new {@link SSLContext} instance by searching for a key store
82+
* file path, and loading the first found one}
83+
*
84+
* @param keyStoreFileRelPath a key store file path to be concatenated with
85+
* the search path(s) obtained from the
86+
* {@code test.src.path} system property
87+
* @param protocol an {@link SSLContext} protocol
88+
*
89+
* @throws NullPointerException if {@code keyStoreFileRelPath} or {@code protocol} is null
90+
* @throws RuntimeException if no key store file can be found or the found
91+
* one cannot be loaded
92+
*/
5793
@SuppressWarnings("removal")
58-
private SimpleSSLContext(Supplier<String> protocols) throws IOException {
94+
public static SSLContext findSSLContext(String keyStoreFileRelPath, String protocol) throws IOException {
95+
Objects.requireNonNull(keyStoreFileRelPath);
96+
Objects.requireNonNull(protocol);
97+
5998
try {
60-
final String proto = protocols.get();
61-
AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() {
62-
@Override
63-
public Void run() throws Exception {
64-
String paths = System.getProperty("test.src.path");
65-
StringTokenizer st = new StringTokenizer(paths, File.pathSeparator);
66-
boolean securityExceptions = false;
67-
while (st.hasMoreTokens()) {
68-
String path = st.nextToken();
69-
try {
70-
File f = new File(path, "jdk/test/lib/net/testkeys");
71-
if (f.exists()) {
72-
try (FileInputStream fis = new FileInputStream(f)) {
73-
init(fis, proto);
74-
return null;
75-
}
76-
}
77-
} catch (SecurityException e) {
78-
// catch and ignore because permission only required
79-
// for one entry on path (at most)
80-
securityExceptions = true;
99+
SSLContext res = (SSLContext) AccessController.doPrivileged((PrivilegedExceptionAction) () -> {
100+
String sourcePaths = System.getProperty("test.src.path");
101+
boolean securityExceptions = false;
102+
for (var sourcePath : Collections.list(new StringTokenizer(sourcePaths, File.pathSeparator))) {
103+
try {
104+
var keyStoreFileAbsPath = Path.of((String) sourcePath, keyStoreFileRelPath);
105+
if (Files.exists(keyStoreFileAbsPath)) {
106+
return loadSSLContext(keyStoreFileAbsPath, protocol);
81107
}
108+
} catch (SecurityException e) {
109+
// catch and ignore because permission only required
110+
// for one entry on path (at most)
111+
securityExceptions = true;
82112
}
83-
if (securityExceptions) {
84-
System.err.println("SecurityExceptions thrown on loading testkeys");
85-
}
86-
return null;
87113
}
114+
if (securityExceptions) {
115+
System.err.println("SecurityExceptions thrown on loading testkeys");
116+
}
117+
return null;
88118
});
119+
if (res != null) return res;
89120
} catch (PrivilegedActionException pae) {
90121
Throwable t = pae.getCause() != null ? pae.getCause() : pae;
91122
if (t instanceof IOException)
@@ -96,49 +127,58 @@ public Void run() throws Exception {
96127
throw (Error)t;
97128
throw new RuntimeException(t);
98129
}
130+
throw new RuntimeException(
131+
"Could not find any key store at source path(s) using key store file relative path '%s'".formatted(
132+
keyStoreFileRelPath));
99133
}
100134

101135
/**
102-
* loads default keystore from given directory
136+
* {@return a new {@link SSLContext} loaded from the provided key store file
137+
* path using the given protocol}
138+
*
139+
* @param keyStoreFilePath a {@link KeyStore} file path
140+
* @param protocol an {@link SSLContext} protocol
141+
*
142+
* @throws RuntimeException if loading fails
103143
*/
104-
public SimpleSSLContext(String dir) throws IOException {
105-
String file = dir + "/testkeys";
106-
try (FileInputStream fis = new FileInputStream(file)) {
107-
init(fis, "TLS");
108-
}
109-
}
110-
111-
private void init(InputStream i, String protocol) throws IOException {
112-
try {
144+
private static SSLContext loadSSLContext(Path keyStoreFilePath, String protocol) {
145+
try (var storeStream = Files.newInputStream(keyStoreFilePath)) {
113146
char[] passphrase = "passphrase".toCharArray();
114147
KeyStore ks = KeyStore.getInstance("PKCS12");
115-
ks.load(i, passphrase);
148+
ks.load(storeStream, passphrase);
116149

117150
KeyManagerFactory kmf = KeyManagerFactory.getInstance("PKIX");
118151
kmf.init(ks, passphrase);
119152

120153
TrustManagerFactory tmf = TrustManagerFactory.getInstance("PKIX");
121154
tmf.init(ks);
122155

123-
ssl = SSLContext.getInstance(protocol);
124-
ssl.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
125-
} catch (KeyManagementException | KeyStoreException |
126-
UnrecoverableKeyException | CertificateException |
127-
NoSuchAlgorithmException e) {
128-
throw new RuntimeException(e.getMessage());
156+
var sslContext = SSLContext.getInstance(protocol);
157+
sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
158+
return sslContext;
159+
} catch (SecurityException e) {
160+
throw e;
161+
} catch (Exception e) {
162+
var message = "Failed loading 'SSLContext' from key store at location '%s' for protocol '%s'".formatted(
163+
keyStoreFilePath, protocol);
164+
throw new RuntimeException(message, e);
129165
}
130166
}
131167

168+
// Kept for backward compatibility
132169
public static SSLContext getContext(String protocol) throws IOException {
133-
if(protocol == null || protocol.isEmpty()) {
134-
return new SimpleSSLContext().get();
135-
}
136-
else {
137-
return new SimpleSSLContext(() -> protocol).get();
170+
try {
171+
return protocol == null || protocol.isEmpty()
172+
? findSSLContext()
173+
: findSSLContext(protocol);
174+
} catch (RuntimeException re) {
175+
throw new IOException(re);
138176
}
139177
}
140178

179+
// Kept for backward compatibility
141180
public SSLContext get() {
142181
return ssl;
143182
}
183+
144184
}

0 commit comments

Comments
 (0)