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    }