001/*
002 * $HeadURL: file:///opt/dev/not-yet-commons-ssl-SVN-repo/tags/commons-ssl-0.3.17/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
032package org.apache.commons.ssl;
033
034import org.apache.commons.ssl.util.ReadLine;
035
036import javax.net.ssl.SSLSocket;
037import java.io.File;
038import java.io.InputStream;
039import java.io.OutputStream;
040import java.net.InetAddress;
041import java.net.Socket;
042import java.security.cert.X509Certificate;
043import java.util.Collections;
044import java.util.HashMap;
045import java.util.Iterator;
046import java.util.Map;
047import java.util.SortedSet;
048import 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 */
056public 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}