1 /*
2 * junixsocket
3 *
4 * Copyright 2009-2026 Christian Kohlschütter
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18 package org.newsclub.net.unix.demo.ssl;
19
20 import java.io.File;
21 import java.io.IOException;
22 import java.io.InputStream;
23 import java.util.concurrent.CompletableFuture;
24
25 /**
26 * SSL-over-UNIX sockets demo.
27 * <p>
28 * Prerequisites:
29 * <ol>
30 * <li>Create server public/private key pair, valid for ~10 years, Store it as a PKCS12 file named
31 * "{@code juxserver.p12}":
32 * <p>
33 * {@code keytool -genkeypair -alias juxserver -keyalg RSA -keysize 2048
34 * -storetype PKCS12 -validity 3650 -ext san=dns:localhost.junixsocket
35 * -dname "CN=First and Last, OU=Organizational Unit, O=Organization, L=City, ST=State, C=XX"
36 * -keystore juxserver.p12 -storepass serverpass}
37 * <p>
38 * Omit {@code -dname "CN=..."} to interactively specify the distinguished name for the certificate.
39 * <p>
40 * You may verify the contents of this p12 file via
41 * {@code keytool -list -v -keystore juxserver.p12 -storepass serverpass}; omit the
42 * {@code -storepass...} parameters to specify the password interactively for additional security.
43 * </li>
44 * <li>Export the server's public key as a X.509 certificate:
45 * <p>
46 * {@code keytool -exportcert -alias juxserver -keystore juxserver.p12 -storepass serverpass -file juxserver.pem}
47 * <p>
48 * You may verify the contents of the certificate file via
49 * {@code keytool -printcert -file juxserver.pem}</li>
50 * <li>Import the server's X.509 certificate into the client truststore:
51 * <p>
52 * {@code keytool -importcert -alias juxserver -keystore juxclient.truststore -storepass clienttrustpass
53 * -file juxserver.pem -noprompt}
54 * <p>
55 * Omit {@code -noprompt} to interactively verify the imported certificate.
56 * </p>
57 * <p>
58 * You may verify the contents of this truststore via
59 * {@code keytool -list -v -keystore juxclient.truststore -storepass clienttrustpass}; omit the
60 * {@code -storepass...} parameters to specify the password interactively for additional security.
61 * </li>
62 * </ol>
63 * <p>
64 * If you want client authentication as well, perform these additional steps:
65 * <ol>
66 * <li>Create client public/private key pair, valid for ~10 years, Store it as a PKCS12 file named
67 * "{@code juxclient.p12}":
68 * <p>
69 * {@code keytool -genkeypair -alias juxclient -keyalg RSA -keysize 2048 -storetype PKCS12
70 * -validity 3650 -ext san=dns:localhost.junixsocket
71 * -dname "CN=First and Last, OU=Organizational Unit, O=Organization, L=City, ST=State, C=XX"
72 * -keystore juxclient.p12 -storepass clientpass}
73 * <p>
74 * Omit {@code -dname "CN=..."} to interactively specify the distinguished name for the certificate.
75 * <p>
76 * You may verify the contents of this p12 file via
77 * {@code keytool -list -v -keystore juxclient.p12 -storepass clientpass}; omit the
78 * {@code -storepass...} parameters to specify the password interactively for additional security.
79 * </li>
80 * <li>Export the client's public key as a X.509 certificate:
81 * <p>
82 * {@code keytool -exportcert -alias juxclient -keystore juxclient.p12 -storepass clientpass -file juxclient.pem}
83 * <p>
84 * You may verify the contents of the certificate file via
85 * {@code keytool -printcert -file juxclient.pem}</li>
86 * <li>Import the client's X.509 certificate into the servers truststore:
87 * <p>
88 * {@code keytool -importcert -alias juxclient -keystore juxserver.truststore -storepass servertrustpass
89 * -file juxclient.pem -noprompt}
90 * <p>
91 * Omit {@code -noprompt} to interactively verify the imported certificate.
92 * </p>
93 * <p>
94 * You may verify the contents of this truststore via
95 * {@code keytool -list -v -keystore juxserver.truststore -storepass servertrustpass}; omit the
96 * {@code -storepass...} parameters to specify the password interactively for additional security.
97 * </li>
98 * </ol>
99 *
100 * @author Christian Kohlschütter
101 * @see org.newsclub.net.unix.demo.ssl.SSLDemoServer
102 * @see org.newsclub.net.unix.demo.ssl.SSLDemoClient
103 */
104 @SuppressWarnings({
105 "FutureReturnValueIgnored", // errorprone
106 "CatchAndPrintStackTrace", // errorprone
107 })
108 public class SSLDemoPrerequisites {
109 private static final boolean runCommand(String explanation, String... command) throws IOException,
110 InterruptedException {
111 System.out.println(explanation + "...");
112
113 StringBuilder sb = new StringBuilder();
114 for (String c : command) {
115 sb.append(" ");
116 if (c.isEmpty() || c.contains(" ") || c.contains("\"")) {
117 sb.append("\"" + c.replace("\"", "\\\"") + "\"");
118 } else {
119 sb.append(c);
120 }
121 }
122 System.out.println("#" + sb);
123
124 Process process = Runtime.getRuntime().exec(command);
125
126 CompletableFuture.runAsync(() -> {
127 try (InputStream stdout = process.getInputStream()) {
128 byte[] buf = new byte[1024];
129 int r;
130 while ((r = stdout.read(buf)) >= 0) {
131 System.out.write(buf, 0, r);
132 }
133 } catch (IOException e) {
134 e.printStackTrace();
135 }
136 });
137 CompletableFuture.runAsync(() -> {
138 try (InputStream stderr = process.getErrorStream()) {
139 byte[] buf = new byte[1024];
140 int r;
141 while ((r = stderr.read(buf)) >= 0) {
142 System.err.write(buf, 0, r);
143 }
144 } catch (IOException e) {
145 e.printStackTrace();
146 }
147 });
148
149 int rc = process.waitFor();
150 System.out.println("rc=" + rc);
151 System.out.println();
152 return rc == 0;
153 }
154
155 public static void main(String[] args) throws Exception {
156 System.out.println("Working directory: " + new File("").getAbsolutePath());
157 System.out.println();
158
159 boolean success = true;
160
161 success &= runCommand("Generating server key pair", //
162 "keytool", "-genkeypair", "-alias", "juxserver", "-keyalg", "RSA", "-keysize", "2048",
163 "-storetype", "PKCS12", "-validity", "3650", "-ext", "san=dns:localhost.junixsocket",
164 "-dname",
165 "CN=First and Last, OU=Organizational Unit, O=Organization, L=City, ST=State, C=XX",
166 "-keystore", "juxserver.p12", "-storepass", "serverpass");
167
168 success &= runCommand("Exporting server certificate", //
169 ("keytool -exportcert -alias juxserver -keystore juxserver.p12 -storepass serverpass -file juxserver.pem")
170 .split("[ ]+"));
171
172 success &= runCommand("Importing server certificate into client truststore", //
173 ("keytool -importcert -alias juxserver -keystore juxclient.truststore -storepass clienttrustpass"
174 + " -file juxserver.pem -noprompt").split("[ ]+"));
175
176 success &= runCommand("Generating client key pair (optional, only for client authentication)", //
177 "keytool", "-genkeypair", "-alias", "juxclient", "-keyalg", "RSA", "-keysize", "2048",
178 "-storetype", "PKCS12", "-validity", "3650", "-ext", "san=dns:localhost.junixsocket",
179 "-dname",
180 "CN=First and Last, OU=Organizational Unit, O=Organization, L=City, ST=State, C=XX",
181 "-keystore", "juxclient.p12", "-storepass", "clientpass");
182
183 success &= runCommand("Exporting client certificate (optional, only for client authentication)", //
184 ("keytool -exportcert -alias juxclient -keystore juxclient.p12 -storepass clientpass -file juxclient.pem")
185 .split("[ ]+"));
186
187 success &= runCommand(
188 "Importing client certificate server client truststore (optional, only for client authentication)", //
189 ("keytool -importcert -alias juxclient -keystore juxserver.truststore -storepass servertrustpass"
190 + " -file juxclient.pem -noprompt").split("[ ]+"));
191
192 System.out.println("DONE. All successful=" + success);
193 }
194 }