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 }