diff --git a/core/java/src/net/i2p/client/datagram/I2PDatagramDissector.java b/core/java/src/net/i2p/client/datagram/I2PDatagramDissector.java
new file mode 100644
index 0000000000000000000000000000000000000000..eb1e0af46b62981516419b6e01e750619076ffac
--- /dev/null
+++ b/core/java/src/net/i2p/client/datagram/I2PDatagramDissector.java
@@ -0,0 +1,160 @@
+package net.i2p.client.datagram;
+/*
+ * free (adj.): unencumbered; not under the control of others
+ * Written by human in 2004 and released into the public domain 
+ * with no warranty of any kind, either expressed or implied.  
+ * It probably won't  make your computer catch on fire, or eat 
+ * your children, but it might.  Use at your own risk.
+ *
+ */
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+import net.i2p.crypto.DSAEngine;
+import net.i2p.crypto.SHA256Generator;
+import net.i2p.data.DataFormatException;
+import net.i2p.data.Destination;
+import net.i2p.data.Signature;
+import net.i2p.util.HexDump;
+import net.i2p.util.Log;
+
+/**
+ * Class for dissecting I2P repliable datagrams, checking the authenticity of
+ * the sender.  Note that objects of this class are NOT THREAD SAFE!
+ *
+ * @author human
+ */
+public final class I2PDatagramDissector {
+
+    private static Log _log = new Log(I2PDatagramDissector.class);
+
+    private static int DGRAM_BUFSIZE = 32768;
+
+    private DSAEngine dsaEng = DSAEngine.getInstance();
+    private SHA256Generator hashGen = SHA256Generator.getInstance();
+
+    private byte[] rxHashBytes = null;
+
+    private Signature rxSign = new Signature();
+
+    private Destination rxDest = new Destination();
+
+    private byte[] rxPayload = new byte[DGRAM_BUFSIZE];
+
+    private int rxPayloadLen = 0;
+
+    /**
+     * Crate a new I2P repliable datagram dissector.
+     */
+    public I2PDatagramDissector() {}
+
+    /**
+     * Load an I2P repliable datagram into the dissector.
+     *
+     * @param dgram I2P repliable datagram to be loader
+     *
+     * @throws DataFormatException If there's an error in the datagram format
+     */
+    public void loadI2PDatagram(byte[] dgram) throws DataFormatException {
+        ByteArrayInputStream dgStream = new ByteArrayInputStream(dgram);
+        byte[] hashedData;
+
+
+        try {
+            rxSign.readBytes(dgStream);
+
+            rxDest.readBytes(dgStream);
+            rxPayloadLen = dgStream.read(rxPayload);
+            
+            hashedData = new byte[dgram.length - Signature.SIGNATURE_BYTES];
+            System.arraycopy(dgram, Signature.SIGNATURE_BYTES,
+                             hashedData, 0,
+                             hashedData.length);
+            rxHashBytes = hashGen.calculateHash(hashedData).toByteArray();
+        } catch (IOException e) {
+            _log.error("Caught IOException - INCONSISTENT STATE!", e);
+        }
+
+        //_log.debug("Datagram payload size: " + rxPayloadLen + "; content:\n"
+        //           + HexDump.dump(rxPayload, 0, rxPayloadLen));
+    }
+
+    /**
+     * Get the payload carried by an I2P repliable datagram (previously loaded
+     * with the loadI2PDatagram() method), verifying the datagram signature.
+     *
+     * @return A byte array containing the datagram payload
+     *
+     * @throws I2PInvalidDatagramException if the signature verification fails
+     */
+    public byte[] getPayload() throws I2PInvalidDatagramException {
+        if (!dsaEng.verifySignature(rxSign, rxHashBytes,
+                                    rxDest.getSigningPublicKey())) {
+            throw new I2PInvalidDatagramException("Incorrect I2P repliable datagram signature");
+        }
+
+        byte[] retPayload = new byte[rxPayloadLen];
+        System.arraycopy(rxPayload, 0, retPayload, 0, rxPayloadLen);
+
+        return retPayload;
+    }
+
+    /**
+     * Get the sender of an I2P repliable datagram (previously loaded with the
+     * loadI2PDatagram() method), verifying the datagram signature.
+     *
+     * @return The Destination of the I2P repliable datagram sender
+     *
+     * @throws I2PInvalidDatagramException if the signature verification fails
+     */
+    public Destination getSender() throws I2PInvalidDatagramException {
+        if (!dsaEng.verifySignature(rxSign, rxHashBytes,
+                                    rxDest.getSigningPublicKey())) {
+            throw new I2PInvalidDatagramException("Incorrect I2P repliable datagram signature");
+        }
+
+        Destination retDest = new Destination();
+        try {
+            retDest.fromByteArray(rxDest.toByteArray());
+        } catch (DataFormatException e) {
+            _log.error("Caught DataFormatException", e);
+            return null;
+        }
+
+        return retDest;
+    }
+
+    /**
+     * Extract the payload carried by an I2P repliable datagram (previously
+     * loaded with the loadI2PDatagram() method), without verifying the
+     * datagram signature.
+     *
+     * @return A byte array containing the datagram payload
+     */
+    public byte[] extractPayload() {
+        byte[] retPayload = new byte[rxPayloadLen];
+        System.arraycopy(rxPayload, 0, retPayload, 0, rxPayloadLen);
+
+        return retPayload;
+    }
+
+    /**
+     * Extract the sender of an I2P repliable datagram (previously loaded with
+     * the loadI2PDatagram() method), without verifying the datagram signature.
+     *
+     * @return The Destination of the I2P repliable datagram sender
+     */
+    public Destination extractSender() {
+        Destination retDest = new Destination();
+        try {
+            retDest.fromByteArray(rxDest.toByteArray());
+        } catch (DataFormatException e) {
+            _log.error("Caught DataFormatException", e);
+            return null;
+        }
+        
+        return retDest;
+    }
+}
diff --git a/core/java/src/net/i2p/client/datagram/I2PDatagramMaker.java b/core/java/src/net/i2p/client/datagram/I2PDatagramMaker.java
new file mode 100644
index 0000000000000000000000000000000000000000..600397948d129511e5c0825c360cce5696dce437
--- /dev/null
+++ b/core/java/src/net/i2p/client/datagram/I2PDatagramMaker.java
@@ -0,0 +1,84 @@
+package net.i2p.client.datagram;
+/*
+ * free (adj.): unencumbered; not under the control of others
+ * Written by human in 2004 and released into the public domain 
+ * with no warranty of any kind, either expressed or implied.  
+ * It probably won't  make your computer catch on fire, or eat 
+ * your children, but it might.  Use at your own risk.
+ *
+ */
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+import net.i2p.client.I2PSession;
+import net.i2p.crypto.DSAEngine;
+import net.i2p.crypto.SHA256Generator;
+import net.i2p.data.DataFormatException;
+import net.i2p.data.SigningPrivateKey;
+import net.i2p.util.Log;
+
+/**
+ * Class for creating I2P repliable datagrams.  Note that objects of this class
+ * are NOT THREAD SAFE!
+ *
+ * @author human
+ */
+public final class I2PDatagramMaker {
+
+    private static Log _log = new Log(I2PDatagramMaker.class);
+
+    private static int DGRAM_BUFSIZE = 32768;
+
+    private SHA256Generator hashGen = SHA256Generator.getInstance();
+    private DSAEngine dsaEng = DSAEngine.getInstance();
+
+    private SigningPrivateKey sxPrivKey = null;
+    private byte[] sxDestBytes = null;
+
+    private ByteArrayOutputStream sxBuf = new ByteArrayOutputStream(DGRAM_BUFSIZE);
+    private ByteArrayOutputStream sxDGram = new ByteArrayOutputStream(DGRAM_BUFSIZE);
+
+    /**
+     * Construct a new I2PDatagramMaker that will be able to create I2P
+     * repliable datagrams going to be sent through the specified I2PSession.
+     *
+     * @param session I2PSession used to send I2PDatagrams through
+     */
+    public I2PDatagramMaker(I2PSession session) {
+        sxPrivKey = session.getPrivateKey();
+        sxDestBytes = session.getMyDestination().toByteArray();
+    }
+
+    /**
+     * Make a repliable I2P datagram containing the specified payload.
+     *
+     * @param payload Bytes to be contained in the I2P datagram.
+     */
+    public byte[] makeI2PDatagram(byte[] payload) {
+        byte[] hashedData;
+
+        sxBuf.reset();
+        sxDGram.reset();
+        
+        try {
+            sxBuf.write(sxDestBytes);
+            sxBuf.write(payload);
+            
+            hashedData = sxBuf.toByteArray();
+            
+            dsaEng.sign(hashGen.calculateHash(hashedData).toByteArray(),
+                        sxPrivKey).writeBytes(sxDGram);
+
+            sxDGram.write(hashedData);
+        
+            return sxDGram.toByteArray();
+        } catch (IOException e) {
+            _log.error("Caught IOException", e);
+            return null;
+        } catch (DataFormatException e) {
+            _log.error("Caught DataFormatException", e);
+            return null;
+        }
+    }
+}
diff --git a/core/java/src/net/i2p/client/datagram/I2PInvalidDatagramException.java b/core/java/src/net/i2p/client/datagram/I2PInvalidDatagramException.java
new file mode 100644
index 0000000000000000000000000000000000000000..97756a7768c57f3490544abc7859106540358f8a
--- /dev/null
+++ b/core/java/src/net/i2p/client/datagram/I2PInvalidDatagramException.java
@@ -0,0 +1,25 @@
+package net.i2p.client.datagram;
+/*
+ * free (adj.): unencumbered; not under the control of others
+ * Written by human in 2004 and released into the public domain 
+ * with no warranty of any kind, either expressed or implied.  
+ * It probably won't  make your computer catch on fire, or eat 
+ * your children, but it might.  Use at your own risk.
+ *
+ */
+
+/**
+ * Exception thrown when I2P repliable datagram signature verification fails.
+ *
+ * @author human
+ */
+public class I2PInvalidDatagramException extends Exception {
+
+    public I2PInvalidDatagramException() {
+	super();
+    }
+    
+    public I2PInvalidDatagramException(String s) {
+	super(s);
+    }
+}