diff --git a/core/java/src/net/i2p/crypto/DSAEngine.java b/core/java/src/net/i2p/crypto/DSAEngine.java
index e59e56343568a175ae96967313d97d2b109ac8f1..0d47954c04222bd2292caeab24459989601ab672 100644
--- a/core/java/src/net/i2p/crypto/DSAEngine.java
+++ b/core/java/src/net/i2p/crypto/DSAEngine.java
@@ -29,10 +29,18 @@ package net.i2p.crypto;
  * POSSIBILITY OF SUCH DAMAGE.
  */
 
+import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.math.BigInteger;
+import java.security.GeneralSecurityException;
+import java.security.KeyFactory;;
 import java.security.MessageDigest;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.spec.DSAPrivateKeySpec;
+import java.security.spec.DSAPublicKeySpec;
+import java.security.spec.KeySpec;
 
 import net.i2p.I2PAppContext;
 import net.i2p.data.Hash;
@@ -44,26 +52,67 @@ import net.i2p.util.Log;
 import net.i2p.util.NativeBigInteger;
 
 /**
+ *  Sign and verify using DSA-SHA1.
+ *  Also contains methods to sign and verify using a SHA-256 Hash, used by Syndie only.
+ *
+ *  The primary implementation is code from TheCryto.
+ *  As of 0.8.7, also included is an alternate implementation using java.security libraries, which
+ *  is slightly slower. This implementation could in the future be easily modified
+ *  to use a new signing algorithm from java.security when we change the signing algorithm.
+ *
  *  Params and rv's changed from Hash to SHA1Hash for version 0.8.1
  *  Hash variants of sign() and verifySignature() restored in 0.8.3, required by Syndie.
  */
 public class DSAEngine {
-    private Log _log;
-    private I2PAppContext _context;
+    private final Log _log;
+    private final I2PAppContext _context;
+
+    //private static final boolean _isAndroid = System.getProperty("java.vendor").contains("Android");
+    private static final boolean _useJavaLibs = false;   // = _isAndroid;
 
     public DSAEngine(I2PAppContext context) {
         _log = context.logManager().getLog(DSAEngine.class);
         _context = context;
     }
+
     public static DSAEngine getInstance() {
         return I2PAppContext.getGlobalContext().dsa();
     }
+
+    /**
+     *  Verify using DSA-SHA1.
+     *  Uses TheCrypto code unless configured to use the java.security libraries.
+     */
     public boolean verifySignature(Signature signature, byte signedData[], SigningPublicKey verifyingKey) {
-        return verifySignature(signature, signedData, 0, signedData.length, verifyingKey);
+        boolean rv;
+        if (_useJavaLibs) {
+            try {
+                rv = altVerifySigSHA1(signature, signedData, verifyingKey);
+                if ((!rv) && _log.shouldLog(Log.WARN))
+                    _log.warn("Lib Verify Fail, sig =\n" + signature + "\npubkey =\n" + verifyingKey);
+                return rv;
+            } catch (GeneralSecurityException gse) {
+                if (_log.shouldLog(Log.WARN))
+                    _log.warn("Lib Verify Fail, sig =\n" + signature + "\npubkey =\n" + verifyingKey, gse);
+                // now try TheCrypto
+            }
+        }
+        rv = verifySignature(signature, signedData, 0, signedData.length, verifyingKey);
+        if ((!rv) && _log.shouldLog(Log.WARN))
+            _log.warn("TheCrypto Verify Fail, sig =\n" + signature + "\npubkey =\n" + verifyingKey);
+        return rv;
     }
+
+    /**
+     *  Verify using DSA-SHA1
+     */
     public boolean verifySignature(Signature signature, byte signedData[], int offset, int size, SigningPublicKey verifyingKey) {
         return verifySignature(signature, calculateHash(signedData, offset, size), verifyingKey);
     }
+
+    /**
+     *  Verify using DSA-SHA1
+     */
     public boolean verifySignature(Signature signature, InputStream in, SigningPublicKey verifyingKey) {
         return verifySignature(signature, calculateHash(in), verifyingKey);
     }
@@ -92,6 +141,8 @@ public class DSAEngine {
             byte[] sigbytes = signature.getData();
             byte rbytes[] = new byte[20];
             byte sbytes[] = new byte[20];
+            //System.arraycopy(sigbytes, 0, rbytes, 0, 20);
+            //System.arraycopy(sigbytes, 20, sbytes, 0, 20);
             for (int x = 0; x < 40; x++) {
                 if (x < 20) {
                     rbytes[x] = sigbytes[x];
@@ -99,6 +150,7 @@ public class DSAEngine {
                     sbytes[x - 20] = sigbytes[x];
                 }
             }
+
             BigInteger s = new NativeBigInteger(1, sbytes);
             BigInteger r = new NativeBigInteger(1, rbytes);
             BigInteger y = new NativeBigInteger(1, verifyingKey.getData());
@@ -106,6 +158,7 @@ public class DSAEngine {
             try {
                 w = s.modInverse(CryptoConstants.dsaq);
             } catch (ArithmeticException ae) {
+                _log.warn("modInverse() error", ae);
                 return false;
             }
             byte data[] = hash.getData();
@@ -130,15 +183,36 @@ public class DSAEngine {
         }
     }
 
+    /**
+     *  Sign using DSA-SHA1.
+     *  Uses TheCrypto code unless configured to use the java.security libraries.
+     */
     public Signature sign(byte data[], SigningPrivateKey signingKey) {
+        if (_useJavaLibs) {
+            try {
+                return altSignSHA1(data, signingKey);
+            } catch (GeneralSecurityException gse) {
+                if (_log.shouldLog(Log.WARN))
+                    _log.warn("Lib Sign Fail, privkey = " + signingKey, gse);
+                // now try TheCrypto
+            }
+        }
         return sign(data, 0, data.length, signingKey);
     }
+
+    /**
+     *  Sign using DSA-SHA1
+     */
     public Signature sign(byte data[], int offset, int length, SigningPrivateKey signingKey) {
         if ((signingKey == null) || (data == null) || (data.length <= 0)) return null;
         SHA1Hash h = calculateHash(data, offset, length);
         return sign(h, signingKey);
     }
     
+    /**
+     *  Sign using DSA-SHA1.
+     *  Reads the stream until EOF. Does not close the stream.
+     */
     public Signature sign(InputStream in, SigningPrivateKey signingKey) {
         if ((signingKey == null) || (in == null) ) return null;
         SHA1Hash h = calculateHash(in);
@@ -192,28 +266,34 @@ public class DSAEngine {
         _context.random().harvester().feedEntropy("DSA.sign", rbytes, 0, rbytes.length);
         
         if (rbytes.length == 20) {
+            //System.arraycopy(rbytes, 0, out, 0, 20);
             for (int i = 0; i < 20; i++) {
                 out[i] = rbytes[i];
             }
         } else if (rbytes.length == 21) {
+            //System.arraycopy(rbytes, 1, out, 0, 20);
             for (int i = 0; i < 20; i++) {
                 out[i] = rbytes[i + 1];
             }
         } else {
             if (_log.shouldLog(Log.DEBUG)) _log.debug("Using short rbytes.length [" + rbytes.length + "]");
+            //System.arraycopy(rbytes, 0, out, 20 - rbytes.length, rbytes.length);
             for (int i = 0; i < rbytes.length; i++)
                 out[i + 20 - rbytes.length] = rbytes[i];
         }
         if (sbytes.length == 20) {
+            //System.arraycopy(sbytes, 0, out, 20, 20);
             for (int i = 0; i < 20; i++) {
                 out[i + 20] = sbytes[i];
             }
         } else if (sbytes.length == 21) {
+            //System.arraycopy(sbytes, 1, out, 20, 20);
             for (int i = 0; i < 20; i++) {
                 out[i + 20] = sbytes[i + 1];
             }
         } else {
             if (_log.shouldLog(Log.DEBUG)) _log.debug("Using short sbytes.length [" + sbytes.length + "]");
+            //System.arraycopy(sbytes, 0, out, 40 - sbytes.length, sbytes.length);
             for (int i = 0; i < sbytes.length; i++)
                 out[i + 20 + 20 - sbytes.length] = sbytes[i];
         }
@@ -227,7 +307,11 @@ public class DSAEngine {
         return sig;
     }
     
-    /** @return hash SHA-1 hash, NOT a SHA-256 hash */
+    /**
+     *  Reads the stream until EOF. Does not close the stream.
+     *
+     *  @return hash SHA-1 hash, NOT a SHA-256 hash
+     */
     public SHA1Hash calculateHash(InputStream in) {
         MessageDigest digest = SHA1.getInstance();
         byte buf[] = new byte[64];
@@ -252,18 +336,222 @@ public class DSAEngine {
         return new SHA1Hash(digested);
     }
 
+    /**
+     *  Alternate to verifySignature() using java.security libraries.
+     *  @throws GeneralSecurityException if algorithm unvailable or on other errors
+     *  @since 0.8.7
+     */
+    private boolean altVerifySigSHA1(Signature signature, byte[] data, SigningPublicKey verifyingKey) throws GeneralSecurityException {
+        java.security.Signature jsig = java.security.Signature.getInstance("SHA1withDSA");
+        KeyFactory keyFact = KeyFactory.getInstance("DSA");
+        // y p q g
+        KeySpec spec = new DSAPublicKeySpec(new NativeBigInteger(1, verifyingKey.getData()),
+                                            CryptoConstants.dsap,
+                                            CryptoConstants.dsaq,
+                                            CryptoConstants.dsag);
+        PublicKey pubKey = keyFact.generatePublic(spec);
+        jsig.initVerify(pubKey);
+        jsig.update(data);
+        boolean rv = jsig.verify(sigBytesToASN1(signature.getData()));
+        //if (!rv) {
+        //    System.out.println("BAD SIG\n" + net.i2p.util.HexDump.dump(signature.getData()));
+        //    System.out.println("BAD SIG\n" + net.i2p.util.HexDump.dump(sigBytesToASN1(signature.getData())));
+        //}
+        return rv;
+    }
+
+    /**
+     *  Alternate to sign() using java.security libraries.
+     *  @throws GeneralSecurityException if algorithm unvailable or on other errors
+     *  @since 0.8.7
+     */
+    private Signature altSignSHA1(byte[] data, SigningPrivateKey privateKey) throws GeneralSecurityException {
+        java.security.Signature jsig = java.security.Signature.getInstance("SHA1withDSA");
+        KeyFactory keyFact = KeyFactory.getInstance("DSA");
+        // y p q g
+        KeySpec spec = new DSAPrivateKeySpec(new NativeBigInteger(1, privateKey.getData()),
+                                            CryptoConstants.dsap,
+                                            CryptoConstants.dsaq,
+                                            CryptoConstants.dsag);
+        PrivateKey privKey = keyFact.generatePrivate(spec);
+        jsig.initSign(privKey, _context.random());
+        jsig.update(data);
+        return new Signature(aSN1ToSigBytes(jsig.sign()));
+    }
+
+    /**
+     *  http://download.oracle.com/javase/1.5.0/docs/guide/security/CryptoSpec.html
+     *  Signature Format	ASN.1 sequence of two INTEGER values: r and s, in that order:
+     *                                SEQUENCE ::= { r INTEGER, s INTEGER }
+     *
+     *  http://en.wikipedia.org/wiki/Abstract_Syntax_Notation_One
+     *  30 -- tag indicating SEQUENCE
+     *  xx - length in octets
+     *
+     *  02 -- tag indicating INTEGER
+     *  xx - length in octets
+     *  xxxxxx - value
+     *
+     *  Convert to BigInteger and back so we have the minimum length representation, as required.
+     *  r and s are always non-negative.
+     *
+     *  @since 0.8.7
+     */
+    private static byte[] sigBytesToASN1(byte[] sig) {
+        //System.out.println("pre TO asn1\n" + net.i2p.util.HexDump.dump(sig));
+        ByteArrayOutputStream baos = new ByteArrayOutputStream(48);
+        baos.write(0x30);
+        baos.write(0);  // length to be filled in below
+
+        byte[] tmp = new byte[20];
+        baos.write(2);
+        System.arraycopy(sig, 0, tmp, 0, 20);
+        BigInteger r = new BigInteger(1, tmp);
+        byte[] b = r.toByteArray();
+        baos.write(b.length);
+        baos.write(b, 0, b.length);
+
+        baos.write(2);
+        System.arraycopy(sig, 20, tmp, 0, 20);
+        BigInteger s = new BigInteger(1, tmp);
+        b = s.toByteArray();
+        baos.write(b.length);
+        baos.write(b, 0, b.length);
+        byte[] rv = baos.toByteArray();
+        rv[1] = (byte) (rv.length - 2);
+        //System.out.println("post TO asn1\n" + net.i2p.util.HexDump.dump(rv));
+        return rv;
+    }
+
+    /**
+     *  See above.
+     *  @since 0.8.7
+     */
+    private static byte[] aSN1ToSigBytes(byte[] asn) {
+        //System.out.println("pre from asn1\n" + net.i2p.util.HexDump.dump(asn));
+        byte[] rv = new byte[40];
+        int rlen = asn[3];
+        if ((asn[4] & 0x80) != 0)
+            throw new IllegalArgumentException("R is negative");
+        if (rlen > 21)
+            throw new IllegalArgumentException("R too big " + rlen);
+        else if (rlen == 21) {
+            System.arraycopy(asn, 5, rv, 0, 20);
+        } else
+            System.arraycopy(asn, 4, rv, 20 - rlen, rlen);
+        int slenloc = 25 + rlen - 20;
+        int slen = asn[slenloc];
+        if ((asn[slenloc + 1] & 0x80) != 0)
+            throw new IllegalArgumentException("S is negative");
+        if (slen > 21)
+            throw new IllegalArgumentException("S too big " + slen);
+        else if (slen == 21) {
+            System.arraycopy(asn, slenloc + 2, rv, 20, 20);
+        } else
+            System.arraycopy(asn, slenloc + 1, rv, 40 - slen, slen);
+        //System.out.println("post from asn1\n" + net.i2p.util.HexDump.dump(rv));
+        return rv;
+    }
+
+    private static final int RUNS = 1000;
+
+    /**
+     *  Run consistency and speed tests with both TheCrypto and java.security libraries.
+     *
+     *  TheCrypto is about 5-15% faster than java.security.
+     */
     public static void main(String args[]) {
         I2PAppContext ctx = I2PAppContext.getGlobalContext();
-        byte data[] = new byte[4096];
+        byte data[] = new byte[1024];
+        // warmump
         ctx.random().nextBytes(data);
-        Object keys[] = ctx.keyGenerator().generateSigningKeypair();
         try {
-            for (int i = 0; i < 10; i++) {
-                Signature sig = ctx.dsa().sign(data, (SigningPrivateKey)keys[1]);
-                boolean ok = ctx.dsa().verifySignature(sig, data, (SigningPublicKey)keys[0]);
-                System.out.println("OK: " + ok);
+            Thread.sleep(1000);
+        } catch (InterruptedException ie) {}
+        SimpleDataStructure keys[] = null;
+
+        System.err.println("100 runs with new data and keys each time");
+        for (int i = 0; i < 100; i++) {
+            ctx.random().nextBytes(data);
+            keys = ctx.keyGenerator().generateSigningKeys();
+            Signature sig = ctx.dsa().sign(data, (SigningPrivateKey)keys[1]);
+            Signature jsig = null;
+            try {
+                 jsig = ctx.dsa().altSignSHA1(data, (SigningPrivateKey)keys[1]);
+            } catch (GeneralSecurityException gse) {
+                gse.printStackTrace();
             }
-        } catch (Exception e) { e.printStackTrace(); }
-        ctx.random().saveSeed();
-    }
+            boolean ok = ctx.dsa().verifySignature(jsig, data, (SigningPublicKey)keys[0]);
+            boolean usok = ctx.dsa().verifySignature(sig, data, (SigningPublicKey)keys[0]);
+            boolean jok = false;
+            try {
+                jok = ctx.dsa().altVerifySigSHA1(sig, data, (SigningPublicKey)keys[0]);
+            } catch (GeneralSecurityException gse) {
+                gse.printStackTrace();
+            }
+            boolean jjok = false;;
+            try {
+                jjok = ctx.dsa().altVerifySigSHA1(jsig, data, (SigningPublicKey)keys[0]);
+            } catch (GeneralSecurityException gse) {
+                gse.printStackTrace();
+            }
+            System.err.println("TC->TC OK: " + usok + "  JL->TC OK: " + ok + "  TC->JK OK: " + jok + "  JL->JL OK: " + jjok);
+            if (!(ok && usok && jok && jjok)) {
+                System.out.println("privkey\n" + net.i2p.util.HexDump.dump(keys[1].getData()));
+                return;
+            }
+        }
+
+        System.err.println("Starting speed test");
+        long start = System.currentTimeMillis();
+        for (int i = 0; i < RUNS; i++) {
+            Signature sig = ctx.dsa().sign(data, (SigningPrivateKey)keys[1]);
+            boolean ok = ctx.dsa().verifySignature(sig, data, (SigningPublicKey)keys[0]);
+            if (!ok) {
+                System.err.println("TheCrypto FAIL");
+                return;
+            }
+        }
+        long time = System.currentTimeMillis() - start;
+        System.err.println("Time for " + RUNS + " DSA sign/verifies:");
+        System.err.println("TheCrypto time (ms): " + time);
+
+        start = System.currentTimeMillis();
+        for (int i = 0; i < RUNS; i++) {
+            boolean ok = false;
+            try {
+                Signature jsig = ctx.dsa().altSignSHA1(data, (SigningPrivateKey)keys[1]);
+                ok = ctx.dsa().altVerifySigSHA1(jsig, data, (SigningPublicKey)keys[0]);
+            } catch (GeneralSecurityException gse) {
+                gse.printStackTrace();
+            }
+            if (!ok) {
+                System.err.println("JavaLib FAIL");
+                return;
+            }
+        }
+        time = System.currentTimeMillis() - start;
+        System.err.println("JavaLib time (ms): " + time);
+
+/****  yes, arraycopy is slower for 20 bytes
+        start = System.currentTimeMillis();
+	byte b[] = new byte[20];
+        for (int i = 0; i < 10000000; i++) {
+            data[0] = data[i % 256];
+            System.arraycopy(data, 0, b, 0, 20);
+        }
+        time = System.currentTimeMillis() - start;
+        System.err.println("arraycopy time (ms): " + time);
+
+        start = System.currentTimeMillis();
+        for (int i = 0; i < 10000000; i++) {
+            data[0] = data[i % 256];
+            for (int j = 0; j < 20; j++) {
+                 b[j] = data[j];
+            }
+        }
+        time = System.currentTimeMillis() - start;
+        System.err.println("loop time (ms): " + time);
+****/
+     }
 }