2 * Copyright (c) 2007, Cameron Rich
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
9 * * Redistributions of source code must retain the above copyright notice,
10 * this list of conditions and the following disclaimer.
11 * * Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
14 * * Neither the name of the axTLS project nor the names of its contributors
15 * may be used to endorse or promote products derived from this software
16 * without specific prior written permission.
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
22 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
25 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
26 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
27 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 * Demonstrate the use of the axTLS library in Java with a set of
33 * command-line parameters similar to openssl. In fact, openssl clients
34 * should be able to communicate with axTLS servers and visa-versa. *
35 * This code has various bits enabled depending on the configuration. To enable
36 * the most interesting version, compile with the 'full mode' enabled.
38 * To see what options you have, run the following:
39 * > java -jar axtls.jar s_server -?
40 * > java -jar axtls.jar s_client -?
42 * The axtls/axtlsj shared libraries must be in the same directory or be found
56 public static void main(String[] args)
58 if (args.length == 1 && args[0].equals("version"))
60 System.out.println("axtls.jar " + SSLUtil.version());
64 axssl runner = new axssl();
68 if (args.length < 1 ||
69 (!args[0].equals("s_server") &&
70 !args[0].equals("s_client")))
72 runner.print_options(args.length > 0 ? args[0] : "");
75 int build_mode = SSLUtil.buildMode();
77 if (args[0].equals("s_server"))
78 runner.do_server(build_mode, args);
80 runner.do_client(build_mode, args);
84 System.out.println(e);
91 private void do_server(int build_mode, String[] args)
96 int options = axtlsj.SSL_DISPLAY_CERTS;
97 boolean quiet = false;
98 String password = null;
99 String private_key_file = null;
101 /* organise the cert/ca_cert lists */
102 int cert_size = SSLUtil.maxCerts();
103 int ca_cert_size = SSLUtil.maxCACerts();
104 String[] cert = new String[cert_size];
105 String[] ca_cert = new String[ca_cert_size];
107 int ca_cert_index = 0;
109 while (i < args.length)
111 if (args[i].equals("-accept"))
113 if (i >= args.length-1)
115 print_server_options(build_mode, args[i]);
118 port = Integer.parseInt(args[++i]);
120 else if (args[i].equals("-quiet"))
123 options &= ~(int)axtlsj.SSL_DISPLAY_CERTS;
125 else if (build_mode >= axtlsj.SSL_BUILD_SERVER_ONLY)
127 if (args[i].equals("-cert"))
129 if (i >= args.length-1 || cert_index >= cert_size)
131 print_server_options(build_mode, args[i]);
134 cert[cert_index++] = args[++i];
136 else if (args[i].equals("-key"))
138 if (i >= args.length-1)
140 print_server_options(build_mode, args[i]);
143 private_key_file = args[++i];
144 options |= axtlsj.SSL_NO_DEFAULT_KEY;
146 else if (args[i].equals("-pass"))
148 if (i >= args.length-1)
150 print_server_options(build_mode, args[i]);
153 password = args[++i];
155 else if (build_mode >= axtlsj.SSL_BUILD_ENABLE_VERIFICATION)
157 if (args[i].equals("-verify"))
159 options |= axtlsj.SSL_CLIENT_AUTHENTICATION;
161 else if (args[i].equals("-CAfile"))
163 if (i >= args.length-1 || ca_cert_index >= ca_cert_size)
165 print_server_options(build_mode, args[i]);
168 ca_cert[ca_cert_index++] = args[++i];
170 else if (build_mode == axtlsj.SSL_BUILD_FULL_MODE)
172 if (args[i].equals("-debug"))
174 options |= axtlsj.SSL_DISPLAY_BYTES;
176 else if (args[i].equals("-state"))
178 options |= axtlsj.SSL_DISPLAY_STATES;
180 else if (args[i].equals("-show-rsa"))
182 options |= axtlsj.SSL_DISPLAY_RSA;
185 print_server_options(build_mode, args[i]);
188 print_server_options(build_mode, args[i]);
191 print_server_options(build_mode, args[i]);
194 print_server_options(build_mode, args[i]);
199 /* Create socket for incoming connections */
200 ServerSocket server_sock = new ServerSocket(port);
202 /**********************************************************************
203 * This is where the interesting stuff happens. Up until now we've
204 * just been setting up sockets etc. Now we do the SSL handshake.
205 **********************************************************************/
206 SSLServer ssl_ctx = new SSLServer(options,
207 axtlsj.SSL_DEFAULT_SVR_SESS);
210 throw new Exception("Error: Server context is invalid");
212 if (private_key_file != null)
214 int obj_type = axtlsj.SSL_OBJ_RSA_KEY;
216 if (private_key_file.endsWith(".p8"))
217 obj_type = axtlsj.SSL_OBJ_PKCS8;
218 else if (private_key_file.endsWith(".p12"))
219 obj_type = axtlsj.SSL_OBJ_PKCS12;
221 if (ssl_ctx.objLoad(obj_type,
222 private_key_file, password) != axtlsj.SSL_OK)
224 throw new Exception("Error: Private key '" + private_key_file +
229 for (i = 0; i < cert_index; i++)
231 if (ssl_ctx.objLoad(axtlsj.SSL_OBJ_X509_CERT,
232 cert[i], null) != axtlsj.SSL_OK)
234 throw new Exception("Certificate '" + cert[i] +
239 for (i = 0; i < ca_cert_index; i++)
241 if (ssl_ctx.objLoad(axtlsj.SSL_OBJ_X509_CACERT,
242 ca_cert[i], null) != axtlsj.SSL_OK)
244 throw new Exception("Certificate '" + ca_cert[i] +
250 SSLReadHolder rh = new SSLReadHolder();
256 System.out.println("ACCEPT");
259 Socket client_sock = server_sock.accept();
261 SSL ssl = ssl_ctx.connect(client_sock);
263 while ((res = ssl_ctx.read(ssl, rh)) == axtlsj.SSL_OK)
265 /* check when the connection has been established */
266 if (ssl.handshakeStatus() == axtlsj.SSL_OK)
269 /* could do something else here */
272 if (res == axtlsj.SSL_OK) /* connection established and ok */
276 display_session_id(ssl);
280 /* now read (and display) whatever the client sends us */
283 /* keep reading until we get something interesting */
284 while ((res = ssl_ctx.read(ssl, rh)) == axtlsj.SSL_OK)
286 /* could do something else here */
289 if (res < axtlsj.SSL_OK)
293 System.out.println("CONNECTION CLOSED");
299 /* convert to String */
300 byte[] buf = rh.getData();
301 char[] str = new char[res];
303 for (i = 0; i < res; i++)
305 str[i] = (char)buf[i];
308 System.out.print(str);
313 SSLUtil.displayError(res);
316 /* client was disconnected or the handshake failed. */
321 /* ssl_ctx.dispose(); */
327 private void do_client(int build_mode, String[] args)
330 if (build_mode < axtlsj.SSL_BUILD_ENABLE_CLIENT)
331 print_client_options(build_mode, args[1]);
335 boolean quiet = false;
336 String password = null;
338 String private_key_file = null;
339 String hostname = "127.0.0.1";
341 /* organise the cert/ca_cert lists */
343 int ca_cert_index = 0;
344 int cert_size = SSLUtil.maxCerts();
345 int ca_cert_size = SSLUtil.maxCACerts();
346 String[] cert = new String[cert_size];
347 String[] ca_cert = new String[ca_cert_size];
349 int options = axtlsj.SSL_SERVER_VERIFY_LATER|axtlsj.SSL_DISPLAY_CERTS;
350 byte[] session_id = null;
352 while (i < args.length)
354 if (args[i].equals("-connect"))
358 if (i >= args.length-1)
360 print_client_options(build_mode, args[i]);
363 host_port = args[++i];
366 if ((index_colon = host_port.indexOf(':')) < 0)
367 print_client_options(build_mode, args[i]);
369 hostname = new String(host_port.toCharArray(),
371 port = Integer.parseInt(new String(host_port.toCharArray(),
372 index_colon+1, host_port.length()-index_colon-1));
374 else if (args[i].equals("-cert"))
376 if (i >= args.length-1 || cert_index >= cert_size)
378 print_client_options(build_mode, args[i]);
381 cert[cert_index++] = args[++i];
383 else if (args[i].equals("-CAfile"))
385 if (i >= args.length-1 || ca_cert_index >= ca_cert_size)
387 print_client_options(build_mode, args[i]);
390 ca_cert[ca_cert_index++] = args[++i];
392 else if (args[i].equals("-key"))
394 if (i >= args.length-1)
396 print_client_options(build_mode, args[i]);
399 private_key_file = args[++i];
400 options |= axtlsj.SSL_NO_DEFAULT_KEY;
402 else if (args[i].equals("-verify"))
404 options &= ~(int)axtlsj.SSL_SERVER_VERIFY_LATER;
406 else if (args[i].equals("-reconnect"))
410 else if (args[i].equals("-quiet"))
413 options &= ~(int)axtlsj.SSL_DISPLAY_CERTS;
415 else if (args[i].equals("-pass"))
417 if (i >= args.length-1)
419 print_server_options(build_mode, args[i]);
422 password = args[++i];
424 else if (build_mode == axtlsj.SSL_BUILD_FULL_MODE)
426 if (args[i].equals("-debug"))
428 options |= axtlsj.SSL_DISPLAY_BYTES;
430 else if (args[i].equals("-state"))
432 options |= axtlsj.SSL_DISPLAY_STATES;
434 else if (args[i].equals("-show-rsa"))
436 options |= axtlsj.SSL_DISPLAY_RSA;
439 print_client_options(build_mode, args[i]);
441 else /* don't know what this is */
442 print_client_options(build_mode, args[i]);
447 Socket client_sock = new Socket(hostname, port);
449 if (!client_sock.isConnected())
451 System.out.println("could not connect");
452 throw new Exception();
457 System.out.println("CONNECTED");
460 /**********************************************************************
461 * This is where the interesting stuff happens. Up until now we've
462 * just been setting up sockets etc. Now we do the SSL handshake.
463 **********************************************************************/
464 SSLClient ssl_ctx = new SSLClient(options,
465 axtlsj.SSL_DEFAULT_CLNT_SESS);
469 throw new Exception("Error: Client context is invalid");
472 if (private_key_file != null)
474 int obj_type = axtlsj.SSL_OBJ_RSA_KEY;
476 if (private_key_file.endsWith(".p8"))
477 obj_type = axtlsj.SSL_OBJ_PKCS8;
478 else if (private_key_file.endsWith(".p12"))
479 obj_type = axtlsj.SSL_OBJ_PKCS12;
481 if (ssl_ctx.objLoad(obj_type,
482 private_key_file, password) != axtlsj.SSL_OK)
484 throw new Exception("Error: Private key '" + private_key_file +
489 for (i = 0; i < cert_index; i++)
491 if (ssl_ctx.objLoad(axtlsj.SSL_OBJ_X509_CERT,
492 cert[i], null) != axtlsj.SSL_OK)
494 throw new Exception("Certificate '" + cert[i] +
499 for (i = 0; i < ca_cert_index; i++)
501 if (ssl_ctx.objLoad(axtlsj.SSL_OBJ_X509_CACERT,
502 ca_cert[i], null) != axtlsj.SSL_OK)
504 throw new Exception("Certificate '" + ca_cert[i] +
511 /* Try session resumption? */
514 while (reconnect-- > 0)
516 ssl = ssl_ctx.connect(client_sock, session_id);
518 if ((res = ssl.handshakeStatus()) != axtlsj.SSL_OK)
522 SSLUtil.displayError(res);
526 throw new Exception();
529 display_session_id(ssl);
530 session_id = ssl.getSessionId();
538 client_sock = new Socket(hostname, port);
544 ssl = ssl_ctx.connect(client_sock, null);
547 /* check the return status */
548 if ((res = ssl.handshakeStatus()) != axtlsj.SSL_OK)
552 SSLUtil.displayError(res);
555 throw new Exception();
561 ssl.getCertificateDN(axtlsj.SSL_X509_CERT_COMMON_NAME);
563 if (common_name != null)
565 System.out.println("Common Name:\t\t\t" + common_name);
568 display_session_id(ssl);
572 BufferedReader in = new BufferedReader(
573 new InputStreamReader(System.in));
577 String user_input = in.readLine();
579 if (user_input == null)
582 byte[] buf = new byte[user_input.length()+2];
583 buf[buf.length-2] = (byte)'\n'; /* add the carriage return */
584 buf[buf.length-1] = 0; /* null terminate */
586 for (i = 0; i < buf.length-2; i++)
588 buf[i] = (byte)user_input.charAt(i);
591 if ((res = ssl_ctx.write(ssl, buf)) < axtlsj.SSL_OK)
595 SSLUtil.displayError(res);
606 * We've had some sort of command-line error. Print out the basic options.
608 private void print_options(String option)
610 System.out.println("axssl: Error: '" + option +
611 "' is an invalid command.");
612 System.out.println("usage: axtlsj.jar [s_server|s_client|version] " +
618 * We've had some sort of command-line error. Print out the server options.
620 private void print_server_options(int build_mode, String option)
622 int cert_size = SSLUtil.maxCerts();
623 int ca_cert_size = SSLUtil.maxCACerts();
625 System.out.println("unknown option " + option);
626 System.out.println("usage: s_server [args ...]");
627 System.out.println(" -accept arg\t- port to accept on (default " +
629 System.out.println(" -quiet\t\t- No server output");
631 if (build_mode >= axtlsj.SSL_BUILD_SERVER_ONLY)
633 System.out.println(" -cert arg\t- certificate file to add (in " +
634 "addition to default) to chain -");
635 System.out.println("\t\t Can repeat up to " + cert_size + " times");
636 System.out.println(" -key arg\t- Private key file to use");
637 System.out.println(" -pass\t\t- private key file pass phrase source");
640 if (build_mode >= axtlsj.SSL_BUILD_ENABLE_VERIFICATION)
642 System.out.println(" -verify\t- turn on peer certificate " +
644 System.out.println(" -CAfile arg\t- Certificate authority. ");
645 System.out.println("\t\t Can repeat up to " +
646 ca_cert_size + " times");
649 if (build_mode == axtlsj.SSL_BUILD_FULL_MODE)
651 System.out.println(" -debug\t\t- Print more output");
652 System.out.println(" -state\t\t- Show state messages");
653 System.out.println(" -show-rsa\t- Show RSA state");
660 * We've had some sort of command-line error. Print out the client options.
662 private void print_client_options(int build_mode, String option)
664 int cert_size = SSLUtil.maxCerts();
665 int ca_cert_size = SSLUtil.maxCACerts();
667 System.out.println("unknown option " + option);
669 if (build_mode >= axtlsj.SSL_BUILD_ENABLE_CLIENT)
671 System.out.println("usage: s_client [args ...]");
672 System.out.println(" -connect host:port - who to connect to " +
673 "(default is localhost:4433)");
674 System.out.println(" -verify\t- turn on peer certificate " +
676 System.out.println(" -cert arg\t- certificate file to use");
677 System.out.println(" -key arg\t- Private key file to use");
678 System.out.println("\t\t Can repeat up to " + cert_size +
680 System.out.println(" -CAfile arg\t- Certificate authority.");
681 System.out.println("\t\t Can repeat up to " + ca_cert_size +
683 System.out.println(" -quiet\t\t- No client output");
684 System.out.println(" -pass\t\t- private key file pass " +
686 System.out.println(" -reconnect\t- Drop and re-make the " +
687 "connection with the same Session-ID");
689 if (build_mode == axtlsj.SSL_BUILD_FULL_MODE)
691 System.out.println(" -debug\t\t- Print more output");
692 System.out.println(" -state\t\t- Show state messages");
693 System.out.println(" -show-rsa\t- Show RSA state");
698 System.out.println("Change configuration to allow this feature");
705 * Display what cipher we are using
707 private void display_cipher(SSL ssl)
709 System.out.print("CIPHER is ");
711 byte ciph_id = ssl.getCipherId();
713 if (ciph_id == axtlsj.SSL_AES128_SHA)
714 System.out.println("AES128-SHA");
715 else if (ciph_id == axtlsj.SSL_AES256_SHA)
716 System.out.println("AES256-SHA");
717 else if (ciph_id == axtlsj.SSL_RC4_128_SHA)
718 System.out.println("RC4-SHA");
719 else if (ciph_id == axtlsj.SSL_RC4_128_MD5)
720 System.out.println("RC4-MD5");
722 System.out.println("Unknown - " + ssl.getCipherId());
725 public char toHexChar(int i)
727 if ((0 <= i) && (i <= 9 ))
728 return (char)('0' + i);
730 return (char)('a' + (i-10));
733 public void bytesToHex(byte[] data)
735 StringBuffer buf = new StringBuffer();
736 for (int i = 0; i < data.length; i++ )
738 buf.append(toHexChar((data[i]>>>4)&0x0F));
739 buf.append(toHexChar(data[i]&0x0F));
742 System.out.println(buf);
747 * Display what session id we have.
749 private void display_session_id(SSL ssl)
751 byte[] session_id = ssl.getSessionId();
753 if (session_id.length > 0)
755 System.out.println("-----BEGIN SSL SESSION PARAMETERS-----");
756 bytesToHex(session_id);
757 System.out.println("-----END SSL SESSION PARAMETERS-----");