Changeset 106af99


Ignore:
Timestamp:
Nov 21, 2010 8:37:49 PM (10 years ago)
Author:
zzz <zzz@…>
Branches:
master
Children:
9e8af73
Parents:
c8cad6a
Message:
  • SSLEepGet, Reseeder:
    • Implement additional CA loading
    • Provide facility to reuse SSL state for speed
    • Provide facility to store previously untrusted certificates
    • Add SSL reseed hosts, prefer them by default
    • Reseed message cleanup
  • build.xml:
    • Add www.cacert.org cert to the installer and updater so SSL on a.netdb.i2p2.de and c.netdb.i2p2.de will work
    • Cleanup, fix distclean error in older ants.
Files:
2 added
5 edited

Legend:

Unmodified
Added
Removed
  • LICENSE.txt

    rc8cad6a r106af99  
    6969   See licenses/LICENSE-LGPLv2.1.txt
    7070
     71   SSLEepGet:
     72   Contains some code Copyright 2006 Sun Microsystems, Inc.
     73   See licenses/LICENSE-InstallCert.txt
     74
    7175
    7276Router:
  • build.xml

    rc8cad6a r106af99  
    246246    </target>
    247247    <target name="distclean" depends="clean">
    248         <delete includeemptydirs="true" removeNotFollowedSymlinks="true" failonerror="false" >
     248        <delete includeemptydirs="true" failonerror="false" >
    249249            <fileset dir="debian/packages" followSymlinks="false" />
    250250        </delete>
     
    369369    </target>
    370370
    371     <target name="preppkg-base" depends="build, preplicenses, prepconsoleDocs">
     371    <target name="preppkg-base" depends="build, preplicenses, prepConsoleDocs, prepthemeupdates, prepCertificates">
    372372        <!-- if updater200 was run previously, it left *.pack files in pkg-temp -->
    373373        <delete>
     
    420420        <copy file="installer/resources/console.ico" todir="pkg-temp/docs/" />
    421421        <copy file="installer/resources/uninstall.ico" todir="pkg-temp/docs/" />
    422         <mkdir dir="pkg-temp/docs/themes/" />
    423         <copy todir="pkg-temp/docs/themes/" >
    424           <fileset dir="installer/resources/themes/" />
    425         </copy>
    426422        <!-- Eepsite stuff here -->
    427423        <mkdir dir="pkg-temp/eepsite" />
     
    456452        <copy file="installer/lib/launch4j/lib/XStream.LICENSE.txt" tofile="pkg-temp/licenses/LICENSE-XStream.txt" />
    457453    </target>
     454
    458455    <target name="prepthemeupdates">
    459         <!-- Migrated all Snark content to its own dir. Need to ensure snark dir excluded from console theme choices!! -->
    460         <!-- Snark's visible Assets -->
    461         <copy todir="pkg-temp/docs/themes/snark/ubergine/" >
    462             <fileset dir="installer/resources/themes/snark/ubergine/" />
    463         </copy>
    464         <!-- No need to copy these individually, we're copying the whole dir below..
    465         <copy file="installer/resources/themes/console/images/favicon.ico" todir="pkg-temp/docs/themes/console/images/" />
    466         <copy file="installer/resources/themes/console/images/i2plogo.png" todir="pkg-temp/docs/themes/console/images/" />
    467         -->
    468         <!-- Since the logo moved, we have to update the error pages -->
    469         <copy todir="pkg-temp/docs/" >
    470           <fileset dir="installer/resources/proxy" />
    471         </copy>
    472         <!-- make a "classic" theme -->
    473         <copy todir="pkg-temp/docs/themes/console/classic/" >
    474             <fileset  dir="installer/resources/themes/console/classic/" />
    475         </copy>
    476         <!-- Add dark theme -->
    477         <copy todir="pkg-temp/docs/themes/console/dark/" >
    478             <fileset  dir="installer/resources/themes/console/dark/" />
    479         </copy>
    480         <!-- Add light theme -->
    481         <copy todir="pkg-temp/docs/themes/console/light/" >
    482             <fileset  dir="installer/resources/themes/console/light/" />
    483         </copy>
    484          <!-- Add midnight theme -->
    485         <copy todir="pkg-temp/docs/themes/console/midnight/" >
    486             <fileset  dir="installer/resources/themes/console/midnight/" />
    487         </copy>       
    488         <!-- Add shared images.. these are subject to flux and change! -->
    489         <copy todir="pkg-temp/docs/themes/console/images/" >
    490             <fileset  dir="installer/resources/themes/console/images/" />
    491         </copy>         
    492         <copy todir="pkg-temp/docs/" >
    493           <fileset dir="installer/resources/readme/" includes="readme*.html" />
     456        <copy todir="pkg-temp/docs/themes/" >
     457            <fileset dir="installer/resources/themes/" />
     458        </copy>
     459    </target>
     460
     461    <!-- SSL Certs -->
     462    <target name="prepCertificates">
     463        <copy todir="pkg-temp/certificates/" >
     464          <fileset dir="installer/resources/certificates/" />
    494465        </copy>
    495466    </target>
     
    501472        </tar>
    502473    </target>
     474
    503475    <target name="deletepkg-temp">
    504476        <delete dir="pkg-temp" />
    505477    </target>
    506     <target name="prepconsoleDocs" depends="prepgeoupdate">
     478
     479    <!-- readme and proxy error page files, GeoIP files, and flag icons -->
     480    <target name="prepConsoleDocs" depends="prepConsoleDocUpdates, prepgeoupdate" />
     481
     482    <!-- readme and proxy error page files -->
     483    <target name="prepConsoleDocUpdates">
    507484        <copy todir="pkg-temp/docs/" >
    508485          <fileset dir="installer/resources/readme/" includes="readme*.html" />
    509           <fileset dir="installer/resources/proxy" />
    510         </copy>
    511     </target>
    512     <target name="consoleDocs" depends="deletepkg-temp, prepconsoleDocs">
     486          <fileset dir="installer/resources/proxy/" includes="*.ht" />
     487        </copy>
     488    </target>
     489
     490    <target name="consoleDocs" depends="deletepkg-temp, prepConsoleDocs">
    513491        <zip destfile="docs.zip" basedir="pkg-temp" whenempty="fail" />
    514492    </target>
     
    561539        <zip destfile="i2pupdate.zip" basedir="pkg-temp" />
    562540    </target>
    563     <target name="prepupdate" depends="build2, prepupdateSmall">
     541
     542    <target name="prepupdate" depends="build2, prepupdateSmall, prepConsoleDocUpdates, prepCertificates">
    564543        <copy file="build/BOB.jar" todir="pkg-temp/lib/" />
    565544        <copy file="build/sam.jar" todir="pkg-temp/lib/" />
     
    588567       -->
    589568    </target>
     569
    590570    <target name="prepupdateSmall" depends="buildSmall, prepupdateRouter, prepthemeupdates">
    591571        <copy file="build/i2ptunnel.jar" todir="pkg-temp/lib/" />
     
    602582       <copy file="installer/resources/countries.txt" todir="pkg-temp/geoip/" />
    603583    </target>
     584
    604585    <target name="prepupdateRouter" depends="buildrouter, deletepkg-temp">
    605586        <copy file="build/i2p.jar" todir="pkg-temp/lib/" />
    606587        <copy file="build/router.jar" todir="pkg-temp/lib/" />
    607588    </target>
     589
     590    <!-- GeoIP files and flag icons -->
    608591    <target name="prepgeoupdate">
    609592        <copy file="installer/resources/geoip.txt" todir="pkg-temp/geoip/" />
     
    613596        </copy>
    614597    </target>
     598
    615599    <target name="prepjupdate" depends="prepupdate, buildWEB">
    616600        <copy file="build/jasper-compiler.jar" todir="pkg-temp/lib/" />
  • core/java/src/net/i2p/util/EepGet.java

    rc8cad6a r106af99  
    457457                    _listeners.get(i).attemptFailed(_url, _bytesTransferred, _bytesRemaining, _currentAttempt, _numRetries, ioe);
    458458                if (_log.shouldLog(Log.WARN))
    459                     _log.warn("ERR: doFetch failed " + ioe);
     459                    _log.warn("ERR: doFetch failed ", ioe);
    460460                if (ioe instanceof MalformedURLException)
    461461                    _keepFetching = false;
  • core/java/src/net/i2p/util/SSLEepGet.java

    rc8cad6a r106af99  
    11package net.i2p.util;
    22
     3/*
     4 * Contains code from:
     5 * http://blogs.sun.com/andreas/resource/InstallCert.java
     6 * http://blogs.sun.com/andreas/entry/no_more_unable_to_find
     7 *
     8 * ===============
     9 *
     10 * Copyright 2006 Sun Microsystems, Inc.  All Rights Reserved.
     11 *
     12 * Redistribution and use in source and binary forms, with or without
     13 * modification, are permitted provided that the following conditions
     14 * are met:
     15 *
     16 *   - Redistributions of source code must retain the above copyright
     17 *     notice, this list of conditions and the following disclaimer.
     18 *
     19 *   - Redistributions in binary form must reproduce the above copyright
     20 *     notice, this list of conditions and the following disclaimer in the
     21 *     documentation and/or other materials provided with the distribution.
     22 *
     23 *   - Neither the name of Sun Microsystems nor the names of its
     24 *     contributors may be used to endorse or promote products derived
     25 *     from this software without specific prior written permission.
     26 *
     27 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
     28 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
     29 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     30 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
     31 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     32 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     33 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
     34 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
     35 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
     36 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
     37 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     38 */
     39
    340import java.io.File;
     41import java.io.FileInputStream;
    442import java.io.FileOutputStream;
    543import java.io.IOException;
     44import java.io.InputStream;
    645import java.io.OutputStream;
     46import java.io.PrintWriter;
    747import java.net.MalformedURLException;
    848import java.net.URL;
     49import java.security.KeyStore;
     50import java.security.GeneralSecurityException;
     51import java.security.cert.Certificate;
     52import java.security.cert.CertificateException;
     53import java.security.cert.CertificateEncodingException;
     54import java.security.cert.CertificateExpiredException;
     55import java.security.cert.CertificateNotYetValidException;
     56import java.security.cert.CertificateFactory;
     57import java.security.cert.X509Certificate;
     58import java.util.Enumeration;
     59import javax.net.ssl.SSLContext;
     60import javax.net.ssl.SSLHandshakeException;
    961import javax.net.ssl.SSLSocketFactory;
    10 
    11 // all part of the CA experiment below
    12 //import java.io.FileInputStream;
    13 //import java.io.InputStream;
    14 //import java.util.Enumeration;
    15 //import java.security.KeyStore;
    16 //import java.security.GeneralSecurityException;
    17 //import java.security.cert.CertificateExpiredException;
    18 //import java.security.cert.CertificateNotYetValidException;
    19 //import java.security.cert.CertificateFactory;
    20 //import java.security.cert.X509Certificate;
    21 //import javax.net.ssl.KeyManagerFactory;
    22 //import javax.net.ssl.SSLContext;
     62import javax.net.ssl.TrustManager;
     63import javax.net.ssl.TrustManagerFactory;
     64import javax.net.ssl.X509TrustManager;
    2365
    2466import net.i2p.I2PAppContext;
     67import net.i2p.data.Base64;
    2568import net.i2p.data.DataHelper;
    2669
     
    2871 * HTTPS only, non-proxied only, no retries, no min and max size options, no timeout option
    2972 * Fails on 301 or 302 (doesn't follow redirect)
    30  * Fails on self-signed certs (must have a valid cert chain)
     73 * Fails on bad certs (must have a valid cert chain)
     74 * Self-signed certs or CAs not in the JVM key store must be loaded to be trusted.
     75 *
     76 * Since 0.8.2, loads additional trusted CA certs from $I2P/certificates/ and ~/.i2p/certificates/
    3177 *
    3278 * @author zzz
     
    3480 */
    3581public class SSLEepGet extends EepGet {
    36     //private static SSLContext _sslContext;
    37 
     82    /** if true, save cert chain on cert error */
     83    private boolean _saveCerts;
     84    /** true if called from main(), used for logging */
     85    private boolean _commandLine;
     86    /** may be null if init failed */
     87    private final SSLContext _sslContext;
     88    /** may be null if init failed */
     89    private SavingTrustManager _stm;
     90
     91    /**
     92     *  A new SSLEepGet with a new SSLState
     93     */
    3894    public SSLEepGet(I2PAppContext ctx, OutputStream outputStream, String url) {
     95        this(ctx, outputStream, url, null);
     96    }
     97
     98    /**
     99     *  @param state an SSLState retrieved from a previous SSLEepGet with getSSLState(), or null.
     100     *               This makes repeated fetches from the same host MUCH faster,
     101     *               and prevents repeated key store loads even for different hosts.
     102     *  @since 0.8.2
     103     */
     104    public SSLEepGet(I2PAppContext ctx, OutputStream outputStream, String url, SSLState state) {
    39105        // we're using this constructor:
    40106        // public EepGet(I2PAppContext ctx, boolean shouldProxy, String proxyHost, int proxyPort, int numRetries, long minSize, long maxSize, String outputFile, OutputStream outputStream, String url, boolean allowCaching, String etag, String postData) {
    41107        super(ctx, false, null, -1, 0, -1, -1, null, outputStream, url, true, null, null);
     108        if (state != null && state.context != null)
     109            _sslContext = state.context;
     110        else
     111            _sslContext = initSSLContext();
     112        if (_sslContext == null)
     113            _log.error("Failed to initialize custom SSL context, using default context");
    42114    }
    43115   
    44116    /**
    45      * SSLEepGet url
    46      * no command line options supported
     117     * SSLEepGet https://foo/bar
     118     *   or to save cert chain:
     119     * SSLEepGet -s https://foo/bar
    47120     */
    48121    public static void main(String args[]) {
    49122        String url = null;
     123        boolean saveCerts = false;
    50124        try {
    51125            for (int i = 0; i < args.length; i++) {
    52                 if (args[i].startsWith("-")) {
     126                if (args[i].equals("-s")) {
     127                    saveCerts = true;
     128                } else if (args[i].startsWith("-")) {
    53129                    usage();
    54130                    return;
     
    78154        }
    79155
    80         /******
    81          *  This is all an experiment to add a CA cert loaded from a file so we can use
    82          *  selfsigned certs on our servers.
    83          *  But it's failing.
    84          *  Run as java -Djava.security.debug=certpath -Djavax.net.debug=trustmanager -cp $I2P/lib/i2p.jar net.i2p.util.SSLEepGet "$@"
    85          *  to see the problems. It isn't including the added cert in the Trust Anchor list.
    86          ******/
    87 
    88         /******
    89         String foo = System.getProperty("javax.net.ssl.keyStore");
    90         if (foo == null) {
    91             File cacerts = new File(System.getProperty("java.home"), "lib/security/cacerts");
    92             foo = cacerts.getAbsolutePath();
    93         }
    94         System.err.println("Location is: " + foo);
    95         try {
    96             KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
    97             try {
    98                 InputStream fis = new FileInputStream(foo);
    99                 ks.load(fis, "changeit".toCharArray());
    100                 fis.close();
    101             } catch (GeneralSecurityException gse) {
    102                 System.err.println("KS error, no default keys: " + gse);
    103                 ks.load(null, "changeit".toCharArray());
    104             } catch (IOException ioe) {
    105                 System.err.println("IO error, no default keys: " + ioe);
    106                 ks.load(null, "changeit".toCharArray());
    107             }
    108 
    109             addCert(ks, "cacert");
    110 
    111             for(Enumeration<String> e = ks.aliases(); e.hasMoreElements();) {
    112                 String alias = e.nextElement();
    113                 System.err.println("Aliases: " + alias + " isCert? " + ks.isCertificateEntry(alias));
    114             }
    115 
    116             KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
    117             kmf.init(ks, "".toCharArray());
    118             SSLContext sslc = SSLContext.getInstance("SSL");
    119             sslc.init(kmf.getKeyManagers(), null, null);
    120             _sslContext = sslc;
    121         } catch (GeneralSecurityException gse) {
    122             System.err.println("KS error: " + gse);
    123             return;
    124         } catch (IOException ioe) {
    125             System.err.println("IO error: " + ioe);
    126             return;
    127         }
    128         *******/
    129 
    130         EepGet get = new SSLEepGet(I2PAppContext.getGlobalContext(), out, url);
     156        SSLEepGet get = new SSLEepGet(I2PAppContext.getGlobalContext(), out, url);
     157        if (saveCerts)
     158            get._saveCerts = true;
     159        get._commandLine = true;
    131160        get.addStatusListener(get.new CLIStatusListener(1024, 40));
    132161        get.fetch(45*1000, -1, 60*1000);
     
    134163   
    135164    private static void usage() {
    136         System.err.println("SSLEepGet url");
    137     }
    138 
    139 /******
    140     private static boolean addCert(KeyStore ks, String file) {
    141 
     165        System.err.println("Usage: SSLEepGet https://url");
     166        System.err.println("To save unknown certs, use: SSLEepGet -s https://url");
     167    }
     168
     169    /**
     170     *  Loads certs from location of javax.net.ssl.keyStore property,
     171     *  else from $JAVA_HOME/lib/security/jssacacerts,
     172     *  else from $JAVA_HOME/lib/security/cacerts.
     173     *
     174     *  Then adds certs found in the $I2P/certificates/ directory
     175     *  and in the ~/.i2p/certificates/ directory.
     176     *
     177     *  @return null on failure
     178     *  @since 0.8.2
     179     */
     180    private SSLContext initSSLContext() {
     181        KeyStore ks;
     182        try {
     183            ks = KeyStore.getInstance(KeyStore.getDefaultType());
     184        } catch (GeneralSecurityException gse) {
     185            _log.error("Key Store init error", gse);
     186            return null;
     187        }
     188        boolean success = false;
     189        String override = System.getProperty("javax.net.ssl.keyStore");
     190        if (override != null)
     191            success = loadCerts(new File(override), ks);
     192        if (!success)
     193            success = loadCerts(new File(System.getProperty("java.home"), "lib/security/jssecacerts"), ks);
     194        if (!success)
     195            success = loadCerts(new File(System.getProperty("java.home"), "lib/security/cacerts"), ks);
     196
     197        if (!success) {
     198            _log.error("All key store loads failed, will only load local certificates");
     199        } else if (_log.shouldLog(Log.INFO)) {
     200            int count = 0;
    142201            try {
    143                 InputStream fis = new FileInputStream(file);
    144                 CertificateFactory cf = CertificateFactory.getInstance("X.509");
    145                 X509Certificate cert = (X509Certificate)cf.generateCertificate(fis);
    146                 fis.close();
    147                 System.err.println("Adding cert, Issuer: " + cert.getIssuerX500Principal());
    148                 try {
    149                     cert.checkValidity();
    150                 } catch (CertificateExpiredException cee) {
    151                     System.err.println("Warning - expired cert: " + cee);
    152                 } catch (CertificateNotYetValidException cnyve) {
    153                     System.err.println("Warning - not yet valid cert: " + cnyve);
     202                for(Enumeration<String> e = ks.aliases(); e.hasMoreElements();) {
     203                    String alias = e.nextElement();
     204                    if (ks.isCertificateEntry(alias))
     205                        count++;
    154206                }
    155                 // use file name as alias
    156                 ks.setCertificateEntry(file, cert);
    157             } catch (GeneralSecurityException gse) {
    158                 System.err.println("Read cert error: " + gse);
     207            } catch (Exception foo) {}
     208            _log.info("Loaded " + count + " default trusted certificates");
     209        }
     210
     211        File dir = new File(_context.getBaseDir(), "certificates");
     212        int adds = addCerts(dir, ks);
     213        int totalAdds = adds;
     214        if (adds > 0 && _log.shouldLog(Log.INFO))
     215            _log.info("Loaded " + adds + " trusted certificates from " + dir.getAbsolutePath());
     216        if (!_context.getBaseDir().getAbsolutePath().equals(_context.getConfigDir().getAbsolutePath())) {
     217            dir = new File(_context.getConfigDir(), "certificates");
     218            adds = addCerts(dir, ks);
     219            totalAdds += adds;
     220            if (adds > 0 && _log.shouldLog(Log.INFO))
     221                _log.info("Loaded " + adds + " trusted certificates from " + dir.getAbsolutePath());
     222        }
     223        dir = new File(System.getProperty("user.dir"));
     224        if (!_context.getBaseDir().getAbsolutePath().equals(dir.getAbsolutePath())) {
     225            dir = new File(_context.getConfigDir(), "certificates");
     226            adds = addCerts(dir, ks);
     227            totalAdds += adds;
     228            if (adds > 0 && _log.shouldLog(Log.INFO))
     229                _log.info("Loaded " + adds + " trusted certificates from " + dir.getAbsolutePath());
     230        }
     231        if (_log.shouldLog(Log.INFO))
     232            _log.info("Loaded total of " + totalAdds + " new trusted certificates");
     233
     234        try {
     235            SSLContext sslc = SSLContext.getInstance("TLS");
     236            TrustManagerFactory tmf =   TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
     237            tmf.init(ks);
     238            X509TrustManager defaultTrustManager = (X509TrustManager)tmf.getTrustManagers()[0];
     239            _stm = new SavingTrustManager(defaultTrustManager);
     240            sslc.init(null, new TrustManager[] {_stm}, null);
     241            return sslc;
     242        } catch (GeneralSecurityException gse) {
     243            _log.error("Key Store update error", gse);
     244        }
     245        return null;
     246    }
     247
     248    /**
     249     *  Load all X509 Certs from a key store File into a KeyStore
     250     *  Note that each call reinitializes the KeyStore
     251     *
     252     *  @return success
     253     *  @since 0.8.2
     254     */
     255    private boolean loadCerts(File file, KeyStore ks) {
     256        if (!file.exists())
     257            return false;
     258        InputStream fis = null;
     259        try {
     260            fis = new FileInputStream(file);
     261            // "changeit" is the default password
     262            ks.load(fis, "changeit".toCharArray());
     263        } catch (GeneralSecurityException gse) {
     264            _log.error("KeyStore load error, no default keys: " + file.getAbsolutePath(), gse);
     265            try {
     266                // not clear if null is allowed for password
     267                ks.load(null, "changeit".toCharArray());
     268            } catch (Exception foo) {}
     269            return false;
     270        } catch (IOException ioe) {
     271            _log.error("KeyStore load error, no default keys: " + file.getAbsolutePath(), ioe);
     272            try {
     273                ks.load(null, "changeit".toCharArray());
     274            } catch (Exception foo) {}
     275            return false;
     276        } finally {
     277            try { if (fis != null) fis.close(); } catch (IOException foo) {}
     278        }
     279        return true;
     280    }
     281
     282    /**
     283     *  Load all X509 Certs from a directory and add them to the
     284     *  trusted set of certificates in the key store
     285     *
     286     *  @return number successfully added
     287     *  @since 0.8.2
     288     */
     289    private int addCerts(File dir, KeyStore ks) {
     290        if (_log.shouldLog(Log.INFO))
     291            _log.info("Looking for X509 Certificates in " + dir.getAbsolutePath());
     292        int added = 0;
     293        if (dir.exists() && dir.isDirectory()) {
     294            File[] files = dir.listFiles();
     295            if (files != null) {
     296                for (int i = 0; i < files.length; i++) {
     297                    File f = files[i];
     298                    if (!f.isFile())
     299                        continue;
     300                    // use file name as alias
     301                    // https://www.sslshopper.com/ssl-converter.html
     302                    // No idea if all these formats can actually be read by CertificateFactory
     303                    String alias = f.getName().toLowerCase();
     304                    if (alias.endsWith(".crt") || alias.endsWith(".pem") || alias.endsWith(".key") ||
     305                        alias.endsWith(".der") || alias.endsWith(".key") || alias.endsWith(".p7b") ||
     306                        alias.endsWith(".p7c") || alias.endsWith(".pfx") || alias.endsWith(".p12"))
     307                        alias = alias.substring(0, alias.length() - 4);
     308                    boolean success = addCert(f, alias, ks);
     309                    if (success)
     310                        added++;
     311                }
     312            }
     313        }
     314        return added;
     315    }
     316
     317    /**
     318     *  Load an X509 Cert from a file and add it to the
     319     *  trusted set of certificates in the key store
     320     *
     321     *  @return success
     322     *  @since 0.8.2
     323     */
     324    private boolean addCert(File file, String alias, KeyStore ks) {
     325        InputStream fis = null;
     326        try {
     327            fis = new FileInputStream(file);
     328            CertificateFactory cf = CertificateFactory.getInstance("X.509");
     329            X509Certificate cert = (X509Certificate)cf.generateCertificate(fis);
     330            if (_log.shouldLog(Log.INFO)) {
     331                _log.info("Read X509 Certificate from " + file.getAbsolutePath() +
     332                          " Issuer: " + cert.getIssuerX500Principal() +
     333                          "; Valid From: " + cert.getNotBefore() +
     334                          " To: " + cert.getNotAfter());
     335            }
     336            try {
     337                cert.checkValidity();
     338            } catch (CertificateExpiredException cee) {
     339                _log.error("Rejecting expired X509 Certificate: " + file.getAbsolutePath(), cee);
    159340                return false;
    160             } catch (IOException ioe) {
    161                 System.err.println("Read cert error: " + ioe);
     341            } catch (CertificateNotYetValidException cnyve) {
     342                _log.error("Rejecting X509 Certificate not yet valid: " + file.getAbsolutePath(), cnyve);
    162343                return false;
    163344            }
     345            ks.setCertificateEntry(alias, cert);
     346            if (_log.shouldLog(Log.INFO))
     347                _log.info("Now trusting X509 Certificate, Issuer: " + cert.getIssuerX500Principal());
     348        } catch (GeneralSecurityException gse) {
     349            _log.error("Error reading X509 Certificate: " + file.getAbsolutePath(), gse);
     350            return false;
     351        } catch (IOException ioe) {
     352            _log.error("Error reading X509 Certificate: " + file.getAbsolutePath(), ioe);
     353            return false;
     354        } finally {
     355            try { if (fis != null) fis.close(); } catch (IOException foo) {}
     356        }
    164357        return true;
    165358    }
    166 *******/
    167359   
     360    /**
     361     *  From http://blogs.sun.com/andreas/resource/InstallCert.java
     362     *  This just saves the certificate chain for later inspection.
     363     *  @since 0.8.2
     364     */
     365    private static class SavingTrustManager implements X509TrustManager {
     366        private final X509TrustManager tm;
     367        private X509Certificate[] chain;
     368
     369        SavingTrustManager(X509TrustManager tm) {
     370            this.tm = tm;
     371        }
     372
     373        public X509Certificate[] getAcceptedIssuers() {
     374            throw new UnsupportedOperationException();
     375        }
     376
     377        public void checkClientTrusted(X509Certificate[] chain, String authType)
     378                throws CertificateException {
     379            throw new UnsupportedOperationException();
     380        }
     381
     382        public void checkServerTrusted(X509Certificate[] chain, String authType)
     383                throws CertificateException {
     384            this.chain = chain;
     385            tm.checkServerTrusted(chain, authType);
     386        }
     387    }
     388
     389    /**
     390     *  Modified from http://blogs.sun.com/andreas/resource/InstallCert.java
     391     *  @since 0.8.2
     392     */
     393    private static void saveCerts(String host, SavingTrustManager stm) {
     394        X509Certificate[] chain = stm.chain;
     395        if (chain == null) {
     396            System.out.println("Could not obtain server certificate chain");
     397            return;
     398        }
     399        for (int k = 0; k < chain.length; k++) {
     400            X509Certificate cert = chain[k];
     401            String name = host + '-' + (k + 1) + ".crt";
     402            System.out.println("NOTE: Saving untrusted X509 certificate as " + name);
     403            System.out.println("      Issuer:     " + cert.getIssuerX500Principal());
     404            System.out.println("      Valid From: " + cert.getNotBefore());
     405            System.out.println("      Valid To:   " + cert.getNotAfter());
     406            try {
     407                cert.checkValidity();
     408            } catch (Exception e) {
     409                System.out.println("      WARNING: Certificate is not currently valid, it cannot be used");
     410            }
     411            saveCert(cert, new File(name));
     412        }
     413        System.out.println("NOTE: To trust them, copy the certificate file(s) to the certificates directory and rerun without the -s option");
     414        System.out.println("NOTE: EepGet failed, certificate error follows:");
     415    }
     416
     417    private static final int LINE_LENGTH = 64;
     418
     419    /**
     420     *  Modified from:
     421     *  http://www.exampledepot.com/egs/java.security.cert/ExportCert.html
     422     *
     423     *  This method writes a certificate to a file in base64 format.
     424     *  @since 0.8.2
     425     */
     426    private static void saveCert(Certificate cert, File file) {
     427        OutputStream os = null;
     428        try {
     429           // Get the encoded form which is suitable for exporting
     430           byte[] buf = cert.getEncoded();
     431           os = new FileOutputStream(file);
     432           PrintWriter wr = new PrintWriter(os);
     433           wr.println("-----BEGIN CERTIFICATE-----");
     434           String b64 = Base64.encode(buf, true);     // true = use standard alphabet
     435           for (int i = 0; i < b64.length(); i += LINE_LENGTH) {
     436               wr.println(b64.substring(i, Math.min(i + LINE_LENGTH, b64.length())));
     437           }
     438           wr.println("-----END CERTIFICATE-----");
     439           wr.flush();
     440        } catch (CertificateEncodingException cee) {
     441            System.out.println("Error writing X509 Certificate " + file.getAbsolutePath() + ' ' + cee);
     442        } catch (IOException ioe) {
     443            System.out.println("Error writing X509 Certificate " + file.getAbsolutePath() + ' ' + ioe);
     444        } finally {
     445            try { if (os != null) os.close(); } catch (IOException foo) {}
     446        }
     447    }
     448
     449    /**
     450     *  An opaque class for the caller to pass to repeated instantiations of SSLEepGet.
     451     *  @since 0.8.2
     452     */
     453    public static class SSLState {
     454        private SSLContext context;
     455
     456        private SSLState(SSLContext ctx) {
     457             context = ctx;
     458        }
     459    }
     460
     461    /**
     462     *  Pass this back to the next SSLEepGet constructor for faster fetches.
     463     *  This may be called either after the constructor or after the fetch.
     464     *  @since 0.8.2
     465     */
     466    public SSLState getSSLState() {
     467        return new SSLState(_sslContext);
     468    }
     469
     470    ///// end of all the SSL stuff
     471    ///// start of overrides
     472
    168473    @Override
    169474    protected void doFetch(SocketTimeout timeout) throws IOException {
     
    289594        //try {
    290595            URL url = new URL(_actualURL);
     596            String host = null;
     597            int port = 0;
    291598            if ("https".equals(url.getProtocol())) {
    292                 String host = url.getHost();
    293                 int port = url.getPort();
     599                host = url.getHost();
     600                port = url.getPort();
    294601                if (port == -1)
    295602                    port = 443;
    296                 // part of the experiment above
    297                 //if (_sslContext != null)
    298                 //    _proxy = _sslContext.getSocketFactory().createSocket(host, port);
    299                 //else
     603                if (_sslContext != null)
     604                    _proxy = _sslContext.getSocketFactory().createSocket(host, port);
     605                else
    300606                    _proxy = SSLSocketFactory.getDefault().createSocket(host, port);
    301607            } else {
     
    310616        _proxyOut = _proxy.getOutputStream();
    311617       
    312         _proxyOut.write(DataHelper.getUTF8(req));
    313         _proxyOut.flush();
     618        // This is where the cert errors happen
     619        try {
     620            _proxyOut.write(DataHelper.getUTF8(req));
     621            _proxyOut.flush();
     622        } catch (SSLHandshakeException sslhe) {
     623            // this maybe would be better done in the catch in super.fetch(), but
     624            // then we'd have to copy it all over here.
     625            _log.error("SSL negotiation error with " + host + ':' + port +
     626                       " - self-signed certificate or untrusted certificate authority?", sslhe);
     627            if (_saveCerts && _stm != null)
     628                saveCerts(host, _stm);
     629            else if (_commandLine) {
     630                System.out.println("FAILED (probably due to untrusted certificates) - Run with -s option to save certificates");
     631            }
     632            // this is an IOE
     633            throw sslhe;
     634        }
    314635       
    315636        if (_log.shouldLog(Log.DEBUG))
  • router/java/src/net/i2p/router/networkdb/reseed/Reseeder.java

    rc8cad6a r106af99  
    3030 * set.  It always writes to ./netDb/, so don't mess with that.
    3131 *
     32 * This is somewhat complicated by trying to log to three places - the console,
     33 * the router log, and the wrapper log.
    3234 */
    3335public class Reseeder {
     
    4143    private static final String DEFAULT_SEED_URL =
    4244              "http://a.netdb.i2p2.de/,http://b.netdb.i2p2.de/,http://c.netdb.i2p2.de/," +
    43               "http://reseed.i2p-projekt.de/,http://i2pbote.net/netDb/,http://r31453.ovh.net/static_media/netDb/";
     45              "http://reseed.i2p-projekt.de/,http://www.i2pbote.net/netDb/,http://r31453.ovh.net/static_media/netDb/";
     46
     47    /** @since 0.8.2 */
     48    private static final String DEFAULT_SSL_SEED_URL =
     49              "https://a.netdb.i2p2.de/,https://c.netdb.i2p2.de/," +
     50              "https://www.i2pbote.net/netDb/";
     51
    4452    private static final String PROP_INPROGRESS = "net.i2p.router.web.ReseedHandler.reseedInProgress";
     53    /** the console shows this message while reseedInProgress == false */
    4554    private static final String PROP_ERROR = "net.i2p.router.web.ReseedHandler.errorMessage";
     55    /** the console shows this message while reseedInProgress == true */
    4656    private static final String PROP_STATUS = "net.i2p.router.web.ReseedHandler.statusMessage";
    4757    public static final String PROP_PROXY_HOST = "router.reseedProxyHost";
    4858    public static final String PROP_PROXY_PORT = "router.reseedProxyPort";
     59    /** @since 0.8.2 */
     60    public static final String PROP_PROXY_ENABLE = "router.reseedProxyEnable";
     61    /** @since 0.8.2 */
     62    public static final String PROP_SSL_DISABLE = "router.reseedSSLDisable";
     63
    4964    private static final String RESEED_TIPS =
    5065            _x("Ensure that nothing blocks outbound HTTP, check <a target=\"_top\" href=\"logs.jsp\">logs</a> " +
     
    6479                return;
    6580            } else {
    66                 System.setProperty(PROP_INPROGRESS, "true");
    6781                // set to daemon so it doesn't hang a shutdown
    6882                Thread reseed = new I2PAppThread(_reseedRunner, "Reseed", true);
     
    7791        private String _proxyHost;
    7892        private int _proxyPort;
     93        private SSLEepGet.SSLState _sslState;
    7994
    8095        public ReseedRunner() {
    8196            _isRunning = false;
     97            System.clearProperty(PROP_ERROR);
    8298            System.setProperty(PROP_STATUS, _("Reseeding"));
     99            System.setProperty(PROP_INPROGRESS, "true");
    83100        }
    84101        public boolean isRunning() { return _isRunning; }
     102
     103        /*
     104         * Do it.
     105         * We update PROP_ERROR here.
     106         */
    85107        public void run() {
    86108            _isRunning = true;
    87             _proxyHost = _context.getProperty(PROP_PROXY_HOST);
    88             _proxyPort = _context.getProperty(PROP_PROXY_PORT, -1);
     109            _sslState = null;  // start fresh
     110            if (_context.getBooleanProperty(PROP_PROXY_ENABLE)) {
     111                _proxyHost = _context.getProperty(PROP_PROXY_HOST);
     112                _proxyPort = _context.getProperty(PROP_PROXY_PORT, -1);
     113            }
    89114            System.out.println("Reseed start");
    90             reseed(false);
    91             System.out.println("Reseed complete");
     115            int total = reseed(false);
     116            if (total >= 50) {
     117                System.out.println("Reseed complete, " + total + " received");
     118                System.clearProperty(PROP_ERROR);
     119            } else if (total > 0) {
     120                System.out.println("Reseed complete, only " + total + " received");
     121                System.setProperty(PROP_ERROR, ngettext("Reseed fetched only 1 router.",
     122                                                        "Reseed fetched only {0} routers.", total));
     123            } else {
     124                System.out.println("Reseed failed, check network connection");
     125                System.out.println(
     126                     "Ensure that nothing blocks outbound HTTP, check the logs, " +
     127                     "and if nothing helps, read the FAQ about reseeding manually.");
     128                System.setProperty(PROP_ERROR, _("Reseed failed.") + ' '  + _(RESEED_TIPS));
     129            }   
    92130            System.setProperty(PROP_INPROGRESS, "false");
     131            System.clearProperty(PROP_STATUS);
     132            _sslState = null;  // don't hold ref
    93133            _isRunning = false;
    94134        }
     
    113153        * save them into this router's netDb dir.
    114154        *
     155        * - If list specified in the properties, use it randomly, without regard to http/https
     156        * - If SSL not disabled, use the https randomly then
     157        *   the http randomly
     158        * - Otherwise just the http randomly.
     159        *
     160        * @param echoStatus apparently always false
     161        * @return count of routerinfos successfully fetched
    115162        */
    116         private void reseed(boolean echoStatus) {
    117             List URLList = new ArrayList();
    118             String URLs = _context.getProperty("i2p.reseedURL", DEFAULT_SEED_URL);
     163        private int reseed(boolean echoStatus) {
     164            List<String> URLList = new ArrayList();
     165            String URLs = _context.getProperty("i2p.reseedURL");
     166            boolean defaulted = URLs == null;
     167            boolean SSLDisable = _context.getBooleanProperty(PROP_SSL_DISABLE);
     168            if (defaulted) {
     169                if (SSLDisable)
     170                    URLs = DEFAULT_SEED_URL;
     171                else
     172                    URLs = DEFAULT_SSL_SEED_URL;
     173            }
    119174            StringTokenizer tok = new StringTokenizer(URLs, " ,");
    120175            while (tok.hasMoreTokens())
    121176                URLList.add(tok.nextToken().trim());
    122177            Collections.shuffle(URLList);
    123             for (int i = 0; i < URLList.size() && _isRunning; i++)
    124                 reseedOne((String) URLList.get(i), echoStatus);
     178            if (defaulted && !SSLDisable) {
     179                // put the non-SSL at the end of the SSL
     180                List<String> URLList2 = new ArrayList();
     181                tok = new StringTokenizer(DEFAULT_SSL_SEED_URL, " ,");
     182                while (tok.hasMoreTokens())
     183                    URLList2.add(tok.nextToken().trim());
     184                Collections.shuffle(URLList2);
     185                URLList.addAll(URLList2);
     186            }
     187            int total = 0;
     188            for (int i = 0; i < URLList.size() && _isRunning; i++) {
     189                String url = URLList.get(i);
     190                int dl = reseedOne(url, echoStatus);
     191                if (dl > 0) {
     192                    total += dl;
     193                    // remove alternate version if we haven't tried it yet
     194                    String alt;
     195                    if (url.startsWith("http://"))
     196                        alt = url.replace("http://", "https://");
     197                    else
     198                        alt = url.replace("https://", "http://");
     199                    int idx = URLList.indexOf(alt);
     200                    if (idx > i)
     201                        URLList.remove(i);
     202                }
     203            }
     204            return total;
    125205        }
    126206
     
    139219         * Jetty directory listings are not compatible, as they look like
    140220         * HREF="/full/path/to/routerInfo-...
     221         *
     222         * We update PROP_STATUS here.
     223         *
     224         * @param echoStatus apparently always false
     225         * @return count of routerinfos successfully fetched
    141226         **/
    142         private void reseedOne(String seedURL, boolean echoStatus) {
    143 
     227        private int reseedOne(String seedURL, boolean echoStatus) {
    144228            try {
    145                 System.setProperty(PROP_ERROR, "");
    146229                System.setProperty(PROP_STATUS, _("Reseeding: fetching seed URL."));
    147                 System.err.println("Reseed from " + seedURL);
     230                System.err.println("Reseeding from " + seedURL);
    148231                URL dir = new URL(seedURL);
    149232                byte contentRaw[] = readURL(dir);
    150233                if (contentRaw == null) {
    151                     System.setProperty(PROP_ERROR,
    152                         _("Last reseed failed fully (failed reading seed URL).") + ' '  +
    153                         _(RESEED_TIPS));
    154234                    // Logging deprecated here since attemptFailed() provides better info
    155                     _log.debug("Failed reading seed URL: " + seedURL);
    156                     return;
     235                    _log.warn("Failed reading seed URL: " + seedURL);
     236                    System.err.println("Reseed got no router infos from " + seedURL);
     237                    return 0;
    157238                }
    158239                String content = new String(contentRaw);
     
    174255                }
    175256                if (total <= 0) {
    176                     _log.error("Read " + contentRaw.length + " bytes from seed " + seedURL + ", but found no routerInfo URLs.");
    177                     System.setProperty(PROP_ERROR,
    178                         _("Last reseed failed fully (no routerInfo URLs at seed URL).") + ' ' +
    179                         _(RESEED_TIPS));
    180                     return;
     257                    _log.warn("Read " + contentRaw.length + " bytes from seed " + seedURL + ", but found no routerInfo URLs.");
     258                    System.err.println("Reseed got no router infos from " + seedURL);
     259                    return 0;
    181260                }
    182261
     
    202281                    }
    203282                }
    204                 System.err.println("Reseed got " + fetched + " router infos from " + seedURL);
    205                
    206                 int failPercent = 100 * errors / total;
    207                
    208                 // Less than 10% of failures is considered success,
    209                 // because some routerInfos will always fail.
    210                 if ((failPercent >= 10) && (failPercent < 90)) {
    211                     System.setProperty(PROP_ERROR,
    212                         _("Last reseed failed partly ({0}% of {1}).", failPercent, total) + ' ' +
    213                         _(RESEED_TIPS));
    214                 }
    215                 if (failPercent >= 90) {
    216                     System.setProperty(PROP_ERROR,
    217                         _("Last reseed failed ({0}% of {1}).", failPercent, total) + ' ' +
    218                         _(RESEED_TIPS));
    219                 }
     283                System.err.println("Reseed got " + fetched + " router infos from " + seedURL + " with " + errors + " errors");
     284
    220285                if (fetched > 0)
    221286                    _context.netDb().rescan();
     
    223288                if (fetched >= 100)
    224289                    _isRunning = false;
     290                return fetched;
    225291            } catch (Throwable t) {
    226                 System.setProperty(PROP_ERROR,
    227                     _("Last reseed failed fully (exception caught).") + ' ' +
    228                     _(RESEED_TIPS));
    229                 _log.error("Error reseeding", t);
     292                _log.warn("Error reseeding", t);
     293                System.err.println("Reseed got no router infos from " + seedURL);
     294                return 0;
    230295            }
    231296        }
     
    249314
    250315            EepGet get;
    251             if (url.toString().startsWith("https")) {
    252                 get = new SSLEepGet(I2PAppContext.getGlobalContext(), baos, url.toString());
     316            boolean ssl = url.toString().startsWith("https");
     317            if (ssl) {
     318                SSLEepGet sslget;
     319                if (_sslState == null) {
     320                    sslget = new SSLEepGet(I2PAppContext.getGlobalContext(), baos, url.toString());
     321                    // save state for next time
     322                    _sslState = sslget.getSSLState();
     323                } else {
     324                    sslget = new SSLEepGet(I2PAppContext.getGlobalContext(), baos, url.toString(), _sslState);
     325                }
     326                get = sslget;
    253327            } else {
    254328                // Do a (probably) non-proxied eepget into our ByteArrayOutputStream with 0 retries
     
    258332            }
    259333            get.addStatusListener(ReseedRunner.this);
    260             if (get.fetch()) return baos.toByteArray(); else return null;
     334            if (get.fetch())
     335                return baos.toByteArray();
     336            return null;
    261337        }
    262338   
     
    296372    }
    297373
     374    /** translate */
     375    private String ngettext(String s, String p, int n) {
     376        return Translate.getString(n, s, p, _context, BUNDLE_NAME);
     377    }
     378
    298379/******
    299380    public static void main(String args[]) {
Note: See TracChangeset for help on using the changeset viewer.