diff --git a/apps/streaming/java/src/net/i2p/client/streaming/Packet.java b/apps/streaming/java/src/net/i2p/client/streaming/Packet.java
index 6fbe1dcce4acf82b94535f7b171a56ddb4ef45d7..1ecdc0771b6cb01f8e3cac49f1e35cfd440fd267 100644
--- a/apps/streaming/java/src/net/i2p/client/streaming/Packet.java
+++ b/apps/streaming/java/src/net/i2p/client/streaming/Packet.java
@@ -5,6 +5,7 @@ import java.io.IOException;
 import java.util.Arrays;
 
 import net.i2p.I2PAppContext;
+import net.i2p.crypto.SigType;
 import net.i2p.data.Base64;
 import net.i2p.data.ByteArray;
 import net.i2p.data.DataFormatException;
@@ -436,8 +437,9 @@ class Packet {
             optionSize += _optionFrom.size();
         if (isFlagSet(FLAG_MAX_PACKET_SIZE_INCLUDED))
             optionSize += 2;
-        if (isFlagSet(FLAG_SIGNATURE_INCLUDED))
-            optionSize += Signature.SIGNATURE_BYTES;
+        if (isFlagSet(FLAG_SIGNATURE_INCLUDED)) {
+            optionSize += _optionSignature.length();
+        }
         
         DataHelper.toLong(buffer, cur, 2, optionSize);
         cur += 2;
@@ -455,10 +457,10 @@ class Packet {
         }
         if (isFlagSet(FLAG_SIGNATURE_INCLUDED)) {
             if (includeSig)
-                System.arraycopy(_optionSignature.getData(), 0, buffer, cur, Signature.SIGNATURE_BYTES);
+                System.arraycopy(_optionSignature.getData(), 0, buffer, cur, _optionSignature.length());
             else // we're signing (or validating)
-                Arrays.fill(buffer, cur, cur+Signature.SIGNATURE_BYTES, (byte)0x0);
-            cur += Signature.SIGNATURE_BYTES;
+                Arrays.fill(buffer, cur, cur + _optionSignature.length(), (byte)0x0);
+            cur += _optionSignature.length();
         }
         
         if (_payload != null) {
@@ -511,7 +513,7 @@ class Packet {
         if (isFlagSet(FLAG_MAX_PACKET_SIZE_INCLUDED))
             size += 2;
         if (isFlagSet(FLAG_SIGNATURE_INCLUDED))
-            size += Signature.SIGNATURE_BYTES;
+            size += _optionSignature.length();
         
         size += 2; // option size
         
@@ -606,12 +608,37 @@ class Packet {
             cur += 2;
         }
         if (isFlagSet(FLAG_SIGNATURE_INCLUDED)) {
-            Signature optionSignature = new Signature();
-            byte buf[] = new byte[Signature.SIGNATURE_BYTES];
-            System.arraycopy(buffer, cur, buf, 0, Signature.SIGNATURE_BYTES);
+            Signature optionSignature;
+            Destination from = getOptionalFrom();
+            if (from != null) {
+                optionSignature = new Signature(from.getSigningPublicKey().getType());
+            } else {
+                // super cheat for now, look for correct type,
+                // assume no more options. If we add to the options
+                // we will have to ask the manager.
+                int siglen = payloadBegin - cur;
+                SigType type = null;
+                for (SigType t : SigType.values()) {
+                    if (t.getSigLen() == siglen) {
+                        type = t;
+                        break;
+                    }
+                }
+                if (type == null) {
+                    if (siglen < Signature.SIGNATURE_BYTES)
+                        throw new IllegalArgumentException("unknown sig type len=" + siglen);
+                    // Hope it's the default type with some unknown options following;
+                    // if not the sig will fail later
+                    type = SigType.DSA_SHA1;
+                    siglen = Signature.SIGNATURE_BYTES;
+                }
+                optionSignature = new Signature(type);
+            }
+            byte buf[] = new byte[optionSignature.length()];
+            System.arraycopy(buffer, cur, buf, 0, buf.length);
             optionSignature.setData(buf);
             setOptionalSignature(optionSignature);
-            cur += Signature.SIGNATURE_BYTES;
+            cur += buf.length;
         }
     }
     
@@ -667,12 +694,12 @@ class Packet {
         setFlag(FLAG_SIGNATURE_INCLUDED);
         int size = writePacket(buffer, offset, false);
         _optionSignature = ctx.dsa().sign(buffer, offset, size, key);
-        if (false) {
-            Log l = ctx.logManager().getLog(Packet.class);
-            l.error("Signing: " + toString());
-            l.error(Base64.encode(buffer, 0, size));
-            l.error("Signature: " + Base64.encode(_optionSignature.getData()));
-        }
+        //if (false) {
+        //    Log l = ctx.logManager().getLog(Packet.class);
+        //    l.error("Signing: " + toString());
+        //    l.error(Base64.encode(buffer, 0, size));
+        //    l.error("Signature: " + Base64.encode(_optionSignature.getData()));
+        //}
         // jump into the signed data and inject the signature where we 
         // previously placed a bunch of zeroes
         int signatureOffset = offset 
@@ -687,7 +714,7 @@ class Packet {
                               + (isFlagSet(FLAG_DELAY_REQUESTED) ? 2 : 0)
                               + (isFlagSet(FLAG_FROM_INCLUDED) ? _optionFrom.size() : 0)
                               + (isFlagSet(FLAG_MAX_PACKET_SIZE_INCLUDED) ? 2 : 0);
-        System.arraycopy(_optionSignature.getData(), 0, buffer, signatureOffset, Signature.SIGNATURE_BYTES);
+        System.arraycopy(_optionSignature.getData(), 0, buffer, signatureOffset, _optionSignature.length());
         return size;
     }
     
diff --git a/core/java/src/net/i2p/client/I2PClient.java b/core/java/src/net/i2p/client/I2PClient.java
index 521ed35b5bc8a6e143a96394d788770b0155e625..84bcf50d2af68c8215124d89f52f7d6f1935f76f 100644
--- a/core/java/src/net/i2p/client/I2PClient.java
+++ b/core/java/src/net/i2p/client/I2PClient.java
@@ -15,6 +15,7 @@ import java.io.OutputStream;
 import java.util.Properties;
 
 import net.i2p.I2PException;
+import net.i2p.crypto.SigType;
 import net.i2p.data.Certificate;
 import net.i2p.data.Destination;
 
@@ -83,6 +84,18 @@ public interface I2PClient {
      */
     public Destination createDestination(OutputStream destKeyStream) throws I2PException, IOException;
 
+    /**
+     * Create a destination with the given signature type.
+     * It will have a null certificate for DSA 1024/160 and KeyCertificate otherwise.
+     * This is not bound to the I2PClient, you must supply the data back again
+     * in createSession().
+     *
+     * @param destKeyStream location to write out the destination, PrivateKey, and SigningPrivateKey,
+     *                      format is specified in {@link net.i2p.data.PrivateKeyFile PrivateKeyFile}
+     * @since 0.9.11
+     */
+    public Destination createDestination(OutputStream destKeyStream, SigType type) throws I2PException, IOException;
+
     /** Create a new destination with the given certificate and store it, along with the private 
      * encryption and signing keys at the specified location
      *
diff --git a/core/java/src/net/i2p/client/I2PClientImpl.java b/core/java/src/net/i2p/client/I2PClientImpl.java
index 932daa61a5a4f0c9fc9476bce23d82253ecee215..7fac17e10d7532508ddaf2a91c7f1ca145022c3d 100644
--- a/core/java/src/net/i2p/client/I2PClientImpl.java
+++ b/core/java/src/net/i2p/client/I2PClientImpl.java
@@ -12,17 +12,22 @@ package net.i2p.client;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.security.GeneralSecurityException;
 import java.util.Properties;
 
 import net.i2p.I2PAppContext;
 import net.i2p.I2PException;
 import net.i2p.crypto.KeyGenerator;
+import net.i2p.crypto.SigType;
 import net.i2p.data.Certificate;
 import net.i2p.data.Destination;
+import net.i2p.data.KeyCertificate;
 import net.i2p.data.PrivateKey;
 import net.i2p.data.PublicKey;
 import net.i2p.data.SigningPrivateKey;
 import net.i2p.data.SigningPublicKey;
+import net.i2p.data.SimpleDataStructure;
+import net.i2p.util.RandomSource;
 
 /**
  * Base client implementation.
@@ -34,7 +39,7 @@ import net.i2p.data.SigningPublicKey;
 class I2PClientImpl implements I2PClient {
 
     /**
-     * Create the destination with a null payload.
+     * Create a destination with a DSA 1024/160 signature type and a null certificate.
      * This is not bound to the I2PClient, you must supply the data back again
      * in createSession().
      *
@@ -42,9 +47,26 @@ class I2PClientImpl implements I2PClient {
      *                      format is specified in {@link net.i2p.data.PrivateKeyFile PrivateKeyFile}
      */
     public Destination createDestination(OutputStream destKeyStream) throws I2PException, IOException {
-        Certificate cert = new Certificate();
-        cert.setCertificateType(Certificate.CERTIFICATE_TYPE_NULL);
-        cert.setPayload(null);
+        return createDestination(destKeyStream, SigType.DSA_SHA1);
+    }
+
+    /**
+     * Create a destination with the given signature type.
+     * It will have a null certificate for DSA 1024/160 and KeyCertificate otherwise.
+     * This is not bound to the I2PClient, you must supply the data back again
+     * in createSession().
+     *
+     * @param destKeyStream location to write out the destination, PrivateKey, and SigningPrivateKey,
+     *                      format is specified in {@link net.i2p.data.PrivateKeyFile PrivateKeyFile}
+     * @since 0.9.11
+     */
+    public Destination createDestination(OutputStream destKeyStream, SigType type) throws I2PException, IOException {
+        Certificate cert;
+        if (type == SigType.DSA_SHA1) {
+            cert = Certificate.NULL_CERT;
+        } else {
+            cert = new KeyCertificate(type);
+        }
         return createDestination(destKeyStream, cert);
     }
 
@@ -52,20 +74,49 @@ class I2PClientImpl implements I2PClient {
      * Create the destination with the given payload and write it out along with
      * the PrivateKey and SigningPrivateKey to the destKeyStream
      *
+     * If cert is a KeyCertificate, the signing keypair will be of the specified type.
+     * The KeyCertificate data must be .............................
+     * The padding if any will be randomized. The extra key data if any will be set in the
+     * key cert.
+     *
      * @param destKeyStream location to write out the destination, PrivateKey, and SigningPrivateKey,
      *                      format is specified in {@link net.i2p.data.PrivateKeyFile PrivateKeyFile}
      */
     public Destination createDestination(OutputStream destKeyStream, Certificate cert) throws I2PException, IOException {
         Destination d = new Destination();
-        d.setCertificate(cert);
         Object keypair[] = KeyGenerator.getInstance().generatePKIKeypair();
         PublicKey publicKey = (PublicKey) keypair[0];
         PrivateKey privateKey = (PrivateKey) keypair[1];
-        Object signingKeys[] = KeyGenerator.getInstance().generateSigningKeypair();
+        SimpleDataStructure signingKeys[];
+        if (cert.getCertificateType() == Certificate.CERTIFICATE_TYPE_KEY) {
+            KeyCertificate kcert = cert.toKeyCertificate();
+            SigType type = kcert.getSigType();
+            try {
+                signingKeys = KeyGenerator.getInstance().generateSigningKeys(type);
+            } catch (GeneralSecurityException gse) {
+                throw new I2PException("keygen fail", gse);
+            }
+        } else {
+            signingKeys = KeyGenerator.getInstance().generateSigningKeys();
+        }
         SigningPublicKey signingPubKey = (SigningPublicKey) signingKeys[0];
         SigningPrivateKey signingPrivKey = (SigningPrivateKey) signingKeys[1];
         d.setPublicKey(publicKey);
         d.setSigningPublicKey(signingPubKey);
+        if (cert.getCertificateType() == Certificate.CERTIFICATE_TYPE_KEY) {
+            // fix up key certificate or padding
+            KeyCertificate kcert = cert.toKeyCertificate();
+            SigType type = kcert.getSigType();
+            int len = type.getPubkeyLen();
+            if (len < 128) {
+                byte[] pad = new byte[128 - len];
+                RandomSource.getInstance().nextBytes(pad);
+                d.setPadding(pad);
+            } else if (len > 128) {
+                System.arraycopy(signingPubKey.getData(), 128, kcert.getPayload(), KeyCertificate.HEADER_LENGTH, len - 128);
+            }
+        }
+        d.setCertificate(cert);
 
         d.writeBytes(destKeyStream);
         privateKey.writeBytes(destKeyStream);
diff --git a/core/java/src/net/i2p/client/I2PSessionImpl.java b/core/java/src/net/i2p/client/I2PSessionImpl.java
index 8cf7ad095e827365bdbd093f3a123d51443fcaf3..cfacb4cec13efb2973e3023c93dea59edc51f30e 100644
--- a/core/java/src/net/i2p/client/I2PSessionImpl.java
+++ b/core/java/src/net/i2p/client/I2PSessionImpl.java
@@ -62,7 +62,7 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
     /** private key for decryption */
     private final PrivateKey _privateKey;
     /** private key for signing */
-    private final SigningPrivateKey _signingPrivateKey;
+    private   /* final */   SigningPrivateKey _signingPrivateKey;
     /** configuration options */
     private final Properties _options;
     /** this session's Id */
@@ -373,6 +373,7 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
     private void readDestination(InputStream destKeyStream) throws DataFormatException, IOException {
         _myDestination.readBytes(destKeyStream);
         _privateKey.readBytes(destKeyStream);
+        _signingPrivateKey = new SigningPrivateKey(_myDestination.getSigningPublicKey().getType());
         _signingPrivateKey.readBytes(destKeyStream);
     }
 
diff --git a/core/java/src/net/i2p/client/I2PSimpleClient.java b/core/java/src/net/i2p/client/I2PSimpleClient.java
index e91ae2f8b7e9416359eb20f2524f3bb4e3e8e8d4..8e198daf9fe38afb1f15ed033a26096cb05102f1 100644
--- a/core/java/src/net/i2p/client/I2PSimpleClient.java
+++ b/core/java/src/net/i2p/client/I2PSimpleClient.java
@@ -12,6 +12,7 @@ import java.util.Properties;
 
 import net.i2p.I2PAppContext;
 import net.i2p.I2PException;
+import net.i2p.crypto.SigType;
 import net.i2p.data.Certificate;
 import net.i2p.data.Destination;
 
@@ -20,14 +21,30 @@ import net.i2p.data.Destination;
  * just used to talk to the router.
  */
 public class I2PSimpleClient implements I2PClient {
-    /** @deprecated Don't do this */
+
+    /**
+     *  @deprecated Don't do this
+     *  @throws UnsupportedOperationException always
+     */
     public Destination createDestination(OutputStream destKeyStream) throws I2PException, IOException {
-        return null;
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     *  @deprecated Don't do this
+     *  @throws UnsupportedOperationException always
+     *  @since 0.9.11
+     */
+    public Destination createDestination(OutputStream destKeyStream, SigType type) throws I2PException, IOException {
+        throw new UnsupportedOperationException();
     }
 
-    /** @deprecated or this */
+    /**
+     *  @deprecated Don't do this
+     *  @throws UnsupportedOperationException always
+     */
     public Destination createDestination(OutputStream destKeyStream, Certificate cert) throws I2PException, IOException {
-        return null;
+        throw new UnsupportedOperationException();
     }
 
     /**
@@ -37,6 +54,7 @@ public class I2PSimpleClient implements I2PClient {
     public I2PSession createSession(InputStream destKeyStream, Properties options) throws I2PSessionException {
         return createSession(I2PAppContext.getGlobalContext(), options);
     }
+
     /**
      * Create a new session (though do not connect it yet)
      *
diff --git a/core/java/src/net/i2p/client/RequestLeaseSetMessageHandler.java b/core/java/src/net/i2p/client/RequestLeaseSetMessageHandler.java
index 620f571f4d328604b50127df06224858754d8c30..09f224efc06f98ab65fc6b5f0fa3e026870be490 100644
--- a/core/java/src/net/i2p/client/RequestLeaseSetMessageHandler.java
+++ b/core/java/src/net/i2p/client/RequestLeaseSetMessageHandler.java
@@ -14,6 +14,7 @@ import java.util.concurrent.ConcurrentHashMap;
 
 import net.i2p.I2PAppContext;
 import net.i2p.crypto.KeyGenerator;
+import net.i2p.crypto.SigType;
 import net.i2p.data.DataFormatException;
 import net.i2p.data.DataHelper;
 import net.i2p.data.Destination;
@@ -105,6 +106,15 @@ class RequestLeaseSetMessageHandler extends HandlerImpl {
         }
         try {
             leaseSet.sign(session.getPrivateKey());
+            // Workaround for unparsable serialized signing private key for revocation
+            // Send him a dummy DSA_SHA1 private key since it's unused anyway
+            // See CreateLeaseSetMessage.doReadMessage()
+            SigningPrivateKey spk = li.getSigningPrivateKey();
+            if (!_context.isRouterContext() && spk.getType() != SigType.DSA_SHA1) {
+                byte[] dummy = new byte[SigningPrivateKey.KEYSIZE_BYTES];
+                _context.random().nextBytes(dummy);
+                spk = new SigningPrivateKey(dummy);
+            }
             session.getProducer().createLeaseSet(session, leaseSet, li.getSigningPrivateKey(), li.getPrivateKey());
             session.setLeaseSet(leaseSet);
         } catch (DataFormatException dfe) {
diff --git a/core/java/src/net/i2p/client/datagram/I2PDatagramDissector.java b/core/java/src/net/i2p/client/datagram/I2PDatagramDissector.java
index 00c897a6f5d4a637afe0c8eb1f6795d593294835..281ea20359d68900d1b7b6d068d98929c9c61e8b 100644
--- a/core/java/src/net/i2p/client/datagram/I2PDatagramDissector.java
+++ b/core/java/src/net/i2p/client/datagram/I2PDatagramDissector.java
@@ -14,6 +14,7 @@ import java.io.IOException;
 import net.i2p.I2PAppContext;
 import net.i2p.crypto.DSAEngine;
 import net.i2p.crypto.SHA256Generator;
+import net.i2p.crypto.SigType;
 import net.i2p.data.DataFormatException;
 import net.i2p.data.Destination;
 import net.i2p.data.Hash;
@@ -67,7 +68,10 @@ public final class I2PDatagramDissector {
         try {
             // read destination
             rxDest = Destination.create(dgStream);
-            rxSign = new Signature();
+            SigType type = rxDest.getSigningPublicKey().getType();
+            if (type != null)
+                throw new DataFormatException("unsupported sig type");
+            rxSign = new Signature(type);
             // read signature
             rxSign.readBytes(dgStream);
             
diff --git a/core/java/src/net/i2p/crypto/SU3File.java b/core/java/src/net/i2p/crypto/SU3File.java
index dd23ad05abf9418f8888bf7711e851b61ca2e856..55e21fea40b6ba982863f04ca80de9702cce67ff 100644
--- a/core/java/src/net/i2p/crypto/SU3File.java
+++ b/core/java/src/net/i2p/crypto/SU3File.java
@@ -518,23 +518,6 @@ public class SU3File {
         return buf.toString();
     }
 
-    /**
-     *  @param stype number or name
-     *  @return null if not found
-     *  @since 0.9.9
-     */
-    private static SigType parseSigType(String stype) {
-        try {
-            return SigType.valueOf(stype.toUpperCase(Locale.US));
-        } catch (IllegalArgumentException iae) {
-            try {
-                int code = Integer.parseInt(stype);
-                return SigType.getByCode(code);
-            } catch (NumberFormatException nfe) {
-                return null;
-             }
-        }
-    }
     /**
      *  @param stype number or name
      *  @return null if not found
@@ -627,7 +610,7 @@ public class SU3File {
      */
     private static final boolean signCLI(String stype, String ctype, String inputFile, String signedFile,
                                          String privateKeyFile, String version, String signerName, String keypw) {
-        SigType type = stype == null ? SigType.getByCode(Integer.valueOf(DEFAULT_SIG_CODE)) : parseSigType(stype);
+        SigType type = stype == null ? SigType.getByCode(Integer.valueOf(DEFAULT_SIG_CODE)) : SigType.parseSigType(stype);
         if (type == null) {
             System.out.println("Signature type " + stype + " is not supported");
             return false;
@@ -719,7 +702,7 @@ public class SU3File {
      *  @since 0.9.9
      */
     private static final boolean genKeysCLI(String stype, String publicKeyFile, String privateKeyFile, String alias) {
-        SigType type = stype == null ? SigType.getByCode(Integer.valueOf(DEFAULT_SIG_CODE)) : parseSigType(stype);
+        SigType type = stype == null ? SigType.getByCode(Integer.valueOf(DEFAULT_SIG_CODE)) : SigType.parseSigType(stype);
         if (type == null) {
             System.out.println("Signature type " + stype + " is not supported");
             return false;
diff --git a/core/java/src/net/i2p/crypto/SigType.java b/core/java/src/net/i2p/crypto/SigType.java
index 105ff09b1a5e7686cdc1d259b5176dfe9d966e07..a37d65dfb040e511f0546664f8b4731589a1bc0d 100644
--- a/core/java/src/net/i2p/crypto/SigType.java
+++ b/core/java/src/net/i2p/crypto/SigType.java
@@ -5,6 +5,7 @@ import java.security.NoSuchAlgorithmException;
 import java.security.spec.AlgorithmParameterSpec;
 import java.security.spec.InvalidParameterSpecException;
 import java.util.HashMap;
+import java.util.Locale;
 import java.util.Map;
 
 import net.i2p.data.Hash;
@@ -170,4 +171,24 @@ public enum SigType {
     public static SigType getByCode(int code) {
         return BY_CODE.get(Integer.valueOf(code));
     }
+
+    /**
+     *  Convenience for user apps
+     *
+     *  @param stype number or name
+     *  @return null if not found
+     *  @since 0.9.9 moved from SU3File in 0.9.11
+     */
+    public static SigType parseSigType(String stype) {
+        try {
+            return valueOf(stype.toUpperCase(Locale.US));
+        } catch (IllegalArgumentException iae) {
+            try {
+                int code = Integer.parseInt(stype);
+                return getByCode(code);
+            } catch (NumberFormatException nfe) {
+                return null;
+             }
+        }
+    }
 }
diff --git a/core/java/src/net/i2p/data/Certificate.java b/core/java/src/net/i2p/data/Certificate.java
index ef6484f31ed4d147e47fb06562ad6e1aa7b75c12..a4a1b3148d22c14e2d548e319aa55a33ec30b8cd 100644
--- a/core/java/src/net/i2p/data/Certificate.java
+++ b/core/java/src/net/i2p/data/Certificate.java
@@ -42,6 +42,8 @@ public class Certificate extends DataStructureImpl {
     public final static int CERTIFICATE_LENGTH_SIGNED_WITH_HASH = Signature.SIGNATURE_BYTES + Hash.HASH_LENGTH;
     /** Contains multiple certs */
     public final static int CERTIFICATE_TYPE_MULTIPLE = 4;
+    /** @since 0.9.11 */
+    public final static int CERTIFICATE_TYPE_KEY = 5;
 
     /**
      * If null cert, return immutable static instance, else create new
@@ -58,6 +60,13 @@ public class Certificate extends DataStructureImpl {
             return new Certificate(type, null);
         byte[] payload = new byte[length];
         System.arraycopy(data, off + 3, payload, 0, length);
+        if (type == CERTIFICATE_TYPE_KEY) {
+            try {
+                return new KeyCertificate(payload);
+            } catch (DataFormatException dfe) {
+                throw new IllegalArgumentException(dfe);
+            }
+        }
         return new Certificate(type, payload);
     }
 
@@ -77,13 +86,20 @@ public class Certificate extends DataStructureImpl {
         int read = DataHelper.read(in, payload);
         if (read != length)
             throw new DataFormatException("Not enough bytes for the payload (read: " + read + " length: " + length + ')');
+        if (type == CERTIFICATE_TYPE_KEY)
+            return new KeyCertificate(payload);
         return new Certificate(type, payload);
     }
 
     public Certificate() {
     }
 
+    /**
+     *  @throws IllegalArgumentException if type < 0
+     */
     public Certificate(int type, byte[] payload) {
+        if (type < 0)
+            throw new IllegalArgumentException();
         _type = type;
         _payload = payload;
     }
@@ -93,7 +109,15 @@ public class Certificate extends DataStructureImpl {
         return _type;
     }
 
+    /**
+     *  @throws IllegalArgumentException if type < 0
+     *  @throws IllegalStateException if already set
+     */
     public void setCertificateType(int type) {
+        if (type < 0)
+            throw new IllegalArgumentException();
+        if (_type != 0 && _type != type)
+            throw new IllegalStateException("already set");
         _type = type;
     }
 
@@ -101,11 +125,21 @@ public class Certificate extends DataStructureImpl {
         return _payload;
     }
 
+    /**
+     *  @throws IllegalStateException if already set
+     */
     public void setPayload(byte[] payload) {
+        if (_payload != null)
+            throw new IllegalStateException("already set");
         _payload = payload;
     }
     
+    /**
+     *  @throws IllegalStateException if already set
+     */
     public void readBytes(InputStream in) throws DataFormatException, IOException {
+        if (_type != 0 || _payload != null)
+            throw new IllegalStateException("already set");
         _type = (int) DataHelper.readLong(in, 1);
         int length = (int) DataHelper.readLong(in, 2);
         if (length > 0) {
@@ -149,7 +183,12 @@ public class Certificate extends DataStructureImpl {
         return cur - offset;
     }
     
+    /**
+     *  @throws IllegalStateException if already set
+     */
     public int readBytes(byte source[], int offset) throws DataFormatException {
+        if (_type != 0 || _payload != null)
+            throw new IllegalStateException("already set");
         if (source == null) throw new DataFormatException("Cert is null");
         if (source.length < offset + 3)
             throw new DataFormatException("Cert is too small [" + source.length + " off=" + offset + "]");
@@ -175,6 +214,18 @@ public class Certificate extends DataStructureImpl {
         return 1 + 2 + (_payload != null ? _payload.length : 0);
     }
     
+    /**
+     *  Up-convert this to a KeyCertificate
+     *
+     *  @throws DataFormatException if cert type != CERTIFICATE_TYPE_KEY
+     *  @since 0.9.11
+     */
+    public KeyCertificate toKeyCertificate() throws DataFormatException {
+        if (_type != CERTIFICATE_TYPE_KEY)
+            throw new DataFormatException("type");
+        return new KeyCertificate(this);
+    }
+
     @Override
     public boolean equals(Object object) {
         if (object == this) return true;
@@ -194,6 +245,8 @@ public class Certificate extends DataStructureImpl {
         buf.append("[Certificate: type: ");
         if (getCertificateType() == CERTIFICATE_TYPE_NULL)
             buf.append("Null certificate");
+        else if (getCertificateType() == CERTIFICATE_TYPE_KEY)
+            buf.append("Key certificate");
         else if (getCertificateType() == CERTIFICATE_TYPE_HASHCASH)
             buf.append("Hashcash certificate");
         else if (getCertificateType() == CERTIFICATE_TYPE_HIDDEN)
diff --git a/core/java/src/net/i2p/data/Destination.java b/core/java/src/net/i2p/data/Destination.java
index ad21985e39f61a1c9fb11ceef4074290a10014a1..4d8affc870570310a5a0e0302902b7cb296aa3a1 100644
--- a/core/java/src/net/i2p/data/Destination.java
+++ b/core/java/src/net/i2p/data/Destination.java
@@ -100,17 +100,22 @@ public class Destination extends KeysAndCert {
         int cur = offset;
         System.arraycopy(_publicKey.getData(), 0, target, cur, PublicKey.KEYSIZE_BYTES);
         cur += PublicKey.KEYSIZE_BYTES;
-        System.arraycopy(_signingKey.getData(), 0, target, cur, SigningPublicKey.KEYSIZE_BYTES);
-        cur += SigningPublicKey.KEYSIZE_BYTES;
+        if (_padding != null) {
+            System.arraycopy(_padding, 0, target, cur, _padding.length);
+            cur += _padding.length;
+        }
+        System.arraycopy(_signingKey.getData(), 0, target, cur, _signingKey.length());
+        cur += _signingKey.length();
         cur += _certificate.writeBytes(target, cur);
         return cur - offset;
     }
     
     /**
-     * @deprecated was used only by Packet.java in streaming, now unused
+     * deprecated was used only by Packet.java in streaming, now unused
      *
      * @throws IllegalStateException if data already set
      */
+/****
     public int readBytes(byte source[], int offset) throws DataFormatException {
         if (source == null) throw new DataFormatException("Null source");
         if (source.length <= offset + PublicKey.KEYSIZE_BYTES + SigningPublicKey.KEYSIZE_BYTES) 
@@ -130,9 +135,10 @@ public class Destination extends KeysAndCert {
         
         return cur - offset;
     }
+****/
 
     public int size() {
-        return PublicKey.KEYSIZE_BYTES + SigningPublicKey.KEYSIZE_BYTES + _certificate.size();
+        return PublicKey.KEYSIZE_BYTES + _signingKey.length() + _certificate.size();
     }
 
     /**
diff --git a/core/java/src/net/i2p/data/KeyCertificate.java b/core/java/src/net/i2p/data/KeyCertificate.java
new file mode 100644
index 0000000000000000000000000000000000000000..5726c80b14f6d908e66ca69b08a716b788be78f2
--- /dev/null
+++ b/core/java/src/net/i2p/data/KeyCertificate.java
@@ -0,0 +1,252 @@
+package net.i2p.data;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import net.i2p.crypto.SigType;
+
+/**
+ * This certificate type gets its own class because it's going to be used a lot.
+ *
+ * The crypto type is assumed to be always 0x0000 (ElG) for now.
+ *
+ * @since 0.9.11
+ */
+public class KeyCertificate extends Certificate {
+
+    public static final int HEADER_LENGTH = 4;
+
+    public static final KeyCertificate ELG_ECDSA256_CERT;
+    static {
+        KeyCertificate kc;
+        try {
+            kc = new ECDSA256Cert();
+        } catch (DataFormatException dfe) {
+            kc = null;  // won't happen
+        }
+        ELG_ECDSA256_CERT = kc;
+    }
+
+    /**
+     *  @param payload 4 bytes minimum if non-null
+     *  @throws DataFormatException
+     */
+    public KeyCertificate(byte[] payload) throws DataFormatException {
+         super(CERTIFICATE_TYPE_KEY, payload);
+         if (payload != null && payload.length < HEADER_LENGTH)
+             throw new DataFormatException("data");
+    }
+
+    /**
+     *  A KeyCertificate with crypto type 0 (ElGamal)
+     *  and the signature type and extra data from the given public key.
+     *
+     *  @param sig non-null data non-null
+     *  @throws IllegalArgumentException
+     */
+    public KeyCertificate(SigningPublicKey spk) {
+         super(CERTIFICATE_TYPE_KEY, null);
+         if (spk == null || spk.getData() == null)
+             throw new IllegalArgumentException();
+         SigType type = spk.getType();
+         int len = type.getPubkeyLen();
+         int extra = Math.max(0, len - 128);
+         _payload = new byte[HEADER_LENGTH + extra];
+         int code = type.getCode();
+         _payload[0] = (byte) (code >> 8);
+         _payload[1] = (byte) (code & 0xff);
+         // 2 and 3 always 0, it is the only crypto code for now
+         if (extra > 0)
+             System.arraycopy(spk.getData(), 128, _payload, HEADER_LENGTH, extra);
+    }
+
+    /**
+     *  A KeyCertificate with crypto type 0 (ElGamal)
+     *  and the signature type as specified.
+     *  Payload is created.
+     *  If type.getPubkeyLen() is greater than 128, caller MUST
+     *  fill in the extra key data in the payload.
+     *
+     *  @param sig non-null data non-null
+     *  @throws IllegalArgumentException
+     */
+    public KeyCertificate(SigType type) {
+         super(CERTIFICATE_TYPE_KEY, null);
+         int len = type.getPubkeyLen();
+         int extra = Math.max(0, len - 128);
+         _payload = new byte[HEADER_LENGTH + extra];
+         int code = type.getCode();
+         _payload[0] = (byte) (code >> 8);
+         _payload[1] = (byte) (code & 0xff);
+         // 2 and 3 always 0, it is the only crypto code for now
+    }
+
+    /**
+     *  Up-convert a cert to this class
+     *
+     *  @param cert payload 4 bytes minimum if non-null
+     *  @throws DataFormatException if cert type != CERTIFICATE_TYPE_KEY
+     */
+    public KeyCertificate(Certificate cert) throws DataFormatException {
+        this(cert.getPayload());
+        if (cert.getCertificateType() != CERTIFICATE_TYPE_KEY)
+            throw new DataFormatException("type");
+    }
+
+    /**
+     *  @return -1 if unset
+     */
+    public int getSigTypeCode() {
+        if (_payload == null)
+            return -1;
+        return ((_payload[0] & 0xff) << 8) | (_payload[1] & 0xff);
+    }
+
+    /**
+     *  @return -1 if unset
+     */
+    public int getCryptoTypeCode() {
+        if (_payload == null)
+            return -1;
+        return ((_payload[2] & 0xff) << 8) | (_payload[3] & 0xff);
+    }
+
+    /**
+     *  @return null if unset or unknown
+     */
+    public SigType getSigType() {
+        return SigType.getByCode(getSigTypeCode());
+    }
+
+    /**
+     *  Signing Key extra data, if any, is first in the array.
+     *  Crypto Key extra data, if any, is second in the array,
+     *  at offset max(0, 128 - getSigType().getPubkeyLen()
+     *
+     *  @return null if unset or none
+     */
+    public byte[] getExtraKeyData() {
+        if (_payload == null || _payload.length <= HEADER_LENGTH)
+            return null;
+        byte[] rv = new byte[_payload.length - HEADER_LENGTH];
+        System.arraycopy(_payload, HEADER_LENGTH, rv, 0, rv.length);
+        return rv;
+    }
+
+
+    /**
+     *  Signing Key extra data, if any.
+     *
+     *  @return null if unset or none
+     *  @throws UnsupportedOperationException if the sig type is unsupported
+     */
+    public byte[] getExtraSigningKeyData() {
+        // we assume no crypto key data
+        if (_payload == null || _payload.length <= HEADER_LENGTH)
+            return null;
+        SigType type = getSigType();
+        if (type == null)
+            throw new UnsupportedOperationException("unknown sig type");
+        int extra = 128 - type.getPubkeyLen();
+        if (_payload.length == HEADER_LENGTH + extra)
+            return getExtraKeyData();
+        byte[] rv = new byte[extra];
+        System.arraycopy(_payload, HEADER_LENGTH, rv, 0, extra);
+        return rv;
+    }
+
+    // todo
+    // constructor w/ crypto type
+    // getCryptoType()
+    // getCryptoDataOffset()
+
+    @Override
+    public KeyCertificate toKeyCertificate() {
+        return this;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder buf = new StringBuilder(64);
+        buf.append("[Certificate: type: Key certificate");
+        if (_payload == null) {
+            buf.append(" null payload");
+        } else {
+            buf.append("\n\tCrypto type: ").append(getCryptoTypeCode());
+            buf.append("\n\tSig type: ").append(getSigTypeCode())
+               .append(" (").append(getSigType()).append(')');
+            if (_payload.length > HEADER_LENGTH)
+                buf.append("\n\tKey data: ").append(_payload.length - HEADER_LENGTH).append(" bytes");
+        }
+        buf.append("]");
+        return buf.toString();
+    }
+
+    /**
+     *  An immutable ElG/ECDSA-256 certificate.
+     *  @since 0.8.3
+     */
+    private static final class ECDSA256Cert extends KeyCertificate {
+        private static final byte[] ECDSA256_DATA = new byte[] {
+            CERTIFICATE_TYPE_KEY, 0, HEADER_LENGTH, 0, (byte) (SigType.ECDSA_SHA256_P256.getCode()), 0, 0
+        };
+        private static final int ECDSA256_LENGTH = ECDSA256_DATA.length;
+        private static final byte[] ECDSA256_PAYLOAD = new byte[] {
+                                        0, (byte) (SigType.ECDSA_SHA256_P256.getCode()), 0, 0
+        };
+
+        public ECDSA256Cert() throws DataFormatException {
+            super(ECDSA256_PAYLOAD);
+        }
+
+        /** @throws RuntimeException always */
+        @Override
+        public void setCertificateType(int type) {
+            throw new RuntimeException("Data already set");
+        }
+
+        /** @throws RuntimeException always */
+        @Override
+        public void setPayload(byte[] payload) {
+            throw new RuntimeException("Data already set");
+        }
+    
+        /** @throws RuntimeException always */
+        @Override
+        public void readBytes(InputStream in) throws DataFormatException, IOException {
+            throw new RuntimeException("Data already set");
+        }
+    
+        /** Overridden for efficiency */
+        @Override
+        public void writeBytes(OutputStream out) throws IOException {
+            out.write(ECDSA256_DATA);
+        }
+    
+        /** Overridden for efficiency */
+        @Override
+        public int writeBytes(byte target[], int offset) {
+            System.arraycopy(ECDSA256_DATA, 0, target, offset, ECDSA256_LENGTH);
+            return ECDSA256_LENGTH;
+        }
+    
+        /** @throws RuntimeException always */
+        @Override
+        public int readBytes(byte source[], int offset) throws DataFormatException {
+            throw new RuntimeException("Data already set");
+        }
+    
+        /** Overridden for efficiency */
+        @Override
+        public int size() {
+            return ECDSA256_LENGTH;
+        }
+    
+        /** Overridden for efficiency */
+        @Override
+        public int hashCode() {
+            return 1234567;
+        }
+    }
+}
diff --git a/core/java/src/net/i2p/data/KeysAndCert.java b/core/java/src/net/i2p/data/KeysAndCert.java
index 429d89be7c56bbeef7d195e9cbf1d12351ead5b8..33a1de2944dc3f6c64b4e48f392c645398b97108 100644
--- a/core/java/src/net/i2p/data/KeysAndCert.java
+++ b/core/java/src/net/i2p/data/KeysAndCert.java
@@ -35,6 +35,7 @@ public class KeysAndCert extends DataStructureImpl {
     protected SigningPublicKey _signingKey;
     protected Certificate _certificate;
     protected Hash __calculatedHash;
+    protected byte[] _padding;
 
     public Certificate getCertificate() {
         return _certificate;
@@ -78,6 +79,17 @@ public class KeysAndCert extends DataStructureImpl {
         __calculatedHash = null;
     }
     
+    /**
+     * @throws IllegalStateException if was already set
+     * @since 0.9.11
+     */
+    public void setPadding(byte[] padding) {
+        if (_padding != null)
+            throw new IllegalStateException();
+        _padding = padding;
+        __calculatedHash = null;
+    }
+    
     /**
      * @throws IllegalStateException if data already set
      */
@@ -85,8 +97,18 @@ public class KeysAndCert extends DataStructureImpl {
         if (_publicKey != null || _signingKey != null || _certificate != null)
             throw new IllegalStateException();
         _publicKey = PublicKey.create(in);
-        _signingKey = SigningPublicKey.create(in);
-        _certificate = Certificate.create(in);
+        SigningPublicKey spk = SigningPublicKey.create(in);
+        Certificate  cert = Certificate.create(in);
+        if (cert.getCertificateType() == Certificate.CERTIFICATE_TYPE_KEY) {
+            // convert SPK to new SPK and padding
+            KeyCertificate kcert = cert.toKeyCertificate();
+            _signingKey = spk.toTypedKey(kcert);
+            _padding = spk.getPadding(kcert);
+            _certificate = kcert;
+        } else {
+            _signingKey = spk;
+            _certificate = cert;
+        }
         __calculatedHash = null;
     }
     
@@ -94,7 +116,9 @@ public class KeysAndCert extends DataStructureImpl {
         if ((_certificate == null) || (_publicKey == null) || (_signingKey == null))
             throw new DataFormatException("Not enough data to format the router identity");
         _publicKey.writeBytes(out);
-        _signingKey.writeBytes(out);
+        if (_padding != null)
+            out.write(_padding);
+        _signingKey.writeTruncatedBytes(out);
         _certificate.writeBytes(out);
     }
     
@@ -106,6 +130,7 @@ public class KeysAndCert extends DataStructureImpl {
         return
                DataHelper.eq(_signingKey, ident._signingKey)
                && DataHelper.eq(_publicKey, ident._publicKey)
+               && DataHelper.eq(_padding, ident._padding)
                && DataHelper.eq(_certificate, ident._certificate);
     }
     
@@ -125,6 +150,8 @@ public class KeysAndCert extends DataStructureImpl {
         buf.append("\n\tCertificate: ").append(_certificate);
         buf.append("\n\tPublicKey: ").append(_publicKey);
         buf.append("\n\tSigningPublicKey: ").append(_signingKey);
+        if (_padding != null)
+            buf.append("\n\tPadding: ").append(_padding.length).append(" bytes");
         buf.append(']');
         return buf.toString();
     }
diff --git a/core/java/src/net/i2p/data/LeaseSet.java b/core/java/src/net/i2p/data/LeaseSet.java
index 75b24220d4d89a399f5b258c38ba3c2171081ca4..cf9b39deb2c467882ffe8fd9091c4cdd0322943e 100644
--- a/core/java/src/net/i2p/data/LeaseSet.java
+++ b/core/java/src/net/i2p/data/LeaseSet.java
@@ -275,11 +275,9 @@ public class LeaseSet extends DatabaseEntry {
     protected byte[] getBytes() {
         if ((_destination == null) || (_encryptionKey == null) || (_signingKey == null))
             return null;
-        int len = PublicKey.KEYSIZE_BYTES  // dest
-                + SigningPublicKey.KEYSIZE_BYTES // dest
-                + 3 // cert minimum, could be more, only used to size the BAOS
+        int len = _destination.size()
                 + PublicKey.KEYSIZE_BYTES // encryptionKey
-                + SigningPublicKey.KEYSIZE_BYTES // signingKey
+                + _signingKey.length() // signingKey
                 + 1
                 + _leases.size() * 44; // leases
         ByteArrayOutputStream out = new ByteArrayOutputStream(len);
@@ -310,7 +308,9 @@ public class LeaseSet extends DatabaseEntry {
             throw new IllegalStateException();
         _destination = Destination.create(in);
         _encryptionKey = PublicKey.create(in);
-        _signingKey = SigningPublicKey.create(in);
+        // revocation signing key must be same type as the destination signing key
+        _signingKey = new SigningPublicKey(_destination.getSigningPublicKey().getType());
+        _signingKey.readBytes(in);
         int numLeases = (int) DataHelper.readLong(in, 1);
         if (numLeases > MAX_LEASES)
             throw new DataFormatException("Too many leases - max is " + MAX_LEASES);
@@ -320,7 +320,8 @@ public class LeaseSet extends DatabaseEntry {
             lease.readBytes(in);
             addLease(lease);
         }
-        _signature = new Signature();
+        // signature must be same type as the destination signing key
+        _signature = new Signature(_destination.getSigningPublicKey().getType());
         _signature.readBytes(in);
     }
     
@@ -345,11 +346,9 @@ public class LeaseSet extends DatabaseEntry {
      *  Number of bytes, NOT including signature
      */
     public int size() {
-        return PublicKey.KEYSIZE_BYTES //destination.pubKey
-             + SigningPublicKey.KEYSIZE_BYTES // destination.signPubKey
-             + _destination.getCertificate().size() // destination.certificate, usually 3
+        return _destination.size()
              + PublicKey.KEYSIZE_BYTES // encryptionKey
-             + SigningPublicKey.KEYSIZE_BYTES // signingKey
+             + _signingKey.length() // signingKey
              + 1 // number of leases
              + _leases.size() * (Hash.HASH_LENGTH + 4 + 8);
     }
diff --git a/core/java/src/net/i2p/data/PrivateKeyFile.java b/core/java/src/net/i2p/data/PrivateKeyFile.java
index 9c71cc96b301a84275b8d7c03a530d45f3c255e0..b97d76257461d08baa56fdb8be98d02b75273f92 100644
--- a/core/java/src/net/i2p/data/PrivateKeyFile.java
+++ b/core/java/src/net/i2p/data/PrivateKeyFile.java
@@ -6,6 +6,8 @@ import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
 import java.io.IOException;
+import java.security.GeneralSecurityException;
+import java.util.Locale;
 import java.util.Map;
 import java.util.Properties;
 
@@ -17,6 +19,9 @@ import net.i2p.client.I2PClientFactory;
 import net.i2p.client.I2PSession;
 import net.i2p.client.I2PSessionException;
 import net.i2p.crypto.DSAEngine;
+import net.i2p.crypto.KeyGenerator;
+import net.i2p.crypto.SigType;
+import net.i2p.util.RandomSource;
 
 /**
  * This helper class reads and writes files in the
@@ -50,7 +55,9 @@ public class PrivateKeyFile {
      *  Copied and expanded from that in Destination.java
      */
     public static void main(String args[]) {
-        if (args.length == 0) {
+        if (args.length == 0 ||
+            (args[0].startsWith("-") && args.length == 1) ||
+            (args[0].equals("-t") && args.length != 3)) {
             System.err.println("Usage: PrivateKeyFile filename (generates if nonexistent, then prints)");
             System.err.println("       PrivateKeyFile -h filename (generates if nonexistent, adds hashcash cert)");
             System.err.println("       PrivateKeyFile -h effort filename (specify HashCash effort instead of default " + HASH_EFFORT + ")");
@@ -58,13 +65,14 @@ public class PrivateKeyFile {
             System.err.println("       PrivateKeyFile -s filename signwithdestfile (generates if nonexistent, adds cert signed by 2nd dest)");
             System.err.println("       PrivateKeyFile -u filename (changes to unknown cert)");
             System.err.println("       PrivateKeyFile -x filename (changes to hidden cert)");
+            System.err.println("       PrivateKeyFile -t sigtype filename (changes to KeyCertificate of the given sig type");
             return;
         }
         I2PClient client = I2PClientFactory.createClient();
 
         int filearg = 0;
         if (args.length > 1) {
-            if (args.length >= 2 && args[0].equals("-h"))
+            if (args.length >= 2 && (args[0].equals("-h") || args[0].equals("-t")))
                 filearg = args.length - 1;
             else
                 filearg = 1;
@@ -101,10 +109,17 @@ public class PrivateKeyFile {
                 PrivateKeyFile pkf2 = new PrivateKeyFile(args[2]);
                 pkf.setSignedCert(pkf2);
                 System.out.println("New destination with signed cert is:");
+            } else if (args.length == 3 && args[0].equals("-t")) {
+                // KeyCert
+                SigType type = SigType.parseSigType(args[1]);
+                if (type == null)
+                    throw new IllegalArgumentException("Signature type " + args[1] + " is not supported");
+                pkf.setKeyCert(type);
+                System.out.println("New destination with key cert is:");
             }
             System.out.println(pkf);
             pkf.write();
-            verifySignature(d);
+            verifySignature(pkf.getDestination());
         } catch (Exception e) {
             e.printStackTrace();
         }
@@ -209,6 +224,43 @@ public class PrivateKeyFile {
         return c;
     }
     
+    /**
+     * Change cert type - caller must also call write().
+     * Side effect - creates new Destination object.
+     * @since 0.9.11
+     */
+    public Certificate setKeyCert(SigType type) {
+        if (type == SigType.DSA_SHA1)
+            return setCertType(Certificate.CERTIFICATE_TYPE_NULL);
+        if (dest == null)
+            throw new IllegalArgumentException("Dest is null");
+        KeyCertificate c = new KeyCertificate(type);
+        SimpleDataStructure signingKeys[];
+        try {
+            signingKeys = KeyGenerator.getInstance().generateSigningKeys(type);
+        } catch (GeneralSecurityException gse) {
+            throw new RuntimeException("keygen fail", gse);
+        }
+        SigningPublicKey signingPubKey = (SigningPublicKey) signingKeys[0];
+        signingPrivKey = (SigningPrivateKey) signingKeys[1];
+        // dests now immutable, must create new
+        Destination newdest = new Destination();
+        newdest.setPublicKey(dest.getPublicKey());
+        newdest.setSigningPublicKey(signingPubKey);
+        // fix up key certificate or padding
+        int len = type.getPubkeyLen();
+        if (len < 128) {
+            byte[] pad = new byte[128 - len];
+            RandomSource.getInstance().nextBytes(pad);
+            newdest.setPadding(pad);
+        } else if (len > 128) {
+            System.arraycopy(signingPubKey.getData(), 128, c.getPayload(), KeyCertificate.HEADER_LENGTH, len - 128);
+        }
+        newdest.setCertificate(c);
+        dest = newdest;
+        return c;
+    }
+    
     /** change to hashcash cert - caller must also call write() */
     public Certificate setHashCashCert(int effort) {
         Certificate c = setCertType(Certificate.CERTIFICATE_TYPE_HASHCASH);
@@ -444,8 +496,6 @@ public class PrivateKeyFile {
     
     private static final int HASH_EFFORT = VerifiedDestination.MIN_HASHCASH_EFFORT;
     
-    
-    
     private final File file;
     private final I2PClient client;
     private Destination dest;
diff --git a/core/java/src/net/i2p/data/RouterInfo.java b/core/java/src/net/i2p/data/RouterInfo.java
index 214937815e164c26b0bbba686eb874b0514c88b7..4900a4e9ee470d1b47b9461c0200750ed7765281 100644
--- a/core/java/src/net/i2p/data/RouterInfo.java
+++ b/core/java/src/net/i2p/data/RouterInfo.java
@@ -554,7 +554,7 @@ public class RouterInfo extends DatabaseEntry {
             }
         }
         DataHelper.readProperties(din, _options);
-        _signature = new Signature();
+        _signature = new Signature(_identity.getSigningPublicKey().getType());
         _signature.readBytes(in);
 
         if (verifySig) {
diff --git a/core/java/src/net/i2p/data/Signature.java b/core/java/src/net/i2p/data/Signature.java
index 83f0410e2458ab1e4c920260aac7cb747da6a2ea..1338cb664140607a2bfc2ee1b55cc0a4e9a77831 100644
--- a/core/java/src/net/i2p/data/Signature.java
+++ b/core/java/src/net/i2p/data/Signature.java
@@ -13,12 +13,15 @@ import net.i2p.crypto.SigType;
 
 /**
  * Defines the signature as defined by the I2P data structure spec.
- * A signature is a 40-byte array verifying the authenticity of some data 
+ * By default, a signature is a 40-byte array verifying the authenticity of some data 
  * using the DSA-SHA1 algorithm.
  *
  * The signature is the 20-byte R followed by the 20-byte S,
  * both are unsigned integers.
  *
+ * As of release 0.9.8, signatures of arbitrary length and type are supported.
+ * See SigType.
+ *
  * @author jrandom
  */
 public class Signature extends SimpleDataStructure {
@@ -39,10 +42,15 @@ public class Signature extends SimpleDataStructure {
     }
 
     /**
+     *  Unknown type not allowed as we won't know the length to read in the data.
+     *
+     *  @param type non-null
      *  @since 0.9.8
      */
     public Signature(SigType type) {
         super();
+        if (type == null)
+            throw new IllegalArgumentException("unknown type");
         _type = type;
     }
 
@@ -51,10 +59,15 @@ public class Signature extends SimpleDataStructure {
     }
 
     /**
+     *  Should we allow an unknown type here?
+     *
+     *  @param type non-null
      *  @since 0.9.8
      */
     public Signature(SigType type, byte data[]) {
         super();
+        if (type == null)
+            throw new IllegalArgumentException("unknown type");
         _type = type;
         setData(data);
     }
@@ -64,6 +77,7 @@ public class Signature extends SimpleDataStructure {
     }
 
     /**
+     *  @return non-null
      *  @since 0.9.8
      */
     public SigType getType() {
diff --git a/core/java/src/net/i2p/data/SigningPrivateKey.java b/core/java/src/net/i2p/data/SigningPrivateKey.java
index 4b60ee7e88fb1f878931f0fd1d4f9d367eced176..a8fcbb208196c88c8310f5b3e33b382a91057e39 100644
--- a/core/java/src/net/i2p/data/SigningPrivateKey.java
+++ b/core/java/src/net/i2p/data/SigningPrivateKey.java
@@ -14,10 +14,13 @@ import net.i2p.crypto.SigType;
 
 /**
  * Defines the SigningPrivateKey as defined by the I2P data structure spec.
- * A signing private key is 20 byte Integer. The private key represents only the 
+ * A signing private key is by default a 20 byte Integer. The private key represents only the 
  * exponent, not the primes, which are constant and defined in the crypto spec.
  * This key varies from the PrivateKey in its usage (signing, not decrypting)
  *
+ * As of release 0.9.8, keys of arbitrary length and type are supported.
+ * See SigType.
+ *
  * @author jrandom
  */
 public class SigningPrivateKey extends SimpleDataStructure {
diff --git a/core/java/src/net/i2p/data/SigningPublicKey.java b/core/java/src/net/i2p/data/SigningPublicKey.java
index e01d6ec39294a510371cf16a9bc8862a3600ee38..c7ec32bb8cccd80191d00905469e99a6d72bad62 100644
--- a/core/java/src/net/i2p/data/SigningPublicKey.java
+++ b/core/java/src/net/i2p/data/SigningPublicKey.java
@@ -11,15 +11,19 @@ package net.i2p.data;
 
 import java.io.InputStream;
 import java.io.IOException;
+import java.io.OutputStream;
 
 import net.i2p.crypto.SigType;
 
 /**
  * Defines the SigningPublicKey as defined by the I2P data structure spec.
- * A signing public key is 128 byte Integer. The public key represents only the 
+ * A signing public key is by default 128 byte Integer. The public key represents only the 
  * exponent, not the primes, which are constant and defined in the crypto spec.
  * This key varies from the PrivateKey in its usage (verifying signatures, not encrypting)
  *
+ * As of release 0.9.8, keys of arbitrary length and type are supported.
+ * See SigType.
+ *
  * @author jrandom
  */
 public class SigningPublicKey extends SimpleDataStructure {
@@ -55,6 +59,7 @@ public class SigningPublicKey extends SimpleDataStructure {
     }
 
     /**
+     *  @param type if null, type is unknown
      *  @since 0.9.8
      */
     public SigningPublicKey(SigType type) {
@@ -67,12 +72,16 @@ public class SigningPublicKey extends SimpleDataStructure {
     }
 
     /**
+     *  @param type if null, type is unknown
      *  @since 0.9.8
      */
     public SigningPublicKey(SigType type, byte data[]) {
         super();
         _type = type;
-        setData(data);
+        if (type != null || data == null)
+            setData(data);
+        else
+            _data = data;  // bypass length check
     }
 
     /** constructs from base64
@@ -84,17 +93,91 @@ public class SigningPublicKey extends SimpleDataStructure {
         fromBase64(base64Data);
     }
 
+    /**
+     *  @return if type unknown, the length of the data, or 128 if no data
+     */
     public int length() {
-        return _type.getPubkeyLen();
+        if (_type != null)
+            return _type.getPubkeyLen();
+        if (_data != null)
+            return _data.length;
+        return KEYSIZE_BYTES;
     }
 
     /**
+     *  @return null if unknown
      *  @since 0.9.8
      */
     public SigType getType() {
         return _type;
     }
 
+    /**
+     *  Up-convert this from an untyped (type 0) SPK to a typed SPK based on the Key Cert given
+     *
+     *  @throws IllegalArgumentException if this is already typed to a different type
+     *  @since 0.9.11
+     */
+    public SigningPublicKey toTypedKey(KeyCertificate kcert) {
+        if (_data == null)
+            throw new IllegalStateException();
+        SigType newType = kcert.getSigType();
+        if (_type == newType)
+            return this;
+        if (_type != SigType.DSA_SHA1)
+            throw new IllegalArgumentException("Cannot convert " + _type + " to " + newType);
+        int newLen = newType.getPubkeyLen();
+        if (newLen == SigType.DSA_SHA1.getPubkeyLen())
+            return new SigningPublicKey(newType, _data);
+        byte[] newData = new byte[newLen];
+        if (newLen < SigType.DSA_SHA1.getPubkeyLen()) {
+            // right-justified
+            System.arraycopy(_data, _data.length - newLen, newData, 0, newLen);
+        } else {
+            // full 128 bytes + fragment in kcert
+            System.arraycopy(_data, 0, newData, 0, _data.length);
+            System.arraycopy(kcert.getPayload(), KeyCertificate.HEADER_LENGTH, newData, _data.length, newLen - _data.length);
+        }
+        return new SigningPublicKey(newType, newData);
+    }
+
+    /**
+     *  Get the portion of this (type 0) SPK that is really padding based on the Key Cert type given,
+     *  if any
+     *
+     *  @return leading padding length > 0 or null
+     *  @throws IllegalArgumentException if this is already typed to a different type
+     *  @since 0.9.11
+     */
+    public byte[] getPadding(KeyCertificate kcert) {
+        if (_data == null)
+            throw new IllegalStateException();
+        SigType newType = kcert.getSigType();
+        if (_type == newType)
+            return null;
+        if (_type != SigType.DSA_SHA1)
+            throw new IllegalStateException("Cannot convert " + _type + " to " + newType);
+        int newLen = newType.getPubkeyLen();
+        if (newLen >= SigType.DSA_SHA1.getPubkeyLen())
+            return null;
+        int padLen = SigType.DSA_SHA1.getPubkeyLen() - newLen;
+        byte[] pad = new byte[padLen];
+        System.arraycopy(_data, 0, pad, 0, padLen);
+        return pad;
+    }
+    
+    /**
+     *  Write the data up to a max of 128 bytes.
+     *  If longer, the rest will be written in the KeyCertificate.
+     *  @since 0.9.11
+     */
+    public void writeTruncatedBytes(OutputStream out) throws DataFormatException, IOException {
+        if (_type.getPubkeyLen() <= KEYSIZE_BYTES)
+            out.write(_data);
+        else
+            out.write(_data, 0, KEYSIZE_BYTES);
+    }
+
     /**
      *  @since 0.9.8
      */
diff --git a/core/java/src/net/i2p/data/i2cp/CreateLeaseSetMessage.java b/core/java/src/net/i2p/data/i2cp/CreateLeaseSetMessage.java
index b57af1f8da89573253470f706dd65c3817238f0a..6477768c53211e7cbcb6d002613b8dface7c3171 100644
--- a/core/java/src/net/i2p/data/i2cp/CreateLeaseSetMessage.java
+++ b/core/java/src/net/i2p/data/i2cp/CreateLeaseSetMessage.java
@@ -71,6 +71,11 @@ public class CreateLeaseSetMessage extends I2CPMessageImpl {
         try {
             _sessionId = new SessionId();
             _sessionId.readBytes(in);
+            // Revocation is unimplemented.
+            // As the SPK comes before the LeaseSet, we don't know the key type.
+            // We could have some sort of callback or state setting so we get the
+            // expected type from the session. But for now, we just assume it's 20 bytes.
+            // Clients outside router context should throw in a dummy 20 bytes.
             _signingPrivateKey = new SigningPrivateKey();
             _signingPrivateKey.readBytes(in);
             _privateKey = new PrivateKey();
@@ -87,7 +92,7 @@ public class CreateLeaseSetMessage extends I2CPMessageImpl {
         if ((_sessionId == null) || (_signingPrivateKey == null) || (_privateKey == null) || (_leaseSet == null))
             throw new I2CPMessageException("Unable to write out the message as there is not enough data");
         int size = 4 // sessionId
-                 + SigningPrivateKey.KEYSIZE_BYTES
+                 + _signingPrivateKey.length()
                  + PrivateKey.KEYSIZE_BYTES
                  + _leaseSet.size();
         ByteArrayOutputStream os = new ByteArrayOutputStream(size);
diff --git a/core/java/src/net/i2p/data/i2cp/SessionConfig.java b/core/java/src/net/i2p/data/i2cp/SessionConfig.java
index cd6db38e4edc9cb06d19a17287cfe9c073db1624..8c5521993edb3556775f3088f5f95a0d11dbf833 100644
--- a/core/java/src/net/i2p/data/i2cp/SessionConfig.java
+++ b/core/java/src/net/i2p/data/i2cp/SessionConfig.java
@@ -189,7 +189,7 @@ public class SessionConfig extends DataStructureImpl {
         _destination = Destination.create(rawConfig);
         _options = DataHelper.readProperties(rawConfig);
         _creationDate = DataHelper.readDate(rawConfig);
-        _signature = new Signature();
+        _signature = new Signature(_destination.getSigningPublicKey().getType());
         _signature.readBytes(rawConfig);
     }