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