View Javadoc
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 }