001 /*
002 * $HeadURL: http://juliusdavies.ca/svn/not-yet-commons-ssl/tags/commons-ssl-0.3.11/src/java/org/apache/commons/ssl/Ping.java $
003 * $Revision: 142 $
004 * $Date: 2008-03-04 00:13:37 -0800 (Tue, 04 Mar 2008) $
005 *
006 * ====================================================================
007 * Licensed to the Apache Software Foundation (ASF) under one
008 * or more contributor license agreements. See the NOTICE file
009 * distributed with this work for additional information
010 * regarding copyright ownership. The ASF licenses this file
011 * to you under the Apache License, Version 2.0 (the
012 * "License"); you may not use this file except in compliance
013 * with the License. You may obtain a copy of the License at
014 *
015 * http://www.apache.org/licenses/LICENSE-2.0
016 *
017 * Unless required by applicable law or agreed to in writing,
018 * software distributed under the License is distributed on an
019 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
020 * KIND, either express or implied. See the License for the
021 * specific language governing permissions and limitations
022 * under the License.
023 * ====================================================================
024 *
025 * This software consists of voluntary contributions made by many
026 * individuals on behalf of the Apache Software Foundation. For more
027 * information on the Apache Software Foundation, please see
028 * <http://www.apache.org/>.
029 *
030 */
031
032 package org.apache.commons.ssl;
033
034 import org.apache.commons.ssl.util.ReadLine;
035
036 import javax.net.ssl.SSLSocket;
037 import java.io.File;
038 import java.io.InputStream;
039 import java.io.OutputStream;
040 import java.net.InetAddress;
041 import java.net.Socket;
042 import java.security.cert.X509Certificate;
043 import java.util.Collections;
044 import java.util.HashMap;
045 import java.util.Iterator;
046 import java.util.Map;
047 import java.util.SortedSet;
048 import java.util.TreeSet;
049
050 /**
051 * @author Credit Union Central of British Columbia
052 * @author <a href="http://www.cucbc.com/">www.cucbc.com</a>
053 * @author <a href="mailto:juliusdavies@cucbc.com">juliusdavies@cucbc.com</a>
054 * @since 30-Mar-2006
055 */
056 public class Ping {
057 protected static SortedSet ARGS = new TreeSet();
058 protected static Map ARGS_MATCH = new HashMap();
059 protected final static Arg ARG_TARGET = new Arg("-t", "--target", "[hostname[:port]] default port=443", true);
060 protected final static Arg ARG_BIND = new Arg("-b", "--bind", "[hostname[:port]] default port=0 \"ANY\"");
061 protected final static Arg ARG_PROXY = new Arg("-r", "--proxy", "[hostname[:port]] default port=80");
062 protected final static Arg ARG_TRUST_CERT = new Arg("-tm", "--trust-cert", "[path to trust material] {pem, der, crt, jks}");
063 protected final static Arg ARG_CLIENT_CERT = new Arg("-km", "--client-cert", "[path to client's private key] {jks, pkcs12, pkcs8}");
064 protected final static Arg ARG_CERT_CHAIN = new Arg("-cc", "--cert-chain", "[path to client's cert chain for pkcs8/OpenSSL key]");
065 protected final static Arg ARG_PASSWORD = new Arg("-p", "--password", "[client cert password]");
066 protected final static Arg ARG_HOST_HEADER = new Arg("-h", "--host-header", "[http-host-header] in case -t is an IP address");
067 protected final static Arg ARG_PATH = new Arg("-u", "--path", "[path for GET/HEAD request] default=/");
068 protected final static Arg ARG_METHOD = new Arg("-m", "--method", "[http method to use] default=HEAD");
069
070 private static HostPort target;
071 private static HostPort local;
072 private static HostPort proxy;
073 private static String hostHeader;
074 private static String httpMethod = "HEAD";
075 private static String path = "/";
076 private static InetAddress targetAddress;
077 private static InetAddress localAddress;
078 private static int targetPort = 443;
079 private static int localPort = 0;
080 private static File clientCert;
081 private static File certChain;
082 private static char[] password;
083 private static TrustChain trustChain = null;
084
085 static {
086 ARGS = Collections.unmodifiableSortedSet(ARGS);
087 ARGS_MATCH = Collections.unmodifiableMap(ARGS_MATCH);
088 }
089
090 public static void main(String[] args) throws Exception {
091 boolean showUsage = args.length == 0;
092 Exception parseException = null;
093 if (!showUsage) {
094 try {
095 parseArgs(args);
096 }
097 catch (Exception e) {
098 parseException = e;
099 showUsage = true;
100 }
101 }
102 if (showUsage) {
103 if (parseException != null) {
104 System.out.println();
105 System.out.println("* Error: " + parseException.getMessage() + ".");
106 parseException.printStackTrace(System.out);
107 System.out.println();
108 }
109 System.out.println("Usage: java -jar not-yet-commons-ssl-" + Version.VERSION + ".jar [options]");
110 System.out.println(Version.versionString());
111 System.out.println("Options: (*=required)");
112 Iterator it = ARGS.iterator();
113 while (it.hasNext()) {
114 Arg a = (Arg) it.next();
115 String s = Util.pad(a.shortArg, 3, false);
116 String l = Util.pad(a.longArg, 18, false);
117 String required = a.isRequired ? "*" : " ";
118 String d = a.description;
119 System.out.println(required + " " + s + " " + l + " " + d);
120 }
121 System.out.println();
122 String example = "java -jar commons-ssl.jar -t host.com:443 -c ./client.pfx -p `cat ./pass.txt` ";
123 System.out.println("Example:");
124 System.out.println();
125 System.out.println(example);
126 System.out.println();
127 System.exit(1);
128 return;
129 }
130
131 SSLClient ssl = new SSLClient();
132 Socket s = null;
133 InputStream in = null;
134 OutputStream out = null;
135 Exception socketException = null;
136 Exception trustException = null;
137 Exception hostnameException = null;
138 Exception crlException = null;
139 Exception expiryException = null;
140 String sslCipher = null;
141 try {
142 try {
143 ssl.setCheckHostname(false);
144 ssl.setCheckExpiry(false);
145 ssl.setCheckCRL(false);
146 ssl.addTrustMaterial(TrustMaterial.TRUST_ALL);
147 if (clientCert != null) {
148
149 KeyMaterial km;
150 if (certChain != null) {
151 km = new KeyMaterial(clientCert, certChain, password);
152 } else {
153 km = new KeyMaterial(clientCert, password);
154 }
155 if (password != null) {
156 for (int i = 0; i < password.length; i++) {
157 password[i] = 0;
158 }
159 }
160 ssl.setKeyMaterial(km);
161 }
162
163 if (trustChain != null) {
164 ssl.addTrustMaterial(trustChain);
165 }
166
167 ssl.setSoTimeout(10000);
168 ssl.setConnectTimeout(5000);
169
170 if (proxy != null) {
171 s = new Socket(proxy.host, proxy.port,
172 local.addr, local.port);
173 s.setSoTimeout(10000);
174 in = s.getInputStream();
175 out = s.getOutputStream();
176 String targetHost = target.host;
177 String line1 = "CONNECT " + targetHost + ":" + targetPort + " HTTP/1.1\r\n";
178 String line2 = "Proxy-Connection: keep-alive\r\n";
179 String line3 = "Host: " + targetHost + "\r\n\r\n";
180 out.write(line1.getBytes());
181 out.write(line2.getBytes());
182 out.write(line3.getBytes());
183 out.flush();
184
185 ReadLine readLine = new ReadLine(in);
186 String read1 = readLine.next();
187 if (read1.startsWith("HTTP/1.1 200")) {
188 int avail = in.available();
189 in.skip(avail);
190 Thread.yield();
191 avail = in.available();
192 while (avail != 0) {
193 in.skip(avail);
194 Thread.yield();
195 avail = in.available();
196 }
197 s = ssl.createSocket(s, targetHost, targetPort, true);
198 } else {
199 System.out.print(line1);
200 System.out.print(line2);
201 System.out.print(line3);
202 System.out.println("Server returned unexpected proxy response!");
203 System.out.println("=============================================");
204 System.out.println(read1);
205 String line = readLine.next();
206 while (line != null) {
207 System.out.println(line);
208 line = readLine.next();
209 }
210 System.exit(1);
211 }
212 } else {
213 s = ssl.createSocket(targetAddress, targetPort,
214 localAddress, localPort);
215 }
216
217 sslCipher = ((SSLSocket) s).getSession().getCipherSuite();
218 System.out.println("Cipher: " + sslCipher);
219 System.out.println("================================================================================");
220
221 String line1 = httpMethod + " " + path + " HTTP/1.1";
222 if (hostHeader == null) {
223 hostHeader = targetAddress.getHostName();
224 }
225 String line2 = "Host: " + hostHeader;
226 byte[] crlf = {'\r', '\n'};
227
228 System.out.println("Writing: ");
229 System.out.println("================================================================================");
230 System.out.println(line1);
231 System.out.println(line2);
232 System.out.println();
233
234 out = s.getOutputStream();
235 out.write(line1.getBytes());
236 out.write(crlf);
237 out.write(line2.getBytes());
238 out.write(crlf);
239 out.write(crlf);
240 out.flush();
241
242 in = s.getInputStream();
243
244 int c = in.read();
245 StringBuffer buf = new StringBuffer();
246 System.out.println("Reading: ");
247 System.out.println("================================================================================");
248 while (c >= 0) {
249 byte b = (byte) c;
250 buf.append((char) b);
251 System.out.print((char) b);
252 if (-1 == buf.toString().indexOf("\r\n\r\n")) {
253 c = in.read();
254 } else {
255 break;
256 }
257 }
258 }
259 catch (Exception e) {
260 socketException = e;
261 }
262 trustException = testTrust(ssl, sslCipher, trustChain);
263 hostnameException = testHostname(ssl);
264 crlException = testCRL(ssl);
265 expiryException = testExpiry(ssl);
266 }
267 finally {
268 if (out != null) {
269 out.close();
270 }
271 if (in != null) {
272 in.close();
273 }
274 if (s != null) {
275 s.close();
276 }
277
278 X509Certificate[] peerChain = ssl.getCurrentServerChain();
279 if (peerChain != null) {
280 String title = "Server Certificate Chain for: ";
281 title = peerChain.length > 1 ? title : "Server Certificate for: ";
282 System.out.println(title + "[" + target + "]");
283 System.out.println("================================================================================");
284 for (int i = 0; i < peerChain.length; i++) {
285 X509Certificate cert = peerChain[i];
286 String certAsString = Certificates.toString(cert);
287 String certAsPEM = Certificates.toPEMString(cert);
288 if (i > 0) {
289 System.out.println();
290 }
291 System.out.print(certAsString);
292 System.out.print(certAsPEM);
293 }
294 }
295 if (hostnameException != null) {
296 hostnameException.printStackTrace();
297 System.out.println();
298 }
299 if (crlException != null) {
300 crlException.printStackTrace();
301 System.out.println();
302 }
303 if (expiryException != null) {
304 expiryException.printStackTrace();
305 System.out.println();
306 }
307 if (trustException != null) {
308 trustException.printStackTrace();
309 System.out.println();
310 }
311 if (socketException != null) {
312 socketException.printStackTrace();
313 System.out.println();
314 }
315 }
316 }
317
318 private static Exception testTrust(SSLClient ssl, String cipher,
319 TrustChain tc) {
320 try {
321 X509Certificate[] chain = ssl.getCurrentServerChain();
322 String authType = Util.cipherToAuthType(cipher);
323 if (authType == null) {
324 // default of "RSA" just for Ping's purposes.
325 authType = "RSA";
326 }
327 if (chain != null) {
328 if (tc == null) {
329 tc = TrustMaterial.DEFAULT;
330 }
331 Object[] trustManagers = tc.getTrustManagers();
332 for (int i = 0; i < trustManagers.length; i++) {
333 JavaImpl.testTrust(trustManagers[i], chain, authType);
334 }
335 }
336 }
337 catch (Exception e) {
338 return e;
339 }
340 return null;
341 }
342
343 private static Exception testHostname(SSLClient ssl) {
344 try {
345 X509Certificate[] chain = ssl.getCurrentServerChain();
346 if (chain != null) {
347 String hostName = target.host;
348 HostnameVerifier.DEFAULT.check(hostName, chain[0]);
349 }
350 }
351 catch (Exception e) {
352 return e;
353 }
354 return null;
355 }
356
357 private static Exception testCRL(SSLClient ssl) {
358 try {
359 X509Certificate[] chain = ssl.getCurrentServerChain();
360 if (chain != null) {
361 for (int i = 0; i < chain.length; i++) {
362 Certificates.checkCRL(chain[i]);
363 }
364 }
365 }
366 catch (Exception e) {
367 return e;
368 }
369 return null;
370 }
371
372 private static Exception testExpiry(SSLClient ssl) {
373 try {
374 X509Certificate[] chain = ssl.getCurrentServerChain();
375 if (chain != null) {
376 for (int i = 0; i < chain.length; i++) {
377 chain[i].checkValidity();
378 }
379 }
380 }
381 catch (Exception e) {
382 return e;
383 }
384 return null;
385 }
386
387
388 public static class Arg implements Comparable {
389 public final String shortArg;
390 public final String longArg;
391 public final String description;
392 public final boolean isRequired;
393 private final int id;
394
395 public Arg(String s, String l, String d) {
396 this(s, l, d, false);
397 }
398
399 public Arg(String s, String l, String d, boolean isRequired) {
400 this.isRequired = isRequired;
401 this.shortArg = s;
402 this.longArg = l;
403 this.description = d;
404 this.id = ARGS.size();
405 ARGS.add(this);
406 if (s != null && s.length() >= 2) {
407 ARGS_MATCH.put(s, this);
408 }
409 if (l != null && l.length() >= 3) {
410 ARGS_MATCH.put(l, this);
411 }
412 }
413
414 public int compareTo(Object o) {
415 return id - ((Arg) o).id;
416 }
417
418 public String toString() {
419 return shortArg + "/" + longArg;
420 }
421 }
422
423 private static void parseArgs(String[] cargs) throws Exception {
424 Map args = Util.parseArgs(cargs);
425 Iterator it = args.entrySet().iterator();
426 while (it.hasNext()) {
427 Map.Entry entry = (Map.Entry) it.next();
428 Arg arg = (Arg) entry.getKey();
429 String[] values = (String[]) entry.getValue();
430 if (arg == ARG_TARGET) {
431 target = Util.toAddress(values[0], 443);
432 targetAddress = target.addr;
433 targetPort = target.port;
434 } else if (arg == ARG_BIND) {
435 local = Util.toAddress(values[0], 443);
436 localAddress = local.addr;
437 localPort = local.port;
438 } else if (arg == ARG_PROXY) {
439 proxy = Util.toAddress(values[0], 80);
440 } else if (arg == ARG_CLIENT_CERT) {
441 clientCert = new File(values[0]);
442 } else if (arg == ARG_CERT_CHAIN) {
443 certChain = new File(values[0]);
444 } else if (arg == ARG_PASSWORD) {
445 password = values[0].toCharArray();
446 } else if (arg == ARG_METHOD) {
447 httpMethod = values[0].trim();
448 } else if (arg == ARG_PATH) {
449 path = values[0].trim();
450 } else if (arg == ARG_HOST_HEADER) {
451 hostHeader = values[0].trim();
452 } else if (arg == ARG_TRUST_CERT) {
453 for (int i = 0; i < values.length; i++) {
454 File f = new File(values[i]);
455 if (f.exists()) {
456 if (trustChain == null) {
457 trustChain = new TrustChain();
458 }
459 TrustMaterial tm = new TrustMaterial(f);
460 trustChain.addTrustMaterial(tm);
461 }
462 }
463 }
464 }
465 args.clear();
466 for (int i = 0; i < cargs.length; i++) {
467 cargs[i] = null;
468 }
469
470 if (targetAddress == null) {
471 throw new IllegalArgumentException("\"" + ARG_TARGET + "\" is mandatory");
472 }
473 }
474 }