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    
032    package org.apache.commons.ssl;
033    
034    import javax.net.ssl.SSLSocket;
035    import java.io.File;
036    import java.io.InputStream;
037    import java.io.OutputStream;
038    import java.net.InetAddress;
039    import java.net.Socket;
040    import java.security.cert.X509Certificate;
041    import java.util.Collections;
042    import java.util.HashMap;
043    import java.util.Iterator;
044    import java.util.Map;
045    import java.util.SortedSet;
046    import 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     */
054    public 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    }