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