diff --git a/apps/routerconsole/java/src/net/i2p/router/web/NetDbRenderer.java b/apps/routerconsole/java/src/net/i2p/router/web/NetDbRenderer.java
index d525cdccd6cc57dc980ba7bc157fca58561aebce..b669f1447c17cbfef7f0f629b552dc621870f865 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/NetDbRenderer.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/NetDbRenderer.java
@@ -415,7 +415,9 @@ public class NetDbRenderer {
             // shouldnt happen
             buf.append("<b>" + _("Published") + ":</b> in ").append(DataHelper.formatDuration2(0-age)).append("???<br>\n");
         }
-        buf.append("<b>" + _("Address(es)") + ":</b> ");
+        buf.append("<b>").append(_("Signing Key")).append(":</b> ")
+           .append(info.getIdentity().getSigningPublicKey().getType().toString());
+        buf.append("<br>\n<b>" + _("Address(es)") + ":</b> ");
         String country = _context.commSystem().getCountry(info.getIdentity().getHash());
         if(country != null) {
             buf.append("<img height=\"11\" width=\"16\" alt=\"").append(country.toUpperCase(Locale.US)).append('\"');
diff --git a/core/java/src/net/i2p/data/KeysAndCert.java b/core/java/src/net/i2p/data/KeysAndCert.java
index b0a8a845b7932ed3d7f6f2551fb7031eec4016eb..e5b56bc868bac7cce38bbbb239a9deee2cd6a576 100644
--- a/core/java/src/net/i2p/data/KeysAndCert.java
+++ b/core/java/src/net/i2p/data/KeysAndCert.java
@@ -114,6 +114,8 @@ public class KeysAndCert extends DataStructureImpl {
         _publicKey.writeBytes(out);
         if (_padding != null)
             out.write(_padding);
+        else if (_signingKey.length() < SigningPublicKey.KEYSIZE_BYTES)
+            throw new DataFormatException("No padding set");
         _signingKey.writeTruncatedBytes(out);
         _certificate.writeBytes(out);
     }
diff --git a/router/java/src/net/i2p/data/router/RouterInfo.java b/router/java/src/net/i2p/data/router/RouterInfo.java
index e314e150fd6cef72098f4aabc53b218ce1a06d0a..71a1157fab27a95db9e8b2884b5a8c66e82c7b55 100644
--- a/router/java/src/net/i2p/data/router/RouterInfo.java
+++ b/router/java/src/net/i2p/data/router/RouterInfo.java
@@ -36,6 +36,7 @@ import net.i2p.data.DataHelper;
 import net.i2p.data.Hash;
 import net.i2p.data.KeysAndCert;
 import net.i2p.data.Signature;
+import net.i2p.data.SimpleDataStructure;
 import net.i2p.util.Clock;
 import net.i2p.util.Log;
 import net.i2p.util.OrderedProperties;
@@ -525,17 +526,20 @@ public class RouterInfo extends DatabaseEntry {
     public void readBytes(InputStream in, boolean verifySig) throws DataFormatException, IOException {
         if (_signature != null)
             throw new IllegalStateException();
+        _identity = new RouterIdentity();
+        _identity.readBytes(in);
+        // can't set the digest until we know the sig type
         InputStream din;
         MessageDigest digest;
         if (verifySig) {
-            digest = SHA1.getInstance();
+            digest = _identity.getSigningPublicKey().getType().getDigestInstance();
+            // TODO any better way?
+            digest.update(_identity.toByteArray());
             din = new DigestInputStream(in, digest);
         } else {
             digest = null;
             din = in;
         }
-        _identity = new RouterIdentity();
-        _identity.readBytes(din);
         // avoid thrashing objects
         //Date when = DataHelper.readDate(in);
         //if (when == null)
@@ -565,7 +569,8 @@ public class RouterInfo extends DatabaseEntry {
         _signature.readBytes(in);
 
         if (verifySig) {
-            SHA1Hash hash = new SHA1Hash(digest.digest());
+            SimpleDataStructure hash = _identity.getSigningPublicKey().getType().getHashInstance();
+            hash.setData(digest.digest());
             _isValid = DSAEngine.getInstance().verifySignature(_signature, hash, _identity.getSigningPublicKey());
             _validated = true;
             if (!_isValid) {
diff --git a/router/java/src/net/i2p/router/KeyManager.java b/router/java/src/net/i2p/router/KeyManager.java
index ce4992f8bd54deef53e963b2b4a4a6173e71dcd6..246f1accf44bea1d1f318849d43877e470925948 100644
--- a/router/java/src/net/i2p/router/KeyManager.java
+++ b/router/java/src/net/i2p/router/KeyManager.java
@@ -18,6 +18,7 @@ import java.io.OutputStream;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 
+import net.i2p.crypto.SigType;
 import net.i2p.data.DataFormatException;
 import net.i2p.data.DataStructure;
 import net.i2p.data.Destination;
@@ -26,6 +27,7 @@ import net.i2p.data.PrivateKey;
 import net.i2p.data.PublicKey;
 import net.i2p.data.SigningPrivateKey;
 import net.i2p.data.SigningPublicKey;
+import net.i2p.router.startup.CreateRouterInfoJob;
 import net.i2p.util.Log;
 import net.i2p.util.SecureDirectory;
 import net.i2p.util.SecureFileOutputStream;
@@ -151,8 +153,9 @@ public class KeyManager {
         private void syncKeys(File keyDir) {
             syncPrivateKey(keyDir);
             syncPublicKey(keyDir);
-            syncSigningKey(keyDir);
-            syncVerificationKey(keyDir);
+            SigType type = CreateRouterInfoJob.getSigTypeConfig(getContext());
+            syncSigningKey(keyDir, type);
+            syncVerificationKey(keyDir, type);
         }
 
         private void syncPrivateKey(File keyDir) {
@@ -181,27 +184,33 @@ public class KeyManager {
                 _publicKey = (PublicKey) readin;
         }
 
-        private void syncSigningKey(File keyDir) {
+        /**
+         *  @param type the SigType to expect on read-in, ignored on write
+         */
+        private void syncSigningKey(File keyDir, SigType type) {
             DataStructure ds;
             File keyFile = new File(keyDir, KEYFILE_PRIVATE_SIGNING);
             boolean exists = (_signingPrivateKey != null);
             if (exists)
                 ds = _signingPrivateKey;
             else
-                ds = new SigningPrivateKey();
+                ds = new SigningPrivateKey(type);
             DataStructure readin = syncKey(keyFile, ds, exists);
             if (readin != null && !exists)
                 _signingPrivateKey = (SigningPrivateKey) readin;
         }
 
-        private void syncVerificationKey(File keyDir) {
+        /**
+         *  @param type the SigType to expect on read-in, ignored on write
+         */
+        private void syncVerificationKey(File keyDir, SigType type) {
             DataStructure ds;
             File keyFile = new File(keyDir, KEYFILE_PUBLIC_SIGNING);
             boolean exists = (_signingPublicKey != null);
             if (exists)
                 ds = _signingPublicKey;
             else
-                ds = new SigningPublicKey();
+                ds = new SigningPublicKey(type);
             DataStructure readin = syncKey(keyFile, ds, exists);
             if (readin != null && !exists)
                 _signingPublicKey  = (SigningPublicKey) readin;
diff --git a/router/java/src/net/i2p/router/Router.java b/router/java/src/net/i2p/router/Router.java
index 8ec1e4367ecf45c5d8f87581c35fd93b92542c79..1726d2ded32e356b5ea5fd3fd19c68e0ae5efff2 100644
--- a/router/java/src/net/i2p/router/Router.java
+++ b/router/java/src/net/i2p/router/Router.java
@@ -98,10 +98,6 @@ public class Router implements RouterClock.ClockShiftListener {
     /** this does not put an 'H' in your routerInfo **/
     public final static String PROP_HIDDEN_HIDDEN = "router.isHidden";
     public final static String PROP_DYNAMIC_KEYS = "router.dynamicKeys";
-    public final static String PROP_INFO_FILENAME = "router.info.location";
-    public final static String PROP_INFO_FILENAME_DEFAULT = "router.info";
-    public final static String PROP_KEYS_FILENAME = "router.keys.location";
-    public final static String PROP_KEYS_FILENAME_DEFAULT = "router.keys";
     public final static String PROP_SHUTDOWN_IN_PROGRESS = "__shutdownInProgress";
     public final static String DNS_CACHE_TIME = "" + (5*60);
     private static final String EVENTLOG = "eventlog.txt";
@@ -671,20 +667,6 @@ public class Router implements RouterClock.ClockShiftListener {
             return Boolean.parseBoolean(h);
         return _context.commSystem().isInBadCountry();
     }
-
-    /**
-     *  Only called at startup via LoadRouterInfoJob and RebuildRouterInfoJob.
-     *  Not called by periodic RepublishLocalRouterInfoJob.
-     *  We don't want to change the cert on the fly as it changes the router hash.
-     *  RouterInfo.isHidden() checks the capability, but RouterIdentity.isHidden() checks the cert.
-     *  There's no reason to ever add a hidden cert?
-     *  @return the certificate for a new RouterInfo - probably a null cert.
-     */
-    public Certificate createCertificate() {
-        if (_context.getBooleanProperty(PROP_HIDDEN))
-            return new Certificate(Certificate.CERTIFICATE_TYPE_HIDDEN, null);
-        return Certificate.NULL_CERT;
-    }
     
     /**
      *  @since 0.9.3
diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/PersistentDataStore.java b/router/java/src/net/i2p/router/networkdb/kademlia/PersistentDataStore.java
index f94940f73480d2adf53d13638ad25ba3dac2ee4e..2a7713cffd7618a3397585927cc31332cb558f43 100644
--- a/router/java/src/net/i2p/router/networkdb/kademlia/PersistentDataStore.java
+++ b/router/java/src/net/i2p/router/networkdb/kademlia/PersistentDataStore.java
@@ -524,6 +524,11 @@ class PersistentDataStore extends TransientDataStore {
                     if (_log.shouldLog(Log.INFO))
                         _log.info("Unable to read the router reference in " + _routerFile.getName(), ioe);
                     corrupt = true;
+                } catch (Exception e) {
+                    // key certificate problems, etc., don't let one bad RI kill the whole thing
+                    if (_log.shouldLog(Log.INFO))
+                        _log.info("Unable to read the router reference in " + _routerFile.getName(), e);
+                    corrupt = true;
                 } finally {
                     if (fis != null) try { fis.close(); } catch (IOException ioe) {}
                 }
diff --git a/router/java/src/net/i2p/router/startup/BootCommSystemJob.java b/router/java/src/net/i2p/router/startup/BootCommSystemJob.java
index 3af4164b5eb8e0f680fca7525dfd7da05e721e4e..305869badae5c22bd6754d0709d67aa015e02b1e 100644
--- a/router/java/src/net/i2p/router/startup/BootCommSystemJob.java
+++ b/router/java/src/net/i2p/router/startup/BootCommSystemJob.java
@@ -16,7 +16,7 @@ import net.i2p.router.tasks.ReadConfigJob;
 import net.i2p.util.Log;
 
 /** This actually boots almost everything */
-public class BootCommSystemJob extends JobImpl {
+class BootCommSystemJob extends JobImpl {
     private Log _log;
     
     public static final String PROP_USE_TRUSTED_LINKS = "router.trustedLinks";
diff --git a/router/java/src/net/i2p/router/startup/BootNetworkDbJob.java b/router/java/src/net/i2p/router/startup/BootNetworkDbJob.java
index bf8d36a7729b4c8c16d2319325207115da1fa25c..e512f9ea381819409c6ead9b27d038f9dcf19d6a 100644
--- a/router/java/src/net/i2p/router/startup/BootNetworkDbJob.java
+++ b/router/java/src/net/i2p/router/startup/BootNetworkDbJob.java
@@ -12,7 +12,7 @@ import net.i2p.router.JobImpl;
 import net.i2p.router.RouterContext;
 
 /** start up the network database */
-public class BootNetworkDbJob extends JobImpl {
+class BootNetworkDbJob extends JobImpl {
     
     public BootNetworkDbJob(RouterContext ctx) {
         super(ctx);
diff --git a/router/java/src/net/i2p/router/startup/BootPeerManagerJob.java b/router/java/src/net/i2p/router/startup/BootPeerManagerJob.java
index 7ac5254f0e936484681751c3503ab67ebc38865f..33f4010236e1f76b76e85a10f44b6f30c3bf7686 100644
--- a/router/java/src/net/i2p/router/startup/BootPeerManagerJob.java
+++ b/router/java/src/net/i2p/router/startup/BootPeerManagerJob.java
@@ -12,7 +12,7 @@ import net.i2p.router.JobImpl;
 import net.i2p.router.RouterContext;
 
 /** start up the peer manager */
-public class BootPeerManagerJob extends JobImpl {
+class BootPeerManagerJob extends JobImpl {
     
     public BootPeerManagerJob(RouterContext ctx) {
         super(ctx);
diff --git a/router/java/src/net/i2p/router/startup/BuildTrustedLinksJob.java b/router/java/src/net/i2p/router/startup/BuildTrustedLinksJob.java
index 3b88a2d9cd179e485ae098a2bfc3145e3b625693..566204068b865e436b978109d5e480dfca2687f4 100644
--- a/router/java/src/net/i2p/router/startup/BuildTrustedLinksJob.java
+++ b/router/java/src/net/i2p/router/startup/BuildTrustedLinksJob.java
@@ -15,7 +15,7 @@ import net.i2p.router.RouterContext;
 /**
  *  For future restricted routes. Does nothing now.
  */
-public class BuildTrustedLinksJob extends JobImpl {
+class BuildTrustedLinksJob extends JobImpl {
     private final Job _next;
     
     public BuildTrustedLinksJob(RouterContext context, Job next) {
diff --git a/router/java/src/net/i2p/router/startup/CreateRouterInfoJob.java b/router/java/src/net/i2p/router/startup/CreateRouterInfoJob.java
index 12bb04d3e6c1ad14fa0bd3fe86e02cb50bbc58e8..5f4d61759040e5643128eae91710b880c7deef91 100644
--- a/router/java/src/net/i2p/router/startup/CreateRouterInfoJob.java
+++ b/router/java/src/net/i2p/router/startup/CreateRouterInfoJob.java
@@ -12,16 +12,21 @@ import java.io.BufferedOutputStream;
 import java.io.File;
 import java.io.IOException;
 import java.io.OutputStream;
+import java.security.GeneralSecurityException;
 import java.util.Properties;
 
+import net.i2p.crypto.SigType;
 import net.i2p.data.Certificate;
 import net.i2p.data.DataFormatException;
+import net.i2p.data.DataHelper;
+import net.i2p.data.KeyCertificate;
 import net.i2p.data.PrivateKey;
 import net.i2p.data.PublicKey;
 import net.i2p.data.router.RouterIdentity;
 import net.i2p.data.router.RouterInfo;
 import net.i2p.data.SigningPrivateKey;
 import net.i2p.data.SigningPublicKey;
+import net.i2p.data.SimpleDataStructure;
 import net.i2p.router.Job;
 import net.i2p.router.JobImpl;
 import net.i2p.router.Router;
@@ -40,7 +45,16 @@ public class CreateRouterInfoJob extends JobImpl {
     private final Log _log;
     private final Job _next;
     
-    public CreateRouterInfoJob(RouterContext ctx, Job next) {
+    public static final String INFO_FILENAME = "router.info";
+    static final String KEYS_FILENAME = "router.keys";
+    static final String KEYS2_FILENAME = "router.keys2";
+    private static final String PROP_ROUTER_SIGTYPE = "router.sigType";
+    /** TODO when changing, check isAvailable() and fallback to DSA_SHA1 */
+    private static final SigType DEFAULT_SIGTYPE = SigType.DSA_SHA1;
+    static final byte[] KEYS2_MAGIC = DataHelper.getASCII("I2Pkeys2");
+    static final int KEYS2_UNUSED_BYTES = 28;
+
+    CreateRouterInfoJob(RouterContext ctx, Job next) {
         super(ctx);
         _next = next;
         _log = ctx.logManager().getLog(CreateRouterInfoJob.class);
@@ -59,9 +73,24 @@ public class CreateRouterInfoJob extends JobImpl {
     
     /**
      *  Writes 6 files: router.info (standard RI format),
-     *  router,keys, and 4 individual key files under keyBackup/
+     *  router.keys2, and 4 individual key files under keyBackup/
+     *
+     *  router.keys2 file format: Note that this is NOT the
+     *  same "eepPriv.dat" format used by the client code.
+     *<pre>
+     *   - Magic "I2Pkeys2"
+     *   - 2 byte crypto type, always 0000 for now
+     *   - 2 byte sig type, see SigType
+     *   - 28 bytes unused
+     *   - Private key (256 bytes)
+     *   - Signing Private key (20 bytes or see SigType)
+     *   - Public key (256 bytes)
+     *   - Random padding for Signing Public Key if less than 128 bytes
+     *   - Signing Public key (128 bytes or see SigTpe)
+     *  Total 660 bytes
+     *</pre>
      *
-     *  router.keys file format: Note that this is NOT the
+     *  Old router.keys file format: Note that this is NOT the
      *  same "eepPriv.dat" format used by the client code.
      *<pre>
      *   - Private key (256 bytes)
@@ -74,6 +103,7 @@ public class CreateRouterInfoJob extends JobImpl {
      *  Caller must hold Router.routerInfoFileLock.
      */
     RouterInfo createRouterInfo() {
+        SigType type = getSigTypeConfig(getContext());
         RouterInfo info = new RouterInfo();
         OutputStream fos1 = null;
         OutputStream fos2 = null;
@@ -86,21 +116,26 @@ public class CreateRouterInfoJob extends JobImpl {
             // not necessary, in constructor
             //info.setPeers(new HashSet());
             info.setPublished(getCurrentPublishDate(getContext()));
+            Object keypair[] = getContext().keyGenerator().generatePKIKeypair();
+            PublicKey pubkey = (PublicKey)keypair[0];
+            PrivateKey privkey = (PrivateKey)keypair[1];
+            SimpleDataStructure signingKeypair[] = getContext().keyGenerator().generateSigningKeys(type);
+            SigningPublicKey signingPubKey = (SigningPublicKey)signingKeypair[0];
+            SigningPrivateKey signingPrivKey = (SigningPrivateKey)signingKeypair[1];
             RouterIdentity ident = new RouterIdentity();
-            Certificate cert = getContext().router().createCertificate();
+            Certificate cert = createCertificate(getContext(), signingPubKey);
             ident.setCertificate(cert);
-            PublicKey pubkey = null;
-            PrivateKey privkey = null;
-            SigningPublicKey signingPubKey = null;
-            SigningPrivateKey signingPrivKey = null;
-            Object keypair[] = getContext().keyGenerator().generatePKIKeypair();
-            pubkey = (PublicKey)keypair[0];
-            privkey = (PrivateKey)keypair[1];
-            Object signingKeypair[] = getContext().keyGenerator().generateSigningKeypair();
-            signingPubKey = (SigningPublicKey)signingKeypair[0];
-            signingPrivKey = (SigningPrivateKey)signingKeypair[1];
             ident.setPublicKey(pubkey);
             ident.setSigningPublicKey(signingPubKey);
+            byte[] padding;
+            int padLen = SigningPublicKey.KEYSIZE_BYTES - signingPubKey.length();
+            if (padLen > 0) {
+                padding = new byte[padLen];
+                getContext().random().nextBytes(padding);
+                ident.setPadding(padding);
+            } else {
+                padding = null;
+            }
             info.setIdentity(ident);
             
             info.sign(signingPrivKey);
@@ -108,23 +143,35 @@ public class CreateRouterInfoJob extends JobImpl {
             if (!info.isValid())
                 throw new DataFormatException("RouterInfo we just built is invalid: " + info);
             
-            String infoFilename = getContext().getProperty(Router.PROP_INFO_FILENAME, Router.PROP_INFO_FILENAME_DEFAULT);
-            File ifile = new File(getContext().getRouterDir(), infoFilename);
+            // remove router.keys
+            (new File(getContext().getRouterDir(), KEYS_FILENAME)).delete();
+
+            // write router.info
+            File ifile = new File(getContext().getRouterDir(), INFO_FILENAME);
             fos1 = new BufferedOutputStream(new SecureFileOutputStream(ifile));
             info.writeBytes(fos1);
             
-            String keyFilename = getContext().getProperty(Router.PROP_KEYS_FILENAME, Router.PROP_KEYS_FILENAME_DEFAULT);
-            File kfile = new File(getContext().getRouterDir(), keyFilename);
+            // write router.keys2
+            File kfile = new File(getContext().getRouterDir(), KEYS2_FILENAME);
             fos2 = new BufferedOutputStream(new SecureFileOutputStream(kfile));
+            fos2.write(KEYS2_MAGIC);
+            DataHelper.writeLong(fos2, 2, 0);
+            DataHelper.writeLong(fos2, 2, type.getCode());
+            fos2.write(new byte[KEYS2_UNUSED_BYTES]);
             privkey.writeBytes(fos2);
             signingPrivKey.writeBytes(fos2);
             pubkey.writeBytes(fos2);
+            if (padding != null)
+                fos2.write(padding);
             signingPubKey.writeBytes(fos2);
             
             getContext().keyManager().setKeys(pubkey, privkey, signingPubKey, signingPrivKey);
             
-            _log.info("Router info created and stored at " + ifile.getAbsolutePath() + " with private keys stored at " + kfile.getAbsolutePath() + " [" + info + "]");
+            if (_log.shouldLog(Log.INFO))
+                _log.info("Router info created and stored at " + ifile.getAbsolutePath() + " with private keys stored at " + kfile.getAbsolutePath() + " [" + info + "]");
             getContext().router().eventLog().addEvent(EventLog.REKEYED, ident.calculateHash().toBase64());
+        } catch (GeneralSecurityException gse) {
+            _log.log(Log.CRIT, "Error building the new router information", gse);
         } catch (DataFormatException dfe) {
             _log.log(Log.CRIT, "Error building the new router information", dfe);
         } catch (IOException ioe) {
@@ -136,6 +183,20 @@ public class CreateRouterInfoJob extends JobImpl {
         return info;
     }
     
+    /**
+     *  The configured SigType to expect on read-in
+     *  @since 0.9.16
+     */
+    public static SigType getSigTypeConfig(RouterContext ctx) {
+        SigType cstype = CreateRouterInfoJob.DEFAULT_SIGTYPE;
+        String sstype = ctx.getProperty(PROP_ROUTER_SIGTYPE);
+        if (sstype != null) {
+            SigType ntype = SigType.parseSigType(sstype);
+            if (ntype != null && ntype.isAvailable())
+                cstype = ntype;
+        }
+        return cstype;
+    }
     
     /**
      * We probably don't want to expose the exact time at which a router published its info.
@@ -146,4 +207,22 @@ public class CreateRouterInfoJob extends JobImpl {
         //_log.info("Setting published date to /now/");
         return context.clock().now();
     }
+
+    /**
+     *  Only called at startup via LoadRouterInfoJob and RebuildRouterInfoJob.
+     *  Not called by periodic RepublishLocalRouterInfoJob.
+     *  We don't want to change the cert on the fly as it changes the router hash.
+     *  RouterInfo.isHidden() checks the capability, but RouterIdentity.isHidden() checks the cert.
+     *  There's no reason to ever add a hidden cert?
+     *
+     *  @return the certificate for a new RouterInfo - probably a null cert.
+     *  @since 0.9.16 moved from Router
+     */
+    static Certificate createCertificate(RouterContext ctx, SigningPublicKey spk) {
+        if (spk.getType() != SigType.DSA_SHA1)
+            return new KeyCertificate(spk);
+        if (ctx.getBooleanProperty(Router.PROP_HIDDEN))
+            return new Certificate(Certificate.CERTIFICATE_TYPE_HIDDEN, null);
+        return Certificate.NULL_CERT;
+    }
 }
diff --git a/router/java/src/net/i2p/router/startup/LoadRouterInfoJob.java b/router/java/src/net/i2p/router/startup/LoadRouterInfoJob.java
index 016b77bc3387c3a075845fa0438592cd614155af..4b5ffdf4fb70a20c5fb35aa95b60bd9bf02a25c9 100644
--- a/router/java/src/net/i2p/router/startup/LoadRouterInfoJob.java
+++ b/router/java/src/net/i2p/router/startup/LoadRouterInfoJob.java
@@ -15,7 +15,9 @@ import java.io.InputStream;
 import java.io.IOException;
 import java.util.concurrent.atomic.AtomicBoolean;
 
+import net.i2p.crypto.SigType;
 import net.i2p.data.DataFormatException;
+import net.i2p.data.DataHelper;
 import net.i2p.data.PrivateKey;
 import net.i2p.data.PublicKey;
 import net.i2p.data.router.RouterInfo;
@@ -26,7 +28,11 @@ import net.i2p.router.Router;
 import net.i2p.router.RouterContext;
 import net.i2p.util.Log;
 
-public class LoadRouterInfoJob extends JobImpl {
+/**
+ *  Run once or twice at startup by StartupJob,
+ *  and then runs BootCommSystemJob
+ */
+class LoadRouterInfoJob extends JobImpl {
     private final Log _log;
     private RouterInfo _us;
     private static final AtomicBoolean _keyLengthChecked = new AtomicBoolean();
@@ -45,6 +51,7 @@ public class LoadRouterInfoJob extends JobImpl {
         if (_us == null) {
             RebuildRouterInfoJob r = new RebuildRouterInfoJob(getContext());
             r.rebuildRouterInfo(false);
+            // run a second time
             getContext().jobQueue().addJob(this);
             return;
         } else {
@@ -54,15 +61,19 @@ public class LoadRouterInfoJob extends JobImpl {
         }
     }
     
+    /**
+     *  Loads router.info and router.keys2 or router.keys.
+     *
+     *  See CreateRouterInfoJob for file formats
+     */
     private void loadRouterInfo() {
-        String routerInfoFile = getContext().getProperty(Router.PROP_INFO_FILENAME, Router.PROP_INFO_FILENAME_DEFAULT);
         RouterInfo info = null;
-        String keyFilename = getContext().getProperty(Router.PROP_KEYS_FILENAME, Router.PROP_KEYS_FILENAME_DEFAULT);
-        
-        File rif = new File(getContext().getRouterDir(), routerInfoFile);
+        File rif = new File(getContext().getRouterDir(), CreateRouterInfoJob.INFO_FILENAME);
         boolean infoExists = rif.exists();
-        File rkf = new File(getContext().getRouterDir(), keyFilename);
+        File rkf = new File(getContext().getRouterDir(), CreateRouterInfoJob.KEYS_FILENAME);
         boolean keysExist = rkf.exists();
+        File rkf2 = new File(getContext().getRouterDir(), CreateRouterInfoJob.KEYS2_FILENAME);
+        boolean keys2Exist = rkf2.exists();
         
         InputStream fis1 = null;
         InputStream fis2 = null;
@@ -73,7 +84,7 @@ public class LoadRouterInfoJob extends JobImpl {
             // CRIT   ...sport.udp.EstablishmentManager: Error in the establisher java.lang.NullPointerException
             // at net.i2p.router.transport.udp.PacketBuilder.buildSessionConfirmedPacket(PacketBuilder.java:574)
             // so pretend the RI isn't there if there is no keyfile
-            if (infoExists && keysExist) {
+            if (infoExists && (keys2Exist || keysExist)) {
                 fis1 = new BufferedInputStream(new FileInputStream(rif));
                 info = new RouterInfo();
                 info.readBytes(fis1);
@@ -85,11 +96,37 @@ public class LoadRouterInfoJob extends JobImpl {
                 _us = info;
             }
             
-            if (keysExist) {
-                fis2 = new BufferedInputStream(new FileInputStream(rkf));
+            if (keys2Exist || keysExist) {
+                SigType stype;
+                if (keys2Exist) {
+                    fis2 = new BufferedInputStream(new FileInputStream(rkf2));
+                    // read keys2 headers
+                    byte[] magic = new byte[CreateRouterInfoJob.KEYS2_MAGIC.length];
+                    DataHelper.read(fis2, magic);
+                    if (!DataHelper.eq(magic, CreateRouterInfoJob.KEYS2_MAGIC))
+                        throw new IOException("Bad magic");
+                    int ctype = (int) DataHelper.readLong(fis2, 2);
+                    if (ctype != 0)
+                        throw new IOException("Unsupported RI crypto type " + ctype);
+                    int sstype = (int) DataHelper.readLong(fis2, 2);
+                    stype = SigType.getByCode(sstype);
+                    if (stype == null || !stype.isAvailable())
+                        throw new IOException("Unsupported RI sig type " + stype);
+                    DataHelper.skip(fis2, CreateRouterInfoJob.KEYS2_UNUSED_BYTES);
+                } else {
+                    fis2 = new BufferedInputStream(new FileInputStream(rkf));
+                    stype = SigType.DSA_SHA1;
+                }
+
+                // check if the sigtype config changed
+                SigType cstype = CreateRouterInfoJob.getSigTypeConfig(getContext());
+                boolean sigTypeChanged = stype != cstype;
+
                 PrivateKey privkey = new PrivateKey();
                 privkey.readBytes(fis2);
-                if (shouldRebuild(privkey)) {
+                if (sigTypeChanged || shouldRebuild(privkey)) {
+                    if (sigTypeChanged)
+                        _log.logAlways(Log.WARN, "Rebuilding RouterInfo with new signature type " + cstype);
                     _us = null;
                     // windows... close before deleting
                     if (fis1 != null) {
@@ -100,13 +137,19 @@ public class LoadRouterInfoJob extends JobImpl {
                     fis2 = null;
                     rif.delete();
                     rkf.delete();
+                    rkf2.delete();
                     return;
                 }
-                SigningPrivateKey signingPrivKey = new SigningPrivateKey();
+                SigningPrivateKey signingPrivKey = new SigningPrivateKey(stype);
                 signingPrivKey.readBytes(fis2);
                 PublicKey pubkey = new PublicKey();
                 pubkey.readBytes(fis2);
-                SigningPublicKey signingPubKey = new SigningPublicKey();
+                SigningPublicKey signingPubKey = new SigningPublicKey(stype);
+                int padLen = SigningPublicKey.KEYSIZE_BYTES - signingPubKey.length();
+                if (padLen > 0) {
+                    // we lose the padding as keymanager doesn't store it, what to do?
+                    DataHelper.skip(fis2, padLen);
+                }
                 signingPubKey.readBytes(fis2);
                 
                 getContext().keyManager().setKeys(pubkey, privkey, signingPubKey, signingPrivKey);
@@ -125,6 +168,7 @@ public class LoadRouterInfoJob extends JobImpl {
             }
             rif.delete();
             rkf.delete();
+            rkf2.delete();
         } catch (DataFormatException dfe) {
             _log.log(Log.CRIT, "Corrupt router info or keys at " + rif.getAbsolutePath() + " / " + rkf.getAbsolutePath(), dfe);
             _us = null;
@@ -139,6 +183,7 @@ public class LoadRouterInfoJob extends JobImpl {
             }
             rif.delete();
             rkf.delete();
+            rkf2.delete();
         } finally {
             if (fis1 != null) try { fis1.close(); } catch (IOException ioe) {}
             if (fis2 != null) try { fis2.close(); } catch (IOException ioe) {}
diff --git a/router/java/src/net/i2p/router/startup/RebuildRouterInfoJob.java b/router/java/src/net/i2p/router/startup/RebuildRouterInfoJob.java
index f531971495178bcdbd92e905a93a66d6739b8ff4..59f822bdaadbd28a577f4ade95cdf53798b6b83b 100644
--- a/router/java/src/net/i2p/router/startup/RebuildRouterInfoJob.java
+++ b/router/java/src/net/i2p/router/startup/RebuildRouterInfoJob.java
@@ -8,14 +8,18 @@ package net.i2p.router.startup;
  *
  */
 
+import java.io.BufferedInputStream;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
+import java.io.InputStream;
 import java.io.IOException;
 import java.util.Properties;
 
+import net.i2p.crypto.SigType;
 import net.i2p.data.Certificate;
 import net.i2p.data.DataFormatException;
+import net.i2p.data.DataHelper;
 import net.i2p.data.PrivateKey;
 import net.i2p.data.PublicKey;
 import net.i2p.data.router.RouterIdentity;
@@ -44,7 +48,7 @@ import net.i2p.util.SecureFileOutputStream;
  * router.info.rebuild file is deleted
  *
  */
-public class RebuildRouterInfoJob extends JobImpl {
+class RebuildRouterInfoJob extends JobImpl {
     private final Log _log;
     
     private final static long REBUILD_DELAY = 45*1000; // every 30 seconds
@@ -57,11 +61,11 @@ public class RebuildRouterInfoJob extends JobImpl {
     public String getName() { return "Rebuild Router Info"; }
     
     public void runJob() {
+        throw new UnsupportedOperationException();
+/****
         _log.debug("Testing to rebuild router info");
-        String infoFile = getContext().getProperty(Router.PROP_INFO_FILENAME, Router.PROP_INFO_FILENAME_DEFAULT);
-        File info = new File(getContext().getRouterDir(), infoFile);
-        String keyFilename = getContext().getProperty(Router.PROP_KEYS_FILENAME, Router.PROP_KEYS_FILENAME_DEFAULT);
-        File keyFile = new File(getContext().getRouterDir(), keyFilename);
+        File info = new File(getContext().getRouterDir(), CreateRouterInfoJob.INFO_FILENAME);
+        File keyFile = new File(getContext().getRouterDir(), CreateRouterInfoJob.KEYS2_FILENAME);
         
         if (!info.exists() || !keyFile.exists()) {
             _log.info("Router info file [" + info.getAbsolutePath() + "] or private key file [" + keyFile.getAbsolutePath() + "] deleted, rebuilding");
@@ -71,41 +75,72 @@ public class RebuildRouterInfoJob extends JobImpl {
         }
         getTiming().setStartAfter(getContext().clock().now() + REBUILD_DELAY);
         getContext().jobQueue().addJob(this);
+****/
     }
     
     void rebuildRouterInfo() {
         rebuildRouterInfo(true);
     }
 
+    /**
+     *  @param alreadyRunning unused
+     */
     void rebuildRouterInfo(boolean alreadyRunning) {
         _log.debug("Rebuilding the new router info");
         RouterInfo info = null;
-        String infoFilename = getContext().getProperty(Router.PROP_INFO_FILENAME, Router.PROP_INFO_FILENAME_DEFAULT);
-        File infoFile = new File(getContext().getRouterDir(), infoFilename);
-        String keyFilename = getContext().getProperty(Router.PROP_KEYS_FILENAME, Router.PROP_KEYS_FILENAME_DEFAULT);
-        File keyFile = new File(getContext().getRouterDir(), keyFilename);
+        File infoFile = new File(getContext().getRouterDir(), CreateRouterInfoJob.INFO_FILENAME);
+        File keyFile = new File(getContext().getRouterDir(), CreateRouterInfoJob.KEYS_FILENAME);
+        File keyFile2 = new File(getContext().getRouterDir(), CreateRouterInfoJob.KEYS2_FILENAME);
         
-        if (keyFile.exists()) {
+        if (keyFile2.exists() || keyFile.exists()) {
             // ok, no need to rebuild a brand new identity, just update what we can
             RouterInfo oldinfo = getContext().router().getRouterInfo();
             if (oldinfo == null) {
                 info = new RouterInfo();
-                FileInputStream fis = null;
+                InputStream fis = null;
                 try {
-                    fis = new FileInputStream(keyFile);
+                    SigType stype;
+                    if (keyFile2.exists()) {
+                        fis = new BufferedInputStream(new FileInputStream(keyFile2));
+                        byte[] magic = new byte[CreateRouterInfoJob.KEYS2_MAGIC.length];
+                        DataHelper.read(fis, magic);
+                        if (!DataHelper.eq(magic, CreateRouterInfoJob.KEYS2_MAGIC))
+                            throw new IOException("Bad magic");
+                        int ctype = (int) DataHelper.readLong(fis, 2);
+                        if (ctype != 0)
+                            throw new IOException("Unsupported RI crypto type " + ctype);
+                        int sstype = (int) DataHelper.readLong(fis, 2);
+                        stype = SigType.getByCode(sstype);
+                        if (stype == null || !stype.isAvailable())
+                            throw new IOException("Unsupported RI sig type " + stype);
+                        DataHelper.skip(fis, CreateRouterInfoJob.KEYS2_UNUSED_BYTES);
+                    } else {
+                        fis = new BufferedInputStream(new FileInputStream(keyFile));
+                        stype = SigType.DSA_SHA1;
+                    }
                     PrivateKey privkey = new PrivateKey();
                     privkey.readBytes(fis);
-                    SigningPrivateKey signingPrivKey = new SigningPrivateKey();
+                    SigningPrivateKey signingPrivKey = new SigningPrivateKey(stype);
                     signingPrivKey.readBytes(fis);
                     PublicKey pubkey = new PublicKey();
                     pubkey.readBytes(fis);
-                    SigningPublicKey signingPubKey = new SigningPublicKey();
+                    SigningPublicKey signingPubKey = new SigningPublicKey(stype);
+                    byte[] padding;
+                    int padLen = SigningPublicKey.KEYSIZE_BYTES - signingPubKey.length();
+                    if (padLen > 0) {
+                        padding = new byte[padLen];
+                        DataHelper.read(fis, padding);
+                    } else {
+                        padding = null;
+                    }
                     signingPubKey.readBytes(fis);
                     RouterIdentity ident = new RouterIdentity();
-                    Certificate cert = getContext().router().createCertificate();
+                    Certificate cert = CreateRouterInfoJob.createCertificate(getContext(), signingPubKey);
                     ident.setCertificate(cert);
                     ident.setPublicKey(pubkey);
                     ident.setSigningPublicKey(signingPubKey);
+                    if (padding != null)
+                        ident.setPadding(padding);
                     info.setIdentity(ident);
                 } catch (Exception e) {
                     _log.log(Log.CRIT, "Error reading in the key data from " + keyFile.getAbsolutePath(), e);
@@ -160,12 +195,14 @@ public class RebuildRouterInfoJob extends JobImpl {
             _log.warn("Private key file " + keyFile.getAbsolutePath() + " deleted!  Rebuilding a brand new router identity!");
             // this proc writes the keys and info to the file as well as builds the latest and greatest info
             CreateRouterInfoJob j = new CreateRouterInfoJob(getContext(), null);
-            info = j.createRouterInfo();
+            synchronized (getContext().router().routerInfoFileLock) {
+                info = j.createRouterInfo();
+            }
         }
         
         //MessageHistory.initialize();
         getContext().router().setRouterInfo(info);
-        _log.info("Router info rebuilt and stored at " + infoFilename + " [" + info + "]");
+        _log.info("Router info rebuilt and stored at " + infoFile + " [" + info + "]");
     }
     
 }
diff --git a/router/java/src/net/i2p/router/startup/StartAcceptingClientsJob.java b/router/java/src/net/i2p/router/startup/StartAcceptingClientsJob.java
index 50d840d4322be182f20990c1d1d584e909cb49c7..672bf702aab4eb6b79ed5da0d54dbd21784d6f01 100644
--- a/router/java/src/net/i2p/router/startup/StartAcceptingClientsJob.java
+++ b/router/java/src/net/i2p/router/startup/StartAcceptingClientsJob.java
@@ -12,7 +12,7 @@ import net.i2p.router.JobImpl;
 import net.i2p.router.RouterContext;
 
 /** start I2CP interface */
-public class StartAcceptingClientsJob extends JobImpl {
+class StartAcceptingClientsJob extends JobImpl {
     
     public StartAcceptingClientsJob(RouterContext context) {
         super(context);
diff --git a/router/java/src/net/i2p/router/startup/WorkingDir.java b/router/java/src/net/i2p/router/startup/WorkingDir.java
index 3763f2e7a798e1f0993ecccbf8dd1b293805b643..15a3e7fb44cd2173d32f87b5197f553b3c726bda 100644
--- a/router/java/src/net/i2p/router/startup/WorkingDir.java
+++ b/router/java/src/net/i2p/router/startup/WorkingDir.java
@@ -147,7 +147,7 @@ public class WorkingDir {
         // Check for a router.keys file or logs dir, if either exists it's an old install,
         // and only migrate the data files if told to do so
         // (router.keys could be deleted later by a killkeys())
-        test = new File(oldDirf, "router.keys");
+        test = new File(oldDirf, CreateRouterInfoJob.KEYS_FILENAME);
         boolean oldInstall = test.exists();
         if (!oldInstall) {
             test = new File(oldDirf, "logs");
diff --git a/router/java/src/net/i2p/router/tasks/PersistRouterInfoJob.java b/router/java/src/net/i2p/router/tasks/PersistRouterInfoJob.java
index 7a8868985ca6216cae537961b3717014e72e4f7d..6a14a51346d510e4da9935e71744b4061d1af620 100644
--- a/router/java/src/net/i2p/router/tasks/PersistRouterInfoJob.java
+++ b/router/java/src/net/i2p/router/tasks/PersistRouterInfoJob.java
@@ -15,8 +15,8 @@ import java.io.IOException;
 import net.i2p.data.DataFormatException;
 import net.i2p.data.router.RouterInfo;
 import net.i2p.router.JobImpl;
-import net.i2p.router.Router;
 import net.i2p.router.RouterContext;
+import net.i2p.router.startup.CreateRouterInfoJob;
 import net.i2p.util.Log;
 import net.i2p.util.SecureFileOutputStream;
 
@@ -37,8 +37,7 @@ public class PersistRouterInfoJob extends JobImpl {
         if (_log.shouldLog(Log.DEBUG))
             _log.debug("Persisting updated router info");
 
-        String infoFilename = getContext().getProperty(Router.PROP_INFO_FILENAME, Router.PROP_INFO_FILENAME_DEFAULT);
-        File infoFile = new File(getContext().getRouterDir(), infoFilename);
+        File infoFile = new File(getContext().getRouterDir(), CreateRouterInfoJob.INFO_FILENAME);
 
         RouterInfo info = getContext().router().getRouterInfo();