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