diff --git a/core/java/src/net/i2p/crypto/SelfSignedGenerator.java b/core/java/src/net/i2p/crypto/SelfSignedGenerator.java
index c60b9a09fd0aa6d624041647f4cba1dd007a256e..bf18a0f1c62a34a24c0564c99b94f4fe6b6a5ff7 100644
--- a/core/java/src/net/i2p/crypto/SelfSignedGenerator.java
+++ b/core/java/src/net/i2p/crypto/SelfSignedGenerator.java
@@ -153,6 +153,12 @@ public final class SelfSignedGenerator {
             throw new GeneralSecurityException("cert error", iae);
         }
 
+        // some simple tests
+        PublicKey cpub = cert.getPublicKey();
+        cert.verify(cpub);
+        if (!cpub.equals(jpub))
+            throw new GeneralSecurityException("pubkey mismatch");
+
         Object[] rv = { jpub, jpriv, cert };
         return rv;
     }
diff --git a/core/java/src/net/i2p/crypto/eddsa/EdDSAPrivateKey.java b/core/java/src/net/i2p/crypto/eddsa/EdDSAPrivateKey.java
index 5a79c61bf554de20d732db21d1e0ae1d642b9bff..ce8bb5783205a89aed9f01b3e396acb132372527 100644
--- a/core/java/src/net/i2p/crypto/eddsa/EdDSAPrivateKey.java
+++ b/core/java/src/net/i2p/crypto/eddsa/EdDSAPrivateKey.java
@@ -3,6 +3,7 @@ package net.i2p.crypto.eddsa;
 import java.security.PrivateKey;
 import java.security.spec.InvalidKeySpecException;
 import java.security.spec.PKCS8EncodedKeySpec;
+import java.util.Arrays;
 
 import net.i2p.crypto.eddsa.math.GroupElement;
 import net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable;
@@ -152,8 +153,9 @@ public class EdDSAPrivateKey implements EdDSAKey, PrivateKey {
                 d[idx++] != 1 ||
                 d[idx++] != 1 ||
                 d[idx++] != 0x04 ||
-                d[idx++] != 32)
-            throw new InvalidKeySpecException("unsupported key spec");
+                d[idx++] != 32) {
+                throw new InvalidKeySpecException("unsupported key spec");
+            }
             byte[] rv = new byte[32];
             System.arraycopy(d, idx, rv, 0, 32);
             return rv;
@@ -185,4 +187,26 @@ public class EdDSAPrivateKey implements EdDSAKey, PrivateKey {
     public byte[] getAbyte() {
         return Abyte;
     }
+
+    /**
+     *  @since 0.9.25
+     */
+    @Override
+    public int hashCode() {
+        return Arrays.hashCode(seed);
+    }
+
+    /**
+     *  @since 0.9.25
+     */
+    @Override
+    public boolean equals(Object o) {
+        if (o == this)
+            return true;
+        if (!(o instanceof EdDSAPrivateKey))
+            return false;
+        EdDSAPrivateKey pk = (EdDSAPrivateKey) o;
+        return Arrays.equals(seed, pk.getSeed()) &&
+               edDsaSpec.equals(pk.getParams());
+    }
 }
diff --git a/core/java/src/net/i2p/crypto/eddsa/EdDSAPublicKey.java b/core/java/src/net/i2p/crypto/eddsa/EdDSAPublicKey.java
index 5b7036547fcac40d4e39dbe5556417c3e2af735d..83817f8238c0f5317c74e4222a1162d9d0889ccf 100644
--- a/core/java/src/net/i2p/crypto/eddsa/EdDSAPublicKey.java
+++ b/core/java/src/net/i2p/crypto/eddsa/EdDSAPublicKey.java
@@ -3,6 +3,7 @@ package net.i2p.crypto.eddsa;
 import java.security.PublicKey;
 import java.security.spec.InvalidKeySpecException;
 import java.security.spec.X509EncodedKeySpec;
+import java.util.Arrays;
 
 import net.i2p.crypto.eddsa.math.GroupElement;
 import net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable;
@@ -126,9 +127,10 @@ public class EdDSAPublicKey implements EdDSAKey, PublicKey {
                 d[idx++] != 1 ||
                 d[idx++] != 1 ||
                 d[idx++] != 0x03 ||
-                d[idx++] != 32 ||
-                d[idx++] != 0)
-            throw new InvalidKeySpecException("unsupported key spec");
+                d[idx++] != 33 ||
+                d[idx++] != 0) {
+                throw new InvalidKeySpecException("unsupported key spec");
+            }
             byte[] rv = new byte[32];
             System.arraycopy(d, idx, rv, 0, 32);
             return rv;
@@ -152,4 +154,26 @@ public class EdDSAPublicKey implements EdDSAKey, PublicKey {
     public byte[] getAbyte() {
         return Abyte;
     }
+
+    /**
+     *  @since 0.9.25
+     */
+    @Override
+    public int hashCode() {
+        return Arrays.hashCode(Abyte);
+    }
+
+    /**
+     *  @since 0.9.25
+     */
+    @Override
+    public boolean equals(Object o) {
+        if (o == this)
+            return true;
+        if (!(o instanceof EdDSAPublicKey))
+            return false;
+        EdDSAPublicKey pk = (EdDSAPublicKey) o;
+        return Arrays.equals(Abyte, pk.getAbyte()) &&
+               edDsaSpec.equals(pk.getParams());
+    }
 }
diff --git a/core/java/src/net/i2p/crypto/eddsa/KeyFactory.java b/core/java/src/net/i2p/crypto/eddsa/KeyFactory.java
index 47f8542498f21562bea98ede18216185e2aaef3e..72fd03152ec098058722cfc2f88c9d73abb00eef 100644
--- a/core/java/src/net/i2p/crypto/eddsa/KeyFactory.java
+++ b/core/java/src/net/i2p/crypto/eddsa/KeyFactory.java
@@ -31,7 +31,7 @@ public final class KeyFactory extends KeyFactorySpi {
         if (keySpec instanceof PKCS8EncodedKeySpec) {
             return new EdDSAPrivateKey((PKCS8EncodedKeySpec) keySpec);
         }
-        throw new InvalidKeySpecException("key spec not recognised");
+        throw new InvalidKeySpecException("key spec not recognised: " + keySpec.getClass());
     }
 
     /**
@@ -45,7 +45,7 @@ public final class KeyFactory extends KeyFactorySpi {
         if (keySpec instanceof X509EncodedKeySpec) {
             return new EdDSAPublicKey((X509EncodedKeySpec) keySpec);
         }
-        throw new InvalidKeySpecException("key spec not recognised");
+        throw new InvalidKeySpecException("key spec not recognised: " + keySpec.getClass());
     }
 
     @SuppressWarnings("unchecked")
diff --git a/core/java/src/net/i2p/crypto/eddsa/math/Curve.java b/core/java/src/net/i2p/crypto/eddsa/math/Curve.java
index 2c6ade4ea17c3e67efce84a356fbd67435b77f44..fba8f29aa8e0c1fa81fba86ac5a17093d5cca30d 100644
--- a/core/java/src/net/i2p/crypto/eddsa/math/Curve.java
+++ b/core/java/src/net/i2p/crypto/eddsa/math/Curve.java
@@ -69,4 +69,29 @@ public class Curve implements Serializable {
             ge.precompute(true);
         return ge;
     }
+
+    /**
+     *  @since 0.9.25
+     */
+    @Override
+    public int hashCode() {
+        return f.hashCode() ^
+               d.hashCode() ^
+               I.hashCode();
+    }
+
+    /**
+     *  @since 0.9.25
+     */
+    @Override
+    public boolean equals(Object o) {
+        if (o == this)
+            return true;
+        if (!(o instanceof Curve))
+            return false;
+        Curve c = (Curve) o;
+        return f.equals(c.getField()) &&
+               d.equals(c.getD()) &&
+               I.equals(c.getI());
+    }
 }
diff --git a/core/java/src/net/i2p/crypto/eddsa/math/FieldElement.java b/core/java/src/net/i2p/crypto/eddsa/math/FieldElement.java
index 4bea7c39e955f5e1f5c1c103be5420831775bbb3..3f111f4449ef009ebbebe7292f5e7723e7cbec85 100644
--- a/core/java/src/net/i2p/crypto/eddsa/math/FieldElement.java
+++ b/core/java/src/net/i2p/crypto/eddsa/math/FieldElement.java
@@ -60,4 +60,6 @@ public abstract class FieldElement implements Serializable {
     public abstract FieldElement invert();
 
     public abstract FieldElement pow22523();
+
+    // Note: concrete subclasses must implement hashCode() and equals()
 }
diff --git a/core/java/src/net/i2p/crypto/eddsa/math/GroupElement.java b/core/java/src/net/i2p/crypto/eddsa/math/GroupElement.java
index ec81b5d408449da9ef91de337f422fb1f234ef3f..3a6c57b8249ffaa324b58a90cf612e024ace075b 100644
--- a/core/java/src/net/i2p/crypto/eddsa/math/GroupElement.java
+++ b/core/java/src/net/i2p/crypto/eddsa/math/GroupElement.java
@@ -716,6 +716,8 @@ public class GroupElement implements Serializable {
 
     @Override
     public boolean equals(Object obj) {
+        if (obj == this)
+            return true;
         if (!(obj instanceof GroupElement))
             return false;
         GroupElement ge = (GroupElement) obj;
diff --git a/core/java/src/net/i2p/crypto/eddsa/spec/EdDSAParameterSpec.java b/core/java/src/net/i2p/crypto/eddsa/spec/EdDSAParameterSpec.java
index 4ace0fb62c699355c1d2712c3e0cabb39648c9b6..75b0140c6ac461a154f58113e5eeda8b29a75746 100644
--- a/core/java/src/net/i2p/crypto/eddsa/spec/EdDSAParameterSpec.java
+++ b/core/java/src/net/i2p/crypto/eddsa/spec/EdDSAParameterSpec.java
@@ -59,4 +59,29 @@ public class EdDSAParameterSpec implements AlgorithmParameterSpec, Serializable
     public GroupElement getB() {
         return B;
     }
+
+    /**
+     *  @since 0.9.25
+     */
+    @Override
+    public int hashCode() {
+        return hashAlgo.hashCode() ^
+               curve.hashCode() ^
+               B.hashCode();
+    }
+
+    /**
+     *  @since 0.9.25
+     */
+    @Override
+    public boolean equals(Object o) {
+        if (o == this)
+            return true;
+        if (!(o instanceof EdDSAParameterSpec))
+            return false;
+        EdDSAParameterSpec s = (EdDSAParameterSpec) o;
+        return hashAlgo.equals(s.getHashAlgorithm()) &&
+               curve.equals(s.getCurve()) &&
+               B.equals(s.getB());
+    }
 }
diff --git a/core/java/src/net/i2p/crypto/provider/I2PProvider.java b/core/java/src/net/i2p/crypto/provider/I2PProvider.java
index f60284a3e0b89da04bddd4172278380f9ab0829d..958f307c4406f893acc90658761d485d93d33604 100644
--- a/core/java/src/net/i2p/crypto/provider/I2PProvider.java
+++ b/core/java/src/net/i2p/crypto/provider/I2PProvider.java
@@ -50,19 +50,22 @@ public final class I2PProvider extends Provider {
         //put("Signature.SHA1withDSA", "net.i2p.crypto.provider.SignatureSpi");
 
         // EdDSA
-        // OID: 1.3.101.100
+        // Key OID: 1.3.101.100; Sig OID: 1.3.101.101
         put("KeyFactory.EdDSA", "net.i2p.crypto.eddsa.KeyFactory");
         put("KeyPairGenerator.EdDSA", "net.i2p.crypto.eddsa.KeyPairGenerator");
         put("Signature.SHA512withEdDSA", "net.i2p.crypto.eddsa.EdDSAEngine");
         // Didn't find much documentation on these at all,
         // see http://docs.oracle.com/javase/7/docs/technotes/guides/security/crypto/HowToImplAProvider.html
         // section "Mapping from OID to name"
+        // without these, Certificate.verify() fails
+        put("Alg.Alias.KeyFactory.1.3.101.100", "EdDSA");
+        put("Alg.Alias.KeyFactory.OID.1.3.101.100", "EdDSA");
         // Without these, keytool fails with:
         // keytool error: java.security.NoSuchAlgorithmException: unrecognized algorithm name: SHA512withEdDSA
         put("Alg.Alias.KeyPairGenerator.1.3.101.100", "EdDSA");
         put("Alg.Alias.KeyPairGenerator.OID.1.3.101.100", "EdDSA");
-        put("Alg.Alias.Signature.1.3.101.100", "SHA512withEdDSA");
-        put("Alg.Alias.Signature.OID.1.3.101.100", "SHA512withEdDSA");
+        put("Alg.Alias.Signature.1.3.101.101", "SHA512withEdDSA");
+        put("Alg.Alias.Signature.OID.1.3.101.101", "SHA512withEdDSA");
         // TODO Ed25519ph
         // OID: 1.3.101.101
 
@@ -75,6 +78,8 @@ public final class I2PProvider extends Provider {
         put("KeyPairGenerator.DiffieHellman", "net.i2p.crypto.elgamal.KeyPairGenerator");
         put("KeyPairGenerator.ElGamal", "net.i2p.crypto.elgamal.KeyPairGenerator");
         put("Signature.SHA256withElGamal", "net.i2p.crypto.elgamal.ElGamalSigEngine");
+        put("Alg.Alias.KeyFactory.1.3.14.7.2.1.1", "ElGamal");
+        put("Alg.Alias.KeyFactory.OID.1.3.14.7.2.1.1", "ElGamal");
         put("Alg.Alias.KeyPairGenerator.1.3.14.7.2.1.1", "ElGamal");
         put("Alg.Alias.KeyPairGenerator.OID.1.3.14.7.2.1.1", "ElGamal");
         put("Alg.Alias.Signature.1.3.14.7.2.1.1", "SHA256withElGamal");