diff --git a/router/java/src/net/i2p/router/transport/udp/IntroductionManager.java b/router/java/src/net/i2p/router/transport/udp/IntroductionManager.java
index 327e26e4b504e97b45d3626a173fe8409327bf1e..d504b78e04036d31777dfed9b0a5b3e94f922440 100644
--- a/router/java/src/net/i2p/router/transport/udp/IntroductionManager.java
+++ b/router/java/src/net/i2p/router/transport/udp/IntroductionManager.java
@@ -298,14 +298,20 @@ class IntroductionManager {
                     continue;
                 }
                 cur.setIntroducerTime();
-                UDPAddress ura = new UDPAddress(ra);
-                byte[] ikey = ura.getIntroKey();
-                if (ikey == null)
-                    continue;
-                introducers.add(new Introducer(ip, port, ikey, cur.getTheyRelayToUsAs(), exp));
+                Introducer intro;
+                if (cur.getVersion() == 1) {
+                    UDPAddress ura = new UDPAddress(ra);
+                    byte[] ikey = ura.getIntroKey();
+                    if (ikey == null)
+                        continue;
+                    intro = new Introducer(ip, port, ikey, cur.getTheyRelayToUsAs(), exp);
+                } else {
+                    intro = new Introducer(cur.getRemotePeer(), cur.getTheyRelayToUsAs(), exp);
+                }
+                introducers.add(intro);
                 found++;
-                // two per router max
-                if (found - oldFound >= 2)
+                // two per router max, one for SSU 2
+                if (found - oldFound >= 2 || cur.getVersion() > 1)
                     break;
             }
             if (oldFound != found && _log.shouldLog(Log.INFO))
@@ -316,32 +322,45 @@ class IntroductionManager {
         Collections.sort(introducers);
         for (int i = 0; i < found; i++) {
             Introducer in = introducers.get(i);
-            ssuOptions.setProperty(UDPAddress.PROP_INTRO_HOST_PREFIX + i, in.sip);
-            ssuOptions.setProperty(UDPAddress.PROP_INTRO_PORT_PREFIX + i, in.sport);
-            ssuOptions.setProperty(UDPAddress.PROP_INTRO_KEY_PREFIX + i, in.skey);
+            if (in.version == 1) {
+                ssuOptions.setProperty(UDPAddress.PROP_INTRO_HOST_PREFIX + i, in.sip);
+                ssuOptions.setProperty(UDPAddress.PROP_INTRO_PORT_PREFIX + i, in.sport);
+                ssuOptions.setProperty(UDPAddress.PROP_INTRO_KEY_PREFIX + i, in.skey);
+            } else {
+                ssuOptions.setProperty(UDPAddress.PROP_INTRO_HASH_PREFIX + i, in.shash);
+            }
             ssuOptions.setProperty(UDPAddress.PROP_INTRO_TAG_PREFIX + i, in.stag);
             String sexp = in.sexp;
             // look for existing expiration in current published
             // and reuse if still recent enough, so deepEquals() won't fail in UDPT.rEA
             if (current != null) {
                 for (int j = 0; j < UDPTransport.PUBLIC_RELAY_COUNT; j++) {
-                    if (in.sip.equals(current.getOption(UDPAddress.PROP_INTRO_HOST_PREFIX + j)) &&
-                        in.sport.equals(current.getOption(UDPAddress.PROP_INTRO_PORT_PREFIX + j)) &&
-                        in.skey.equals(current.getOption(UDPAddress.PROP_INTRO_KEY_PREFIX + j)) &&
-                        in.stag.equals(current.getOption(UDPAddress.PROP_INTRO_TAG_PREFIX + j))) {
-                        // found old one
-                        String oexp = current.getOption(UDPAddress.PROP_INTRO_EXP_PREFIX + j);
-                        if (oexp != null) {
-                            try {
-                                long oex = Long.parseLong(oexp) * 1000;
-                                if (oex > now + UDPTransport.INTRODUCER_EXPIRATION_MARGIN) {
-                                    // still good, use old expiration time
-                                    sexp = oexp;
-                                }
-                            } catch (NumberFormatException nfe) {}
+                    String oexp = null;
+                    if (in.version == 1) {
+                        if (in.sip.equals(current.getOption(UDPAddress.PROP_INTRO_HOST_PREFIX + j)) &&
+                            in.sport.equals(current.getOption(UDPAddress.PROP_INTRO_PORT_PREFIX + j)) &&
+                            in.skey.equals(current.getOption(UDPAddress.PROP_INTRO_KEY_PREFIX + j)) &&
+                            in.stag.equals(current.getOption(UDPAddress.PROP_INTRO_TAG_PREFIX + j))) {
+                            // found old one
+                            oexp = current.getOption(UDPAddress.PROP_INTRO_EXP_PREFIX + j);
+                        }
+                    } else {
+                        if (in.shash.equals(current.getOption(UDPAddress.PROP_INTRO_HASH_PREFIX + j)) &&
+                            in.stag.equals(current.getOption(UDPAddress.PROP_INTRO_TAG_PREFIX + j))) {
+                            // found old one
+                            oexp = current.getOption(UDPAddress.PROP_INTRO_EXP_PREFIX + j);
                         }
-                        break;
                     }
+                    if (oexp != null) {
+                        try {
+                            long oex = Long.parseLong(oexp) * 1000;
+                            if (oex > now + UDPTransport.INTRODUCER_EXPIRATION_MARGIN) {
+                                // still good, use old expiration time
+                                sexp = oexp;
+                            }
+                        } catch (NumberFormatException nfe) {}
+                    }
+                    break;
                 }
             }
             ssuOptions.setProperty(UDPAddress.PROP_INTRO_EXP_PREFIX + i, sexp);
@@ -358,19 +377,43 @@ class IntroductionManager {
      *  @since 0.9.18
      */
     private static class Introducer implements Comparable<Introducer> {
-        public final String sip, sport, skey, stag, sexp;
+        public final String sip, sport, skey, stag, sexp, shash;
+        public final int version;
 
+        /**
+         * SSU 1
+         */
         public Introducer(byte[] ip, int port, byte[] key, long tag, String exp) {
             sip = Addresses.toString(ip);
             sport = String.valueOf(port);
             skey = Base64.encode(key);
             stag = String.valueOf(tag);
             sexp = exp;
+            version = 1;
+            shash = null;
+        }
+
+        /**
+         * SSU 2
+         * @since 0.9.55
+         */
+        public Introducer(Hash h, long tag, String exp) {
+            stag = String.valueOf(tag);
+            sexp = exp;
+            shash = h.toBase64();
+            version = 2;
+            sip = null;
+            sport = null;
+            skey = null;
         }
 
         @Override
         public int compareTo(Introducer i) {
-            return skey.compareTo(i.skey);
+            // put SSU 2 at the end to not confuse SSU 1
+            int diff = version - i.version;
+            if (diff != 0)
+                return diff;
+            return stag.compareTo(i.stag);
         }
         
         @Override
@@ -388,7 +431,7 @@ class IntroductionManager {
         
         @Override
         public int hashCode() {
-        	return skey.hashCode(); 
+            return stag.hashCode();
         }
     }
 
diff --git a/router/java/src/net/i2p/router/transport/udp/UDPAddress.java b/router/java/src/net/i2p/router/transport/udp/UDPAddress.java
index 83a8e7a7709b8d0bfb6750952211ce8acc1b7a32..57022f8db00512b1f946b8c7323903c80cc8885f 100644
--- a/router/java/src/net/i2p/router/transport/udp/UDPAddress.java
+++ b/router/java/src/net/i2p/router/transport/udp/UDPAddress.java
@@ -43,6 +43,9 @@ class UDPAddress {
     public static final String PROP_INTRO_TAG_PREFIX = "itag";
     /** @since 0.9.30 */
     public static final String PROP_INTRO_EXP_PREFIX = "iexp";
+    /** @since 0.9.55 for SSU2 */
+    public static final String PROP_INTRO_HASH_PREFIX = "ih";
+
     static final int MAX_INTRODUCERS = 3;
     private static final String[] PROP_INTRO_HOST;
     private static final String[] PROP_INTRO_PORT;