    001/*
    002 * $HeadURL: http://juliusdavies.ca/svn/not-yet-commons-ssl/tags/commons-ssl-0.3.15/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}