diff --git a/router/java/src/com/southernstorm/noise/protocol/HandshakeState.java b/router/java/src/com/southernstorm/noise/protocol/HandshakeState.java
index 2297ae25298d164bea5854dccb8557531a3b0112..3dc56bed55a4ce56095f3b0d1f1e42e786143217 100644
--- a/router/java/src/com/southernstorm/noise/protocol/HandshakeState.java
+++ b/router/java/src/com/southernstorm/noise/protocol/HandshakeState.java
@@ -136,6 +136,8 @@ public class HandshakeState implements Destroyable, Cloneable {
 	public static final String PATTERN_ID_XK = "XK";
 	public static final String PATTERN_ID_IK = "IK";
 	public static final String PATTERN_ID_N = "N";
+	/** same as N but no post-mixHash needed */
+	public static final String PATTERN_ID_N_NO_RESPONSE = "N!";
 	private static String dh;
 	private static final String cipher;
 	private static final String hash;
@@ -205,6 +207,8 @@ public class HandshakeState implements Destroyable, Cloneable {
 			pattern = PATTERN_IK;
 		else if (patternId.equals(PATTERN_ID_N))
 			pattern = PATTERN_N;
+		else if (patternId.equals(PATTERN_ID_N_NO_RESPONSE))  // same as N but no post-mixHash needed
+			pattern = PATTERN_N;
 		else
 			throw new IllegalArgumentException("Handshake pattern is not recognized");
 		short flags = pattern[0];
@@ -650,13 +654,13 @@ public class HandshakeState implements Destroyable, Cloneable {
 			// Add the payload to the message buffer and encrypt it.
 			if (payload != null) {
 				// no need to hash for N, we don't split() and no more messages follow
-				if (patternId.equals(PATTERN_ID_N))
+				if (patternId.equals(PATTERN_ID_N_NO_RESPONSE))
 					messagePosn += symmetric.encryptOnly(payload, payloadOffset, message, messagePosn, payloadLength);
 				else
 					messagePosn += symmetric.encryptAndHash(payload, payloadOffset, message, messagePosn, payloadLength);
 			} else {
 				// no need to hash for N, we don't split() and no more messages follow
-				if (patternId.equals(PATTERN_ID_N))
+				if (patternId.equals(PATTERN_ID_N_NO_RESPONSE))
 					messagePosn += symmetric.encryptOnly(message, messagePosn, message, messagePosn, 0);
 				else
 					messagePosn += symmetric.encryptAndHash(message, messagePosn, message, messagePosn, 0);
@@ -830,7 +834,7 @@ public class HandshakeState implements Destroyable, Cloneable {
 			// Decrypt the message payload.
 			int payloadLength;
 			// no need to hash for N, we don't split() and no more messages follow
-			if (patternId.equals(PATTERN_ID_N))
+			if (patternId.equals(PATTERN_ID_N_NO_RESPONSE))
 				payloadLength = symmetric.decryptOnly(message, messageOffset, payload, payloadOffset, messageEnd - messageOffset);
 			else
 				payloadLength = symmetric.decryptAndHash(message, messageOffset, payload, payloadOffset, messageEnd - messageOffset);
diff --git a/router/java/src/com/southernstorm/noise/protocol/SymmetricState.java b/router/java/src/com/southernstorm/noise/protocol/SymmetricState.java
index 4c7f09ee5fee9bd1e38f96bc0f278a0ecf38e2f8..4e17269656199e5c9e2e64bf7630e63a3b87196e 100644
--- a/router/java/src/com/southernstorm/noise/protocol/SymmetricState.java
+++ b/router/java/src/com/southernstorm/noise/protocol/SymmetricState.java
@@ -121,7 +121,8 @@ class SymmetricState implements Destroyable, Cloneable {
 		} else if (patternId.equals(HandshakeState.PATTERN_ID_IK)) {
 			initCK = INIT_CK_IK;
 			initHash = INIT_HASH_IK;
-		} else if (patternId.equals(HandshakeState.PATTERN_ID_N)) {
+		} else if (patternId.equals(HandshakeState.PATTERN_ID_N) ||
+		           patternId.equals(HandshakeState.PATTERN_ID_N_NO_RESPONSE)) {
 			initCK = INIT_CK_N;
 			initHash = INIT_HASH_N;
 		} else {
diff --git a/router/java/src/net/i2p/router/crypto/ratchet/ECIESAEADEngine.java b/router/java/src/net/i2p/router/crypto/ratchet/ECIESAEADEngine.java
index 133e8c4cf7d4e74fbb7d7d9c044fcb7f90807753..c57d22f72d21d438df4b2b2edfbf14b82d19ef88 100644
--- a/router/java/src/net/i2p/router/crypto/ratchet/ECIESAEADEngine.java
+++ b/router/java/src/net/i2p/router/crypto/ratchet/ECIESAEADEngine.java
@@ -503,7 +503,7 @@ public final class ECIESAEADEngine {
 
         HandshakeState state;
         try {
-            state = new HandshakeState(HandshakeState.PATTERN_ID_N, HandshakeState.RESPONDER, _context.commSystem().getXDHFactory());
+            state = new HandshakeState(HandshakeState.PATTERN_ID_N_NO_RESPONSE, HandshakeState.RESPONDER, _context.commSystem().getXDHFactory());
         } catch (GeneralSecurityException gse) {
             throw new IllegalStateException("bad proto", gse);
         }
@@ -1009,7 +1009,7 @@ public final class ECIESAEADEngine {
     private byte[] encryptNewSession(CloveSet cloves, PublicKey target) {
         HandshakeState state;
         try {
-            state = new HandshakeState(HandshakeState.PATTERN_ID_N, HandshakeState.INITIATOR, _context.commSystem().getXDHFactory());
+            state = new HandshakeState(HandshakeState.PATTERN_ID_N_NO_RESPONSE, HandshakeState.INITIATOR, _context.commSystem().getXDHFactory());
         } catch (GeneralSecurityException gse) {
             throw new IllegalStateException("bad proto", gse);
         }