diff --git a/build.gradle b/build.gradle
index 753ab0d10..04d55e661 100644
--- a/build.gradle
+++ b/build.gradle
@@ -14,7 +14,7 @@ apply plugin: 'java'
group = 'network.casper'
// Version number update for release
-version='2.5.3'
+version='2.5.4'
sourceCompatibility = 1.8
targetCompatibility = 1.8
@@ -26,9 +26,11 @@ repositories {
dependencies {
implementation "dev.oak3:sbs4j:${sbs4jVersion}"
implementation "io.github.oak:jsonrpc4j:${jsonrpc4jVersion}"
- implementation "com.syntifi.crypto:crypto-key-common:${cryptokeyVersion}"
- implementation "com.syntifi.crypto:crypto-key-ed25519:${cryptokeyVersion}"
- implementation "com.syntifi.crypto:crypto-key-secp256k1:${cryptokeyVersion}"
+// implementation "com.syntifi.crypto:crypto-key-common:${cryptokeyVersion}"
+// implementation "com.syntifi.crypto:crypto-key-ed25519:${cryptokeyVersion}"
+// implementation "com.syntifi.crypto:crypto-key-secp256k1:${cryptokeyVersion}"
+ implementation "org.bouncycastle:bcpkix-jdk15on:${bouncyCastleVersion}"
+ implementation "org.web3j:core:${web3jVersion}"
implementation "com.fasterxml.jackson.core:jackson-core:${jacksonVersion}"
implementation "com.fasterxml.jackson.core:jackson-databind:${jacksonVersion}"
implementation "com.fasterxml.jackson.core:jackson-annotations:${jacksonVersion}"
diff --git a/gradle.properties b/gradle.properties
index ffe888527..713102dfa 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,15 +1,17 @@
-sbs4jVersion=0.1.8
+bouncyCastleVersion=1.69
commonsIoVersion=2.11.0
cryptokeyVersion=0.4.0
cxfRtRsSseVersion=3.5.5
-lombokPluginVersion=6.2.0
-jupiterVersion=5.9.0
-jsonrpc4jVersion=1.6.1-oak
jacksonVersion=2.13.4
+javaTuplesVersion=1.2
+jodaTimeVersion=2.11.1
jsonassertVersion=1.5.1
jsonPathAssertVersion=2.7.0
+jsonrpc4jVersion=1.6.1-oak
+jupiterVersion=5.9.0
mockwebserverVersion=4.10.0
log4jVersion=2.18.0
+lombokPluginVersion=6.2.0
+sbs4jVersion=0.1.8
slf4jApiVersion=2.0.0
-javaTuplesVersion=1.2
-jodaTimeVersion=2.11.1
+web3jVersion=5.0.0
diff --git a/src/main/java/com/casper/sdk/exception/InvalidKeyBytesException.java b/src/main/java/com/casper/sdk/exception/InvalidKeyBytesException.java
new file mode 100644
index 000000000..b615e1756
--- /dev/null
+++ b/src/main/java/com/casper/sdk/exception/InvalidKeyBytesException.java
@@ -0,0 +1,11 @@
+package com.casper.sdk.exception;
+
+/**
+ * @author ian@meywood.com
+ */
+public class InvalidKeyBytesException extends RuntimeException {
+
+ public InvalidKeyBytesException(Throwable cause) {
+ super(cause);
+ }
+}
diff --git a/src/main/java/com/casper/sdk/jackson/deserializer/AbstractAnyOfDeserializer.java b/src/main/java/com/casper/sdk/jackson/deserializer/AbstractAnyOfDeserializer.java
index 07643f629..e989f30f1 100644
--- a/src/main/java/com/casper/sdk/jackson/deserializer/AbstractAnyOfDeserializer.java
+++ b/src/main/java/com/casper/sdk/jackson/deserializer/AbstractAnyOfDeserializer.java
@@ -1,36 +1,31 @@
package com.casper.sdk.jackson.deserializer;
-import java.io.IOException;
-
import com.casper.sdk.exception.NoSuchTypeException;
+import com.casper.sdk.jackson.resolver.CLValueResolver;
+import com.casper.sdk.model.clvalue.AbstractCLValue;
import com.fasterxml.jackson.core.JsonParser;
-import com.fasterxml.jackson.databind.BeanProperty;
-import com.fasterxml.jackson.databind.DeserializationContext;
-import com.fasterxml.jackson.databind.JavaType;
-import com.fasterxml.jackson.databind.JsonDeserializer;
-import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.jsontype.TypeIdResolver;
import com.fasterxml.jackson.databind.jsontype.impl.AsPropertyTypeDeserializer;
import com.fasterxml.jackson.databind.node.TreeTraversingParser;
import com.fasterxml.jackson.databind.type.TypeFactory;
-import com.casper.sdk.jackson.resolver.CLValueResolver;
-import com.casper.sdk.model.clvalue.AbstractCLValue;
+
+import java.io.IOException;
/**
* Core Deserializer for the CLValue property. This deserializer is used by the
* {@link CLValueResolver} to return the correct CLType object in Java depending
* on the cl_type sent over json
- *
+ *
* @author Alexandre Carvalho
* @author Andre Bertolace
- * @since 0.0.1
* @see AbstractCLValue
+ * @since 0.0.1
*/
public abstract class AbstractAnyOfDeserializer extends AsPropertyTypeDeserializer {
protected AbstractAnyOfDeserializer(final JavaType bt, final TypeIdResolver idRes, final String typePropertyName,
- final boolean typeIdVisible, JavaType defaultImpl) {
+ final boolean typeIdVisible, JavaType defaultImpl) {
super(bt, idRes, typePropertyName, typeIdVisible, defaultImpl);
}
@@ -50,7 +45,7 @@ public Object deserializeTypedFromObject(final JsonParser jp, final Deserializat
} catch (NoSuchTypeException e) {
throw new IOException("Parse error", e);
}
- TypeFactory factory = new ObjectMapper().getTypeFactory();
+ TypeFactory factory = ctxt.getTypeFactory();
JavaType type = factory.constructType(subType);
try (JsonParser jsonParser = new TreeTraversingParser(node, jp.getCodec())) {
@@ -64,9 +59,9 @@ public Object deserializeTypedFromObject(final JsonParser jp, final Deserializat
/**
* Returns the node which contains the type key.
- *
+ *
* Override if you have a child node which holds the type information.
- *
+ *
* @param currentNode the current deserialization node
* @return node which contains the type key.
*/
@@ -76,7 +71,7 @@ protected JsonNode getTypeNode(JsonNode currentNode) {
/**
* Method that returns the instance of the found type
- *
+ *
* @param classType the name of the class type
* @return {@link Class} of the type
* @throws NoSuchTypeException thrown if no type for the given classType String
diff --git a/src/main/java/com/syntifi/crypto/key/ASN1Identifiers.java b/src/main/java/com/syntifi/crypto/key/ASN1Identifiers.java
new file mode 100644
index 000000000..e28db82b6
--- /dev/null
+++ b/src/main/java/com/syntifi/crypto/key/ASN1Identifiers.java
@@ -0,0 +1,25 @@
+package com.syntifi.crypto.key;
+
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+
+/**
+ * ASN1 identifiers for working with key cryptography
+ *
+ * @author Alexandre Carvalho
+ * @author Andre Bertolace
+ * @since 0.1.0
+ */
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
+public class ASN1Identifiers {
+
+ public static final ASN1ObjectIdentifier Secp256k1OIDCurve = new ASN1ObjectIdentifier("1.3.132.0.10");
+ public static final ASN1ObjectIdentifier Secp256k1OIDkey = new ASN1ObjectIdentifier("1.2.840.10045.2.1");
+
+ public static final ASN1ObjectIdentifier Ed25519OID = new ASN1ObjectIdentifier("1.3.101.112");
+
+ public static final String PUBLIC_KEY_DER_HEADER = "PUBLIC KEY";
+ public static final String PRIVATE_KEY_DER_HEADER = "PRIVATE KEY";
+ public static final String EC_PRIVATE_KEY_DER_HEADER = "EC PRIVATE KEY";
+}
diff --git a/src/main/java/com/syntifi/crypto/key/AbstractPrivateKey.java b/src/main/java/com/syntifi/crypto/key/AbstractPrivateKey.java
new file mode 100644
index 000000000..d4ed373ba
--- /dev/null
+++ b/src/main/java/com/syntifi/crypto/key/AbstractPrivateKey.java
@@ -0,0 +1,86 @@
+package com.syntifi.crypto.key;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.*;
+import java.security.GeneralSecurityException;
+
+/**
+ * Abstract class for needed shared functionalities
+ *
+ * @author Alexandre Carvalho
+ * @author Andre Bertolace
+ * @since 0.1.0
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public abstract class AbstractPrivateKey {
+
+ private byte[] key;
+
+ /**
+ * Loads a private key from a byte array
+ *
+ * @param privateKey the private key bytes
+ */
+ public abstract void loadPrivateKey(final byte[] privateKey) throws IOException;
+
+ /**
+ * Reads the private key from a file
+ *
+ * @param filename the source filename
+ * @throws IOException thrown if an error occurs reading the file
+ */
+ public final void readPrivateKey(final String filename) throws IOException {
+ try (final Reader fileReader = new FileReader(filename)) {
+ readPrivateKey(fileReader);
+ }
+ }
+
+ /**
+ * Reads the private key from a stream
+ *
+ * @param reader the source of the private key
+ * @throws IOException thrown if an error occurs reading the file
+ */
+ public abstract void readPrivateKey(final Reader reader) throws IOException;
+
+ /**
+ * Writes the private key to a file
+ *
+ * @param filename the target filename
+ * @throws IOException thrown if an error occurs writing the file
+ */
+ public final void writePrivateKey(final String filename) throws IOException {
+ try (final Writer fileWriter = new FileWriter(filename)) {
+ writePrivateKey(fileWriter);
+ }
+ }
+
+ /**
+ * Writes the private key to a file
+ *
+ * @param writer the target writer
+ * @throws IOException thrown if an error occurs writing the file
+ */
+ public abstract void writePrivateKey(final Writer writer) throws IOException;
+
+ /**
+ * Signs a message with the loaded key
+ *
+ * @param message message to sign
+ * @return signed message
+ * @throws GeneralSecurityException thrown if an error occurs processing message or signature
+ */
+ public abstract byte[] sign(final byte[] message) throws GeneralSecurityException;
+
+ /**
+ * Derives the public key from the loaded private key
+ *
+ * @return the derived {@link AbstractPublicKey}
+ */
+ public abstract AbstractPublicKey derivePublicKey();
+}
diff --git a/src/main/java/com/syntifi/crypto/key/AbstractPublicKey.java b/src/main/java/com/syntifi/crypto/key/AbstractPublicKey.java
new file mode 100644
index 000000000..22fd2a821
--- /dev/null
+++ b/src/main/java/com/syntifi/crypto/key/AbstractPublicKey.java
@@ -0,0 +1,80 @@
+package com.syntifi.crypto.key;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.*;
+import java.security.GeneralSecurityException;
+
+/**
+ * Abstract class for needed shared functionalities
+ *
+ * @author Alexandre Carvalho
+ * @author Andre Bertolace
+ * @since 0.1.0
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public abstract class AbstractPublicKey {
+
+ private byte[] key;
+
+ /**
+ * Loads a public key from a byte array
+ *
+ * @param publicKey the public key bytes
+ */
+ public abstract void loadPublicKey(final byte[] publicKey) throws IOException;
+
+ /**
+ * Reads the public key from a file
+ *
+ * @param filename the source filename
+ * @throws IOException thrown if an error occurs reading the file
+ */
+ public final void readPublicKey(final String filename) throws IOException {
+ try (final Reader fileReader = new FileReader(filename)) {
+ readPublicKey(fileReader);
+ }
+ }
+
+ /**
+ * Reads the public key from a file
+ *
+ * @param reader the source filename
+ * @throws IOException thrown if an error occurs reading the file
+ */
+ public abstract void readPublicKey(final Reader reader) throws IOException;
+
+ /**
+ * Writes the public key to a file
+ *
+ * @param filename the target filename
+ * @throws IOException thrown if an error occurs writing the file
+ */
+ public final void writePublicKey(final String filename) throws IOException {
+ try (final Writer fileWriter = new FileWriter(filename)) {
+ writePublicKey(fileWriter);
+ }
+ }
+
+ /**
+ * Writes the public key to a file
+ *
+ * @param writer the target to write the public key
+ * @throws IOException thrown if an error occurs writing the file
+ */
+ public abstract void writePublicKey(final Writer writer) throws IOException;
+
+ /**
+ * Verifies message with given signature
+ *
+ * @param message the signed message
+ * @param signature the signature to check against
+ * @return true if matches, false otherwise
+ * @throws GeneralSecurityException thrown if an error occurs processing message and signature
+ */
+ public abstract Boolean verify(final byte[] message, final byte[] signature) throws GeneralSecurityException;
+}
diff --git a/src/main/java/com/syntifi/crypto/key/Ed25519PrivateKey.java b/src/main/java/com/syntifi/crypto/key/Ed25519PrivateKey.java
new file mode 100644
index 000000000..86f08aaba
--- /dev/null
+++ b/src/main/java/com/syntifi/crypto/key/Ed25519PrivateKey.java
@@ -0,0 +1,101 @@
+package com.syntifi.crypto.key;
+
+import com.syntifi.crypto.key.deterministic.HierarchicalDeterministicKey;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+import org.bouncycastle.asn1.*;
+import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
+import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.crypto.CryptoException;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.Signer;
+import org.bouncycastle.crypto.generators.Ed25519KeyPairGenerator;
+import org.bouncycastle.crypto.params.Ed25519KeyGenerationParameters;
+import org.bouncycastle.crypto.params.Ed25519PrivateKeyParameters;
+import org.bouncycastle.crypto.signers.Ed25519Signer;
+
+import java.io.*;
+import java.nio.charset.StandardCharsets;
+import java.security.SecureRandom;
+
+/**
+ * ed25519 implementation of {@link AbstractPrivateKey}
+ *
+ * @author Alexandre Carvalho
+ * @author Andre Bertolace
+ * @since 0.1.0
+ */
+@NoArgsConstructor
+@EqualsAndHashCode(callSuper = true)
+public class Ed25519PrivateKey extends AbstractPrivateKey {
+
+ private Ed25519PrivateKeyParameters privateKeyParameters;
+
+ public Ed25519PrivateKey(final byte[] privateKey) {
+ super(privateKey);
+ loadPrivateKey(privateKey);
+ }
+
+ @Override
+ public void loadPrivateKey(final byte[] privateKey) {
+ privateKeyParameters = new Ed25519PrivateKeyParameters(privateKey, 0);
+ }
+
+ @Override
+ public void readPrivateKey(final Reader reader) throws IOException {
+ final ASN1Primitive key = ASN1Primitive.fromByteArray(PemFileHelper.readPemFile(reader));
+ final PrivateKeyInfo keyInfo = PrivateKeyInfo.getInstance(key);
+ final String algoId = keyInfo.getPrivateKeyAlgorithm().getAlgorithm().toString();
+ if (algoId.equals(ASN1Identifiers.Ed25519OID.getId())) {
+ privateKeyParameters = new Ed25519PrivateKeyParameters(keyInfo.getPrivateKey().getEncoded(), 4);
+ setKey(privateKeyParameters.getEncoded());
+ }
+ }
+
+ @Override
+ public void writePrivateKey(final Writer writer) throws IOException {
+ final DERSequence derPrefix = new DERSequence(ASN1Identifiers.Ed25519OID);
+ final DEROctetString key = new DEROctetString(new DEROctetString(getKey()));
+ final ASN1EncodableVector vector = new ASN1EncodableVector();
+ vector.add(new ASN1Integer(0));
+ vector.add(derPrefix);
+ vector.add(key);
+ final DERSequence derKey = new DERSequence(vector);
+ PemFileHelper.writePemFile(writer, derKey.getEncoded(), ASN1Identifiers.PRIVATE_KEY_DER_HEADER);
+ }
+
+ @Override
+ public byte[] sign(final byte[] message) {
+ final Signer signer = new Ed25519Signer();
+ signer.init(true, privateKeyParameters);
+ signer.update(message, 0, message.length);
+ byte[] signature;
+ try {
+ signature = signer.generateSignature();
+ return signature;
+ } catch (DataLengthException | CryptoException e) {
+ // TODO: throw new SomeException();
+ return null;
+ }
+ }
+
+ @Override
+ public AbstractPublicKey derivePublicKey() {
+ return new Ed25519PublicKey(privateKeyParameters.generatePublicKey().getEncoded());
+ }
+
+ public static Ed25519PrivateKey deriveFromSeed(final byte[] seed, final int[] path) throws IOException {
+ final byte[] init = "ed25519 seed".getBytes(StandardCharsets.UTF_8);
+ final byte[] key = HierarchicalDeterministicKey.getFromSeed(seed, init, path);
+ return new Ed25519PrivateKey(key);
+ }
+
+ public static Ed25519PrivateKey deriveRandomKey() {
+ final SecureRandom rnd = new SecureRandom();
+ final Ed25519KeyPairGenerator keyPairGenerator = new Ed25519KeyPairGenerator();
+ keyPairGenerator.init(new Ed25519KeyGenerationParameters(rnd));
+ final AsymmetricCipherKeyPair asymmetricCipherKeyPair = keyPairGenerator.generateKeyPair();
+ final Ed25519PrivateKeyParameters privateKeyParameters = (Ed25519PrivateKeyParameters) asymmetricCipherKeyPair.getPrivate();
+ return new Ed25519PrivateKey(privateKeyParameters.getEncoded());
+ }
+}
diff --git a/src/main/java/com/syntifi/crypto/key/Ed25519PublicKey.java b/src/main/java/com/syntifi/crypto/key/Ed25519PublicKey.java
new file mode 100644
index 000000000..59706a685
--- /dev/null
+++ b/src/main/java/com/syntifi/crypto/key/Ed25519PublicKey.java
@@ -0,0 +1,66 @@
+package com.syntifi.crypto.key;
+
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+import org.bouncycastle.asn1.*;
+import org.bouncycastle.crypto.Signer;
+import org.bouncycastle.crypto.params.Ed25519PublicKeyParameters;
+import org.bouncycastle.crypto.signers.Ed25519Signer;
+
+import java.io.*;
+
+/**
+ * ed25519 implementation of {@link AbstractPublicKey}
+ *
+ * @author Alexandre Carvalho
+ * @author Andre Bertolace
+ * @since 0.1.0
+ */
+@NoArgsConstructor
+@EqualsAndHashCode(callSuper = true)
+public class Ed25519PublicKey extends AbstractPublicKey {
+
+ private Ed25519PublicKeyParameters publicKeyParameters;
+
+ public Ed25519PublicKey(final byte[] publicKey) {
+ super(publicKey);
+ loadPublicKey(publicKey);
+ }
+
+ @Override
+ public void loadPublicKey(final byte[] publicKey) {
+ publicKeyParameters = new Ed25519PublicKeyParameters(publicKey, 0);
+ }
+
+ @Override
+ public void readPublicKey(final Reader reader) throws IOException {
+ final ASN1Primitive derKey = ASN1Primitive.fromByteArray(PemFileHelper.readPemFile(reader));
+ final ASN1Sequence objBaseSeq = ASN1Sequence.getInstance(derKey);
+ final String objId = ASN1ObjectIdentifier
+ .getInstance(ASN1Sequence.getInstance(objBaseSeq.getObjectAt(0)).getObjectAt(0)).getId();
+ if (objId.equals(ASN1Identifiers.Ed25519OID.getId())) {
+ final DERBitString key = DERBitString.getInstance(objBaseSeq.getObjectAt(1));
+ publicKeyParameters = new Ed25519PublicKeyParameters(key.getBytes(), 0);
+ setKey(publicKeyParameters.getEncoded());
+ }
+ }
+
+ @Override
+ public void writePublicKey(final Writer writer) throws IOException {
+ final DERSequence derPrefix = new DERSequence(ASN1Identifiers.Ed25519OID);
+ final DERBitString key = new DERBitString(getKey());
+ final ASN1EncodableVector vector = new ASN1EncodableVector();
+ vector.add(derPrefix);
+ vector.add(key);
+ final DERSequence derKey = new DERSequence(vector);
+ PemFileHelper.writePemFile(writer, derKey.getEncoded(), ASN1Identifiers.PUBLIC_KEY_DER_HEADER);
+ }
+
+ @Override
+ public Boolean verify(final byte[] message, final byte[] signature) {
+ final Signer verifier = new Ed25519Signer();
+ verifier.init(false, publicKeyParameters);
+ verifier.update(message, 0, message.length);
+ return verifier.verifySignature(signature);
+ }
+}
diff --git a/src/main/java/com/syntifi/crypto/key/PemFileHelper.java b/src/main/java/com/syntifi/crypto/key/PemFileHelper.java
new file mode 100644
index 000000000..0308d87fc
--- /dev/null
+++ b/src/main/java/com/syntifi/crypto/key/PemFileHelper.java
@@ -0,0 +1,49 @@
+package com.syntifi.crypto.key;
+
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
+import org.bouncycastle.util.io.pem.PemObject;
+import org.bouncycastle.util.io.pem.PemReader;
+import org.bouncycastle.util.io.pem.PemWriter;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.io.Writer;
+
+/**
+ * Helper methods for dealing with PEM files
+ *
+ * @author Alexandre Carvalho
+ * @author Andre Bertolace
+ * @since 0.1.0
+ */
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
+public class PemFileHelper {
+ /**
+ * Reads a PEM file
+ *
+ * @param keyReader the reader to load the key from
+ * @return a byte array with file content
+ * @throws IOException thrown if error reading file
+ */
+ public static byte[] readPemFile(final Reader keyReader) throws IOException {
+ try (final PemReader pemReader = new PemReader(keyReader)) {
+ final PemObject pemObject = pemReader.readPemObject();
+ return pemObject.getContent();
+ }
+ }
+
+ /**
+ * Writes a PEM file
+ *
+ * @param fileWriter the writer of the key
+ * @param encodedKey the encoded key
+ * @param keyType the key type
+ * @throws IOException thrown if error writing file
+ */
+ public static void writePemFile(final Writer fileWriter, final byte[] encodedKey, final String keyType) throws IOException {
+ try (final PemWriter pemWriter = new PemWriter(fileWriter)) {
+ pemWriter.writeObject(new PemObject(keyType, encodedKey));
+ }
+ }
+}
diff --git a/src/main/java/com/syntifi/crypto/key/Secp256k1PrivateKey.java b/src/main/java/com/syntifi/crypto/key/Secp256k1PrivateKey.java
new file mode 100644
index 000000000..06e13580f
--- /dev/null
+++ b/src/main/java/com/syntifi/crypto/key/Secp256k1PrivateKey.java
@@ -0,0 +1,114 @@
+package com.syntifi.crypto.key;
+
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import org.bouncycastle.asn1.*;
+import org.web3j.crypto.ECKeyPair;
+import org.web3j.crypto.Hash;
+import org.web3j.crypto.Sign;
+import org.web3j.crypto.Sign.SignatureData;
+
+import java.io.*;
+import java.math.BigInteger;
+import java.nio.ByteBuffer;
+import java.security.SecureRandom;
+
+/**
+ * secp256k1 implementation of {@link AbstractPrivateKey}
+ *
+ * @author Alexandre Carvalho
+ * @author Andre Bertolace
+ * @since 0.1.0
+ */
+@NoArgsConstructor
+@EqualsAndHashCode(callSuper = true)
+public class Secp256k1PrivateKey extends AbstractPrivateKey {
+ @Getter
+ @Setter
+ private ECKeyPair keyPair;
+
+ public Secp256k1PrivateKey(final byte[] privateKey) throws IOException {
+ super(privateKey);
+ loadPrivateKey(privateKey);
+ }
+
+ @Override
+ public void loadPrivateKey(final byte[] privateKey) throws IOException {
+ final ASN1Sequence key = (ASN1Sequence) ASN1Primitive.fromByteArray(privateKey);
+ final String algoId = key.getObjectAt(2).toString();
+ if (algoId.equals("[0]" + ASN1Identifiers.Secp256k1OIDCurve) && key.getObjectAt(0).toString().equals("1")) {
+ final DEROctetString pk = (DEROctetString) key.getObjectAt(1);
+ keyPair = ECKeyPair.create(pk.getOctets());
+ this.setKey(keyPair.getPrivateKey().toByteArray());
+ }
+ }
+
+ @Override
+ public void readPrivateKey(final Reader reader) throws IOException {
+ loadPrivateKey(PemFileHelper.readPemFile(reader));
+ }
+
+ @Override
+ public void writePrivateKey(final Writer writer) throws IOException {
+ final DERTaggedObject derPrefix = new DERTaggedObject(0, ASN1Identifiers.Secp256k1OIDCurve);
+ final DEROctetString key = new DEROctetString(getKey());
+ final ASN1EncodableVector vector = new ASN1EncodableVector();
+ vector.add(new ASN1Integer(1));
+ vector.add(key);
+ vector.add(derPrefix);
+ final DERSequence derKey = new DERSequence(vector);
+ PemFileHelper.writePemFile(writer, derKey.getEncoded(), ASN1Identifiers.EC_PRIVATE_KEY_DER_HEADER);
+ }
+
+ /**
+ * When encoded in DER, this becomes the following sequence of bytes:
+ *
+ * 0x30 b1 0x02 b2 (vr) 0x02 b3 (vs)
+ *
+ * where:
+ *
+ * b1 is a single byte value, equal to the length, in bytes, of the remaining
+ * list of bytes (from the first 0x02 to the end of the encoding);
+ * b2 is a single byte value, equal to the length, in bytes, of (vr);
+ * b3 is a single byte value, equal to the length, in bytes, of (vs);
+ * (vr) is the signed big-endian encoding of the value "r
+ *
+ * ", of minimal length;
+ * (vs) is the signed big-endian encoding of the value "s
+ * ", of minimal length.
+ *
+ * @param message message to sign
+ * @return signed message
+ */
+ @Override
+ public byte[] sign(final byte[] message) {
+ final SignatureData signature = Sign.signMessage(Hash.sha256(message), keyPair, false);
+ final ByteBuffer bb = ByteBuffer.allocate(signature.getR().length + signature.getS().length);
+ bb.put(signature.getR());
+ bb.put(signature.getS());
+ return bb.array();
+ }
+
+ /**
+ * Returns a Secp256k1PublicKey object in a compressed format
+ * adding the prefix 02/03 to identify the positive or negative Y followed
+ * by the X value in the elliptic curve
+ */
+ @Override
+ public AbstractPublicKey derivePublicKey() {
+ final BigInteger pubKey = keyPair.getPublicKey();
+ final byte[] pubKeyBytes = Secp256k1PublicKey.getShortKey(pubKey.toByteArray());
+ return new Secp256k1PublicKey(pubKeyBytes);
+ }
+
+ public static Secp256k1PrivateKey deriveRandomKey() {
+ final SecureRandom rnd = new SecureRandom();
+ final ECKeyPair keyPair = ECKeyPair.create(rnd.generateSeed(32));
+ final Secp256k1PrivateKey sk = new Secp256k1PrivateKey();
+ sk.setKeyPair(keyPair);
+ sk.setKey(keyPair.getPrivateKey().toByteArray());
+ return sk;
+ }
+}
diff --git a/src/main/java/com/syntifi/crypto/key/Secp256k1PublicKey.java b/src/main/java/com/syntifi/crypto/key/Secp256k1PublicKey.java
new file mode 100644
index 000000000..8a2283f5e
--- /dev/null
+++ b/src/main/java/com/syntifi/crypto/key/Secp256k1PublicKey.java
@@ -0,0 +1,94 @@
+package com.syntifi.crypto.key;
+
+import com.syntifi.crypto.key.encdec.Hex;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+import org.bouncycastle.asn1.*;
+import org.web3j.crypto.Hash;
+import org.web3j.crypto.Sign;
+import org.web3j.crypto.Sign.SignatureData;
+
+import java.io.*;
+import java.math.BigInteger;
+import java.security.GeneralSecurityException;
+import java.util.Arrays;
+
+/**
+ * secp256k1 implementation of {@link AbstractPublicKey}
+ *
+ * @author Alexandre Carvalho
+ * @author Andre Bertolace
+ * @since 0.1.0
+ */
+@NoArgsConstructor
+@EqualsAndHashCode(callSuper = true)
+public class Secp256k1PublicKey extends AbstractPublicKey {
+
+ public Secp256k1PublicKey(final byte[] bytes) {
+ super(bytes);
+ }
+
+ @Override
+ public void loadPublicKey(final byte[] publicKey) throws IOException {
+ final ASN1Primitive derKey = ASN1Primitive.fromByteArray(publicKey);
+ final ASN1Sequence objBaseSeq = ASN1Sequence.getInstance(derKey);
+ final String keyId = ASN1ObjectIdentifier.getInstance(ASN1Sequence.getInstance(objBaseSeq.getObjectAt(0)).getObjectAt(0)).getId();
+ final String curveId = ASN1ObjectIdentifier.getInstance(ASN1Sequence.getInstance(objBaseSeq.getObjectAt(0)).getObjectAt(1)).getId();
+ if (curveId.equals(ASN1Identifiers.Secp256k1OIDCurve.getId())
+ && keyId.equals(ASN1Identifiers.Secp256k1OIDkey.getId())) {
+ final DERBitString key = DERBitString.getInstance(objBaseSeq.getObjectAt(1));
+ setKey(key.getBytes());
+ } else {
+ throw new IOException();
+ }
+ }
+
+ @Override
+ public void readPublicKey(final Reader reader) throws IOException {
+ loadPublicKey(PemFileHelper.readPemFile(reader));
+ }
+
+ @Override
+ public void writePublicKey(final Writer writer) throws IOException {
+ final DERBitString key = new DERBitString(getKey());
+ final ASN1EncodableVector v1 = new ASN1EncodableVector();
+ v1.add(ASN1Identifiers.Secp256k1OIDkey);
+ v1.add(ASN1Identifiers.Secp256k1OIDCurve);
+ final DERSequence derPrefix = new DERSequence(v1);
+ final ASN1EncodableVector v2 = new ASN1EncodableVector();
+ v2.add(derPrefix);
+ v2.add(key);
+ final DERSequence derKey = new DERSequence(v2);
+ PemFileHelper.writePemFile(writer, derKey.getEncoded(), ASN1Identifiers.PUBLIC_KEY_DER_HEADER);
+ }
+
+ @Override
+ public Boolean verify(final byte[] message, final byte[] signature) throws GeneralSecurityException {
+ //TODO: Double check the issue the getV(), for now we are trying with both (27 and 28)
+ final SignatureData signatureData1 = new SignatureData(
+ (byte) 27,
+ Arrays.copyOfRange(signature, 0, 32),
+ Arrays.copyOfRange(signature, 32, 64));
+ final BigInteger derivedKey1 = Sign.signedMessageHashToKey(Hash.sha256(message), signatureData1);
+ final SignatureData signatureData2 = new SignatureData(
+ (byte) 28,
+ Arrays.copyOfRange(signature, 0, 32),
+ Arrays.copyOfRange(signature, 32, 64));
+ final BigInteger derivedKey2 = Sign.signedMessageHashToKey(Hash.sha256(message), signatureData2);
+ return Arrays.equals(Secp256k1PublicKey.getShortKey(derivedKey1.toByteArray()), getKey()) ||
+ Arrays.equals(Secp256k1PublicKey.getShortKey(derivedKey2.toByteArray()), getKey());
+ }
+
+ /**
+ * Gets a short key
+ *
+ * @param key the key as a byte array
+ * @return short key as byte array
+ */
+ public static byte[] getShortKey(final byte[] key) {
+ final BigInteger pubKey = new BigInteger(key);
+ final String pubKeyPrefix = pubKey.testBit(0) ? "03" : "02";
+ final byte[] pubKeyBytes = Arrays.copyOfRange(key, 0, 32);
+ return Hex.decode(pubKeyPrefix + Hex.encode(pubKeyBytes));
+ }
+}
diff --git a/src/main/java/com/syntifi/crypto/key/checksum/MixedCaseChecksum.java b/src/main/java/com/syntifi/crypto/key/checksum/MixedCaseChecksum.java
new file mode 100644
index 000000000..2097ec710
--- /dev/null
+++ b/src/main/java/com/syntifi/crypto/key/checksum/MixedCaseChecksum.java
@@ -0,0 +1,60 @@
+package com.syntifi.crypto.key.checksum;
+
+import com.syntifi.crypto.key.encdec.Hex;
+import com.syntifi.crypto.key.hash.Blake2b;
+import com.syntifi.crypto.key.hash.Keccak256;
+
+/**
+ * Mixed checksumEncoding
+ *
+ * @author Alexandre Carvalho
+ * @author Andre Bertolace
+ * @since 0.5.0
+ */
+public class MixedCaseChecksum {
+
+ /**
+ * Implementation of the EIP-55: Mixed-case checksum address encoding
+ * Documentation available at: https://eips.ethereum.org/EIPS/eip-55
+ *
+ * @param value Hex string
+ * @return
+ */
+ public static String checksumEncodeEIP55(String value) {
+ char[] hash = Hex.encode(Keccak256.digest(value.toLowerCase().getBytes())).toCharArray();
+ char[] chars = value.toCharArray();
+ char[] encoded = new char[chars.length];
+ for (int i = 0; i < chars.length; i++) {
+ encoded[i] = Character.digit(hash[i], 16) > 7
+ ? Character.toUpperCase(chars[i])
+ : Character.toLowerCase(chars[i]);
+ }
+ return String.valueOf(encoded);
+ }
+
+ /**
+ * Implementation of the CEP-57
+ * Documentation available at: https://github.com/casper-network/ceps/blob/master/text/0057-checksummed-addresses.md
+ *
+ * @param value Hex string
+ * @return
+ */
+ public static String checksumEncodeCEP57(String value) {
+ byte[] hash = Blake2b.digest(Hex.decode(value), 32);
+ char[] chars = value.toCharArray();
+ char[] encoded = new char[chars.length];
+ int whichByte = 0;
+ int whichBit = 0;
+ for (int i = 0; i < chars.length; i++) {
+ boolean bitSet = (Byte.toUnsignedInt(hash[whichByte]) & (1<
+ * Note that this is not the same base58 as used by Flickr, which you may find referenced around the Internet.
+ *
+ * You may want to consider working with PrefixedChecksummedBytes instead, which
+ * adds support for testing the prefix and suffix bytes commonly found in addresses.
+ *
+ * Satoshi explains: why base-58 instead of standard base-64 encoding?
+ *
+ * - Don't want 0OIl characters that look the same in some fonts and
+ * could be used to create visually identical looking account numbers.
+ * - A string with non-alphanumeric characters is not as easily accepted as an account number.
+ * - E-mail usually won't line-break if there's no punctuation to break at.
+ * - Doubleclicking selects the whole number as one word if it's all alphanumeric.
+ *
+ *
+ * However, note that the encoding/decoding runs in O(n²) time, so it is not useful for large data.
+ *
+ * The basic idea of the encoding is to treat the data bytes as a large number represented using
+ * base-256 digits, convert the number to be represented using base-58 digits, preserve the exact
+ * number of leading zeros (which are otherwise lost during the mathematical operations on the
+ * numbers), and finally represent the resulting base-58 digits as alphanumeric ASCII characters.
+ *
+ * @author Alexandre Carvalho
+ * @author Andre Bertolace
+ * @since 0.3.0
+ */
+public final class Base58 {
+ public static final char[] ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz".toCharArray();
+ private static final char ENCODED_ZERO = ALPHABET[0];
+ private static final int[] INDEXES = new int[128];
+
+ static {
+ Arrays.fill(INDEXES, -1);
+ for (int i = 0; i < ALPHABET.length; i++) {
+ INDEXES[ALPHABET[i]] = i;
+ }
+ }
+
+ /**
+ * Encodes the given bytes as a base58 string (no checksum is appended).
+ *
+ * @param input the bytes to encode
+ * @return the base58-encoded string
+ */
+ public static String encode(byte[] input) {
+ if (input.length == 0) {
+ return "";
+ }
+ // Count leading zeros.
+ int zeros = 0;
+ while (zeros < input.length && input[zeros] == 0) {
+ ++zeros;
+ }
+ // Convert base-256 digits to base-58 digits (plus conversion to ASCII characters)
+ input = Arrays.copyOf(input, input.length); // since we modify it in-place
+ char[] encoded = new char[input.length * 2]; // upper bound
+ int outputStart = encoded.length;
+ for (int inputStart = zeros; inputStart < input.length; ) {
+ encoded[--outputStart] = ALPHABET[divmod(input, inputStart, 256, 58)];
+ if (input[inputStart] == 0) {
+ ++inputStart; // optimization - skip leading zeros
+ }
+ }
+ // Preserve exactly as many leading encoded zeros in output as there were leading zeros in input.
+ while (outputStart < encoded.length && encoded[outputStart] == ENCODED_ZERO) {
+ ++outputStart;
+ }
+ while (--zeros >= 0) {
+ encoded[--outputStart] = ENCODED_ZERO;
+ }
+ // Return encoded string (including encoded leading zeros).
+ return new String(encoded, outputStart, encoded.length - outputStart);
+ }
+
+ /**
+ * Decodes the given base58 string into the original data bytes.
+ *
+ * @param input the base58-encoded string to decode
+ * @return the decoded data bytes
+ * @throws RuntimeException if the given string is not a valid base58 string
+ */
+ public static byte[] decode(String input) throws RuntimeException {
+ if (input.length() == 0) {
+ return new byte[0];
+ }
+ // Convert the base58-encoded ASCII chars to a base58 byte sequence (base58 digits).
+ byte[] input58 = new byte[input.length()];
+ for (int i = 0; i < input.length(); ++i) {
+ char c = input.charAt(i);
+ int digit = c < 128 ? INDEXES[c] : -1;
+ if (digit < 0) {
+ throw new RuntimeException(String.format("Invalid char %s at %s", c, i));
+ }
+ input58[i] = (byte) digit;
+ }
+ // Count leading zeros.
+ int zeros = 0;
+ while (zeros < input58.length && input58[zeros] == 0) {
+ ++zeros;
+ }
+ // Convert base-58 digits to base-256 digits.
+ byte[] decoded = new byte[input.length()];
+ int outputStart = decoded.length;
+ for (int inputStart = zeros; inputStart < input58.length; ) {
+ decoded[--outputStart] = divmod(input58, inputStart, 58, 256);
+ if (input58[inputStart] == 0) {
+ ++inputStart; // optimization - skip leading zeros
+ }
+ }
+ // Ignore extra leading zeroes that were added during the calculation.
+ while (outputStart < decoded.length && decoded[outputStart] == 0) {
+ ++outputStart;
+ }
+ // Return decoded data (including original number of leading zeros).
+ return Arrays.copyOfRange(decoded, outputStart - zeros, decoded.length);
+ }
+
+ /**
+ * Divides a number, represented as an array of bytes each containing a single digit
+ * in the specified base, by the given divisor. The given number is modified in-place
+ * to contain the quotient, and the return value is the remainder.
+ *
+ * @param number the number to divide
+ * @param firstDigit the index within the array of the first non-zero digit
+ * (this is used for optimization by skipping the leading zeros)
+ * @param base the base in which the number's digits are represented (up to 256)
+ * @param divisor the number to divide by (up to 256)
+ * @return the remainder of the division operation
+ */
+ private static byte divmod(byte[] number, int firstDigit, int base, int divisor) {
+ // this is just long division which accounts for the base of the input digits
+ int remainder = 0;
+ for (int i = firstDigit; i < number.length; i++) {
+ int digit = (int) number[i] & 0xFF;
+ int temp = remainder * base + digit;
+ number[i] = (byte) (temp / divisor);
+ remainder = temp % divisor;
+ }
+ return (byte) remainder;
+ }
+}
diff --git a/src/main/java/com/syntifi/crypto/key/encdec/Hex.java b/src/main/java/com/syntifi/crypto/key/encdec/Hex.java
new file mode 100644
index 000000000..e8262d2c9
--- /dev/null
+++ b/src/main/java/com/syntifi/crypto/key/encdec/Hex.java
@@ -0,0 +1,32 @@
+package com.syntifi.crypto.key.encdec;
+
+/**
+ * Hex encoder/decoder
+ *
+ * @author Alexandre Carvalho
+ * @author Andre Bertolace
+ * @since 0.3.0
+ */
+public class Hex {
+
+ /**
+ * Byte array encoder
+ *
+ * @param bytes byte array
+ * @return Hex String
+ */
+ public static String encode(byte[] bytes) {
+ return org.bouncycastle.util.encoders.Hex.toHexString(bytes);
+ }
+
+ /**
+ * Hex string decoder
+ *
+ * @param hex Hex string
+ * @return byte array
+ */
+ public static byte[] decode(String hex) {
+ return org.bouncycastle.util.encoders.Hex.decode(hex);
+ }
+
+}
diff --git a/src/main/java/com/syntifi/crypto/key/hash/Blake2b.java b/src/main/java/com/syntifi/crypto/key/hash/Blake2b.java
new file mode 100644
index 000000000..51faf7185
--- /dev/null
+++ b/src/main/java/com/syntifi/crypto/key/hash/Blake2b.java
@@ -0,0 +1,27 @@
+package com.syntifi.crypto.key.hash;
+
+import org.bouncycastle.crypto.digests.Blake2bDigest;
+
+/**
+ * Blake2 Hash helper class
+ *
+ * @author Alexandre Carvalho
+ * @author Andre Bertolace
+ * @since 0.2.0
+ */
+public class Blake2b {
+ /**
+ * returns a Blake2b Hash of size length in bytes
+ *
+ * @param input byte array to hash
+ * @param length desired output length in bytes
+ * @return a byte array of size 'length' bytes
+ */
+ public static byte[] digest(byte[] input, int length) {
+ Blake2bDigest d = new Blake2bDigest(length * 8);
+ d.update(input, 0, input.length);
+ byte[] result = new byte[d.getDigestSize()];
+ d.doFinal(result, 0);
+ return result;
+ }
+}
diff --git a/src/main/java/com/syntifi/crypto/key/hash/Keccak256.java b/src/main/java/com/syntifi/crypto/key/hash/Keccak256.java
new file mode 100644
index 000000000..8baa147c7
--- /dev/null
+++ b/src/main/java/com/syntifi/crypto/key/hash/Keccak256.java
@@ -0,0 +1,12 @@
+package com.syntifi.crypto.key.hash;
+
+import org.bouncycastle.jcajce.provider.digest.Keccak;
+
+public class Keccak256 {
+
+ public static byte[] digest(byte[] value) {
+ Keccak.DigestKeccak kekkac256 = new Keccak.Digest256();
+ byte[] hash = kekkac256.digest(value);
+ return hash;
+ }
+}
diff --git a/src/main/java/com/syntifi/crypto/key/hash/Sha256.java b/src/main/java/com/syntifi/crypto/key/hash/Sha256.java
new file mode 100644
index 000000000..91f77f24e
--- /dev/null
+++ b/src/main/java/com/syntifi/crypto/key/hash/Sha256.java
@@ -0,0 +1,27 @@
+package com.syntifi.crypto.key.hash;
+
+import org.bouncycastle.crypto.digests.SHA256Digest;
+
+/**
+ * Sha256 Hash helper class
+ *
+ * @author Alexandre Carvalho
+ * @author Andre Bertolace
+ * @since 0.2.0
+ */
+public final class Sha256 {
+
+ /**
+ * returns a Sha 256 Hash of size length in bytes
+ *
+ * @param input byte array to hash
+ * @return a byte array of size 'length' bytes
+ */
+ public static byte[] digest(byte[] input) {
+ SHA256Digest d = new SHA256Digest();
+ d.update(input, 0, input.length);
+ byte[] result = new byte[d.getDigestSize()];
+ d.doFinal(result, 0);
+ return result;
+ }
+}
diff --git a/src/main/java/com/syntifi/crypto/key/mnemonic/Language.java b/src/main/java/com/syntifi/crypto/key/mnemonic/Language.java
new file mode 100644
index 000000000..4fa0900b0
--- /dev/null
+++ b/src/main/java/com/syntifi/crypto/key/mnemonic/Language.java
@@ -0,0 +1,51 @@
+package com.syntifi.crypto.key.mnemonic;
+
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.util.Locale;
+
+/**
+ * Enum with the languages, dictionaries and checkSum supported for Mnemonic generation
+ * Abstract class for needed shared functionalities
+ *
+ * @author Alexandre Carvalho
+ * @author Andre Bertolace
+ * @since 0.3.0
+ */
+public enum Language {
+ EN("english.txt", "ad90bf3beb7b0eb7e5acd74727dc0da96e0a280a258354e7293fb7e211ac03db", StandardCharsets.UTF_8, Locale.ENGLISH),
+ PT("portuguese.txt", "eed387d44cf8f32f60754527e265230d8019e8a2277937c71ef812e7a46c93fd", StandardCharsets.UTF_8, Locale.forLanguageTag("PT")),
+ ES("spanish.txt", "a556a26c6a5bb36db0fb7d8bf579cb7465fcaeec03957c0dda61b569962d9da5", StandardCharsets.UTF_8, Locale.forLanguageTag("ES")),
+ FR("french.txt", "9cbdaadbd3ce9cbaee1b360fce45e935b21e3e2c56d9fcd56b3398ced2371866", StandardCharsets.UTF_8, Locale.FRENCH),
+ CNS("chinese_simplified.txt", "bfd683b91db88609fabad8968c7efe4bf69606bf5a49ac4a4ba5e355955670cb", StandardCharsets.UTF_8, Locale.CHINA),
+ CNT("chinese_traditional.txt", "", StandardCharsets.UTF_8, Locale.CHINESE);
+
+ private final String fileName;
+ private final String checkSum;
+ private final Charset charset;
+ private final Locale locale;
+
+ Language(String fileName, String checkSum, Charset charset, Locale locale) {
+ this.fileName = fileName;
+ this.checkSum = checkSum;
+ this.charset = charset;
+ this.locale = locale;
+ }
+
+
+ public String getFileName() {
+ return fileName;
+ }
+
+ public String getCheckSum() {
+ return checkSum;
+ }
+
+ public Charset getCharset() {
+ return charset;
+ }
+
+ public Locale getLocale() {
+ return locale;
+ }
+}
diff --git a/src/main/java/com/syntifi/crypto/key/mnemonic/MnemonicCode.java b/src/main/java/com/syntifi/crypto/key/mnemonic/MnemonicCode.java
new file mode 100644
index 000000000..804e9550f
--- /dev/null
+++ b/src/main/java/com/syntifi/crypto/key/mnemonic/MnemonicCode.java
@@ -0,0 +1,284 @@
+package com.syntifi.crypto.key.mnemonic;
+
+/*
+ * Copyright 2013 Ken Sedgwick
+ * Copyright 2014 Andreas Schildbach
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * CHANGES:
+ * - Removing static initializer
+ * - Using own Hex and Sha256 implementations
+ * - Removing logs, watches, ...
+ * - Removing internal dependencies to helper/utils
+ * - Adding method to secure random derive the key
+ * - Adding support for multiple languages
+ *
+ */
+
+import com.syntifi.crypto.key.encdec.Hex;
+import com.syntifi.crypto.key.hash.Sha256;
+import com.syntifi.crypto.key.mnemonic.exception.MnemonicException;
+import org.bouncycastle.crypto.digests.SHA512Digest;
+import org.bouncycastle.crypto.generators.PKCS5S2ParametersGenerator;
+import org.bouncycastle.crypto.params.KeyParameter;
+
+import java.io.*;
+import java.nio.charset.StandardCharsets;
+import java.security.SecureRandom;
+import java.text.Collator;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * A MnemonicCode object may be used to convert between binary seed values and
+ * lists of words per the BIP 39
+ * specification
+ * Original implementation at:
+ * https://github.com/bitcoinj/bitcoinj/blob/master/core/src/main/java/org/bitcoinj/crypto/MnemonicCode.java
+ *
+ * @author Alexandre Carvalho
+ * @author Andre Bertolace
+ * @since 0.3.0
+ */
+public class MnemonicCode {
+ private static final int PBKDF2_ROUNDS = 2048;
+ private final List wordList;
+ private final Language language;
+
+ /**
+ * Creates an MnemonicCode object, initializing with words read from the supplied input stream.
+ * If a wordListDigest is supplied the digest of the words will be checked.
+ *
+ * @param language words languages
+ * @throws IOException if an error ocrurs when processing the file buffers
+ */
+ public MnemonicCode(Language language) throws IOException {
+ this.language = language;
+ InputStream wordStream = getClass().getResourceAsStream("/" + language.getFileName());
+ if (wordStream == null)
+ throw new FileNotFoundException(language.getFileName());
+ try (BufferedReader br = new BufferedReader(new InputStreamReader(wordStream, language.getCharset()))) {
+ this.wordList = br.lines()
+ .collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList));
+ }
+
+ if (this.wordList.size() != 2048)
+ throw new IllegalArgumentException("input stream did not contain 2048 bytes");
+
+ // If a wordListDigest is supplied check to make sure it matches.
+ if (language.getCheckSum()!= null) {
+ StringBuilder stringBuilder = new StringBuilder();
+ for (String s : this.getWordList()) {
+ stringBuilder.append(s);
+ }
+ byte[] digest = Sha256.digest(String.valueOf(stringBuilder).getBytes(language.getCharset()));
+ String hexDigest = Hex.encode(digest);
+ if (!hexDigest.equals(language.getCheckSum()))
+ throw new IllegalArgumentException("wordlist checksum mismatch");
+ }
+ }
+
+ /**
+ * Convert mnemonic word list to seed.
+ *
+ * @param words list of words
+ * @return derived seed in byte array
+ */
+ public byte[] toSeed(List words) {
+ return toSeed(words, "");
+ }
+
+ /**
+ * Convert mnemonic word list to seed.
+ *
+ * @param words list of words
+ * @param passphrase password, use {@link #toSeed(List)} if not required
+ * @return derived seed in byte array
+ */
+ public byte[] toSeed(List words, String passphrase) {
+ if (passphrase == null)
+ throw new RuntimeException("A null passphrase is not allowed.");
+
+ // To create binary seed from mnemonic, we use PBKDF2 function
+ // with mnemonic sentence (in UTF-8) used as a password and
+ // string "mnemonic" + passphrase (again in UTF-8) used as a
+ // salt. Iteration count is set to 2048 and HMAC-SHA512 is
+ // used as a pseudo-random function. Desired length of the
+ // derived key is 512 bits (= 64 bytes).
+ //
+ String pass = String.join(" ", words);
+ String salt = "mnemonic" + passphrase;
+
+ final PKCS5S2ParametersGenerator pbkdf2 = new PKCS5S2ParametersGenerator(new SHA512Digest());
+ pbkdf2.init(
+ pass.getBytes(StandardCharsets.UTF_8),
+ salt.getBytes(StandardCharsets.UTF_8),
+ PBKDF2_ROUNDS);
+
+ final KeyParameter key = (KeyParameter) pbkdf2.generateDerivedParameters(512);
+ return key.getKey();
+ }
+
+ private boolean[] bytesToBits(byte[] data) {
+ boolean[] bits = new boolean[data.length * 8];
+ for (int i = 0; i < data.length; ++i)
+ for (int j = 0; j < 8; ++j)
+ bits[(i * 8) + j] = (data[i] & (1 << (7 - j))) != 0;
+ return bits;
+ }
+
+ /**
+ * Gets the word list this code uses.
+ *
+ * @return unmodifiable word list
+ */
+ public List getWordList() {
+ return wordList;
+ }
+
+ /**
+ * Convert mnemonic word list to original entropy value.
+ *
+ * @param words list of words
+ * @return entropy byte array
+ * @throws MnemonicException.MnemonicLengthException if the number of words in the list is not multiple of 3 or empty
+ * @throws MnemonicException.MnemonicWordException if a word in the list is not part of the dictionary
+ * @throws MnemonicException.MnemonicChecksumException if the checksum of the file does not match the specified one
+ */
+ public byte[] toEntropy(List words) throws MnemonicException.MnemonicLengthException,
+ MnemonicException.MnemonicWordException, MnemonicException.MnemonicChecksumException {
+ if (words.size() % 3 > 0)
+ throw new MnemonicException.MnemonicLengthException("Word list size must be multiple of three words.");
+
+ if (words.size() == 0)
+ throw new MnemonicException.MnemonicLengthException("Word list is empty.");
+
+ // Look up all the words in the list and construct the
+ // concatenation of the original entropy and the checksum.
+ //
+ int concatLenBits = words.size() * 11;
+ boolean[] concatBits = new boolean[concatLenBits];
+ int wordindex = 0;
+ Collator collator = Collator.getInstance(language.getLocale());
+ //collator.setDecomposition(Collator.FULL_DECOMPOSITION);
+ //collator.setStrength(Collator.PRIMARY);
+ for (String word : words) {
+ // Find the words index in the wordlist.
+ int ndx = Collections.binarySearch(this.wordList, word, collator);
+ if (ndx < 0)
+ throw new MnemonicException.MnemonicWordException(word);
+
+ // Set the next 11 bits to the value of the index.
+ for (int ii = 0; ii < 11; ++ii)
+ concatBits[(wordindex * 11) + ii] = (ndx & (1 << (10 - ii))) != 0;
+ ++wordindex;
+ }
+
+ int checksumLengthBits = concatLenBits / 33;
+ int entropyLengthBits = concatLenBits - checksumLengthBits;
+
+ // Extract original entropy as bytes.
+ byte[] entropy = new byte[entropyLengthBits / 8];
+ for (int ii = 0; ii < entropy.length; ++ii)
+ for (int jj = 0; jj < 8; ++jj)
+ if (concatBits[(ii * 8) + jj])
+ entropy[ii] |= 1 << (7 - jj);
+
+ // Take the digest of the entropy.
+ byte[] hash = Sha256.digest(entropy);
+ boolean[] hashBits = bytesToBits(hash);
+
+ // Check all the checksum bits.
+ for (int i = 0; i < checksumLengthBits; ++i)
+ if (concatBits[entropyLengthBits + i] != hashBits[i])
+ throw new MnemonicException.MnemonicChecksumException();
+
+ return entropy;
+ }
+
+ /**
+ * Convert entropy data to mnemonic word list.
+ *
+ * @param entropy byte array
+ * @return list of words
+ * @throws MnemonicException.MnemonicLengthException if the number of words in the list is not multiple of 3 or empty
+ */
+ public List toMnemonic(byte[] entropy) throws MnemonicException.MnemonicLengthException {
+ if (entropy.length % 4 > 0)
+ throw new MnemonicException.MnemonicLengthException("Entropy length not multiple of 32 bits.");
+
+ if (entropy.length == 0)
+ throw new MnemonicException.MnemonicLengthException("Entropy is empty.");
+
+ // We take initial entropy of ENT bits and compute its
+ // checksum by taking first ENT / 32 bits of its SHA256 hash.
+
+ byte[] hash = Sha256.digest(entropy);
+ boolean[] hashBits = bytesToBits(hash);
+
+ boolean[] entropyBits = bytesToBits(entropy);
+ int checksumLengthBits = entropyBits.length / 32;
+
+ // We append these bits to the end of the initial entropy.
+ boolean[] concatBits = new boolean[entropyBits.length + checksumLengthBits];
+ System.arraycopy(entropyBits, 0, concatBits, 0, entropyBits.length);
+ System.arraycopy(hashBits, 0, concatBits, entropyBits.length, checksumLengthBits);
+
+ // Next we take these concatenated bits and split them into
+ // groups of 11 bits. Each group encodes number from 0-2047
+ // which is a position in a wordlist. We convert numbers into
+ // words and use joined words as mnemonic sentence.
+
+ ArrayList words = new ArrayList<>();
+ int nWords = concatBits.length / 11;
+ for (int i = 0; i < nWords; ++i) {
+ int index = 0;
+ for (int j = 0; j < 11; ++j) {
+ index <<= 1;
+ if (concatBits[(i * 11) + j])
+ index |= 0x1;
+ }
+ words.add(this.wordList.get(index));
+ }
+
+ return words;
+ }
+
+ /**
+ * Check to see if a mnemonic word list is valid.
+ *
+ * @param words list of words
+ * @throws MnemonicException if an errors occurs on reading the dictionary of on the given words
+ */
+ public void check(List words) throws MnemonicException {
+ toEntropy(words);
+ }
+
+ /**
+ * Method to generate words from securerandom entropy
+ *
+ * @return list of mnemonic words
+ * @throws IOException if an error occurs in reading the dictionary file
+ * @throws MnemonicException.MnemonicLengthException if the number of words in the list is not multiple of 3 or empty
+ */
+ public List generateSecureRandomWords() throws IOException, MnemonicException.MnemonicLengthException {
+ MnemonicCode mnemonicCode = new MnemonicCode(this.language);
+ SecureRandom rnd = new SecureRandom();
+ byte[] entropy = new byte[16];
+ rnd.nextBytes(entropy);
+ return mnemonicCode.toMnemonic(entropy);
+ }
+}
diff --git a/src/main/java/com/syntifi/crypto/key/mnemonic/exception/MnemonicException.java b/src/main/java/com/syntifi/crypto/key/mnemonic/exception/MnemonicException.java
new file mode 100644
index 000000000..0474d64c3
--- /dev/null
+++ b/src/main/java/com/syntifi/crypto/key/mnemonic/exception/MnemonicException.java
@@ -0,0 +1,65 @@
+package com.syntifi.crypto.key.mnemonic.exception;
+/*
+ * Copyright 2013 Ken Sedgwick
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+/**
+ * Exceptions thrown by the MnemonicCode module.
+ *
+ * @author Alexandre Carvalho
+ * @author Andre Bertolace
+ * @since 0.3.0
+ */
+public class MnemonicException extends Exception {
+ public MnemonicException() {
+ super();
+ }
+
+ public MnemonicException(String msg) {
+ super(msg);
+ }
+
+ /**
+ * Thrown when an argument to MnemonicCode is the wrong length.
+ */
+ public static class MnemonicLengthException extends MnemonicException {
+ public MnemonicLengthException(String msg) {
+ super(msg);
+ }
+ }
+
+ /**
+ * Thrown when a list of MnemonicCode words fails the checksum check.
+ */
+ public static class MnemonicChecksumException extends MnemonicException {
+ public MnemonicChecksumException() {
+ super();
+ }
+ }
+
+ /**
+ * Thrown when a word is encountered which is not in the MnemonicCode's word list.
+ */
+ public static class MnemonicWordException extends MnemonicException {
+ /** Contains the word that was not found in the word list. */
+ public final String badWord;
+
+ public MnemonicWordException(String badWord) {
+ super();
+ this.badWord = badWord;
+ }
+ }
+}
diff --git a/src/main/resources/chinese_simplified.txt b/src/main/resources/chinese_simplified.txt
new file mode 100644
index 000000000..b90f1ed85
--- /dev/null
+++ b/src/main/resources/chinese_simplified.txt
@@ -0,0 +1,2048 @@
+的
+一
+是
+在
+不
+了
+有
+和
+人
+这
+中
+大
+为
+上
+个
+国
+我
+以
+要
+他
+时
+来
+用
+们
+生
+到
+作
+地
+于
+出
+就
+分
+对
+成
+会
+可
+主
+发
+年
+动
+同
+工
+也
+能
+下
+过
+子
+说
+产
+种
+面
+而
+方
+后
+多
+定
+行
+学
+法
+所
+民
+得
+经
+十
+三
+之
+进
+着
+等
+部
+度
+家
+电
+力
+里
+如
+水
+化
+高
+自
+二
+理
+起
+小
+物
+现
+实
+加
+量
+都
+两
+体
+制
+机
+当
+使
+点
+从
+业
+本
+去
+把
+性
+好
+应
+开
+它
+合
+还
+因
+由
+其
+些
+然
+前
+外
+天
+政
+四
+日
+那
+社
+义
+事
+平
+形
+相
+全
+表
+间
+样
+与
+关
+各
+重
+新
+线
+内
+数
+正
+心
+反
+你
+明
+看
+原
+又
+么
+利
+比
+或
+但
+质
+气
+第
+向
+道
+命
+此
+变
+条
+只
+没
+结
+解
+问
+意
+建
+月
+公
+无
+系
+军
+很
+情
+者
+最
+立
+代
+想
+已
+通
+并
+提
+直
+题
+党
+程
+展
+五
+果
+料
+象
+员
+革
+位
+入
+常
+文
+总
+次
+品
+式
+活
+设
+及
+管
+特
+件
+长
+求
+老
+头
+基
+资
+边
+流
+路
+级
+少
+图
+山
+统
+接
+知
+较
+将
+组
+见
+计
+别
+她
+手
+角
+期
+根
+论
+运
+农
+指
+几
+九
+区
+强
+放
+决
+西
+被
+干
+做
+必
+战
+先
+回
+则
+任
+取
+据
+处
+队
+南
+给
+色
+光
+门
+即
+保
+治
+北
+造
+百
+规
+热
+领
+七
+海
+口
+东
+导
+器
+压
+志
+世
+金
+增
+争
+济
+阶
+油
+思
+术
+极
+交
+受
+联
+什
+认
+六
+共
+权
+收
+证
+改
+清
+美
+再
+采
+转
+更
+单
+风
+切
+打
+白
+教
+速
+花
+带
+安
+场
+身
+车
+例
+真
+务
+具
+万
+每
+目
+至
+达
+走
+积
+示
+议
+声
+报
+斗
+完
+类
+八
+离
+华
+名
+确
+才
+科
+张
+信
+马
+节
+话
+米
+整
+空
+元
+况
+今
+集
+温
+传
+土
+许
+步
+群
+广
+石
+记
+需
+段
+研
+界
+拉
+林
+律
+叫
+且
+究
+观
+越
+织
+装
+影
+算
+低
+持
+音
+众
+书
+布
+复
+容
+儿
+须
+际
+商
+非
+验
+连
+断
+深
+难
+近
+矿
+千
+周
+委
+素
+技
+备
+半
+办
+青
+省
+列
+习
+响
+约
+支
+般
+史
+感
+劳
+便
+团
+往
+酸
+历
+市
+克
+何
+除
+消
+构
+府
+称
+太
+准
+精
+值
+号
+率
+族
+维
+划
+选
+标
+写
+存
+候
+毛
+亲
+快
+效
+斯
+院
+查
+江
+型
+眼
+王
+按
+格
+养
+易
+置
+派
+层
+片
+始
+却
+专
+状
+育
+厂
+京
+识
+适
+属
+圆
+包
+火
+住
+调
+满
+县
+局
+照
+参
+红
+细
+引
+听
+该
+铁
+价
+严
+首
+底
+液
+官
+德
+随
+病
+苏
+失
+尔
+死
+讲
+配
+女
+黄
+推
+显
+谈
+罪
+神
+艺
+呢
+席
+含
+企
+望
+密
+批
+营
+项
+防
+举
+球
+英
+氧
+势
+告
+李
+台
+落
+木
+帮
+轮
+破
+亚
+师
+围
+注
+远
+字
+材
+排
+供
+河
+态
+封
+另
+施
+减
+树
+溶
+怎
+止
+案
+言
+士
+均
+武
+固
+叶
+鱼
+波
+视
+仅
+费
+紧
+爱
+左
+章
+早
+朝
+害
+续
+轻
+服
+试
+食
+充
+兵
+源
+判
+护
+司
+足
+某
+练
+差
+致
+板
+田
+降
+黑
+犯
+负
+击
+范
+继
+兴
+似
+余
+坚
+曲
+输
+修
+故
+城
+夫
+够
+送
+笔
+船
+占
+右
+财
+吃
+富
+春
+职
+觉
+汉
+画
+功
+巴
+跟
+虽
+杂
+飞
+检
+吸
+助
+升
+阳
+互
+初
+创
+抗
+考
+投
+坏
+策
+古
+径
+换
+未
+跑
+留
+钢
+曾
+端
+责
+站
+简
+述
+钱
+副
+尽
+帝
+射
+草
+冲
+承
+独
+令
+限
+阿
+宣
+环
+双
+请
+超
+微
+让
+控
+州
+良
+轴
+找
+否
+纪
+益
+依
+优
+顶
+础
+载
+倒
+房
+突
+坐
+粉
+敌
+略
+客
+袁
+冷
+胜
+绝
+析
+块
+剂
+测
+丝
+协
+诉
+念
+陈
+仍
+罗
+盐
+友
+洋
+错
+苦
+夜
+刑
+移
+频
+逐
+靠
+混
+母
+短
+皮
+终
+聚
+汽
+村
+云
+哪
+既
+距
+卫
+停
+烈
+央
+察
+烧
+迅
+境
+若
+印
+洲
+刻
+括
+激
+孔
+搞
+甚
+室
+待
+核
+校
+散
+侵
+吧
+甲
+游
+久
+菜
+味
+旧
+模
+湖
+货
+损
+预
+阻
+毫
+普
+稳
+乙
+妈
+植
+息
+扩
+银
+语
+挥
+酒
+守
+拿
+序
+纸
+医
+缺
+雨
+吗
+针
+刘
+啊
+急
+唱
+误
+训
+愿
+审
+附
+获
+茶
+鲜
+粮
+斤
+孩
+脱
+硫
+肥
+善
+龙
+演
+父
+渐
+血
+欢
+械
+掌
+歌
+沙
+刚
+攻
+谓
+盾
+讨
+晚
+粒
+乱
+燃
+矛
+乎
+杀
+药
+宁
+鲁
+贵
+钟
+煤
+读
+班
+伯
+香
+介
+迫
+句
+丰
+培
+握
+兰
+担
+弦
+蛋
+沉
+假
+穿
+执
+答
+乐
+谁
+顺
+烟
+缩
+征
+脸
+喜
+松
+脚
+困
+异
+免
+背
+星
+福
+买
+染
+井
+概
+慢
+怕
+磁
+倍
+祖
+皇
+促
+静
+补
+评
+翻
+肉
+践
+尼
+衣
+宽
+扬
+棉
+希
+伤
+操
+垂
+秋
+宜
+氢
+套
+督
+振
+架
+亮
+末
+宪
+庆
+编
+牛
+触
+映
+雷
+销
+诗
+座
+居
+抓
+裂
+胞
+呼
+娘
+景
+威
+绿
+晶
+厚
+盟
+衡
+鸡
+孙
+延
+危
+胶
+屋
+乡
+临
+陆
+顾
+掉
+呀
+灯
+岁
+措
+束
+耐
+剧
+玉
+赵
+跳
+哥
+季
+课
+凯
+胡
+额
+款
+绍
+卷
+齐
+伟
+蒸
+殖
+永
+宗
+苗
+川
+炉
+岩
+弱
+零
+杨
+奏
+沿
+露
+杆
+探
+滑
+镇
+饭
+浓
+航
+怀
+赶
+库
+夺
+伊
+灵
+税
+途
+灭
+赛
+归
+召
+鼓
+播
+盘
+裁
+险
+康
+唯
+录
+菌
+纯
+借
+糖
+盖
+横
+符
+私
+努
+堂
+域
+枪
+润
+幅
+哈
+竟
+熟
+虫
+泽
+脑
+壤
+碳
+欧
+遍
+侧
+寨
+敢
+彻
+虑
+斜
+薄
+庭
+纳
+弹
+饲
+伸
+折
+麦
+湿
+暗
+荷
+瓦
+塞
+床
+筑
+恶
+户
+访
+塔
+奇
+透
+梁
+刀
+旋
+迹
+卡
+氯
+遇
+份
+毒
+泥
+退
+洗
+摆
+灰
+彩
+卖
+耗
+夏
+择
+忙
+铜
+献
+硬
+予
+繁
+圈
+雪
+函
+亦
+抽
+篇
+阵
+阴
+丁
+尺
+追
+堆
+雄
+迎
+泛
+爸
+楼
+避
+谋
+吨
+野
+猪
+旗
+累
+偏
+典
+馆
+索
+秦
+脂
+潮
+爷
+豆
+忽
+托
+惊
+塑
+遗
+愈
+朱
+替
+纤
+粗
+倾
+尚
+痛
+楚
+谢
+奋
+购
+磨
+君
+池
+旁
+碎
+骨
+监
+捕
+弟
+暴
+割
+贯
+殊
+释
+词
+亡
+壁
+顿
+宝
+午
+尘
+闻
+揭
+炮
+残
+冬
+桥
+妇
+警
+综
+招
+吴
+付
+浮
+遭
+徐
+您
+摇
+谷
+赞
+箱
+隔
+订
+男
+吹
+园
+纷
+唐
+败
+宋
+玻
+巨
+耕
+坦
+荣
+闭
+湾
+键
+凡
+驻
+锅
+救
+恩
+剥
+凝
+碱
+齿
+截
+炼
+麻
+纺
+禁
+废
+盛
+版
+缓
+净
+睛
+昌
+婚
+涉
+筒
+嘴
+插
+岸
+朗
+庄
+街
+藏
+姑
+贸
+腐
+奴
+啦
+惯
+乘
+伙
+恢
+匀
+纱
+扎
+辩
+耳
+彪
+臣
+亿
+璃
+抵
+脉
+秀
+萨
+俄
+网
+舞
+店
+喷
+纵
+寸
+汗
+挂
+洪
+贺
+闪
+柬
+爆
+烯
+津
+稻
+墙
+软
+勇
+像
+滚
+厘
+蒙
+芳
+肯
+坡
+柱
+荡
+腿
+仪
+旅
+尾
+轧
+冰
+贡
+登
+黎
+削
+钻
+勒
+逃
+障
+氨
+郭
+峰
+币
+港
+伏
+轨
+亩
+毕
+擦
+莫
+刺
+浪
+秘
+援
+株
+健
+售
+股
+岛
+甘
+泡
+睡
+童
+铸
+汤
+阀
+休
+汇
+舍
+牧
+绕
+炸
+哲
+磷
+绩
+朋
+淡
+尖
+启
+陷
+柴
+呈
+徒
+颜
+泪
+稍
+忘
+泵
+蓝
+拖
+洞
+授
+镜
+辛
+壮
+锋
+贫
+虚
+弯
+摩
+泰
+幼
+廷
+尊
+窗
+纲
+弄
+隶
+疑
+氏
+宫
+姐
+震
+瑞
+怪
+尤
+琴
+循
+描
+膜
+违
+夹
+腰
+缘
+珠
+穷
+森
+枝
+竹
+沟
+催
+绳
+忆
+邦
+剩
+幸
+浆
+栏
+拥
+牙
+贮
+礼
+滤
+钠
+纹
+罢
+拍
+咱
+喊
+袖
+埃
+勤
+罚
+焦
+潜
+伍
+墨
+欲
+缝
+姓
+刊
+饱
+仿
+奖
+铝
+鬼
+丽
+跨
+默
+挖
+链
+扫
+喝
+袋
+炭
+污
+幕
+诸
+弧
+励
+梅
+奶
+洁
+灾
+舟
+鉴
+苯
+讼
+抱
+毁
+懂
+寒
+智
+埔
+寄
+届
+跃
+渡
+挑
+丹
+艰
+贝
+碰
+拔
+爹
+戴
+码
+梦
+芽
+熔
+赤
+渔
+哭
+敬
+颗
+奔
+铅
+仲
+虎
+稀
+妹
+乏
+珍
+申
+桌
+遵
+允
+隆
+螺
+仓
+魏
+锐
+晓
+氮
+兼
+隐
+碍
+赫
+拨
+忠
+肃
+缸
+牵
+抢
+博
+巧
+壳
+兄
+杜
+讯
+诚
+碧
+祥
+柯
+页
+巡
+矩
+悲
+灌
+龄
+伦
+票
+寻
+桂
+铺
+圣
+恐
+恰
+郑
+趣
+抬
+荒
+腾
+贴
+柔
+滴
+猛
+阔
+辆
+妻
+填
+撤
+储
+签
+闹
+扰
+紫
+砂
+递
+戏
+吊
+陶
+伐
+喂
+疗
+瓶
+婆
+抚
+臂
+摸
+忍
+虾
+蜡
+邻
+胸
+巩
+挤
+偶
+弃
+槽
+劲
+乳
+邓
+吉
+仁
+烂
+砖
+租
+乌
+舰
+伴
+瓜
+浅
+丙
+暂
+燥
+橡
+柳
+迷
+暖
+牌
+秧
+胆
+详
+簧
+踏
+瓷
+谱
+呆
+宾
+糊
+洛
+辉
+愤
+竞
+隙
+怒
+粘
+乃
+绪
+肩
+籍
+敏
+涂
+熙
+皆
+侦
+悬
+掘
+享
+纠
+醒
+狂
+锁
+淀
+恨
+牲
+霸
+爬
+赏
+逆
+玩
+陵
+祝
+秒
+浙
+貌
+役
+彼
+悉
+鸭
+趋
+凤
+晨
+畜
+辈
+秩
+卵
+署
+梯
+炎
+滩
+棋
+驱
+筛
+峡
+冒
+啥
+寿
+译
+浸
+泉
+帽
+迟
+硅
+疆
+贷
+漏
+稿
+冠
+嫩
+胁
+芯
+牢
+叛
+蚀
+奥
+鸣
+岭
+羊
+凭
+串
+塘
+绘
+酵
+融
+盆
+锡
+庙
+筹
+冻
+辅
+摄
+袭
+筋
+拒
+僚
+旱
+钾
+鸟
+漆
+沈
+眉
+疏
+添
+棒
+穗
+硝
+韩
+逼
+扭
+侨
+凉
+挺
+碗
+栽
+炒
+杯
+患
+馏
+劝
+豪
+辽
+勃
+鸿
+旦
+吏
+拜
+狗
+埋
+辊
+掩
+饮
+搬
+骂
+辞
+勾
+扣
+估
+蒋
+绒
+雾
+丈
+朵
+姆
+拟
+宇
+辑
+陕
+雕
+偿
+蓄
+崇
+剪
+倡
+厅
+咬
+驶
+薯
+刷
+斥
+番
+赋
+奉
+佛
+浇
+漫
+曼
+扇
+钙
+桃
+扶
+仔
+返
+俗
+亏
+腔
+鞋
+棱
+覆
+框
+悄
+叔
+撞
+骗
+勘
+旺
+沸
+孤
+吐
+孟
+渠
+屈
+疾
+妙
+惜
+仰
+狠
+胀
+谐
+抛
+霉
+桑
+岗
+嘛
+衰
+盗
+渗
+脏
+赖
+涌
+甜
+曹
+阅
+肌
+哩
+厉
+烃
+纬
+毅
+昨
+伪
+症
+煮
+叹
+钉
+搭
+茎
+笼
+酷
+偷
+弓
+锥
+恒
+杰
+坑
+鼻
+翼
+纶
+叙
+狱
+逮
+罐
+络
+棚
+抑
+膨
+蔬
+寺
+骤
+穆
+冶
+枯
+册
+尸
+凸
+绅
+坯
+牺
+焰
+轰
+欣
+晋
+瘦
+御
+锭
+锦
+丧
+旬
+锻
+垄
+搜
+扑
+邀
+亭
+酯
+迈
+舒
+脆
+酶
+闲
+忧
+酚
+顽
+羽
+涨
+卸
+仗
+陪
+辟
+惩
+杭
+姚
+肚
+捉
+飘
+漂
+昆
+欺
+吾
+郎
+烷
+汁
+呵
+饰
+萧
+雅
+邮
+迁
+燕
+撒
+姻
+赴
+宴
+烦
+债
+帐
+斑
+铃
+旨
+醇
+董
+饼
+雏
+姿
+拌
+傅
+腹
+妥
+揉
+贤
+拆
+歪
+葡
+胺
+丢
+浩
+徽
+昂
+垫
+挡
+览
+贪
+慰
+缴
+汪
+慌
+冯
+诺
+姜
+谊
+凶
+劣
+诬
+耀
+昏
+躺
+盈
+骑
+乔
+溪
+丛
+卢
+抹
+闷
+咨
+刮
+驾
+缆
+悟
+摘
+铒
+掷
+颇
+幻
+柄
+惠
+惨
+佳
+仇
+腊
+窝
+涤
+剑
+瞧
+堡
+泼
+葱
+罩
+霍
+捞
+胎
+苍
+滨
+俩
+捅
+湘
+砍
+霞
+邵
+萄
+疯
+淮
+遂
+熊
+粪
+烘
+宿
+档
+戈
+驳
+嫂
+裕
+徙
+箭
+捐
+肠
+撑
+晒
+辨
+殿
+莲
+摊
+搅
+酱
+屏
+疫
+哀
+蔡
+堵
+沫
+皱
+畅
+叠
+阁
+莱
+敲
+辖
+钩
+痕
+坝
+巷
+饿
+祸
+丘
+玄
+溜
+曰
+逻
+彭
+尝
+卿
+妨
+艇
+吞
+韦
+怨
+矮
+歇
diff --git a/src/main/resources/chinese_traditional.txt b/src/main/resources/chinese_traditional.txt
new file mode 100644
index 000000000..9b0204792
--- /dev/null
+++ b/src/main/resources/chinese_traditional.txt
@@ -0,0 +1,2048 @@
+的
+一
+是
+在
+不
+了
+有
+和
+人
+這
+中
+大
+為
+上
+個
+國
+我
+以
+要
+他
+時
+來
+用
+們
+生
+到
+作
+地
+於
+出
+就
+分
+對
+成
+會
+可
+主
+發
+年
+動
+同
+工
+也
+能
+下
+過
+子
+說
+產
+種
+面
+而
+方
+後
+多
+定
+行
+學
+法
+所
+民
+得
+經
+十
+三
+之
+進
+著
+等
+部
+度
+家
+電
+力
+裡
+如
+水
+化
+高
+自
+二
+理
+起
+小
+物
+現
+實
+加
+量
+都
+兩
+體
+制
+機
+當
+使
+點
+從
+業
+本
+去
+把
+性
+好
+應
+開
+它
+合
+還
+因
+由
+其
+些
+然
+前
+外
+天
+政
+四
+日
+那
+社
+義
+事
+平
+形
+相
+全
+表
+間
+樣
+與
+關
+各
+重
+新
+線
+內
+數
+正
+心
+反
+你
+明
+看
+原
+又
+麼
+利
+比
+或
+但
+質
+氣
+第
+向
+道
+命
+此
+變
+條
+只
+沒
+結
+解
+問
+意
+建
+月
+公
+無
+系
+軍
+很
+情
+者
+最
+立
+代
+想
+已
+通
+並
+提
+直
+題
+黨
+程
+展
+五
+果
+料
+象
+員
+革
+位
+入
+常
+文
+總
+次
+品
+式
+活
+設
+及
+管
+特
+件
+長
+求
+老
+頭
+基
+資
+邊
+流
+路
+級
+少
+圖
+山
+統
+接
+知
+較
+將
+組
+見
+計
+別
+她
+手
+角
+期
+根
+論
+運
+農
+指
+幾
+九
+區
+強
+放
+決
+西
+被
+幹
+做
+必
+戰
+先
+回
+則
+任
+取
+據
+處
+隊
+南
+給
+色
+光
+門
+即
+保
+治
+北
+造
+百
+規
+熱
+領
+七
+海
+口
+東
+導
+器
+壓
+志
+世
+金
+增
+爭
+濟
+階
+油
+思
+術
+極
+交
+受
+聯
+什
+認
+六
+共
+權
+收
+證
+改
+清
+美
+再
+採
+轉
+更
+單
+風
+切
+打
+白
+教
+速
+花
+帶
+安
+場
+身
+車
+例
+真
+務
+具
+萬
+每
+目
+至
+達
+走
+積
+示
+議
+聲
+報
+鬥
+完
+類
+八
+離
+華
+名
+確
+才
+科
+張
+信
+馬
+節
+話
+米
+整
+空
+元
+況
+今
+集
+溫
+傳
+土
+許
+步
+群
+廣
+石
+記
+需
+段
+研
+界
+拉
+林
+律
+叫
+且
+究
+觀
+越
+織
+裝
+影
+算
+低
+持
+音
+眾
+書
+布
+复
+容
+兒
+須
+際
+商
+非
+驗
+連
+斷
+深
+難
+近
+礦
+千
+週
+委
+素
+技
+備
+半
+辦
+青
+省
+列
+習
+響
+約
+支
+般
+史
+感
+勞
+便
+團
+往
+酸
+歷
+市
+克
+何
+除
+消
+構
+府
+稱
+太
+準
+精
+值
+號
+率
+族
+維
+劃
+選
+標
+寫
+存
+候
+毛
+親
+快
+效
+斯
+院
+查
+江
+型
+眼
+王
+按
+格
+養
+易
+置
+派
+層
+片
+始
+卻
+專
+狀
+育
+廠
+京
+識
+適
+屬
+圓
+包
+火
+住
+調
+滿
+縣
+局
+照
+參
+紅
+細
+引
+聽
+該
+鐵
+價
+嚴
+首
+底
+液
+官
+德
+隨
+病
+蘇
+失
+爾
+死
+講
+配
+女
+黃
+推
+顯
+談
+罪
+神
+藝
+呢
+席
+含
+企
+望
+密
+批
+營
+項
+防
+舉
+球
+英
+氧
+勢
+告
+李
+台
+落
+木
+幫
+輪
+破
+亞
+師
+圍
+注
+遠
+字
+材
+排
+供
+河
+態
+封
+另
+施
+減
+樹
+溶
+怎
+止
+案
+言
+士
+均
+武
+固
+葉
+魚
+波
+視
+僅
+費
+緊
+愛
+左
+章
+早
+朝
+害
+續
+輕
+服
+試
+食
+充
+兵
+源
+判
+護
+司
+足
+某
+練
+差
+致
+板
+田
+降
+黑
+犯
+負
+擊
+范
+繼
+興
+似
+餘
+堅
+曲
+輸
+修
+故
+城
+夫
+夠
+送
+筆
+船
+佔
+右
+財
+吃
+富
+春
+職
+覺
+漢
+畫
+功
+巴
+跟
+雖
+雜
+飛
+檢
+吸
+助
+昇
+陽
+互
+初
+創
+抗
+考
+投
+壞
+策
+古
+徑
+換
+未
+跑
+留
+鋼
+曾
+端
+責
+站
+簡
+述
+錢
+副
+盡
+帝
+射
+草
+衝
+承
+獨
+令
+限
+阿
+宣
+環
+雙
+請
+超
+微
+讓
+控
+州
+良
+軸
+找
+否
+紀
+益
+依
+優
+頂
+礎
+載
+倒
+房
+突
+坐
+粉
+敵
+略
+客
+袁
+冷
+勝
+絕
+析
+塊
+劑
+測
+絲
+協
+訴
+念
+陳
+仍
+羅
+鹽
+友
+洋
+錯
+苦
+夜
+刑
+移
+頻
+逐
+靠
+混
+母
+短
+皮
+終
+聚
+汽
+村
+雲
+哪
+既
+距
+衛
+停
+烈
+央
+察
+燒
+迅
+境
+若
+印
+洲
+刻
+括
+激
+孔
+搞
+甚
+室
+待
+核
+校
+散
+侵
+吧
+甲
+遊
+久
+菜
+味
+舊
+模
+湖
+貨
+損
+預
+阻
+毫
+普
+穩
+乙
+媽
+植
+息
+擴
+銀
+語
+揮
+酒
+守
+拿
+序
+紙
+醫
+缺
+雨
+嗎
+針
+劉
+啊
+急
+唱
+誤
+訓
+願
+審
+附
+獲
+茶
+鮮
+糧
+斤
+孩
+脫
+硫
+肥
+善
+龍
+演
+父
+漸
+血
+歡
+械
+掌
+歌
+沙
+剛
+攻
+謂
+盾
+討
+晚
+粒
+亂
+燃
+矛
+乎
+殺
+藥
+寧
+魯
+貴
+鐘
+煤
+讀
+班
+伯
+香
+介
+迫
+句
+豐
+培
+握
+蘭
+擔
+弦
+蛋
+沉
+假
+穿
+執
+答
+樂
+誰
+順
+煙
+縮
+徵
+臉
+喜
+松
+腳
+困
+異
+免
+背
+星
+福
+買
+染
+井
+概
+慢
+怕
+磁
+倍
+祖
+皇
+促
+靜
+補
+評
+翻
+肉
+踐
+尼
+衣
+寬
+揚
+棉
+希
+傷
+操
+垂
+秋
+宜
+氫
+套
+督
+振
+架
+亮
+末
+憲
+慶
+編
+牛
+觸
+映
+雷
+銷
+詩
+座
+居
+抓
+裂
+胞
+呼
+娘
+景
+威
+綠
+晶
+厚
+盟
+衡
+雞
+孫
+延
+危
+膠
+屋
+鄉
+臨
+陸
+顧
+掉
+呀
+燈
+歲
+措
+束
+耐
+劇
+玉
+趙
+跳
+哥
+季
+課
+凱
+胡
+額
+款
+紹
+卷
+齊
+偉
+蒸
+殖
+永
+宗
+苗
+川
+爐
+岩
+弱
+零
+楊
+奏
+沿
+露
+桿
+探
+滑
+鎮
+飯
+濃
+航
+懷
+趕
+庫
+奪
+伊
+靈
+稅
+途
+滅
+賽
+歸
+召
+鼓
+播
+盤
+裁
+險
+康
+唯
+錄
+菌
+純
+借
+糖
+蓋
+橫
+符
+私
+努
+堂
+域
+槍
+潤
+幅
+哈
+竟
+熟
+蟲
+澤
+腦
+壤
+碳
+歐
+遍
+側
+寨
+敢
+徹
+慮
+斜
+薄
+庭
+納
+彈
+飼
+伸
+折
+麥
+濕
+暗
+荷
+瓦
+塞
+床
+築
+惡
+戶
+訪
+塔
+奇
+透
+梁
+刀
+旋
+跡
+卡
+氯
+遇
+份
+毒
+泥
+退
+洗
+擺
+灰
+彩
+賣
+耗
+夏
+擇
+忙
+銅
+獻
+硬
+予
+繁
+圈
+雪
+函
+亦
+抽
+篇
+陣
+陰
+丁
+尺
+追
+堆
+雄
+迎
+泛
+爸
+樓
+避
+謀
+噸
+野
+豬
+旗
+累
+偏
+典
+館
+索
+秦
+脂
+潮
+爺
+豆
+忽
+托
+驚
+塑
+遺
+愈
+朱
+替
+纖
+粗
+傾
+尚
+痛
+楚
+謝
+奮
+購
+磨
+君
+池
+旁
+碎
+骨
+監
+捕
+弟
+暴
+割
+貫
+殊
+釋
+詞
+亡
+壁
+頓
+寶
+午
+塵
+聞
+揭
+炮
+殘
+冬
+橋
+婦
+警
+綜
+招
+吳
+付
+浮
+遭
+徐
+您
+搖
+谷
+贊
+箱
+隔
+訂
+男
+吹
+園
+紛
+唐
+敗
+宋
+玻
+巨
+耕
+坦
+榮
+閉
+灣
+鍵
+凡
+駐
+鍋
+救
+恩
+剝
+凝
+鹼
+齒
+截
+煉
+麻
+紡
+禁
+廢
+盛
+版
+緩
+淨
+睛
+昌
+婚
+涉
+筒
+嘴
+插
+岸
+朗
+莊
+街
+藏
+姑
+貿
+腐
+奴
+啦
+慣
+乘
+夥
+恢
+勻
+紗
+扎
+辯
+耳
+彪
+臣
+億
+璃
+抵
+脈
+秀
+薩
+俄
+網
+舞
+店
+噴
+縱
+寸
+汗
+掛
+洪
+賀
+閃
+柬
+爆
+烯
+津
+稻
+牆
+軟
+勇
+像
+滾
+厘
+蒙
+芳
+肯
+坡
+柱
+盪
+腿
+儀
+旅
+尾
+軋
+冰
+貢
+登
+黎
+削
+鑽
+勒
+逃
+障
+氨
+郭
+峰
+幣
+港
+伏
+軌
+畝
+畢
+擦
+莫
+刺
+浪
+秘
+援
+株
+健
+售
+股
+島
+甘
+泡
+睡
+童
+鑄
+湯
+閥
+休
+匯
+舍
+牧
+繞
+炸
+哲
+磷
+績
+朋
+淡
+尖
+啟
+陷
+柴
+呈
+徒
+顏
+淚
+稍
+忘
+泵
+藍
+拖
+洞
+授
+鏡
+辛
+壯
+鋒
+貧
+虛
+彎
+摩
+泰
+幼
+廷
+尊
+窗
+綱
+弄
+隸
+疑
+氏
+宮
+姐
+震
+瑞
+怪
+尤
+琴
+循
+描
+膜
+違
+夾
+腰
+緣
+珠
+窮
+森
+枝
+竹
+溝
+催
+繩
+憶
+邦
+剩
+幸
+漿
+欄
+擁
+牙
+貯
+禮
+濾
+鈉
+紋
+罷
+拍
+咱
+喊
+袖
+埃
+勤
+罰
+焦
+潛
+伍
+墨
+欲
+縫
+姓
+刊
+飽
+仿
+獎
+鋁
+鬼
+麗
+跨
+默
+挖
+鏈
+掃
+喝
+袋
+炭
+污
+幕
+諸
+弧
+勵
+梅
+奶
+潔
+災
+舟
+鑑
+苯
+訟
+抱
+毀
+懂
+寒
+智
+埔
+寄
+屆
+躍
+渡
+挑
+丹
+艱
+貝
+碰
+拔
+爹
+戴
+碼
+夢
+芽
+熔
+赤
+漁
+哭
+敬
+顆
+奔
+鉛
+仲
+虎
+稀
+妹
+乏
+珍
+申
+桌
+遵
+允
+隆
+螺
+倉
+魏
+銳
+曉
+氮
+兼
+隱
+礙
+赫
+撥
+忠
+肅
+缸
+牽
+搶
+博
+巧
+殼
+兄
+杜
+訊
+誠
+碧
+祥
+柯
+頁
+巡
+矩
+悲
+灌
+齡
+倫
+票
+尋
+桂
+鋪
+聖
+恐
+恰
+鄭
+趣
+抬
+荒
+騰
+貼
+柔
+滴
+猛
+闊
+輛
+妻
+填
+撤
+儲
+簽
+鬧
+擾
+紫
+砂
+遞
+戲
+吊
+陶
+伐
+餵
+療
+瓶
+婆
+撫
+臂
+摸
+忍
+蝦
+蠟
+鄰
+胸
+鞏
+擠
+偶
+棄
+槽
+勁
+乳
+鄧
+吉
+仁
+爛
+磚
+租
+烏
+艦
+伴
+瓜
+淺
+丙
+暫
+燥
+橡
+柳
+迷
+暖
+牌
+秧
+膽
+詳
+簧
+踏
+瓷
+譜
+呆
+賓
+糊
+洛
+輝
+憤
+競
+隙
+怒
+粘
+乃
+緒
+肩
+籍
+敏
+塗
+熙
+皆
+偵
+懸
+掘
+享
+糾
+醒
+狂
+鎖
+淀
+恨
+牲
+霸
+爬
+賞
+逆
+玩
+陵
+祝
+秒
+浙
+貌
+役
+彼
+悉
+鴨
+趨
+鳳
+晨
+畜
+輩
+秩
+卵
+署
+梯
+炎
+灘
+棋
+驅
+篩
+峽
+冒
+啥
+壽
+譯
+浸
+泉
+帽
+遲
+矽
+疆
+貸
+漏
+稿
+冠
+嫩
+脅
+芯
+牢
+叛
+蝕
+奧
+鳴
+嶺
+羊
+憑
+串
+塘
+繪
+酵
+融
+盆
+錫
+廟
+籌
+凍
+輔
+攝
+襲
+筋
+拒
+僚
+旱
+鉀
+鳥
+漆
+沈
+眉
+疏
+添
+棒
+穗
+硝
+韓
+逼
+扭
+僑
+涼
+挺
+碗
+栽
+炒
+杯
+患
+餾
+勸
+豪
+遼
+勃
+鴻
+旦
+吏
+拜
+狗
+埋
+輥
+掩
+飲
+搬
+罵
+辭
+勾
+扣
+估
+蔣
+絨
+霧
+丈
+朵
+姆
+擬
+宇
+輯
+陝
+雕
+償
+蓄
+崇
+剪
+倡
+廳
+咬
+駛
+薯
+刷
+斥
+番
+賦
+奉
+佛
+澆
+漫
+曼
+扇
+鈣
+桃
+扶
+仔
+返
+俗
+虧
+腔
+鞋
+棱
+覆
+框
+悄
+叔
+撞
+騙
+勘
+旺
+沸
+孤
+吐
+孟
+渠
+屈
+疾
+妙
+惜
+仰
+狠
+脹
+諧
+拋
+黴
+桑
+崗
+嘛
+衰
+盜
+滲
+臟
+賴
+湧
+甜
+曹
+閱
+肌
+哩
+厲
+烴
+緯
+毅
+昨
+偽
+症
+煮
+嘆
+釘
+搭
+莖
+籠
+酷
+偷
+弓
+錐
+恆
+傑
+坑
+鼻
+翼
+綸
+敘
+獄
+逮
+罐
+絡
+棚
+抑
+膨
+蔬
+寺
+驟
+穆
+冶
+枯
+冊
+屍
+凸
+紳
+坯
+犧
+焰
+轟
+欣
+晉
+瘦
+禦
+錠
+錦
+喪
+旬
+鍛
+壟
+搜
+撲
+邀
+亭
+酯
+邁
+舒
+脆
+酶
+閒
+憂
+酚
+頑
+羽
+漲
+卸
+仗
+陪
+闢
+懲
+杭
+姚
+肚
+捉
+飄
+漂
+昆
+欺
+吾
+郎
+烷
+汁
+呵
+飾
+蕭
+雅
+郵
+遷
+燕
+撒
+姻
+赴
+宴
+煩
+債
+帳
+斑
+鈴
+旨
+醇
+董
+餅
+雛
+姿
+拌
+傅
+腹
+妥
+揉
+賢
+拆
+歪
+葡
+胺
+丟
+浩
+徽
+昂
+墊
+擋
+覽
+貪
+慰
+繳
+汪
+慌
+馮
+諾
+姜
+誼
+兇
+劣
+誣
+耀
+昏
+躺
+盈
+騎
+喬
+溪
+叢
+盧
+抹
+悶
+諮
+刮
+駕
+纜
+悟
+摘
+鉺
+擲
+頗
+幻
+柄
+惠
+慘
+佳
+仇
+臘
+窩
+滌
+劍
+瞧
+堡
+潑
+蔥
+罩
+霍
+撈
+胎
+蒼
+濱
+倆
+捅
+湘
+砍
+霞
+邵
+萄
+瘋
+淮
+遂
+熊
+糞
+烘
+宿
+檔
+戈
+駁
+嫂
+裕
+徙
+箭
+捐
+腸
+撐
+曬
+辨
+殿
+蓮
+攤
+攪
+醬
+屏
+疫
+哀
+蔡
+堵
+沫
+皺
+暢
+疊
+閣
+萊
+敲
+轄
+鉤
+痕
+壩
+巷
+餓
+禍
+丘
+玄
+溜
+曰
+邏
+彭
+嘗
+卿
+妨
+艇
+吞
+韋
+怨
+矮
+歇
diff --git a/src/main/resources/english.txt b/src/main/resources/english.txt
new file mode 100644
index 000000000..f78ccaf2c
--- /dev/null
+++ b/src/main/resources/english.txt
@@ -0,0 +1,2048 @@
+abandon
+ability
+able
+about
+above
+absent
+absorb
+abstract
+absurd
+abuse
+access
+accident
+account
+accuse
+achieve
+acid
+acoustic
+acquire
+across
+act
+action
+actor
+actress
+actual
+adapt
+add
+addict
+address
+adjust
+admit
+adult
+advance
+advice
+aerobic
+affair
+afford
+afraid
+again
+age
+agent
+agree
+ahead
+aim
+air
+airport
+aisle
+alarm
+album
+alcohol
+alert
+alien
+all
+alley
+allow
+almost
+alone
+alpha
+already
+also
+alter
+always
+amateur
+amazing
+among
+amount
+amused
+analyst
+anchor
+ancient
+anger
+angle
+angry
+animal
+ankle
+announce
+annual
+another
+answer
+antenna
+antique
+anxiety
+any
+apart
+apology
+appear
+apple
+approve
+april
+arch
+arctic
+area
+arena
+argue
+arm
+armed
+armor
+army
+around
+arrange
+arrest
+arrive
+arrow
+art
+artefact
+artist
+artwork
+ask
+aspect
+assault
+asset
+assist
+assume
+asthma
+athlete
+atom
+attack
+attend
+attitude
+attract
+auction
+audit
+august
+aunt
+author
+auto
+autumn
+average
+avocado
+avoid
+awake
+aware
+away
+awesome
+awful
+awkward
+axis
+baby
+bachelor
+bacon
+badge
+bag
+balance
+balcony
+ball
+bamboo
+banana
+banner
+bar
+barely
+bargain
+barrel
+base
+basic
+basket
+battle
+beach
+bean
+beauty
+because
+become
+beef
+before
+begin
+behave
+behind
+believe
+below
+belt
+bench
+benefit
+best
+betray
+better
+between
+beyond
+bicycle
+bid
+bike
+bind
+biology
+bird
+birth
+bitter
+black
+blade
+blame
+blanket
+blast
+bleak
+bless
+blind
+blood
+blossom
+blouse
+blue
+blur
+blush
+board
+boat
+body
+boil
+bomb
+bone
+bonus
+book
+boost
+border
+boring
+borrow
+boss
+bottom
+bounce
+box
+boy
+bracket
+brain
+brand
+brass
+brave
+bread
+breeze
+brick
+bridge
+brief
+bright
+bring
+brisk
+broccoli
+broken
+bronze
+broom
+brother
+brown
+brush
+bubble
+buddy
+budget
+buffalo
+build
+bulb
+bulk
+bullet
+bundle
+bunker
+burden
+burger
+burst
+bus
+business
+busy
+butter
+buyer
+buzz
+cabbage
+cabin
+cable
+cactus
+cage
+cake
+call
+calm
+camera
+camp
+can
+canal
+cancel
+candy
+cannon
+canoe
+canvas
+canyon
+capable
+capital
+captain
+car
+carbon
+card
+cargo
+carpet
+carry
+cart
+case
+cash
+casino
+castle
+casual
+cat
+catalog
+catch
+category
+cattle
+caught
+cause
+caution
+cave
+ceiling
+celery
+cement
+census
+century
+cereal
+certain
+chair
+chalk
+champion
+change
+chaos
+chapter
+charge
+chase
+chat
+cheap
+check
+cheese
+chef
+cherry
+chest
+chicken
+chief
+child
+chimney
+choice
+choose
+chronic
+chuckle
+chunk
+churn
+cigar
+cinnamon
+circle
+citizen
+city
+civil
+claim
+clap
+clarify
+claw
+clay
+clean
+clerk
+clever
+click
+client
+cliff
+climb
+clinic
+clip
+clock
+clog
+close
+cloth
+cloud
+clown
+club
+clump
+cluster
+clutch
+coach
+coast
+coconut
+code
+coffee
+coil
+coin
+collect
+color
+column
+combine
+come
+comfort
+comic
+common
+company
+concert
+conduct
+confirm
+congress
+connect
+consider
+control
+convince
+cook
+cool
+copper
+copy
+coral
+core
+corn
+correct
+cost
+cotton
+couch
+country
+couple
+course
+cousin
+cover
+coyote
+crack
+cradle
+craft
+cram
+crane
+crash
+crater
+crawl
+crazy
+cream
+credit
+creek
+crew
+cricket
+crime
+crisp
+critic
+crop
+cross
+crouch
+crowd
+crucial
+cruel
+cruise
+crumble
+crunch
+crush
+cry
+crystal
+cube
+culture
+cup
+cupboard
+curious
+current
+curtain
+curve
+cushion
+custom
+cute
+cycle
+dad
+damage
+damp
+dance
+danger
+daring
+dash
+daughter
+dawn
+day
+deal
+debate
+debris
+decade
+december
+decide
+decline
+decorate
+decrease
+deer
+defense
+define
+defy
+degree
+delay
+deliver
+demand
+demise
+denial
+dentist
+deny
+depart
+depend
+deposit
+depth
+deputy
+derive
+describe
+desert
+design
+desk
+despair
+destroy
+detail
+detect
+develop
+device
+devote
+diagram
+dial
+diamond
+diary
+dice
+diesel
+diet
+differ
+digital
+dignity
+dilemma
+dinner
+dinosaur
+direct
+dirt
+disagree
+discover
+disease
+dish
+dismiss
+disorder
+display
+distance
+divert
+divide
+divorce
+dizzy
+doctor
+document
+dog
+doll
+dolphin
+domain
+donate
+donkey
+donor
+door
+dose
+double
+dove
+draft
+dragon
+drama
+drastic
+draw
+dream
+dress
+drift
+drill
+drink
+drip
+drive
+drop
+drum
+dry
+duck
+dumb
+dune
+during
+dust
+dutch
+duty
+dwarf
+dynamic
+eager
+eagle
+early
+earn
+earth
+easily
+east
+easy
+echo
+ecology
+economy
+edge
+edit
+educate
+effort
+egg
+eight
+either
+elbow
+elder
+electric
+elegant
+element
+elephant
+elevator
+elite
+else
+embark
+embody
+embrace
+emerge
+emotion
+employ
+empower
+empty
+enable
+enact
+end
+endless
+endorse
+enemy
+energy
+enforce
+engage
+engine
+enhance
+enjoy
+enlist
+enough
+enrich
+enroll
+ensure
+enter
+entire
+entry
+envelope
+episode
+equal
+equip
+era
+erase
+erode
+erosion
+error
+erupt
+escape
+essay
+essence
+estate
+eternal
+ethics
+evidence
+evil
+evoke
+evolve
+exact
+example
+excess
+exchange
+excite
+exclude
+excuse
+execute
+exercise
+exhaust
+exhibit
+exile
+exist
+exit
+exotic
+expand
+expect
+expire
+explain
+expose
+express
+extend
+extra
+eye
+eyebrow
+fabric
+face
+faculty
+fade
+faint
+faith
+fall
+false
+fame
+family
+famous
+fan
+fancy
+fantasy
+farm
+fashion
+fat
+fatal
+father
+fatigue
+fault
+favorite
+feature
+february
+federal
+fee
+feed
+feel
+female
+fence
+festival
+fetch
+fever
+few
+fiber
+fiction
+field
+figure
+file
+film
+filter
+final
+find
+fine
+finger
+finish
+fire
+firm
+first
+fiscal
+fish
+fit
+fitness
+fix
+flag
+flame
+flash
+flat
+flavor
+flee
+flight
+flip
+float
+flock
+floor
+flower
+fluid
+flush
+fly
+foam
+focus
+fog
+foil
+fold
+follow
+food
+foot
+force
+forest
+forget
+fork
+fortune
+forum
+forward
+fossil
+foster
+found
+fox
+fragile
+frame
+frequent
+fresh
+friend
+fringe
+frog
+front
+frost
+frown
+frozen
+fruit
+fuel
+fun
+funny
+furnace
+fury
+future
+gadget
+gain
+galaxy
+gallery
+game
+gap
+garage
+garbage
+garden
+garlic
+garment
+gas
+gasp
+gate
+gather
+gauge
+gaze
+general
+genius
+genre
+gentle
+genuine
+gesture
+ghost
+giant
+gift
+giggle
+ginger
+giraffe
+girl
+give
+glad
+glance
+glare
+glass
+glide
+glimpse
+globe
+gloom
+glory
+glove
+glow
+glue
+goat
+goddess
+gold
+good
+goose
+gorilla
+gospel
+gossip
+govern
+gown
+grab
+grace
+grain
+grant
+grape
+grass
+gravity
+great
+green
+grid
+grief
+grit
+grocery
+group
+grow
+grunt
+guard
+guess
+guide
+guilt
+guitar
+gun
+gym
+habit
+hair
+half
+hammer
+hamster
+hand
+happy
+harbor
+hard
+harsh
+harvest
+hat
+have
+hawk
+hazard
+head
+health
+heart
+heavy
+hedgehog
+height
+hello
+helmet
+help
+hen
+hero
+hidden
+high
+hill
+hint
+hip
+hire
+history
+hobby
+hockey
+hold
+hole
+holiday
+hollow
+home
+honey
+hood
+hope
+horn
+horror
+horse
+hospital
+host
+hotel
+hour
+hover
+hub
+huge
+human
+humble
+humor
+hundred
+hungry
+hunt
+hurdle
+hurry
+hurt
+husband
+hybrid
+ice
+icon
+idea
+identify
+idle
+ignore
+ill
+illegal
+illness
+image
+imitate
+immense
+immune
+impact
+impose
+improve
+impulse
+inch
+include
+income
+increase
+index
+indicate
+indoor
+industry
+infant
+inflict
+inform
+inhale
+inherit
+initial
+inject
+injury
+inmate
+inner
+innocent
+input
+inquiry
+insane
+insect
+inside
+inspire
+install
+intact
+interest
+into
+invest
+invite
+involve
+iron
+island
+isolate
+issue
+item
+ivory
+jacket
+jaguar
+jar
+jazz
+jealous
+jeans
+jelly
+jewel
+job
+join
+joke
+journey
+joy
+judge
+juice
+jump
+jungle
+junior
+junk
+just
+kangaroo
+keen
+keep
+ketchup
+key
+kick
+kid
+kidney
+kind
+kingdom
+kiss
+kit
+kitchen
+kite
+kitten
+kiwi
+knee
+knife
+knock
+know
+lab
+label
+labor
+ladder
+lady
+lake
+lamp
+language
+laptop
+large
+later
+latin
+laugh
+laundry
+lava
+law
+lawn
+lawsuit
+layer
+lazy
+leader
+leaf
+learn
+leave
+lecture
+left
+leg
+legal
+legend
+leisure
+lemon
+lend
+length
+lens
+leopard
+lesson
+letter
+level
+liar
+liberty
+library
+license
+life
+lift
+light
+like
+limb
+limit
+link
+lion
+liquid
+list
+little
+live
+lizard
+load
+loan
+lobster
+local
+lock
+logic
+lonely
+long
+loop
+lottery
+loud
+lounge
+love
+loyal
+lucky
+luggage
+lumber
+lunar
+lunch
+luxury
+lyrics
+machine
+mad
+magic
+magnet
+maid
+mail
+main
+major
+make
+mammal
+man
+manage
+mandate
+mango
+mansion
+manual
+maple
+marble
+march
+margin
+marine
+market
+marriage
+mask
+mass
+master
+match
+material
+math
+matrix
+matter
+maximum
+maze
+meadow
+mean
+measure
+meat
+mechanic
+medal
+media
+melody
+melt
+member
+memory
+mention
+menu
+mercy
+merge
+merit
+merry
+mesh
+message
+metal
+method
+middle
+midnight
+milk
+million
+mimic
+mind
+minimum
+minor
+minute
+miracle
+mirror
+misery
+miss
+mistake
+mix
+mixed
+mixture
+mobile
+model
+modify
+mom
+moment
+monitor
+monkey
+monster
+month
+moon
+moral
+more
+morning
+mosquito
+mother
+motion
+motor
+mountain
+mouse
+move
+movie
+much
+muffin
+mule
+multiply
+muscle
+museum
+mushroom
+music
+must
+mutual
+myself
+mystery
+myth
+naive
+name
+napkin
+narrow
+nasty
+nation
+nature
+near
+neck
+need
+negative
+neglect
+neither
+nephew
+nerve
+nest
+net
+network
+neutral
+never
+news
+next
+nice
+night
+noble
+noise
+nominee
+noodle
+normal
+north
+nose
+notable
+note
+nothing
+notice
+novel
+now
+nuclear
+number
+nurse
+nut
+oak
+obey
+object
+oblige
+obscure
+observe
+obtain
+obvious
+occur
+ocean
+october
+odor
+off
+offer
+office
+often
+oil
+okay
+old
+olive
+olympic
+omit
+once
+one
+onion
+online
+only
+open
+opera
+opinion
+oppose
+option
+orange
+orbit
+orchard
+order
+ordinary
+organ
+orient
+original
+orphan
+ostrich
+other
+outdoor
+outer
+output
+outside
+oval
+oven
+over
+own
+owner
+oxygen
+oyster
+ozone
+pact
+paddle
+page
+pair
+palace
+palm
+panda
+panel
+panic
+panther
+paper
+parade
+parent
+park
+parrot
+party
+pass
+patch
+path
+patient
+patrol
+pattern
+pause
+pave
+payment
+peace
+peanut
+pear
+peasant
+pelican
+pen
+penalty
+pencil
+people
+pepper
+perfect
+permit
+person
+pet
+phone
+photo
+phrase
+physical
+piano
+picnic
+picture
+piece
+pig
+pigeon
+pill
+pilot
+pink
+pioneer
+pipe
+pistol
+pitch
+pizza
+place
+planet
+plastic
+plate
+play
+please
+pledge
+pluck
+plug
+plunge
+poem
+poet
+point
+polar
+pole
+police
+pond
+pony
+pool
+popular
+portion
+position
+possible
+post
+potato
+pottery
+poverty
+powder
+power
+practice
+praise
+predict
+prefer
+prepare
+present
+pretty
+prevent
+price
+pride
+primary
+print
+priority
+prison
+private
+prize
+problem
+process
+produce
+profit
+program
+project
+promote
+proof
+property
+prosper
+protect
+proud
+provide
+public
+pudding
+pull
+pulp
+pulse
+pumpkin
+punch
+pupil
+puppy
+purchase
+purity
+purpose
+purse
+push
+put
+puzzle
+pyramid
+quality
+quantum
+quarter
+question
+quick
+quit
+quiz
+quote
+rabbit
+raccoon
+race
+rack
+radar
+radio
+rail
+rain
+raise
+rally
+ramp
+ranch
+random
+range
+rapid
+rare
+rate
+rather
+raven
+raw
+razor
+ready
+real
+reason
+rebel
+rebuild
+recall
+receive
+recipe
+record
+recycle
+reduce
+reflect
+reform
+refuse
+region
+regret
+regular
+reject
+relax
+release
+relief
+rely
+remain
+remember
+remind
+remove
+render
+renew
+rent
+reopen
+repair
+repeat
+replace
+report
+require
+rescue
+resemble
+resist
+resource
+response
+result
+retire
+retreat
+return
+reunion
+reveal
+review
+reward
+rhythm
+rib
+ribbon
+rice
+rich
+ride
+ridge
+rifle
+right
+rigid
+ring
+riot
+ripple
+risk
+ritual
+rival
+river
+road
+roast
+robot
+robust
+rocket
+romance
+roof
+rookie
+room
+rose
+rotate
+rough
+round
+route
+royal
+rubber
+rude
+rug
+rule
+run
+runway
+rural
+sad
+saddle
+sadness
+safe
+sail
+salad
+salmon
+salon
+salt
+salute
+same
+sample
+sand
+satisfy
+satoshi
+sauce
+sausage
+save
+say
+scale
+scan
+scare
+scatter
+scene
+scheme
+school
+science
+scissors
+scorpion
+scout
+scrap
+screen
+script
+scrub
+sea
+search
+season
+seat
+second
+secret
+section
+security
+seed
+seek
+segment
+select
+sell
+seminar
+senior
+sense
+sentence
+series
+service
+session
+settle
+setup
+seven
+shadow
+shaft
+shallow
+share
+shed
+shell
+sheriff
+shield
+shift
+shine
+ship
+shiver
+shock
+shoe
+shoot
+shop
+short
+shoulder
+shove
+shrimp
+shrug
+shuffle
+shy
+sibling
+sick
+side
+siege
+sight
+sign
+silent
+silk
+silly
+silver
+similar
+simple
+since
+sing
+siren
+sister
+situate
+six
+size
+skate
+sketch
+ski
+skill
+skin
+skirt
+skull
+slab
+slam
+sleep
+slender
+slice
+slide
+slight
+slim
+slogan
+slot
+slow
+slush
+small
+smart
+smile
+smoke
+smooth
+snack
+snake
+snap
+sniff
+snow
+soap
+soccer
+social
+sock
+soda
+soft
+solar
+soldier
+solid
+solution
+solve
+someone
+song
+soon
+sorry
+sort
+soul
+sound
+soup
+source
+south
+space
+spare
+spatial
+spawn
+speak
+special
+speed
+spell
+spend
+sphere
+spice
+spider
+spike
+spin
+spirit
+split
+spoil
+sponsor
+spoon
+sport
+spot
+spray
+spread
+spring
+spy
+square
+squeeze
+squirrel
+stable
+stadium
+staff
+stage
+stairs
+stamp
+stand
+start
+state
+stay
+steak
+steel
+stem
+step
+stereo
+stick
+still
+sting
+stock
+stomach
+stone
+stool
+story
+stove
+strategy
+street
+strike
+strong
+struggle
+student
+stuff
+stumble
+style
+subject
+submit
+subway
+success
+such
+sudden
+suffer
+sugar
+suggest
+suit
+summer
+sun
+sunny
+sunset
+super
+supply
+supreme
+sure
+surface
+surge
+surprise
+surround
+survey
+suspect
+sustain
+swallow
+swamp
+swap
+swarm
+swear
+sweet
+swift
+swim
+swing
+switch
+sword
+symbol
+symptom
+syrup
+system
+table
+tackle
+tag
+tail
+talent
+talk
+tank
+tape
+target
+task
+taste
+tattoo
+taxi
+teach
+team
+tell
+ten
+tenant
+tennis
+tent
+term
+test
+text
+thank
+that
+theme
+then
+theory
+there
+they
+thing
+this
+thought
+three
+thrive
+throw
+thumb
+thunder
+ticket
+tide
+tiger
+tilt
+timber
+time
+tiny
+tip
+tired
+tissue
+title
+toast
+tobacco
+today
+toddler
+toe
+together
+toilet
+token
+tomato
+tomorrow
+tone
+tongue
+tonight
+tool
+tooth
+top
+topic
+topple
+torch
+tornado
+tortoise
+toss
+total
+tourist
+toward
+tower
+town
+toy
+track
+trade
+traffic
+tragic
+train
+transfer
+trap
+trash
+travel
+tray
+treat
+tree
+trend
+trial
+tribe
+trick
+trigger
+trim
+trip
+trophy
+trouble
+truck
+true
+truly
+trumpet
+trust
+truth
+try
+tube
+tuition
+tumble
+tuna
+tunnel
+turkey
+turn
+turtle
+twelve
+twenty
+twice
+twin
+twist
+two
+type
+typical
+ugly
+umbrella
+unable
+unaware
+uncle
+uncover
+under
+undo
+unfair
+unfold
+unhappy
+uniform
+unique
+unit
+universe
+unknown
+unlock
+until
+unusual
+unveil
+update
+upgrade
+uphold
+upon
+upper
+upset
+urban
+urge
+usage
+use
+used
+useful
+useless
+usual
+utility
+vacant
+vacuum
+vague
+valid
+valley
+valve
+van
+vanish
+vapor
+various
+vast
+vault
+vehicle
+velvet
+vendor
+venture
+venue
+verb
+verify
+version
+very
+vessel
+veteran
+viable
+vibrant
+vicious
+victory
+video
+view
+village
+vintage
+violin
+virtual
+virus
+visa
+visit
+visual
+vital
+vivid
+vocal
+voice
+void
+volcano
+volume
+vote
+voyage
+wage
+wagon
+wait
+walk
+wall
+walnut
+want
+warfare
+warm
+warrior
+wash
+wasp
+waste
+water
+wave
+way
+wealth
+weapon
+wear
+weasel
+weather
+web
+wedding
+weekend
+weird
+welcome
+west
+wet
+whale
+what
+wheat
+wheel
+when
+where
+whip
+whisper
+wide
+width
+wife
+wild
+will
+win
+window
+wine
+wing
+wink
+winner
+winter
+wire
+wisdom
+wise
+wish
+witness
+wolf
+woman
+wonder
+wood
+wool
+word
+work
+world
+worry
+worth
+wrap
+wreck
+wrestle
+wrist
+write
+wrong
+yard
+year
+yellow
+you
+young
+youth
+zebra
+zero
+zone
+zoo
\ No newline at end of file
diff --git a/src/main/resources/french.txt b/src/main/resources/french.txt
new file mode 100644
index 000000000..1d749904f
--- /dev/null
+++ b/src/main/resources/french.txt
@@ -0,0 +1,2048 @@
+abaisser
+abandon
+abdiquer
+abeille
+abolir
+aborder
+aboutir
+aboyer
+abrasif
+abreuver
+abriter
+abroger
+abrupt
+absence
+absolu
+absurde
+abusif
+abyssal
+académie
+acajou
+acarien
+accabler
+accepter
+acclamer
+accolade
+accroche
+accuser
+acerbe
+achat
+acheter
+aciduler
+acier
+acompte
+acquérir
+acronyme
+acteur
+actif
+actuel
+adepte
+adéquat
+adhésif
+adjectif
+adjuger
+admettre
+admirer
+adopter
+adorer
+adoucir
+adresse
+adroit
+adulte
+adverbe
+aérer
+aéronef
+affaire
+affecter
+affiche
+affreux
+affubler
+agacer
+agencer
+agile
+agiter
+agrafer
+agréable
+agrume
+aider
+aiguille
+ailier
+aimable
+aisance
+ajouter
+ajuster
+alarmer
+alchimie
+alerte
+algèbre
+algue
+aliéner
+aliment
+alléger
+alliage
+allouer
+allumer
+alourdir
+alpaga
+altesse
+alvéole
+amateur
+ambigu
+ambre
+aménager
+amertume
+amidon
+amiral
+amorcer
+amour
+amovible
+amphibie
+ampleur
+amusant
+analyse
+anaphore
+anarchie
+anatomie
+ancien
+anéantir
+angle
+angoisse
+anguleux
+animal
+annexer
+annonce
+annuel
+anodin
+anomalie
+anonyme
+anormal
+antenne
+antidote
+anxieux
+apaiser
+apéritif
+aplanir
+apologie
+appareil
+appeler
+apporter
+appuyer
+aquarium
+aqueduc
+arbitre
+arbuste
+ardeur
+ardoise
+argent
+arlequin
+armature
+armement
+armoire
+armure
+arpenter
+arracher
+arriver
+arroser
+arsenic
+artériel
+article
+aspect
+asphalte
+aspirer
+assaut
+asservir
+assiette
+associer
+assurer
+asticot
+astre
+astuce
+atelier
+atome
+atrium
+atroce
+attaque
+attentif
+attirer
+attraper
+aubaine
+auberge
+audace
+audible
+augurer
+aurore
+automne
+autruche
+avaler
+avancer
+avarice
+avenir
+averse
+aveugle
+aviateur
+avide
+avion
+aviser
+avoine
+avouer
+avril
+axial
+axiome
+badge
+bafouer
+bagage
+baguette
+baignade
+balancer
+balcon
+baleine
+balisage
+bambin
+bancaire
+bandage
+banlieue
+bannière
+banquier
+barbier
+baril
+baron
+barque
+barrage
+bassin
+bastion
+bataille
+bateau
+batterie
+baudrier
+bavarder
+belette
+bélier
+belote
+bénéfice
+berceau
+berger
+berline
+bermuda
+besace
+besogne
+bétail
+beurre
+biberon
+bicycle
+bidule
+bijou
+bilan
+bilingue
+billard
+binaire
+biologie
+biopsie
+biotype
+biscuit
+bison
+bistouri
+bitume
+bizarre
+blafard
+blague
+blanchir
+blessant
+blinder
+blond
+bloquer
+blouson
+bobard
+bobine
+boire
+boiser
+bolide
+bonbon
+bondir
+bonheur
+bonifier
+bonus
+bordure
+borne
+botte
+boucle
+boueux
+bougie
+boulon
+bouquin
+bourse
+boussole
+boutique
+boxeur
+branche
+brasier
+brave
+brebis
+brèche
+breuvage
+bricoler
+brigade
+brillant
+brioche
+brique
+brochure
+broder
+bronzer
+brousse
+broyeur
+brume
+brusque
+brutal
+bruyant
+buffle
+buisson
+bulletin
+bureau
+burin
+bustier
+butiner
+butoir
+buvable
+buvette
+cabanon
+cabine
+cachette
+cadeau
+cadre
+caféine
+caillou
+caisson
+calculer
+calepin
+calibre
+calmer
+calomnie
+calvaire
+camarade
+caméra
+camion
+campagne
+canal
+caneton
+canon
+cantine
+canular
+capable
+caporal
+caprice
+capsule
+capter
+capuche
+carabine
+carbone
+caresser
+caribou
+carnage
+carotte
+carreau
+carton
+cascade
+casier
+casque
+cassure
+causer
+caution
+cavalier
+caverne
+caviar
+cédille
+ceinture
+céleste
+cellule
+cendrier
+censurer
+central
+cercle
+cérébral
+cerise
+cerner
+cerveau
+cesser
+chagrin
+chaise
+chaleur
+chambre
+chance
+chapitre
+charbon
+chasseur
+chaton
+chausson
+chavirer
+chemise
+chenille
+chéquier
+chercher
+cheval
+chien
+chiffre
+chignon
+chimère
+chiot
+chlorure
+chocolat
+choisir
+chose
+chouette
+chrome
+chute
+cigare
+cigogne
+cimenter
+cinéma
+cintrer
+circuler
+cirer
+cirque
+citerne
+citoyen
+citron
+civil
+clairon
+clameur
+claquer
+classe
+clavier
+client
+cligner
+climat
+clivage
+cloche
+clonage
+cloporte
+cobalt
+cobra
+cocasse
+cocotier
+coder
+codifier
+coffre
+cogner
+cohésion
+coiffer
+coincer
+colère
+colibri
+colline
+colmater
+colonel
+combat
+comédie
+commande
+compact
+concert
+conduire
+confier
+congeler
+connoter
+consonne
+contact
+convexe
+copain
+copie
+corail
+corbeau
+cordage
+corniche
+corpus
+correct
+cortège
+cosmique
+costume
+coton
+coude
+coupure
+courage
+couteau
+couvrir
+coyote
+crabe
+crainte
+cravate
+crayon
+créature
+créditer
+crémeux
+creuser
+crevette
+cribler
+crier
+cristal
+critère
+croire
+croquer
+crotale
+crucial
+cruel
+crypter
+cubique
+cueillir
+cuillère
+cuisine
+cuivre
+culminer
+cultiver
+cumuler
+cupide
+curatif
+curseur
+cyanure
+cycle
+cylindre
+cynique
+daigner
+damier
+danger
+danseur
+dauphin
+débattre
+débiter
+déborder
+débrider
+débutant
+décaler
+décembre
+déchirer
+décider
+déclarer
+décorer
+décrire
+décupler
+dédale
+déductif
+déesse
+défensif
+défiler
+défrayer
+dégager
+dégivrer
+déglutir
+dégrafer
+déjeuner
+délice
+déloger
+demander
+demeurer
+démolir
+dénicher
+dénouer
+dentelle
+dénuder
+départ
+dépenser
+déphaser
+déplacer
+déposer
+déranger
+dérober
+désastre
+descente
+désert
+désigner
+désobéir
+dessiner
+destrier
+détacher
+détester
+détourer
+détresse
+devancer
+devenir
+deviner
+devoir
+diable
+dialogue
+diamant
+dicter
+différer
+digérer
+digital
+digne
+diluer
+dimanche
+diminuer
+dioxyde
+directif
+diriger
+discuter
+disposer
+dissiper
+distance
+divertir
+diviser
+docile
+docteur
+dogme
+doigt
+domaine
+domicile
+dompter
+donateur
+donjon
+donner
+dopamine
+dortoir
+dorure
+dosage
+doseur
+dossier
+dotation
+douanier
+double
+douceur
+douter
+doyen
+dragon
+draper
+dresser
+dribbler
+droiture
+duperie
+duplexe
+durable
+durcir
+dynastie
+éblouir
+écarter
+écharpe
+échelle
+éclairer
+éclipse
+éclore
+écluse
+école
+économie
+écorce
+écouter
+écraser
+écrémer
+écrivain
+écrou
+écume
+écureuil
+édifier
+éduquer
+effacer
+effectif
+effigie
+effort
+effrayer
+effusion
+égaliser
+égarer
+éjecter
+élaborer
+élargir
+électron
+élégant
+éléphant
+élève
+éligible
+élitisme
+éloge
+élucider
+éluder
+emballer
+embellir
+embryon
+émeraude
+émission
+emmener
+émotion
+émouvoir
+empereur
+employer
+emporter
+emprise
+émulsion
+encadrer
+enchère
+enclave
+encoche
+endiguer
+endosser
+endroit
+enduire
+énergie
+enfance
+enfermer
+enfouir
+engager
+engin
+englober
+énigme
+enjamber
+enjeu
+enlever
+ennemi
+ennuyeux
+enrichir
+enrobage
+enseigne
+entasser
+entendre
+entier
+entourer
+entraver
+énumérer
+envahir
+enviable
+envoyer
+enzyme
+éolien
+épaissir
+épargne
+épatant
+épaule
+épicerie
+épidémie
+épier
+épilogue
+épine
+épisode
+épitaphe
+époque
+épreuve
+éprouver
+épuisant
+équerre
+équipe
+ériger
+érosion
+erreur
+éruption
+escalier
+espadon
+espèce
+espiègle
+espoir
+esprit
+esquiver
+essayer
+essence
+essieu
+essorer
+estime
+estomac
+estrade
+étagère
+étaler
+étanche
+étatique
+éteindre
+étendoir
+éternel
+éthanol
+éthique
+ethnie
+étirer
+étoffer
+étoile
+étonnant
+étourdir
+étrange
+étroit
+étude
+euphorie
+évaluer
+évasion
+éventail
+évidence
+éviter
+évolutif
+évoquer
+exact
+exagérer
+exaucer
+exceller
+excitant
+exclusif
+excuse
+exécuter
+exemple
+exercer
+exhaler
+exhorter
+exigence
+exiler
+exister
+exotique
+expédier
+explorer
+exposer
+exprimer
+exquis
+extensif
+extraire
+exulter
+fable
+fabuleux
+facette
+facile
+facture
+faiblir
+falaise
+fameux
+famille
+farceur
+farfelu
+farine
+farouche
+fasciner
+fatal
+fatigue
+faucon
+fautif
+faveur
+favori
+fébrile
+féconder
+fédérer
+félin
+femme
+fémur
+fendoir
+féodal
+fermer
+féroce
+ferveur
+festival
+feuille
+feutre
+février
+fiasco
+ficeler
+fictif
+fidèle
+figure
+filature
+filetage
+filière
+filleul
+filmer
+filou
+filtrer
+financer
+finir
+fiole
+firme
+fissure
+fixer
+flairer
+flamme
+flasque
+flatteur
+fléau
+flèche
+fleur
+flexion
+flocon
+flore
+fluctuer
+fluide
+fluvial
+folie
+fonderie
+fongible
+fontaine
+forcer
+forgeron
+formuler
+fortune
+fossile
+foudre
+fougère
+fouiller
+foulure
+fourmi
+fragile
+fraise
+franchir
+frapper
+frayeur
+frégate
+freiner
+frelon
+frémir
+frénésie
+frère
+friable
+friction
+frisson
+frivole
+froid
+fromage
+frontal
+frotter
+fruit
+fugitif
+fuite
+fureur
+furieux
+furtif
+fusion
+futur
+gagner
+galaxie
+galerie
+gambader
+garantir
+gardien
+garnir
+garrigue
+gazelle
+gazon
+géant
+gélatine
+gélule
+gendarme
+général
+génie
+genou
+gentil
+géologie
+géomètre
+géranium
+germe
+gestuel
+geyser
+gibier
+gicler
+girafe
+givre
+glace
+glaive
+glisser
+globe
+gloire
+glorieux
+golfeur
+gomme
+gonfler
+gorge
+gorille
+goudron
+gouffre
+goulot
+goupille
+gourmand
+goutte
+graduel
+graffiti
+graine
+grand
+grappin
+gratuit
+gravir
+grenat
+griffure
+griller
+grimper
+grogner
+gronder
+grotte
+groupe
+gruger
+grutier
+gruyère
+guépard
+guerrier
+guide
+guimauve
+guitare
+gustatif
+gymnaste
+gyrostat
+habitude
+hachoir
+halte
+hameau
+hangar
+hanneton
+haricot
+harmonie
+harpon
+hasard
+hélium
+hématome
+herbe
+hérisson
+hermine
+héron
+hésiter
+heureux
+hiberner
+hibou
+hilarant
+histoire
+hiver
+homard
+hommage
+homogène
+honneur
+honorer
+honteux
+horde
+horizon
+horloge
+hormone
+horrible
+houleux
+housse
+hublot
+huileux
+humain
+humble
+humide
+humour
+hurler
+hydromel
+hygiène
+hymne
+hypnose
+idylle
+ignorer
+iguane
+illicite
+illusion
+image
+imbiber
+imiter
+immense
+immobile
+immuable
+impact
+impérial
+implorer
+imposer
+imprimer
+imputer
+incarner
+incendie
+incident
+incliner
+incolore
+indexer
+indice
+inductif
+inédit
+ineptie
+inexact
+infini
+infliger
+informer
+infusion
+ingérer
+inhaler
+inhiber
+injecter
+injure
+innocent
+inoculer
+inonder
+inscrire
+insecte
+insigne
+insolite
+inspirer
+instinct
+insulter
+intact
+intense
+intime
+intrigue
+intuitif
+inutile
+invasion
+inventer
+inviter
+invoquer
+ironique
+irradier
+irréel
+irriter
+isoler
+ivoire
+ivresse
+jaguar
+jaillir
+jambe
+janvier
+jardin
+jauger
+jaune
+javelot
+jetable
+jeton
+jeudi
+jeunesse
+joindre
+joncher
+jongler
+joueur
+jouissif
+journal
+jovial
+joyau
+joyeux
+jubiler
+jugement
+junior
+jupon
+juriste
+justice
+juteux
+juvénile
+kayak
+kimono
+kiosque
+label
+labial
+labourer
+lacérer
+lactose
+lagune
+laine
+laisser
+laitier
+lambeau
+lamelle
+lampe
+lanceur
+langage
+lanterne
+lapin
+largeur
+larme
+laurier
+lavabo
+lavoir
+lecture
+légal
+léger
+légume
+lessive
+lettre
+levier
+lexique
+lézard
+liasse
+libérer
+libre
+licence
+licorne
+liège
+lièvre
+ligature
+ligoter
+ligue
+limer
+limite
+limonade
+limpide
+linéaire
+lingot
+lionceau
+liquide
+lisière
+lister
+lithium
+litige
+littoral
+livreur
+logique
+lointain
+loisir
+lombric
+loterie
+louer
+lourd
+loutre
+louve
+loyal
+lubie
+lucide
+lucratif
+lueur
+lugubre
+luisant
+lumière
+lunaire
+lundi
+luron
+lutter
+luxueux
+machine
+magasin
+magenta
+magique
+maigre
+maillon
+maintien
+mairie
+maison
+majorer
+malaxer
+maléfice
+malheur
+malice
+mallette
+mammouth
+mandater
+maniable
+manquant
+manteau
+manuel
+marathon
+marbre
+marchand
+mardi
+maritime
+marqueur
+marron
+marteler
+mascotte
+massif
+matériel
+matière
+matraque
+maudire
+maussade
+mauve
+maximal
+méchant
+méconnu
+médaille
+médecin
+méditer
+méduse
+meilleur
+mélange
+mélodie
+membre
+mémoire
+menacer
+mener
+menhir
+mensonge
+mentor
+mercredi
+mérite
+merle
+messager
+mesure
+métal
+météore
+méthode
+métier
+meuble
+miauler
+microbe
+miette
+mignon
+migrer
+milieu
+million
+mimique
+mince
+minéral
+minimal
+minorer
+minute
+miracle
+miroiter
+missile
+mixte
+mobile
+moderne
+moelleux
+mondial
+moniteur
+monnaie
+monotone
+monstre
+montagne
+monument
+moqueur
+morceau
+morsure
+mortier
+moteur
+motif
+mouche
+moufle
+moulin
+mousson
+mouton
+mouvant
+multiple
+munition
+muraille
+murène
+murmure
+muscle
+muséum
+musicien
+mutation
+muter
+mutuel
+myriade
+myrtille
+mystère
+mythique
+nageur
+nappe
+narquois
+narrer
+natation
+nation
+nature
+naufrage
+nautique
+navire
+nébuleux
+nectar
+néfaste
+négation
+négliger
+négocier
+neige
+nerveux
+nettoyer
+neurone
+neutron
+neveu
+niche
+nickel
+nitrate
+niveau
+noble
+nocif
+nocturne
+noirceur
+noisette
+nomade
+nombreux
+nommer
+normatif
+notable
+notifier
+notoire
+nourrir
+nouveau
+novateur
+novembre
+novice
+nuage
+nuancer
+nuire
+nuisible
+numéro
+nuptial
+nuque
+nutritif
+obéir
+objectif
+obliger
+obscur
+observer
+obstacle
+obtenir
+obturer
+occasion
+occuper
+océan
+octobre
+octroyer
+octupler
+oculaire
+odeur
+odorant
+offenser
+officier
+offrir
+ogive
+oiseau
+oisillon
+olfactif
+olivier
+ombrage
+omettre
+onctueux
+onduler
+onéreux
+onirique
+opale
+opaque
+opérer
+opinion
+opportun
+opprimer
+opter
+optique
+orageux
+orange
+orbite
+ordonner
+oreille
+organe
+orgueil
+orifice
+ornement
+orque
+ortie
+osciller
+osmose
+ossature
+otarie
+ouragan
+ourson
+outil
+outrager
+ouvrage
+ovation
+oxyde
+oxygène
+ozone
+paisible
+palace
+palmarès
+palourde
+palper
+panache
+panda
+pangolin
+paniquer
+panneau
+panorama
+pantalon
+papaye
+papier
+papoter
+papyrus
+paradoxe
+parcelle
+paresse
+parfumer
+parler
+parole
+parrain
+parsemer
+partager
+parure
+parvenir
+passion
+pastèque
+paternel
+patience
+patron
+pavillon
+pavoiser
+payer
+paysage
+peigne
+peintre
+pelage
+pélican
+pelle
+pelouse
+peluche
+pendule
+pénétrer
+pénible
+pensif
+pénurie
+pépite
+péplum
+perdrix
+perforer
+période
+permuter
+perplexe
+persil
+perte
+peser
+pétale
+petit
+pétrir
+peuple
+pharaon
+phobie
+phoque
+photon
+phrase
+physique
+piano
+pictural
+pièce
+pierre
+pieuvre
+pilote
+pinceau
+pipette
+piquer
+pirogue
+piscine
+piston
+pivoter
+pixel
+pizza
+placard
+plafond
+plaisir
+planer
+plaque
+plastron
+plateau
+pleurer
+plexus
+pliage
+plomb
+plonger
+pluie
+plumage
+pochette
+poésie
+poète
+pointe
+poirier
+poisson
+poivre
+polaire
+policier
+pollen
+polygone
+pommade
+pompier
+ponctuel
+pondérer
+poney
+portique
+position
+posséder
+posture
+potager
+poteau
+potion
+pouce
+poulain
+poumon
+pourpre
+poussin
+pouvoir
+prairie
+pratique
+précieux
+prédire
+préfixe
+prélude
+prénom
+présence
+prétexte
+prévoir
+primitif
+prince
+prison
+priver
+problème
+procéder
+prodige
+profond
+progrès
+proie
+projeter
+prologue
+promener
+propre
+prospère
+protéger
+prouesse
+proverbe
+prudence
+pruneau
+psychose
+public
+puceron
+puiser
+pulpe
+pulsar
+punaise
+punitif
+pupitre
+purifier
+puzzle
+pyramide
+quasar
+querelle
+question
+quiétude
+quitter
+quotient
+racine
+raconter
+radieux
+ragondin
+raideur
+raisin
+ralentir
+rallonge
+ramasser
+rapide
+rasage
+ratisser
+ravager
+ravin
+rayonner
+réactif
+réagir
+réaliser
+réanimer
+recevoir
+réciter
+réclamer
+récolter
+recruter
+reculer
+recycler
+rédiger
+redouter
+refaire
+réflexe
+réformer
+refrain
+refuge
+régalien
+région
+réglage
+régulier
+réitérer
+rejeter
+rejouer
+relatif
+relever
+relief
+remarque
+remède
+remise
+remonter
+remplir
+remuer
+renard
+renfort
+renifler
+renoncer
+rentrer
+renvoi
+replier
+reporter
+reprise
+reptile
+requin
+réserve
+résineux
+résoudre
+respect
+rester
+résultat
+rétablir
+retenir
+réticule
+retomber
+retracer
+réunion
+réussir
+revanche
+revivre
+révolte
+révulsif
+richesse
+rideau
+rieur
+rigide
+rigoler
+rincer
+riposter
+risible
+risque
+rituel
+rival
+rivière
+rocheux
+romance
+rompre
+ronce
+rondin
+roseau
+rosier
+rotatif
+rotor
+rotule
+rouge
+rouille
+rouleau
+routine
+royaume
+ruban
+rubis
+ruche
+ruelle
+rugueux
+ruiner
+ruisseau
+ruser
+rustique
+rythme
+sabler
+saboter
+sabre
+sacoche
+safari
+sagesse
+saisir
+salade
+salive
+salon
+saluer
+samedi
+sanction
+sanglier
+sarcasme
+sardine
+saturer
+saugrenu
+saumon
+sauter
+sauvage
+savant
+savonner
+scalpel
+scandale
+scélérat
+scénario
+sceptre
+schéma
+science
+scinder
+score
+scrutin
+sculpter
+séance
+sécable
+sécher
+secouer
+sécréter
+sédatif
+séduire
+seigneur
+séjour
+sélectif
+semaine
+sembler
+semence
+séminal
+sénateur
+sensible
+sentence
+séparer
+séquence
+serein
+sergent
+sérieux
+serrure
+sérum
+service
+sésame
+sévir
+sevrage
+sextuple
+sidéral
+siècle
+siéger
+siffler
+sigle
+signal
+silence
+silicium
+simple
+sincère
+sinistre
+siphon
+sirop
+sismique
+situer
+skier
+social
+socle
+sodium
+soigneux
+soldat
+soleil
+solitude
+soluble
+sombre
+sommeil
+somnoler
+sonde
+songeur
+sonnette
+sonore
+sorcier
+sortir
+sosie
+sottise
+soucieux
+soudure
+souffle
+soulever
+soupape
+source
+soutirer
+souvenir
+spacieux
+spatial
+spécial
+sphère
+spiral
+stable
+station
+sternum
+stimulus
+stipuler
+strict
+studieux
+stupeur
+styliste
+sublime
+substrat
+subtil
+subvenir
+succès
+sucre
+suffixe
+suggérer
+suiveur
+sulfate
+superbe
+supplier
+surface
+suricate
+surmener
+surprise
+sursaut
+survie
+suspect
+syllabe
+symbole
+symétrie
+synapse
+syntaxe
+système
+tabac
+tablier
+tactile
+tailler
+talent
+talisman
+talonner
+tambour
+tamiser
+tangible
+tapis
+taquiner
+tarder
+tarif
+tartine
+tasse
+tatami
+tatouage
+taupe
+taureau
+taxer
+témoin
+temporel
+tenaille
+tendre
+teneur
+tenir
+tension
+terminer
+terne
+terrible
+tétine
+texte
+thème
+théorie
+thérapie
+thorax
+tibia
+tiède
+timide
+tirelire
+tiroir
+tissu
+titane
+titre
+tituber
+toboggan
+tolérant
+tomate
+tonique
+tonneau
+toponyme
+torche
+tordre
+tornade
+torpille
+torrent
+torse
+tortue
+totem
+toucher
+tournage
+tousser
+toxine
+traction
+trafic
+tragique
+trahir
+train
+trancher
+travail
+trèfle
+tremper
+trésor
+treuil
+triage
+tribunal
+tricoter
+trilogie
+triomphe
+tripler
+triturer
+trivial
+trombone
+tronc
+tropical
+troupeau
+tuile
+tulipe
+tumulte
+tunnel
+turbine
+tuteur
+tutoyer
+tuyau
+tympan
+typhon
+typique
+tyran
+ubuesque
+ultime
+ultrason
+unanime
+unifier
+union
+unique
+unitaire
+univers
+uranium
+urbain
+urticant
+usage
+usine
+usuel
+usure
+utile
+utopie
+vacarme
+vaccin
+vagabond
+vague
+vaillant
+vaincre
+vaisseau
+valable
+valise
+vallon
+valve
+vampire
+vanille
+vapeur
+varier
+vaseux
+vassal
+vaste
+vecteur
+vedette
+végétal
+véhicule
+veinard
+véloce
+vendredi
+vénérer
+venger
+venimeux
+ventouse
+verdure
+vérin
+vernir
+verrou
+verser
+vertu
+veston
+vétéran
+vétuste
+vexant
+vexer
+viaduc
+viande
+victoire
+vidange
+vidéo
+vignette
+vigueur
+vilain
+village
+vinaigre
+violon
+vipère
+virement
+virtuose
+virus
+visage
+viseur
+vision
+visqueux
+visuel
+vital
+vitesse
+viticole
+vitrine
+vivace
+vivipare
+vocation
+voguer
+voile
+voisin
+voiture
+volaille
+volcan
+voltiger
+volume
+vorace
+vortex
+voter
+vouloir
+voyage
+voyelle
+wagon
+xénon
+yacht
+zèbre
+zénith
+zeste
+zoologie
diff --git a/src/main/resources/portuguese.txt b/src/main/resources/portuguese.txt
new file mode 100644
index 000000000..4a8910550
--- /dev/null
+++ b/src/main/resources/portuguese.txt
@@ -0,0 +1,2048 @@
+abacate
+abaixo
+abalar
+abater
+abduzir
+abelha
+aberto
+abismo
+abotoar
+abranger
+abreviar
+abrigar
+abrupto
+absinto
+absoluto
+absurdo
+abutre
+acabado
+acalmar
+acampar
+acanhar
+acaso
+aceitar
+acelerar
+acenar
+acervo
+acessar
+acetona
+achatar
+acidez
+acima
+acionado
+acirrar
+aclamar
+aclive
+acolhida
+acomodar
+acoplar
+acordar
+acumular
+acusador
+adaptar
+adega
+adentro
+adepto
+adequar
+aderente
+adesivo
+adeus
+adiante
+aditivo
+adjetivo
+adjunto
+admirar
+adorar
+adquirir
+adubo
+adverso
+advogado
+aeronave
+afastar
+aferir
+afetivo
+afinador
+afivelar
+aflito
+afluente
+afrontar
+agachar
+agarrar
+agasalho
+agenciar
+agilizar
+agiota
+agitado
+agora
+agradar
+agreste
+agrupar
+aguardar
+agulha
+ajoelhar
+ajudar
+ajustar
+alameda
+alarme
+alastrar
+alavanca
+albergue
+albino
+alcatra
+aldeia
+alecrim
+alegria
+alertar
+alface
+alfinete
+algum
+alheio
+aliar
+alicate
+alienar
+alinhar
+aliviar
+almofada
+alocar
+alpiste
+alterar
+altitude
+alucinar
+alugar
+aluno
+alusivo
+alvo
+amaciar
+amador
+amarelo
+amassar
+ambas
+ambiente
+ameixa
+amenizar
+amido
+amistoso
+amizade
+amolador
+amontoar
+amoroso
+amostra
+amparar
+ampliar
+ampola
+anagrama
+analisar
+anarquia
+anatomia
+andaime
+anel
+anexo
+angular
+animar
+anjo
+anomalia
+anotado
+ansioso
+anterior
+anuidade
+anunciar
+anzol
+apagador
+apalpar
+apanhado
+apego
+apelido
+apertada
+apesar
+apetite
+apito
+aplauso
+aplicada
+apoio
+apontar
+aposta
+aprendiz
+aprovar
+aquecer
+arame
+aranha
+arara
+arcada
+ardente
+areia
+arejar
+arenito
+aresta
+argiloso
+argola
+arma
+arquivo
+arraial
+arrebate
+arriscar
+arroba
+arrumar
+arsenal
+arterial
+artigo
+arvoredo
+asfaltar
+asilado
+aspirar
+assador
+assinar
+assoalho
+assunto
+astral
+atacado
+atadura
+atalho
+atarefar
+atear
+atender
+aterro
+ateu
+atingir
+atirador
+ativo
+atoleiro
+atracar
+atrevido
+atriz
+atual
+atum
+auditor
+aumentar
+aura
+aurora
+autismo
+autoria
+autuar
+avaliar
+avante
+avaria
+avental
+avesso
+aviador
+avisar
+avulso
+axila
+azarar
+azedo
+azeite
+azulejo
+babar
+babosa
+bacalhau
+bacharel
+bacia
+bagagem
+baiano
+bailar
+baioneta
+bairro
+baixista
+bajular
+baleia
+baliza
+balsa
+banal
+bandeira
+banho
+banir
+banquete
+barato
+barbado
+baronesa
+barraca
+barulho
+baseado
+bastante
+batata
+batedor
+batida
+batom
+batucar
+baunilha
+beber
+beijo
+beirada
+beisebol
+beldade
+beleza
+belga
+beliscar
+bendito
+bengala
+benzer
+berimbau
+berlinda
+berro
+besouro
+bexiga
+bezerro
+bico
+bicudo
+bienal
+bifocal
+bifurcar
+bigorna
+bilhete
+bimestre
+bimotor
+biologia
+biombo
+biosfera
+bipolar
+birrento
+biscoito
+bisneto
+bispo
+bissexto
+bitola
+bizarro
+blindado
+bloco
+bloquear
+boato
+bobagem
+bocado
+bocejo
+bochecha
+boicotar
+bolada
+boletim
+bolha
+bolo
+bombeiro
+bonde
+boneco
+bonita
+borbulha
+borda
+boreal
+borracha
+bovino
+boxeador
+branco
+brasa
+braveza
+breu
+briga
+brilho
+brincar
+broa
+brochura
+bronzear
+broto
+bruxo
+bucha
+budismo
+bufar
+bule
+buraco
+busca
+busto
+buzina
+cabana
+cabelo
+cabide
+cabo
+cabrito
+cacau
+cacetada
+cachorro
+cacique
+cadastro
+cadeado
+cafezal
+caiaque
+caipira
+caixote
+cajado
+caju
+calafrio
+calcular
+caldeira
+calibrar
+calmante
+calota
+camada
+cambista
+camisa
+camomila
+campanha
+camuflar
+canavial
+cancelar
+caneta
+canguru
+canhoto
+canivete
+canoa
+cansado
+cantar
+canudo
+capacho
+capela
+capinar
+capotar
+capricho
+captador
+capuz
+caracol
+carbono
+cardeal
+careca
+carimbar
+carneiro
+carpete
+carreira
+cartaz
+carvalho
+casaco
+casca
+casebre
+castelo
+casulo
+catarata
+cativar
+caule
+causador
+cautelar
+cavalo
+caverna
+cebola
+cedilha
+cegonha
+celebrar
+celular
+cenoura
+censo
+centeio
+cercar
+cerrado
+certeiro
+cerveja
+cetim
+cevada
+chacota
+chaleira
+chamado
+chapada
+charme
+chatice
+chave
+chefe
+chegada
+cheiro
+cheque
+chicote
+chifre
+chinelo
+chocalho
+chover
+chumbo
+chutar
+chuva
+cicatriz
+ciclone
+cidade
+cidreira
+ciente
+cigana
+cimento
+cinto
+cinza
+ciranda
+circuito
+cirurgia
+citar
+clareza
+clero
+clicar
+clone
+clube
+coado
+coagir
+cobaia
+cobertor
+cobrar
+cocada
+coelho
+coentro
+coeso
+cogumelo
+coibir
+coifa
+coiote
+colar
+coleira
+colher
+colidir
+colmeia
+colono
+coluna
+comando
+combinar
+comentar
+comitiva
+comover
+complexo
+comum
+concha
+condor
+conectar
+confuso
+congelar
+conhecer
+conjugar
+consumir
+contrato
+convite
+cooperar
+copeiro
+copiador
+copo
+coquetel
+coragem
+cordial
+corneta
+coronha
+corporal
+correio
+cortejo
+coruja
+corvo
+cosseno
+costela
+cotonete
+couro
+couve
+covil
+cozinha
+cratera
+cravo
+creche
+credor
+creme
+crer
+crespo
+criada
+criminal
+crioulo
+crise
+criticar
+crosta
+crua
+cruzeiro
+cubano
+cueca
+cuidado
+cujo
+culatra
+culminar
+culpar
+cultura
+cumprir
+cunhado
+cupido
+curativo
+curral
+cursar
+curto
+cuspir
+custear
+cutelo
+damasco
+datar
+debater
+debitar
+deboche
+debulhar
+decalque
+decimal
+declive
+decote
+decretar
+dedal
+dedicado
+deduzir
+defesa
+defumar
+degelo
+degrau
+degustar
+deitado
+deixar
+delator
+delegado
+delinear
+delonga
+demanda
+demitir
+demolido
+dentista
+depenado
+depilar
+depois
+depressa
+depurar
+deriva
+derramar
+desafio
+desbotar
+descanso
+desenho
+desfiado
+desgaste
+desigual
+deslize
+desmamar
+desova
+despesa
+destaque
+desviar
+detalhar
+detentor
+detonar
+detrito
+deusa
+dever
+devido
+devotado
+dezena
+diagrama
+dialeto
+didata
+difuso
+digitar
+dilatado
+diluente
+diminuir
+dinastia
+dinheiro
+diocese
+direto
+discreta
+disfarce
+disparo
+disquete
+dissipar
+distante
+ditador
+diurno
+diverso
+divisor
+divulgar
+dizer
+dobrador
+dolorido
+domador
+dominado
+donativo
+donzela
+dormente
+dorsal
+dosagem
+dourado
+doutor
+drenagem
+drible
+drogaria
+duelar
+duende
+dueto
+duplo
+duquesa
+durante
+duvidoso
+eclodir
+ecoar
+ecologia
+edificar
+edital
+educado
+efeito
+efetivar
+ejetar
+elaborar
+eleger
+eleitor
+elenco
+elevador
+eliminar
+elogiar
+embargo
+embolado
+embrulho
+embutido
+emenda
+emergir
+emissor
+empatia
+empenho
+empinado
+empolgar
+emprego
+empurrar
+emulador
+encaixe
+encenado
+enchente
+encontro
+endeusar
+endossar
+enfaixar
+enfeite
+enfim
+engajado
+engenho
+englobar
+engomado
+engraxar
+enguia
+enjoar
+enlatar
+enquanto
+enraizar
+enrolado
+enrugar
+ensaio
+enseada
+ensino
+ensopado
+entanto
+enteado
+entidade
+entortar
+entrada
+entulho
+envergar
+enviado
+envolver
+enxame
+enxerto
+enxofre
+enxuto
+epiderme
+equipar
+ereto
+erguido
+errata
+erva
+ervilha
+esbanjar
+esbelto
+escama
+escola
+escrita
+escuta
+esfinge
+esfolar
+esfregar
+esfumado
+esgrima
+esmalte
+espanto
+espelho
+espiga
+esponja
+espreita
+espumar
+esquerda
+estaca
+esteira
+esticar
+estofado
+estrela
+estudo
+esvaziar
+etanol
+etiqueta
+euforia
+europeu
+evacuar
+evaporar
+evasivo
+eventual
+evidente
+evoluir
+exagero
+exalar
+examinar
+exato
+exausto
+excesso
+excitar
+exclamar
+executar
+exemplo
+exibir
+exigente
+exonerar
+expandir
+expelir
+expirar
+explanar
+exposto
+expresso
+expulsar
+externo
+extinto
+extrato
+fabricar
+fabuloso
+faceta
+facial
+fada
+fadiga
+faixa
+falar
+falta
+familiar
+fandango
+fanfarra
+fantoche
+fardado
+farelo
+farinha
+farofa
+farpa
+fartura
+fatia
+fator
+favorita
+faxina
+fazenda
+fechado
+feijoada
+feirante
+felino
+feminino
+fenda
+feno
+fera
+feriado
+ferrugem
+ferver
+festejar
+fetal
+feudal
+fiapo
+fibrose
+ficar
+ficheiro
+figurado
+fileira
+filho
+filme
+filtrar
+firmeza
+fisgada
+fissura
+fita
+fivela
+fixador
+fixo
+flacidez
+flamingo
+flanela
+flechada
+flora
+flutuar
+fluxo
+focal
+focinho
+fofocar
+fogo
+foguete
+foice
+folgado
+folheto
+forjar
+formiga
+forno
+forte
+fosco
+fossa
+fragata
+fralda
+frango
+frasco
+fraterno
+freira
+frente
+fretar
+frieza
+friso
+fritura
+fronha
+frustrar
+fruteira
+fugir
+fulano
+fuligem
+fundar
+fungo
+funil
+furador
+furioso
+futebol
+gabarito
+gabinete
+gado
+gaiato
+gaiola
+gaivota
+galega
+galho
+galinha
+galocha
+ganhar
+garagem
+garfo
+gargalo
+garimpo
+garoupa
+garrafa
+gasoduto
+gasto
+gata
+gatilho
+gaveta
+gazela
+gelado
+geleia
+gelo
+gemada
+gemer
+gemido
+generoso
+gengiva
+genial
+genoma
+genro
+geologia
+gerador
+germinar
+gesso
+gestor
+ginasta
+gincana
+gingado
+girafa
+girino
+glacial
+glicose
+global
+glorioso
+goela
+goiaba
+golfe
+golpear
+gordura
+gorjeta
+gorro
+gostoso
+goteira
+governar
+gracejo
+gradual
+grafite
+gralha
+grampo
+granada
+gratuito
+graveto
+graxa
+grego
+grelhar
+greve
+grilo
+grisalho
+gritaria
+grosso
+grotesco
+grudado
+grunhido
+gruta
+guache
+guarani
+guaxinim
+guerrear
+guiar
+guincho
+guisado
+gula
+guloso
+guru
+habitar
+harmonia
+haste
+haver
+hectare
+herdar
+heresia
+hesitar
+hiato
+hibernar
+hidratar
+hiena
+hino
+hipismo
+hipnose
+hipoteca
+hoje
+holofote
+homem
+honesto
+honrado
+hormonal
+hospedar
+humorado
+iate
+ideia
+idoso
+ignorado
+igreja
+iguana
+ileso
+ilha
+iludido
+iluminar
+ilustrar
+imagem
+imediato
+imenso
+imersivo
+iminente
+imitador
+imortal
+impacto
+impedir
+implante
+impor
+imprensa
+impune
+imunizar
+inalador
+inapto
+inativo
+incenso
+inchar
+incidir
+incluir
+incolor
+indeciso
+indireto
+indutor
+ineficaz
+inerente
+infantil
+infestar
+infinito
+inflamar
+informal
+infrator
+ingerir
+inibido
+inicial
+inimigo
+injetar
+inocente
+inodoro
+inovador
+inox
+inquieto
+inscrito
+inseto
+insistir
+inspetor
+instalar
+insulto
+intacto
+integral
+intimar
+intocado
+intriga
+invasor
+inverno
+invicto
+invocar
+iogurte
+iraniano
+ironizar
+irreal
+irritado
+isca
+isento
+isolado
+isqueiro
+italiano
+janeiro
+jangada
+janta
+jararaca
+jardim
+jarro
+jasmim
+jato
+javali
+jazida
+jejum
+joaninha
+joelhada
+jogador
+joia
+jornal
+jorrar
+jovem
+juba
+judeu
+judoca
+juiz
+julgador
+julho
+jurado
+jurista
+juro
+justa
+labareda
+laboral
+lacre
+lactante
+ladrilho
+lagarta
+lagoa
+laje
+lamber
+lamentar
+laminar
+lampejo
+lanche
+lapidar
+lapso
+laranja
+lareira
+largura
+lasanha
+lastro
+lateral
+latido
+lavanda
+lavoura
+lavrador
+laxante
+lazer
+lealdade
+lebre
+legado
+legendar
+legista
+leigo
+leiloar
+leitura
+lembrete
+leme
+lenhador
+lentilha
+leoa
+lesma
+leste
+letivo
+letreiro
+levar
+leveza
+levitar
+liberal
+libido
+liderar
+ligar
+ligeiro
+limitar
+limoeiro
+limpador
+linda
+linear
+linhagem
+liquidez
+listagem
+lisura
+litoral
+livro
+lixa
+lixeira
+locador
+locutor
+lojista
+lombo
+lona
+longe
+lontra
+lorde
+lotado
+loteria
+loucura
+lousa
+louvar
+luar
+lucidez
+lucro
+luneta
+lustre
+lutador
+luva
+macaco
+macete
+machado
+macio
+madeira
+madrinha
+magnata
+magreza
+maior
+mais
+malandro
+malha
+malote
+maluco
+mamilo
+mamoeiro
+mamute
+manada
+mancha
+mandato
+manequim
+manhoso
+manivela
+manobrar
+mansa
+manter
+manusear
+mapeado
+maquinar
+marcador
+maresia
+marfim
+margem
+marinho
+marmita
+maroto
+marquise
+marreco
+martelo
+marujo
+mascote
+masmorra
+massagem
+mastigar
+matagal
+materno
+matinal
+matutar
+maxilar
+medalha
+medida
+medusa
+megafone
+meiga
+melancia
+melhor
+membro
+memorial
+menino
+menos
+mensagem
+mental
+merecer
+mergulho
+mesada
+mesclar
+mesmo
+mesquita
+mestre
+metade
+meteoro
+metragem
+mexer
+mexicano
+micro
+migalha
+migrar
+milagre
+milenar
+milhar
+mimado
+minerar
+minhoca
+ministro
+minoria
+miolo
+mirante
+mirtilo
+misturar
+mocidade
+moderno
+modular
+moeda
+moer
+moinho
+moita
+moldura
+moleza
+molho
+molinete
+molusco
+montanha
+moqueca
+morango
+morcego
+mordomo
+morena
+mosaico
+mosquete
+mostarda
+motel
+motim
+moto
+motriz
+muda
+muito
+mulata
+mulher
+multar
+mundial
+munido
+muralha
+murcho
+muscular
+museu
+musical
+nacional
+nadador
+naja
+namoro
+narina
+narrado
+nascer
+nativa
+natureza
+navalha
+navegar
+navio
+neblina
+nebuloso
+negativa
+negociar
+negrito
+nervoso
+neta
+neural
+nevasca
+nevoeiro
+ninar
+ninho
+nitidez
+nivelar
+nobreza
+noite
+noiva
+nomear
+nominal
+nordeste
+nortear
+notar
+noticiar
+noturno
+novelo
+novilho
+novo
+nublado
+nudez
+numeral
+nupcial
+nutrir
+nuvem
+obcecado
+obedecer
+objetivo
+obrigado
+obscuro
+obstetra
+obter
+obturar
+ocidente
+ocioso
+ocorrer
+oculista
+ocupado
+ofegante
+ofensiva
+oferenda
+oficina
+ofuscado
+ogiva
+olaria
+oleoso
+olhar
+oliveira
+ombro
+omelete
+omisso
+omitir
+ondulado
+oneroso
+ontem
+opcional
+operador
+oponente
+oportuno
+oposto
+orar
+orbitar
+ordem
+ordinal
+orfanato
+orgasmo
+orgulho
+oriental
+origem
+oriundo
+orla
+ortodoxo
+orvalho
+oscilar
+ossada
+osso
+ostentar
+otimismo
+ousadia
+outono
+outubro
+ouvido
+ovelha
+ovular
+oxidar
+oxigenar
+pacato
+paciente
+pacote
+pactuar
+padaria
+padrinho
+pagar
+pagode
+painel
+pairar
+paisagem
+palavra
+palestra
+palheta
+palito
+palmada
+palpitar
+pancada
+panela
+panfleto
+panqueca
+pantanal
+papagaio
+papelada
+papiro
+parafina
+parcial
+pardal
+parede
+partida
+pasmo
+passado
+pastel
+patamar
+patente
+patinar
+patrono
+paulada
+pausar
+peculiar
+pedalar
+pedestre
+pediatra
+pedra
+pegada
+peitoral
+peixe
+pele
+pelicano
+penca
+pendurar
+peneira
+penhasco
+pensador
+pente
+perceber
+perfeito
+pergunta
+perito
+permitir
+perna
+perplexo
+persiana
+pertence
+peruca
+pescado
+pesquisa
+pessoa
+petiscar
+piada
+picado
+piedade
+pigmento
+pilastra
+pilhado
+pilotar
+pimenta
+pincel
+pinguim
+pinha
+pinote
+pintar
+pioneiro
+pipoca
+piquete
+piranha
+pires
+pirueta
+piscar
+pistola
+pitanga
+pivete
+planta
+plaqueta
+platina
+plebeu
+plumagem
+pluvial
+pneu
+poda
+poeira
+poetisa
+polegada
+policiar
+poluente
+polvilho
+pomar
+pomba
+ponderar
+pontaria
+populoso
+porta
+possuir
+postal
+pote
+poupar
+pouso
+povoar
+praia
+prancha
+prato
+praxe
+prece
+predador
+prefeito
+premiar
+prensar
+preparar
+presilha
+pretexto
+prevenir
+prezar
+primata
+princesa
+prisma
+privado
+processo
+produto
+profeta
+proibido
+projeto
+prometer
+propagar
+prosa
+protetor
+provador
+publicar
+pudim
+pular
+pulmonar
+pulseira
+punhal
+punir
+pupilo
+pureza
+puxador
+quadra
+quantia
+quarto
+quase
+quebrar
+queda
+queijo
+quente
+querido
+quimono
+quina
+quiosque
+rabanada
+rabisco
+rachar
+racionar
+radial
+raiar
+rainha
+raio
+raiva
+rajada
+ralado
+ramal
+ranger
+ranhura
+rapadura
+rapel
+rapidez
+raposa
+raquete
+raridade
+rasante
+rascunho
+rasgar
+raspador
+rasteira
+rasurar
+ratazana
+ratoeira
+realeza
+reanimar
+reaver
+rebaixar
+rebelde
+rebolar
+recado
+recente
+recheio
+recibo
+recordar
+recrutar
+recuar
+rede
+redimir
+redonda
+reduzida
+reenvio
+refinar
+refletir
+refogar
+refresco
+refugiar
+regalia
+regime
+regra
+reinado
+reitor
+rejeitar
+relativo
+remador
+remendo
+remorso
+renovado
+reparo
+repelir
+repleto
+repolho
+represa
+repudiar
+requerer
+resenha
+resfriar
+resgatar
+residir
+resolver
+respeito
+ressaca
+restante
+resumir
+retalho
+reter
+retirar
+retomada
+retratar
+revelar
+revisor
+revolta
+riacho
+rica
+rigidez
+rigoroso
+rimar
+ringue
+risada
+risco
+risonho
+robalo
+rochedo
+rodada
+rodeio
+rodovia
+roedor
+roleta
+romano
+roncar
+rosado
+roseira
+rosto
+rota
+roteiro
+rotina
+rotular
+rouco
+roupa
+roxo
+rubro
+rugido
+rugoso
+ruivo
+rumo
+rupestre
+russo
+sabor
+saciar
+sacola
+sacudir
+sadio
+safira
+saga
+sagrada
+saibro
+salada
+saleiro
+salgado
+saliva
+salpicar
+salsicha
+saltar
+salvador
+sambar
+samurai
+sanar
+sanfona
+sangue
+sanidade
+sapato
+sarda
+sargento
+sarjeta
+saturar
+saudade
+saxofone
+sazonal
+secar
+secular
+seda
+sedento
+sediado
+sedoso
+sedutor
+segmento
+segredo
+segundo
+seiva
+seleto
+selvagem
+semanal
+semente
+senador
+senhor
+sensual
+sentado
+separado
+sereia
+seringa
+serra
+servo
+setembro
+setor
+sigilo
+silhueta
+silicone
+simetria
+simpatia
+simular
+sinal
+sincero
+singular
+sinopse
+sintonia
+sirene
+siri
+situado
+soberano
+sobra
+socorro
+sogro
+soja
+solda
+soletrar
+solteiro
+sombrio
+sonata
+sondar
+sonegar
+sonhador
+sono
+soprano
+soquete
+sorrir
+sorteio
+sossego
+sotaque
+soterrar
+sovado
+sozinho
+suavizar
+subida
+submerso
+subsolo
+subtrair
+sucata
+sucesso
+suco
+sudeste
+sufixo
+sugador
+sugerir
+sujeito
+sulfato
+sumir
+suor
+superior
+suplicar
+suposto
+suprimir
+surdina
+surfista
+surpresa
+surreal
+surtir
+suspiro
+sustento
+tabela
+tablete
+tabuada
+tacho
+tagarela
+talher
+talo
+talvez
+tamanho
+tamborim
+tampa
+tangente
+tanto
+tapar
+tapioca
+tardio
+tarefa
+tarja
+tarraxa
+tatuagem
+taurino
+taxativo
+taxista
+teatral
+tecer
+tecido
+teclado
+tedioso
+teia
+teimar
+telefone
+telhado
+tempero
+tenente
+tensor
+tentar
+termal
+terno
+terreno
+tese
+tesoura
+testado
+teto
+textura
+texugo
+tiara
+tigela
+tijolo
+timbrar
+timidez
+tingido
+tinteiro
+tiragem
+titular
+toalha
+tocha
+tolerar
+tolice
+tomada
+tomilho
+tonel
+tontura
+topete
+tora
+torcido
+torneio
+torque
+torrada
+torto
+tostar
+touca
+toupeira
+toxina
+trabalho
+tracejar
+tradutor
+trafegar
+trajeto
+trama
+trancar
+trapo
+traseiro
+tratador
+travar
+treino
+tremer
+trepidar
+trevo
+triagem
+tribo
+triciclo
+tridente
+trilogia
+trindade
+triplo
+triturar
+triunfal
+trocar
+trombeta
+trova
+trunfo
+truque
+tubular
+tucano
+tudo
+tulipa
+tupi
+turbo
+turma
+turquesa
+tutelar
+tutorial
+uivar
+umbigo
+unha
+unidade
+uniforme
+urologia
+urso
+urtiga
+urubu
+usado
+usina
+usufruir
+vacina
+vadiar
+vagaroso
+vaidoso
+vala
+valente
+validade
+valores
+vantagem
+vaqueiro
+varanda
+vareta
+varrer
+vascular
+vasilha
+vassoura
+vazar
+vazio
+veado
+vedar
+vegetar
+veicular
+veleiro
+velhice
+veludo
+vencedor
+vendaval
+venerar
+ventre
+verbal
+verdade
+vereador
+vergonha
+vermelho
+verniz
+versar
+vertente
+vespa
+vestido
+vetorial
+viaduto
+viagem
+viajar
+viatura
+vibrador
+videira
+vidraria
+viela
+viga
+vigente
+vigiar
+vigorar
+vilarejo
+vinco
+vinheta
+vinil
+violeta
+virada
+virtude
+visitar
+visto
+vitral
+viveiro
+vizinho
+voador
+voar
+vogal
+volante
+voleibol
+voltagem
+volumoso
+vontade
+vulto
+vuvuzela
+xadrez
+xarope
+xeque
+xeretar
+xerife
+xingar
+zangado
+zarpar
+zebu
+zelador
+zombar
+zoologia
+zumbido
diff --git a/src/main/resources/spanish.txt b/src/main/resources/spanish.txt
new file mode 100644
index 000000000..fdbc23c73
--- /dev/null
+++ b/src/main/resources/spanish.txt
@@ -0,0 +1,2048 @@
+ábaco
+abdomen
+abeja
+abierto
+abogado
+abono
+aborto
+abrazo
+abrir
+abuelo
+abuso
+acabar
+academia
+acceso
+acción
+aceite
+acelga
+acento
+aceptar
+ácido
+aclarar
+acné
+acoger
+acoso
+activo
+acto
+actriz
+actuar
+acudir
+acuerdo
+acusar
+adicto
+admitir
+adoptar
+adorno
+aduana
+adulto
+aéreo
+afectar
+afición
+afinar
+afirmar
+ágil
+agitar
+agonía
+agosto
+agotar
+agregar
+agrio
+agua
+agudo
+águila
+aguja
+ahogo
+ahorro
+aire
+aislar
+ajedrez
+ajeno
+ajuste
+alacrán
+alambre
+alarma
+alba
+álbum
+alcalde
+aldea
+alegre
+alejar
+alerta
+aleta
+alfiler
+alga
+algodón
+aliado
+aliento
+alivio
+alma
+almeja
+almíbar
+altar
+alteza
+altivo
+alto
+altura
+alumno
+alzar
+amable
+amante
+amapola
+amargo
+amasar
+ámbar
+ámbito
+ameno
+amigo
+amistad
+amor
+amparo
+amplio
+ancho
+anciano
+ancla
+andar
+andén
+anemia
+ángulo
+anillo
+ánimo
+anís
+anotar
+antena
+antiguo
+antojo
+anual
+anular
+anuncio
+añadir
+añejo
+año
+apagar
+aparato
+apetito
+apio
+aplicar
+apodo
+aporte
+apoyo
+aprender
+aprobar
+apuesta
+apuro
+arado
+araña
+arar
+árbitro
+árbol
+arbusto
+archivo
+arco
+arder
+ardilla
+arduo
+área
+árido
+aries
+armonía
+arnés
+aroma
+arpa
+arpón
+arreglo
+arroz
+arruga
+arte
+artista
+asa
+asado
+asalto
+ascenso
+asegurar
+aseo
+asesor
+asiento
+asilo
+asistir
+asno
+asombro
+áspero
+astilla
+astro
+astuto
+asumir
+asunto
+atajo
+ataque
+atar
+atento
+ateo
+ático
+atleta
+átomo
+atraer
+atroz
+atún
+audaz
+audio
+auge
+aula
+aumento
+ausente
+autor
+aval
+avance
+avaro
+ave
+avellana
+avena
+avestruz
+avión
+aviso
+ayer
+ayuda
+ayuno
+azafrán
+azar
+azote
+azúcar
+azufre
+azul
+baba
+babor
+bache
+bahía
+baile
+bajar
+balanza
+balcón
+balde
+bambú
+banco
+banda
+baño
+barba
+barco
+barniz
+barro
+báscula
+bastón
+basura
+batalla
+batería
+batir
+batuta
+baúl
+bazar
+bebé
+bebida
+bello
+besar
+beso
+bestia
+bicho
+bien
+bingo
+blanco
+bloque
+blusa
+boa
+bobina
+bobo
+boca
+bocina
+boda
+bodega
+boina
+bola
+bolero
+bolsa
+bomba
+bondad
+bonito
+bono
+bonsái
+borde
+borrar
+bosque
+bote
+botín
+bóveda
+bozal
+bravo
+brazo
+brecha
+breve
+brillo
+brinco
+brisa
+broca
+broma
+bronce
+brote
+bruja
+brusco
+bruto
+buceo
+bucle
+bueno
+buey
+bufanda
+bufón
+búho
+buitre
+bulto
+burbuja
+burla
+burro
+buscar
+butaca
+buzón
+caballo
+cabeza
+cabina
+cabra
+cacao
+cadáver
+cadena
+caer
+café
+caída
+caimán
+caja
+cajón
+cal
+calamar
+calcio
+caldo
+calidad
+calle
+calma
+calor
+calvo
+cama
+cambio
+camello
+camino
+campo
+cáncer
+candil
+canela
+canguro
+canica
+canto
+caña
+cañón
+caoba
+caos
+capaz
+capitán
+capote
+captar
+capucha
+cara
+carbón
+cárcel
+careta
+carga
+cariño
+carne
+carpeta
+carro
+carta
+casa
+casco
+casero
+caspa
+castor
+catorce
+catre
+caudal
+causa
+cazo
+cebolla
+ceder
+cedro
+celda
+célebre
+celoso
+célula
+cemento
+ceniza
+centro
+cerca
+cerdo
+cereza
+cero
+cerrar
+certeza
+césped
+cetro
+chacal
+chaleco
+champú
+chancla
+chapa
+charla
+chico
+chiste
+chivo
+choque
+choza
+chuleta
+chupar
+ciclón
+ciego
+cielo
+cien
+cierto
+cifra
+cigarro
+cima
+cinco
+cine
+cinta
+ciprés
+circo
+ciruela
+cisne
+cita
+ciudad
+clamor
+clan
+claro
+clase
+clave
+cliente
+clima
+clínica
+cobre
+cocción
+cochino
+cocina
+coco
+código
+codo
+cofre
+coger
+cohete
+cojín
+cojo
+cola
+colcha
+colegio
+colgar
+colina
+collar
+colmo
+columna
+combate
+comer
+comida
+cómodo
+compra
+conde
+conejo
+conga
+conocer
+consejo
+contar
+copa
+copia
+corazón
+corbata
+corcho
+cordón
+corona
+correr
+coser
+cosmos
+costa
+cráneo
+cráter
+crear
+crecer
+creído
+crema
+cría
+crimen
+cripta
+crisis
+cromo
+crónica
+croqueta
+crudo
+cruz
+cuadro
+cuarto
+cuatro
+cubo
+cubrir
+cuchara
+cuello
+cuento
+cuerda
+cuesta
+cueva
+cuidar
+culebra
+culpa
+culto
+cumbre
+cumplir
+cuna
+cuneta
+cuota
+cupón
+cúpula
+curar
+curioso
+curso
+curva
+cutis
+dama
+danza
+dar
+dardo
+dátil
+deber
+débil
+década
+decir
+dedo
+defensa
+definir
+dejar
+delfín
+delgado
+delito
+demora
+denso
+dental
+deporte
+derecho
+derrota
+desayuno
+deseo
+desfile
+desnudo
+destino
+desvío
+detalle
+detener
+deuda
+día
+diablo
+diadema
+diamante
+diana
+diario
+dibujo
+dictar
+diente
+dieta
+diez
+difícil
+digno
+dilema
+diluir
+dinero
+directo
+dirigir
+disco
+diseño
+disfraz
+diva
+divino
+doble
+doce
+dolor
+domingo
+don
+donar
+dorado
+dormir
+dorso
+dos
+dosis
+dragón
+droga
+ducha
+duda
+duelo
+dueño
+dulce
+dúo
+duque
+durar
+dureza
+duro
+ébano
+ebrio
+echar
+eco
+ecuador
+edad
+edición
+edificio
+editor
+educar
+efecto
+eficaz
+eje
+ejemplo
+elefante
+elegir
+elemento
+elevar
+elipse
+élite
+elixir
+elogio
+eludir
+embudo
+emitir
+emoción
+empate
+empeño
+empleo
+empresa
+enano
+encargo
+enchufe
+encía
+enemigo
+enero
+enfado
+enfermo
+engaño
+enigma
+enlace
+enorme
+enredo
+ensayo
+enseñar
+entero
+entrar
+envase
+envío
+época
+equipo
+erizo
+escala
+escena
+escolar
+escribir
+escudo
+esencia
+esfera
+esfuerzo
+espada
+espejo
+espía
+esposa
+espuma
+esquí
+estar
+este
+estilo
+estufa
+etapa
+eterno
+ética
+etnia
+evadir
+evaluar
+evento
+evitar
+exacto
+examen
+exceso
+excusa
+exento
+exigir
+exilio
+existir
+éxito
+experto
+explicar
+exponer
+extremo
+fábrica
+fábula
+fachada
+fácil
+factor
+faena
+faja
+falda
+fallo
+falso
+faltar
+fama
+familia
+famoso
+faraón
+farmacia
+farol
+farsa
+fase
+fatiga
+fauna
+favor
+fax
+febrero
+fecha
+feliz
+feo
+feria
+feroz
+fértil
+fervor
+festín
+fiable
+fianza
+fiar
+fibra
+ficción
+ficha
+fideo
+fiebre
+fiel
+fiera
+fiesta
+figura
+fijar
+fijo
+fila
+filete
+filial
+filtro
+fin
+finca
+fingir
+finito
+firma
+flaco
+flauta
+flecha
+flor
+flota
+fluir
+flujo
+flúor
+fobia
+foca
+fogata
+fogón
+folio
+folleto
+fondo
+forma
+forro
+fortuna
+forzar
+fosa
+foto
+fracaso
+frágil
+franja
+frase
+fraude
+freír
+freno
+fresa
+frío
+frito
+fruta
+fuego
+fuente
+fuerza
+fuga
+fumar
+función
+funda
+furgón
+furia
+fusil
+fútbol
+futuro
+gacela
+gafas
+gaita
+gajo
+gala
+galería
+gallo
+gamba
+ganar
+gancho
+ganga
+ganso
+garaje
+garza
+gasolina
+gastar
+gato
+gavilán
+gemelo
+gemir
+gen
+género
+genio
+gente
+geranio
+gerente
+germen
+gesto
+gigante
+gimnasio
+girar
+giro
+glaciar
+globo
+gloria
+gol
+golfo
+goloso
+golpe
+goma
+gordo
+gorila
+gorra
+gota
+goteo
+gozar
+grada
+gráfico
+grano
+grasa
+gratis
+grave
+grieta
+grillo
+gripe
+gris
+grito
+grosor
+grúa
+grueso
+grumo
+grupo
+guante
+guapo
+guardia
+guerra
+guía
+guiño
+guion
+guiso
+guitarra
+gusano
+gustar
+haber
+hábil
+hablar
+hacer
+hacha
+hada
+hallar
+hamaca
+harina
+haz
+hazaña
+hebilla
+hebra
+hecho
+helado
+helio
+hembra
+herir
+hermano
+héroe
+hervir
+hielo
+hierro
+hígado
+higiene
+hijo
+himno
+historia
+hocico
+hogar
+hoguera
+hoja
+hombre
+hongo
+honor
+honra
+hora
+hormiga
+horno
+hostil
+hoyo
+hueco
+huelga
+huerta
+hueso
+huevo
+huida
+huir
+humano
+húmedo
+humilde
+humo
+hundir
+huracán
+hurto
+icono
+ideal
+idioma
+ídolo
+iglesia
+iglú
+igual
+ilegal
+ilusión
+imagen
+imán
+imitar
+impar
+imperio
+imponer
+impulso
+incapaz
+índice
+inerte
+infiel
+informe
+ingenio
+inicio
+inmenso
+inmune
+innato
+insecto
+instante
+interés
+íntimo
+intuir
+inútil
+invierno
+ira
+iris
+ironía
+isla
+islote
+jabalí
+jabón
+jamón
+jarabe
+jardín
+jarra
+jaula
+jazmín
+jefe
+jeringa
+jinete
+jornada
+joroba
+joven
+joya
+juerga
+jueves
+juez
+jugador
+jugo
+juguete
+juicio
+junco
+jungla
+junio
+juntar
+júpiter
+jurar
+justo
+juvenil
+juzgar
+kilo
+koala
+labio
+lacio
+lacra
+lado
+ladrón
+lagarto
+lágrima
+laguna
+laico
+lamer
+lámina
+lámpara
+lana
+lancha
+langosta
+lanza
+lápiz
+largo
+larva
+lástima
+lata
+látex
+latir
+laurel
+lavar
+lazo
+leal
+lección
+leche
+lector
+leer
+legión
+legumbre
+lejano
+lengua
+lento
+leña
+león
+leopardo
+lesión
+letal
+letra
+leve
+leyenda
+libertad
+libro
+licor
+líder
+lidiar
+lienzo
+liga
+ligero
+lima
+límite
+limón
+limpio
+lince
+lindo
+línea
+lingote
+lino
+linterna
+líquido
+liso
+lista
+litera
+litio
+litro
+llaga
+llama
+llanto
+llave
+llegar
+llenar
+llevar
+llorar
+llover
+lluvia
+lobo
+loción
+loco
+locura
+lógica
+logro
+lombriz
+lomo
+lonja
+lote
+lucha
+lucir
+lugar
+lujo
+luna
+lunes
+lupa
+lustro
+luto
+luz
+maceta
+macho
+madera
+madre
+maduro
+maestro
+mafia
+magia
+mago
+maíz
+maldad
+maleta
+malla
+malo
+mamá
+mambo
+mamut
+manco
+mando
+manejar
+manga
+maniquí
+manjar
+mano
+manso
+manta
+mañana
+mapa
+máquina
+mar
+marco
+marea
+marfil
+margen
+marido
+mármol
+marrón
+martes
+marzo
+masa
+máscara
+masivo
+matar
+materia
+matiz
+matriz
+máximo
+mayor
+mazorca
+mecha
+medalla
+medio
+médula
+mejilla
+mejor
+melena
+melón
+memoria
+menor
+mensaje
+mente
+menú
+mercado
+merengue
+mérito
+mes
+mesón
+meta
+meter
+método
+metro
+mezcla
+miedo
+miel
+miembro
+miga
+mil
+milagro
+militar
+millón
+mimo
+mina
+minero
+mínimo
+minuto
+miope
+mirar
+misa
+miseria
+misil
+mismo
+mitad
+mito
+mochila
+moción
+moda
+modelo
+moho
+mojar
+molde
+moler
+molino
+momento
+momia
+monarca
+moneda
+monja
+monto
+moño
+morada
+morder
+moreno
+morir
+morro
+morsa
+mortal
+mosca
+mostrar
+motivo
+mover
+móvil
+mozo
+mucho
+mudar
+mueble
+muela
+muerte
+muestra
+mugre
+mujer
+mula
+muleta
+multa
+mundo
+muñeca
+mural
+muro
+músculo
+museo
+musgo
+música
+muslo
+nácar
+nación
+nadar
+naipe
+naranja
+nariz
+narrar
+nasal
+natal
+nativo
+natural
+náusea
+naval
+nave
+navidad
+necio
+néctar
+negar
+negocio
+negro
+neón
+nervio
+neto
+neutro
+nevar
+nevera
+nicho
+nido
+niebla
+nieto
+niñez
+niño
+nítido
+nivel
+nobleza
+noche
+nómina
+noria
+norma
+norte
+nota
+noticia
+novato
+novela
+novio
+nube
+nuca
+núcleo
+nudillo
+nudo
+nuera
+nueve
+nuez
+nulo
+número
+nutria
+oasis
+obeso
+obispo
+objeto
+obra
+obrero
+observar
+obtener
+obvio
+oca
+ocaso
+océano
+ochenta
+ocho
+ocio
+ocre
+octavo
+octubre
+oculto
+ocupar
+ocurrir
+odiar
+odio
+odisea
+oeste
+ofensa
+oferta
+oficio
+ofrecer
+ogro
+oído
+oír
+ojo
+ola
+oleada
+olfato
+olivo
+olla
+olmo
+olor
+olvido
+ombligo
+onda
+onza
+opaco
+opción
+ópera
+opinar
+oponer
+optar
+óptica
+opuesto
+oración
+orador
+oral
+órbita
+orca
+orden
+oreja
+órgano
+orgía
+orgullo
+oriente
+origen
+orilla
+oro
+orquesta
+oruga
+osadía
+oscuro
+osezno
+oso
+ostra
+otoño
+otro
+oveja
+óvulo
+óxido
+oxígeno
+oyente
+ozono
+pacto
+padre
+paella
+página
+pago
+país
+pájaro
+palabra
+palco
+paleta
+pálido
+palma
+paloma
+palpar
+pan
+panal
+pánico
+pantera
+pañuelo
+papá
+papel
+papilla
+paquete
+parar
+parcela
+pared
+parir
+paro
+párpado
+parque
+párrafo
+parte
+pasar
+paseo
+pasión
+paso
+pasta
+pata
+patio
+patria
+pausa
+pauta
+pavo
+payaso
+peatón
+pecado
+pecera
+pecho
+pedal
+pedir
+pegar
+peine
+pelar
+peldaño
+pelea
+peligro
+pellejo
+pelo
+peluca
+pena
+pensar
+peñón
+peón
+peor
+pepino
+pequeño
+pera
+percha
+perder
+pereza
+perfil
+perico
+perla
+permiso
+perro
+persona
+pesa
+pesca
+pésimo
+pestaña
+pétalo
+petróleo
+pez
+pezuña
+picar
+pichón
+pie
+piedra
+pierna
+pieza
+pijama
+pilar
+piloto
+pimienta
+pino
+pintor
+pinza
+piña
+piojo
+pipa
+pirata
+pisar
+piscina
+piso
+pista
+pitón
+pizca
+placa
+plan
+plata
+playa
+plaza
+pleito
+pleno
+plomo
+pluma
+plural
+pobre
+poco
+poder
+podio
+poema
+poesía
+poeta
+polen
+policía
+pollo
+polvo
+pomada
+pomelo
+pomo
+pompa
+poner
+porción
+portal
+posada
+poseer
+posible
+poste
+potencia
+potro
+pozo
+prado
+precoz
+pregunta
+premio
+prensa
+preso
+previo
+primo
+príncipe
+prisión
+privar
+proa
+probar
+proceso
+producto
+proeza
+profesor
+programa
+prole
+promesa
+pronto
+propio
+próximo
+prueba
+público
+puchero
+pudor
+pueblo
+puerta
+puesto
+pulga
+pulir
+pulmón
+pulpo
+pulso
+puma
+punto
+puñal
+puño
+pupa
+pupila
+puré
+quedar
+queja
+quemar
+querer
+queso
+quieto
+química
+quince
+quitar
+rábano
+rabia
+rabo
+ración
+radical
+raíz
+rama
+rampa
+rancho
+rango
+rapaz
+rápido
+rapto
+rasgo
+raspa
+rato
+rayo
+raza
+razón
+reacción
+realidad
+rebaño
+rebote
+recaer
+receta
+rechazo
+recoger
+recreo
+recto
+recurso
+red
+redondo
+reducir
+reflejo
+reforma
+refrán
+refugio
+regalo
+regir
+regla
+regreso
+rehén
+reino
+reír
+reja
+relato
+relevo
+relieve
+relleno
+reloj
+remar
+remedio
+remo
+rencor
+rendir
+renta
+reparto
+repetir
+reposo
+reptil
+res
+rescate
+resina
+respeto
+resto
+resumen
+retiro
+retorno
+retrato
+reunir
+revés
+revista
+rey
+rezar
+rico
+riego
+rienda
+riesgo
+rifa
+rígido
+rigor
+rincón
+riñón
+río
+riqueza
+risa
+ritmo
+rito
+rizo
+roble
+roce
+rociar
+rodar
+rodeo
+rodilla
+roer
+rojizo
+rojo
+romero
+romper
+ron
+ronco
+ronda
+ropa
+ropero
+rosa
+rosca
+rostro
+rotar
+rubí
+rubor
+rudo
+rueda
+rugir
+ruido
+ruina
+ruleta
+rulo
+rumbo
+rumor
+ruptura
+ruta
+rutina
+sábado
+saber
+sabio
+sable
+sacar
+sagaz
+sagrado
+sala
+saldo
+salero
+salir
+salmón
+salón
+salsa
+salto
+salud
+salvar
+samba
+sanción
+sandía
+sanear
+sangre
+sanidad
+sano
+santo
+sapo
+saque
+sardina
+sartén
+sastre
+satán
+sauna
+saxofón
+sección
+seco
+secreto
+secta
+sed
+seguir
+seis
+sello
+selva
+semana
+semilla
+senda
+sensor
+señal
+señor
+separar
+sepia
+sequía
+ser
+serie
+sermón
+servir
+sesenta
+sesión
+seta
+setenta
+severo
+sexo
+sexto
+sidra
+siesta
+siete
+siglo
+signo
+sílaba
+silbar
+silencio
+silla
+símbolo
+simio
+sirena
+sistema
+sitio
+situar
+sobre
+socio
+sodio
+sol
+solapa
+soldado
+soledad
+sólido
+soltar
+solución
+sombra
+sondeo
+sonido
+sonoro
+sonrisa
+sopa
+soplar
+soporte
+sordo
+sorpresa
+sorteo
+sostén
+sótano
+suave
+subir
+suceso
+sudor
+suegra
+suelo
+sueño
+suerte
+sufrir
+sujeto
+sultán
+sumar
+superar
+suplir
+suponer
+supremo
+sur
+surco
+sureño
+surgir
+susto
+sutil
+tabaco
+tabique
+tabla
+tabú
+taco
+tacto
+tajo
+talar
+talco
+talento
+talla
+talón
+tamaño
+tambor
+tango
+tanque
+tapa
+tapete
+tapia
+tapón
+taquilla
+tarde
+tarea
+tarifa
+tarjeta
+tarot
+tarro
+tarta
+tatuaje
+tauro
+taza
+tazón
+teatro
+techo
+tecla
+técnica
+tejado
+tejer
+tejido
+tela
+teléfono
+tema
+temor
+templo
+tenaz
+tender
+tener
+tenis
+tenso
+teoría
+terapia
+terco
+término
+ternura
+terror
+tesis
+tesoro
+testigo
+tetera
+texto
+tez
+tibio
+tiburón
+tiempo
+tienda
+tierra
+tieso
+tigre
+tijera
+tilde
+timbre
+tímido
+timo
+tinta
+tío
+típico
+tipo
+tira
+tirón
+titán
+títere
+título
+tiza
+toalla
+tobillo
+tocar
+tocino
+todo
+toga
+toldo
+tomar
+tono
+tonto
+topar
+tope
+toque
+tórax
+torero
+tormenta
+torneo
+toro
+torpedo
+torre
+torso
+tortuga
+tos
+tosco
+toser
+tóxico
+trabajo
+tractor
+traer
+tráfico
+trago
+traje
+tramo
+trance
+trato
+trauma
+trazar
+trébol
+tregua
+treinta
+tren
+trepar
+tres
+tribu
+trigo
+tripa
+triste
+triunfo
+trofeo
+trompa
+tronco
+tropa
+trote
+trozo
+truco
+trueno
+trufa
+tubería
+tubo
+tuerto
+tumba
+tumor
+túnel
+túnica
+turbina
+turismo
+turno
+tutor
+ubicar
+úlcera
+umbral
+unidad
+unir
+universo
+uno
+untar
+uña
+urbano
+urbe
+urgente
+urna
+usar
+usuario
+útil
+utopía
+uva
+vaca
+vacío
+vacuna
+vagar
+vago
+vaina
+vajilla
+vale
+válido
+valle
+valor
+válvula
+vampiro
+vara
+variar
+varón
+vaso
+vecino
+vector
+vehículo
+veinte
+vejez
+vela
+velero
+veloz
+vena
+vencer
+venda
+veneno
+vengar
+venir
+venta
+venus
+ver
+verano
+verbo
+verde
+vereda
+verja
+verso
+verter
+vía
+viaje
+vibrar
+vicio
+víctima
+vida
+vídeo
+vidrio
+viejo
+viernes
+vigor
+vil
+villa
+vinagre
+vino
+viñedo
+violín
+viral
+virgo
+virtud
+visor
+víspera
+vista
+vitamina
+viudo
+vivaz
+vivero
+vivir
+vivo
+volcán
+volumen
+volver
+voraz
+votar
+voto
+voz
+vuelo
+vulgar
+yacer
+yate
+yegua
+yema
+yerno
+yeso
+yodo
+yoga
+yogur
+zafiro
+zanja
+zapato
+zarza
+zona
+zorro
+zumo
+zurdo
diff --git a/src/test/java/com/syntifi/crypto/key/AbstractCryptoTests.java b/src/test/java/com/syntifi/crypto/key/AbstractCryptoTests.java
new file mode 100644
index 000000000..0001df3f1
--- /dev/null
+++ b/src/test/java/com/syntifi/crypto/key/AbstractCryptoTests.java
@@ -0,0 +1,72 @@
+package com.syntifi.crypto.key;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.net.URISyntaxException;
+import java.nio.MappedByteBuffer;
+import java.nio.channels.FileChannel;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.Arrays;
+import java.util.Objects;
+
+/**
+ * Shared key testing functionality
+ *
+ * @author Alexandre Carvalho
+ * @author Andre Bertolace
+ * @since 0.1.0
+ */
+public abstract class AbstractCryptoTests {
+ /**
+ * Loads test key file from resources
+ *
+ * @param filename the file name
+ * @return a string with file path from resources
+ * @throws URISyntaxException thrown if it can't parse file url to URI for fetching the path
+ */
+ protected String getResourcesKeyPath(String filename) throws URISyntaxException {
+ return Paths.get(Objects.requireNonNull(getClass().getClassLoader().getResource(filename)).toURI()).toString();
+ }
+
+ /**
+ * Compare text files
+ *
+ * @param file1 a file object
+ * @param file2 another file object
+ * @return true if matches, false otherwise
+ * @throws IOException thrown if error reading files
+ */
+ protected boolean compareTextFiles(File file1, File file2) throws IOException {
+ byte[] contentFile1 = Files.readAllBytes(file1.toPath());
+ byte[] contentFile2 = Files.readAllBytes(file2.toPath());
+
+ return Arrays.equals(contentFile1, contentFile2);
+ }
+
+ /**
+ * @param file1 a file
+ * @param file2 another file
+ * @return true if matches, false otherwise
+ * @throws IOException thrown if error reading files
+ */
+ protected boolean compareFiles(File file1, File file2) throws IOException {
+ try (RandomAccessFile randomAccessFile1 = new RandomAccessFile(file1, "r");
+ RandomAccessFile randomAccessFile2 = new RandomAccessFile(file2, "r")) {
+
+ FileChannel ch1 = randomAccessFile1.getChannel();
+ FileChannel ch2 = randomAccessFile2.getChannel();
+
+ if (ch1.size() != ch2.size()) {
+ return false;
+ }
+
+ long size = ch1.size();
+ MappedByteBuffer m1 = ch1.map(FileChannel.MapMode.READ_ONLY, 0L, size);
+ MappedByteBuffer m2 = ch2.map(FileChannel.MapMode.READ_ONLY, 0L, size);
+
+ return m1.equals(m2);
+ }
+ }
+}
diff --git a/src/test/java/com/syntifi/crypto/key/Ed25519PrivateKeyTests.java b/src/test/java/com/syntifi/crypto/key/Ed25519PrivateKeyTests.java
new file mode 100644
index 000000000..28756e8dd
--- /dev/null
+++ b/src/test/java/com/syntifi/crypto/key/Ed25519PrivateKeyTests.java
@@ -0,0 +1,128 @@
+package com.syntifi.crypto.key;
+
+import com.syntifi.crypto.key.deterministic.HierarchicalDeterministicKey;
+import com.syntifi.crypto.key.encdec.Base58;
+import com.syntifi.crypto.key.encdec.Hex;
+import com.syntifi.crypto.key.mnemonic.Language;
+import com.syntifi.crypto.key.mnemonic.MnemonicCode;
+import org.junit.jupiter.api.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Arrays;
+import java.util.Date;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * Tests for {@link Ed25519PrivateKey}
+ *
+ * @author Alexandre Carvalho
+ * @author Andre Bertolace
+ * @since 0.1.0
+ */
+public class Ed25519PrivateKeyTests extends AbstractCryptoTests {
+ private static final Logger LOGGER = LoggerFactory.getLogger(Ed25519PrivateKeyTests.class);
+
+ @Test
+ void readPrivateKey_should_load_private_key() throws IOException, URISyntaxException {
+ Ed25519PrivateKey privateKey = readPrivateKey("ed25519/secret_key.pem");
+ assertNotNull(privateKey.getKey());
+ }
+
+ @Test
+ void readPrivateKey_derived_public_key_should_equal_generated() throws IOException, URISyntaxException {
+ Ed25519PrivateKey privateKey = readPrivateKey("ed25519/secret_key.pem");
+
+ // Compare derived public key to generated hex without leading id byte
+ Path hexKeyFilePath = Paths.get(getResourcesKeyPath("ed25519/public_key_hex"));
+ String hexKey = new String(Files.readAllBytes(hexKeyFilePath));
+ LOGGER.debug("Derived public hex Key from {}: {}", hexKeyFilePath,
+ Hex.encode(privateKey.derivePublicKey().getKey()));
+
+ assertEquals(hexKey.substring(2), Hex.encode(privateKey.derivePublicKey().getKey()));
+ }
+
+ @Test
+ void writePrivateKey_should_equal_source_file() throws URISyntaxException, IOException {
+ Ed25519PrivateKey privateKey = readPrivateKey("ed25519/secret_key.pem");
+
+ DateFormat df = new SimpleDateFormat("yyyyMMdd-HHmmss");
+ File privateKeyFile = File.createTempFile(df.format(new Date()), "-private-key-test.pem");
+
+ LOGGER.debug("Writing private key to {}", privateKeyFile.getPath());
+ privateKey.writePrivateKey(privateKeyFile.getPath());
+
+ assertTrue(compareTextFiles(new File(getResourcesKeyPath("ed25519/secret_key.pem")),
+ privateKeyFile));
+ }
+
+ @Test
+ void sign_should_sign_message() throws URISyntaxException, IOException {
+ Ed25519PrivateKey privateKey = readPrivateKey("ed25519/secret_key.pem");
+
+ byte[] signature = privateKey.sign("Test message".getBytes());
+
+ assertEquals(
+ "4555103678684364a98478112ce0c298ed841d806d2b67b09e8f0215cc738f3c5a1fca5beaf0474ff636613821bcb97e88b3b4d700e65c6cf7574489e09f170c",
+ Hex.encode(signature));
+
+ LOGGER.debug("Signed as {}", signature);
+ }
+
+ @Test
+ void derived_public_key_should_match_generated() {
+ String base58GeneratedPrivateKey = "32UfEkBGTFpfu6M7RebN1JqMDrdf1YyztgYmcUG5XcRkEraJioFZLPtBvYVmAVvnjWAToSsWScJYSFViv8MaATRF";
+ String base58GeneratedPublicKey = "F8jARHGZdHqnwrxrnv1pFVzzirXZR2vJzeYbvwQbxZyP";
+
+ Ed25519PrivateKey pk = new Ed25519PrivateKey(Base58.decode(base58GeneratedPrivateKey));
+
+ assertEquals(base58GeneratedPublicKey, Base58.encode(pk.derivePublicKey().getKey()));
+ }
+
+ @Test
+ void create_privateKey_from_seed() throws IOException {
+ MnemonicCode mnemonicCode = new MnemonicCode(Language.EN);
+ String words = "shoot island position soft burden budget tooth cruel issue economy destroy above";
+ byte[] seed = mnemonicCode.toSeed(Arrays.asList(words.split(" ")), "");
+ int[] path = {44, 397, 0};
+
+ Ed25519PrivateKey pk1 = Ed25519PrivateKey.deriveFromSeed(seed, path);
+ byte[] init = "ed25519 seed".getBytes(StandardCharsets.UTF_8);
+ byte[] key = HierarchicalDeterministicKey.getFromSeed(seed, init, path);
+ Ed25519PrivateKey pk2 = new Ed25519PrivateKey(key);
+ assertEquals("88793a8eeec537c67ee8d459f1899a47a2f1b752d06a4c793c66fd751df8049838d300841c903867050c222b9f0b43893a5675f0a87756cfce4e3fd71c23334a",
+ Hex.encode(pk1.getKey()));
+ assertEquals(Hex.encode(pk2.getKey()), Hex.encode(pk1.getKey()));
+ assertEquals("0c91f6106ff835c0195d5388565a2d69e25038a7e23d26198f85caf6594117ec",
+ Hex.encode(pk1.derivePublicKey().getKey()));
+ assertEquals(Hex.encode(pk2.derivePublicKey().getKey()), Hex.encode(pk1.derivePublicKey().getKey()));
+ }
+
+ @Test
+ void create_random_key() {
+ Ed25519PrivateKey sk = Ed25519PrivateKey.deriveRandomKey();
+ Ed25519PublicKey pk = (Ed25519PublicKey) sk.derivePublicKey();
+ byte[] msg = "this is a test".getBytes();
+ byte[] signature = sk.sign(msg);
+ assertTrue(pk.verify(msg, signature));
+ }
+
+ private Ed25519PrivateKey readPrivateKey(String privateKeyPath) throws URISyntaxException, IOException {
+ Ed25519PrivateKey privateKey = new Ed25519PrivateKey();
+ String keyFilePath = getResourcesKeyPath(privateKeyPath);
+ LOGGER.debug("Reading key from {}", keyFilePath);
+ privateKey.readPrivateKey(keyFilePath);
+ LOGGER.debug("Key: {}", Hex.encode(privateKey.getKey()));
+ return privateKey;
+ }
+}
diff --git a/src/test/java/com/syntifi/crypto/key/Ed25519PublicKeyTests.java b/src/test/java/com/syntifi/crypto/key/Ed25519PublicKeyTests.java
new file mode 100644
index 000000000..a73b064b9
--- /dev/null
+++ b/src/test/java/com/syntifi/crypto/key/Ed25519PublicKeyTests.java
@@ -0,0 +1,82 @@
+package com.syntifi.crypto.key;
+
+import com.syntifi.crypto.key.encdec.Hex;
+import org.junit.jupiter.api.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * Tests for {@link Ed25519PublicKey}
+ *
+ * @author Alexandre Carvalho
+ * @author Andre Bertolace
+ * @since 0.1.0
+ */
+public class Ed25519PublicKeyTests extends AbstractCryptoTests {
+ private static final Logger LOGGER = LoggerFactory.getLogger(Ed25519PublicKeyTests.class);
+
+ @Test
+ void readPublicKey_should_load_and_be_equal_to_generated_public_key() throws IOException, URISyntaxException {
+ Ed25519PublicKey publicKey = loadPublicKey("ed25519/public_key.pem");
+ assertNotNull(publicKey.getKey());
+
+ // Compare to generated hex without leading id byte
+ Path hexKeyFilePath = Paths.get(getResourcesKeyPath("ed25519/public_key_hex"));
+ String hexKey = new String(Files.readAllBytes(hexKeyFilePath));
+ LOGGER.debug("Hex Key from {}: {}", hexKeyFilePath, Hex.encode(publicKey.getKey()));
+ assertEquals(hexKey.substring(2), Hex.encode(publicKey.getKey()));
+ }
+
+ @Test
+ void writePublicKey_should_equal_source_file() throws URISyntaxException, IOException {
+ Ed25519PublicKey publicKey = loadPublicKey("ed25519/public_key.pem");
+
+ DateFormat df = new SimpleDateFormat("yyyyMMdd-HHmmss");
+ File publicKeyFile = File.createTempFile(df.format(new Date()), "-public-key-test.pem");
+
+ LOGGER.debug("Writing public key to {}", publicKeyFile.getPath());
+ publicKey.writePublicKey(publicKeyFile.getPath());
+
+ assertTrue(compareTextFiles(new File(getResourcesKeyPath("ed25519/public_key.pem")),
+ publicKeyFile));
+ }
+
+ @Test
+ void verify_should_work_with_previously_signed_message() throws URISyntaxException, IOException {
+ String message = "Test message";
+ String hexSignature = "4555103678684364a98478112ce0c298ed841d806d2b67b09e8f0215cc738f3c5a1fca5beaf0474ff636613821bcb97e88b3b4d700e65c6cf7574489e09f170c";
+
+ Ed25519PublicKey publicKey = loadPublicKey("ed25519/public_key.pem");
+
+ String hexKey = Hex.encode(publicKey.getKey());
+
+ LOGGER.debug("Verifying Ed25519 signature of {} with key {}", message, hexKey);
+
+ Boolean verified = publicKey.verify(message.getBytes(), Hex.decode(hexSignature));
+
+ LOGGER.debug("Signature verified: {}", verified);
+
+ assertTrue(verified);
+ }
+
+ private Ed25519PublicKey loadPublicKey(String publicKeyPath) throws URISyntaxException, IOException {
+ Ed25519PublicKey publicKey = new Ed25519PublicKey();
+ String keyFilePath = getResourcesKeyPath(publicKeyPath);
+ LOGGER.debug("Reading key from {}", keyFilePath);
+ publicKey.readPublicKey(keyFilePath);
+ LOGGER.debug("Key: {}", Hex.encode(publicKey.getKey()));
+ return publicKey;
+ }
+}
diff --git a/src/test/java/com/syntifi/crypto/key/Secp256k1PrivateKeyTests.java b/src/test/java/com/syntifi/crypto/key/Secp256k1PrivateKeyTests.java
new file mode 100644
index 000000000..b62e6a0cc
--- /dev/null
+++ b/src/test/java/com/syntifi/crypto/key/Secp256k1PrivateKeyTests.java
@@ -0,0 +1,109 @@
+package com.syntifi.crypto.key;
+
+import com.syntifi.crypto.key.encdec.Hex;
+import org.junit.jupiter.api.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.security.GeneralSecurityException;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * Tests for {@link Secp256k1PrivateKey}
+ *
+ * @author Alexandre Carvalho
+ * @author Andre Bertolace
+ * @since 0.1.0
+ */
+public class Secp256k1PrivateKeyTests extends AbstractCryptoTests {
+ private static final Logger LOGGER = LoggerFactory.getLogger(Secp256k1PublicKeyTests.class);
+
+ @Test
+ void readPrivateKey_should_load_private_key() throws URISyntaxException, IOException {
+ Secp256k1PrivateKey privKey = new Secp256k1PrivateKey();
+ String filePath = getResourcesKeyPath("secp256k1/secret_key.pem");
+ privKey.readPrivateKey(filePath);
+
+ assertNotNull(privKey.getKey());
+ }
+
+ @Test
+ void writePrivateKey_should_equal_source_file() throws URISyntaxException, IOException {
+ Secp256k1PrivateKey privKey = new Secp256k1PrivateKey();
+ String filePath = getResourcesKeyPath("secp256k1/secret_key.pem");
+ privKey.readPrivateKey(filePath);
+
+ DateFormat df = new SimpleDateFormat("yyyyMMdd-HHmmss");
+ File privateKeyFile = File.createTempFile(df.format(new Date()), "-secret-key-test.pem");
+ privKey.writePrivateKey(privateKeyFile.getPath());
+
+ assertTrue(compareTextFiles(new File(getResourcesKeyPath("secp256k1/secret_key.pem")),
+ privateKeyFile));
+ }
+
+ @Test
+ void readPrivateKey_derived_public_key_should_equal_generated() throws URISyntaxException, IOException {
+ Secp256k1PrivateKey privKey = new Secp256k1PrivateKey();
+ String filePath = getResourcesKeyPath("secp256k1/secret_key.pem");
+ privKey.readPrivateKey(filePath);
+ Secp256k1PublicKey pubKey = (Secp256k1PublicKey) privKey.derivePublicKey();
+
+ DateFormat df = new SimpleDateFormat("yyyyMMdd-HHmmss");
+ File derivedPublicKeyFile = File.createTempFile(df.format(new Date()), "-derived-public-key-test.pem");
+ pubKey.writePublicKey(derivedPublicKeyFile.getPath());
+ LOGGER.info(privKey.getKeyPair().getPrivateKey().toString(16));
+ LOGGER.info(privKey.getKeyPair().getPublicKey().toString(16));
+ LOGGER.info(Hex.encode(pubKey.getKey()));
+ assertTrue(compareTextFiles(new File(getResourcesKeyPath("secp256k1/public_key.pem")),
+ derivedPublicKeyFile));
+ }
+
+ @Test
+ void sign_should_sign_message() throws URISyntaxException, IOException {
+ Secp256k1PrivateKey privKey = new Secp256k1PrivateKey();
+ String filePath = getResourcesKeyPath("secp256k1/secret_key.pem");
+ privKey.readPrivateKey(filePath);
+ LOGGER.info(privKey.getKeyPair().getPublicKey().toString(16));
+ LOGGER.info(Hex.encode(privKey.getKey()));
+
+ byte[] signature = privKey.sign("Test message".getBytes());
+
+ LOGGER.info(Hex.encode(signature));
+ assertEquals(
+ "ea5b38fd0db5fb3d871c47fde1fa4c4db75d1a9e1c0ac54d826e178ee0e63707176b4e63b4f838bd031f007fffd6a4f71d920a10c48ea53dd1573fa2b58a829e",
+ Hex.encode(signature));
+ }
+
+ @Test
+ void create_random_key() throws GeneralSecurityException, IOException {
+ Secp256k1PrivateKey sk = Secp256k1PrivateKey.deriveRandomKey();
+ Secp256k1PublicKey pk = (Secp256k1PublicKey) sk.derivePublicKey();
+ LOGGER.info(sk.getKeyPair().getPrivateKey().toString(16));
+ LOGGER.info(sk.getKeyPair().getPublicKey().toString(16));
+ LOGGER.info(Hex.encode(pk.getKey()));
+ byte[] msg = "this is a test".getBytes();
+ byte[] signature = sk.sign(msg);
+ LOGGER.info(Hex.encode(signature));
+ assertTrue(pk.verify(msg, signature));
+ }
+
+ @Test
+ void randomKeyCanBeWrittenToAndReadFromPemFile() throws IOException {
+
+ final Secp256k1PrivateKey secp256k1PrivateKey = Secp256k1PrivateKey.deriveRandomKey();
+ final File pemFile = File.createTempFile("secp256k1", ".pem");
+ pemFile.deleteOnExit();
+ secp256k1PrivateKey.writePrivateKey(pemFile.getPath());
+
+ final Secp256k1PrivateKey readKey = new Secp256k1PrivateKey();
+ readKey.readPrivateKey(pemFile.getPath());
+ assertEquals(Hex.encode(secp256k1PrivateKey.getKey()), Hex.encode(readKey.getKey()));
+ }
+}
diff --git a/src/test/java/com/syntifi/crypto/key/Secp256k1PublicKeyTests.java b/src/test/java/com/syntifi/crypto/key/Secp256k1PublicKeyTests.java
new file mode 100644
index 000000000..74a6672c2
--- /dev/null
+++ b/src/test/java/com/syntifi/crypto/key/Secp256k1PublicKeyTests.java
@@ -0,0 +1,63 @@
+package com.syntifi.crypto.key;
+
+import com.syntifi.crypto.key.encdec.Hex;
+import org.junit.jupiter.api.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.security.GeneralSecurityException;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * Tests for {@link Secp256k1PublicKey}
+ *
+ * @author Alexandre Carvalho
+ * @author Andre Bertolace
+ * @since 0.1.0
+ */
+public class Secp256k1PublicKeyTests extends AbstractCryptoTests {
+ private static final Logger LOGGER = LoggerFactory.getLogger(Secp256k1PublicKeyTests.class);
+
+ @Test
+ void readPublicKey_should_load_public_key() throws IOException, URISyntaxException {
+ Secp256k1PublicKey pubKey = new Secp256k1PublicKey();
+ String filePath = getResourcesKeyPath("secp256k1/public_key.pem");
+ pubKey.readPublicKey(filePath);
+
+ assertNotNull(pubKey.getKey());
+ }
+
+ @Test
+ void writePublicKey_should_equal_source_file() throws URISyntaxException, IOException {
+ Secp256k1PublicKey pubKey = new Secp256k1PublicKey();
+ String filePath = getResourcesKeyPath("secp256k1/public_key.pem");
+ pubKey.readPublicKey(filePath);
+
+ DateFormat df = new SimpleDateFormat("yyyyMMdd-HHmmss");
+ File publicKeyFile = File.createTempFile(df.format(new Date()), "-public-key-test.pem");
+ pubKey.writePublicKey(publicKeyFile.getPath());
+
+ assertTrue(compareTextFiles(new File(getResourcesKeyPath("secp256k1/public_key.pem")),
+ publicKeyFile));
+ }
+
+ @Test
+ void verify_should_be_ok() throws URISyntaxException, IOException, GeneralSecurityException {
+ String hexSignature = "ea5b38fd0db5fb3d871c47fde1fa4c4db75d1a9e1c0ac54d826e178ee0e63707176b4e63b4f838bd031f007fffd6a4f71d920a10c48ea53dd1573fa2b58a829e";
+
+ Secp256k1PublicKey pubKey = new Secp256k1PublicKey();
+ String filePath = getResourcesKeyPath("secp256k1/public_key.pem");
+ pubKey.readPublicKey(filePath);
+ LOGGER.info(Hex.encode(pubKey.getKey()));
+
+ assertTrue(pubKey.verify("Test message".getBytes(), Hex.decode(hexSignature)));
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/com/syntifi/crypto/key/checksum/MixedCaseChecksumTest.java b/src/test/java/com/syntifi/crypto/key/checksum/MixedCaseChecksumTest.java
new file mode 100644
index 000000000..814cd9c14
--- /dev/null
+++ b/src/test/java/com/syntifi/crypto/key/checksum/MixedCaseChecksumTest.java
@@ -0,0 +1,37 @@
+package com.syntifi.crypto.key.checksum;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class MixedCaseChecksumTest {
+
+ @Test
+ void eip55ShouldMatchLowerCaseAddress() {
+ assertEquals("fB6916095ca1df60bB79Ce92cE3Ea74c37c5d359",
+ MixedCaseChecksum.checksumEncodeEIP55("fb6916095ca1df60bb79ce92ce3ea74c37c5d359"));
+ }
+
+ @Test
+ void eip55ShouldMatchUpperCaseAddress() {
+ assertEquals("fB6916095ca1df60bB79Ce92cE3Ea74c37c5d359",
+ MixedCaseChecksum.checksumEncodeEIP55("FB6916095CA1DF60BB79CE92CE3EA74C37C5D359"));
+ }
+
+ @Test
+ void cep57houldMatchEmptyAddress() {
+ assertEquals("",
+ MixedCaseChecksum.checksumEncodeEIP55(""));
+ }
+ @Test
+ void cep57houldMatchLowerCaseAddress() {
+ assertEquals("010573D52dFA032716fDC2AE5396987f280304106fC11f6Ac6Ccf287B81dc09ED7",
+ MixedCaseChecksum.checksumEncodeCEP57("010573d52dfa032716fdc2ae5396987f280304106fc11f6ac6ccf287b81dc09ed7"));
+ }
+
+ @Test
+ void cep57houldMatchUpperCaseAddress() {
+ assertEquals("51DA5aE5C39880Bfe4f94B0898332d1BD37e647F72f79Cf23475df1Bb1f85bEA",
+ MixedCaseChecksum.checksumEncodeCEP57("51da5ae5c39880bfe4f94b0898332d1bd37e647f72f79cf23475df1bb1f85bea"));
+ }
+}
diff --git a/src/test/java/com/syntifi/crypto/key/deterministic/HierarchicalDeterministicKeyTest.java b/src/test/java/com/syntifi/crypto/key/deterministic/HierarchicalDeterministicKeyTest.java
new file mode 100644
index 000000000..ab54b2809
--- /dev/null
+++ b/src/test/java/com/syntifi/crypto/key/deterministic/HierarchicalDeterministicKeyTest.java
@@ -0,0 +1,44 @@
+package com.syntifi.crypto.key.deterministic;
+
+import com.syntifi.crypto.key.encdec.Hex;
+import com.syntifi.crypto.key.mnemonic.Language;
+import com.syntifi.crypto.key.mnemonic.MnemonicCode;
+import org.junit.jupiter.api.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class HierarchicalDeterministicKeyTest {
+ private static final Logger LOGGER = LoggerFactory.getLogger(HierarchicalDeterministicKey.class);
+
+ @Test
+ void getMasterKeyFromSeed_seed_should_match() {
+ byte[] init = "ed25519 seed".getBytes(StandardCharsets.UTF_8);
+ assertEquals("a89c4655ab993cf786e4a8517899f0e5ddbdb35ebedc05da341b510bf0c5b8902eb38e1fb071b5a75f0e3b176ddea874ce81fafd6c44cd74c3c381ac5a7ed9fc",
+ Hex.encode(HierarchicalDeterministicKey.getMasterKeyFromSeed(
+ "randomkey".getBytes(StandardCharsets.UTF_8), init)));
+ }
+
+ @Test
+ void getKeyFromSeed_seed_should_match() throws IOException {
+ MnemonicCode mnemonicCode = new MnemonicCode(Language.EN);
+ String words = "shoot island position soft burden budget tooth cruel issue economy destroy above";
+ byte[] seed = mnemonicCode.toSeed(Arrays.asList(words.split(" ")), "");
+ assertEquals("577cd910aede2582668a741d476b45e7998e905a4286f701b87b25923501f9d4ea19513b460bcccbc069ebbe4327a59af3d6463045c4b6fa21a5e7004ccfcc3e",
+ Hex.encode(seed));
+ byte[] init = "ed25519 seed".getBytes(StandardCharsets.UTF_8);
+ assertEquals("17ffa27bec941a557d88ef8d491171eab1dcb147b24b8c7f034766b5a2c425ab" +
+ "6f1eef3d85f72c2500f3d42cad8632725e830d20ffa61d8dfda1c961c84302f0",
+ Hex.encode(HierarchicalDeterministicKey.getMasterKeyFromSeed(seed, init)));
+ int[] path = {44, 397, 0};
+ assertEquals("88793a8eeec537c67ee8d459f1899a47a2f1b752d06a4c793c66fd751df8049838d300841c903867050c222b9f0b43893a5675f0a87756cfce4e3fd71c23334a",
+ Hex.encode(HierarchicalDeterministicKey.getFromSeed(seed, init, path)));
+ assertTrue(true);
+ }
+}
diff --git a/src/test/java/com/syntifi/crypto/key/encdec/Base58Test.java b/src/test/java/com/syntifi/crypto/key/encdec/Base58Test.java
new file mode 100644
index 000000000..4c386b149
--- /dev/null
+++ b/src/test/java/com/syntifi/crypto/key/encdec/Base58Test.java
@@ -0,0 +1,22 @@
+package com.syntifi.crypto.key.encdec;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+class Base58Test {
+ @Test
+ void bs58() {
+ String expected = "16UjcYNBG9GTK4uq2f7yYEbuifqCzoLMGS";
+ byte[] bytes = new byte[]{
+ 0, 60, 23, 110, 101, (byte) 155, (byte) 234,
+ 15, 41, (byte) 163, (byte) 233, (byte) 191, 120, (byte) 128,
+ (byte) 193, 18, (byte) 177, (byte) 179, 27, 77, (byte) 200,
+ 38, 38, (byte) 129, (byte) 135
+ };
+
+ assertEquals(expected, Base58.encode(bytes));
+ assertArrayEquals(Base58.decode(expected), bytes);
+ }
+}
diff --git a/src/test/java/com/syntifi/crypto/key/hash/Blake2bTest.java b/src/test/java/com/syntifi/crypto/key/hash/Blake2bTest.java
new file mode 100644
index 000000000..041c4e9cf
--- /dev/null
+++ b/src/test/java/com/syntifi/crypto/key/hash/Blake2bTest.java
@@ -0,0 +1,16 @@
+package com.syntifi.crypto.key.hash;
+
+import com.syntifi.crypto.key.encdec.Hex;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.nio.charset.StandardCharsets;
+
+public class Blake2bTest {
+
+ @Test
+ void test() {
+ Assertions.assertEquals("44682ea86b704fb3c65cd16f84a76b621e04bbdb3746280f25cf062220e471b4",
+ Hex.encode(Blake2b.digest("أبو يوسف يعقوب بن إسحاق الصبّاح الكندي".getBytes(StandardCharsets.UTF_8), 32)));
+ }
+}
diff --git a/src/test/java/com/syntifi/crypto/key/hash/Keccak256Test.java b/src/test/java/com/syntifi/crypto/key/hash/Keccak256Test.java
new file mode 100644
index 000000000..114637b3f
--- /dev/null
+++ b/src/test/java/com/syntifi/crypto/key/hash/Keccak256Test.java
@@ -0,0 +1,14 @@
+package com.syntifi.crypto.key.hash;
+
+import com.syntifi.crypto.key.encdec.Hex;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+public class Keccak256Test {
+
+ @Test
+ void kekkac256ShouldMatch() {
+ Assertions.assertEquals("a9059cbb2ab09eb219583f4a59a5d0623ade346d962bcd4e46b11da047c9049b",
+ Hex.encode(Keccak256.digest("transfer(address,uint256)".getBytes())));
+ }
+}
diff --git a/src/test/java/com/syntifi/crypto/key/mnemonic/MnemonicCodeTest.java b/src/test/java/com/syntifi/crypto/key/mnemonic/MnemonicCodeTest.java
new file mode 100644
index 000000000..c66f2738a
--- /dev/null
+++ b/src/test/java/com/syntifi/crypto/key/mnemonic/MnemonicCodeTest.java
@@ -0,0 +1,105 @@
+package com.syntifi.crypto.key.mnemonic;
+
+import com.syntifi.crypto.key.AbstractCryptoTests;
+import com.syntifi.crypto.key.encdec.Hex;
+import com.syntifi.crypto.key.mnemonic.exception.MnemonicException;
+import org.junit.jupiter.api.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * examples at
+ * https://github.com/bitcoinbook/bitcoinbook/blob/develop/ch05.asciidoc#creating-an-hd-wallet-from-the-seed
+ *
+ * comparing with the output from
+ * https://iancoleman.io/bip39/
+ */
+public class MnemonicCodeTest extends AbstractCryptoTests {
+ private static final Logger LOGGER = LoggerFactory.getLogger(MnemonicCodeTest.class);
+
+ @Test
+ void getSeedFromWordlist_seed_should_match() throws IOException, MnemonicException.MnemonicWordException, MnemonicException.MnemonicChecksumException, MnemonicException.MnemonicLengthException {
+ String words = "army van defense carry jealous true garbage claim echo media make crunch";
+ List wordList = Arrays.asList(words.split(" "));
+ MnemonicCode mnemonicCode = new MnemonicCode(Language.EN);
+ byte[] seed = mnemonicCode.toSeed(wordList, "");
+ assertEquals("5b56c417303faa3fcba7e57400e120a0ca83ec5a4fc9ffba757fbe63fbd77a89a1a3be4c67196f57c39a88b76373733891bfaba16ed27a813ceed498804c0570", Hex.encode(seed));
+ }
+
+ @Test
+ void getEntropyFromWordlist_entropy_should_match() throws IOException, MnemonicException.MnemonicWordException, MnemonicException.MnemonicChecksumException, MnemonicException.MnemonicLengthException {
+ String words = "army van defense carry jealous true garbage claim echo media make crunch";
+ List wordList = Arrays.asList(words.split(" "));
+ MnemonicCode mnemonicCode = new MnemonicCode(Language.EN);
+ byte[] entropy = mnemonicCode.toEntropy(wordList);
+ assertEquals("0c1e24e5917779d297e14d45f14e1a1a", Hex.encode(entropy));
+ }
+
+ @Test
+ void getWordListFromEntropy_words_should_match() throws IOException, MnemonicException.MnemonicWordException, MnemonicException.MnemonicChecksumException, MnemonicException.MnemonicLengthException {
+ MnemonicCode mnemonicCode = new MnemonicCode(Language.EN);
+ List w = mnemonicCode.toMnemonic(Hex.decode("0c1e24e5917779d297e14d45f14e1a1a"));
+ String words = "army van defense carry jealous true garbage claim echo media make crunch";
+ List wordList = Arrays.asList(words.split(" "));
+ assertEquals(w, wordList);
+ }
+
+ @Test
+ void generateRandomList_should_generate_12_words() throws IOException, MnemonicException.MnemonicWordException, MnemonicException.MnemonicChecksumException, MnemonicException.MnemonicLengthException {
+ MnemonicCode mnemonicCode = new MnemonicCode(Language.EN);
+ List words = mnemonicCode.generateSecureRandomWords();
+ assertEquals(12, words.size());
+ }
+
+ @Test
+ void getEntropyAndSeedFromWordlistPT_entropy_should_match() throws IOException, MnemonicException.MnemonicWordException, MnemonicException.MnemonicChecksumException, MnemonicException.MnemonicLengthException {
+ String words = "alfinete trilogia citar berro graveto teimar evacuar broa debitar jurista irritado cerrado";
+ List wordList = Arrays.asList(words.split(" "));
+ MnemonicCode mnemonicCode = new MnemonicCode(Language.PT);
+ byte[] entropy = mnemonicCode.toEntropy(wordList);
+ assertEquals("0c1e24e5917779d297e14d45f14e1a1a", Hex.encode(entropy));
+ byte[] seed = mnemonicCode.toSeed(wordList, "");
+ assertEquals("0d2d2982b58e3faf152b5e3276198829bea74db45fdbce7fa8f2e2e5c2ac8cd0f634bc35e33960456d09299af65763b7c094ea52c88f60cbb37251c067c6a9f4", Hex.encode(seed));
+ }
+
+ @Test
+ void getEntropyAndSeedFromWordlistFR_entropy_should_match() throws IOException, MnemonicException.MnemonicWordException, MnemonicException.MnemonicChecksumException, MnemonicException.MnemonicLengthException {
+ String words = "amour troupeau couteau brèche gustatif tenaille exécuter capuche dicter lagune jaune cogner";
+ List wordList = Arrays.asList(words.split(" "));
+ MnemonicCode mnemonicCode = new MnemonicCode(Language.FR);
+ byte[] entropy = mnemonicCode.toEntropy(wordList);
+ assertEquals("0c1e24e5917779d297e14d45f14e1a1a", Hex.encode(entropy));
+ byte[] seed = mnemonicCode.toSeed(wordList, "");
+ assertEquals("895debca7a86928a0c4cb5712aefd6d4cf4c7cfd23448ccd5418932e4f00c940089a3501f4f33eaf115f1a689d6c1e54b6bb6d5f40e4791234cb2ac87d62df68", Hex.encode(seed));
+ }
+
+ @Test
+ void getEntropyAndSeedFromWordlistES_entropy_should_match() throws IOException, MnemonicException.MnemonicWordException, MnemonicException.MnemonicChecksumException, MnemonicException.MnemonicLengthException {
+ String words = "amistad túnica costa broma juicio toalla furgón caña domingo masivo maldad código";
+ List wordList = Arrays.asList(words.split(" "));
+ MnemonicCode mnemonicCode = new MnemonicCode(Language.ES);
+ byte[] entropy = mnemonicCode.toEntropy(wordList);
+ assertEquals("0c1e24e5917779d297e14d45f14e1a1a", Hex.encode(entropy));
+ byte[] seed = mnemonicCode.toSeed(wordList, "");
+ assertEquals("847ebfae3823c7ebf1cb2e8313774784751f554bd6c772c4966a860920852f4e96a058f191b0140a3190ca2d47e7766cfa69aae9a2a44b457ef86df1336e6847", Hex.encode(seed));
+ }
+
+/*TODO: Chinese
+ @Test
+ void getEntropyAndSeedFromWordlistCNSimplified_entropy_should_match() throws IOException, MnemonicException.MnemonicWordException, MnemonicException.MnemonicChecksumException, MnemonicException.MnemonicLengthException {
+ String words = "点 挡 眼 器 哥 舒 久 示 止 累 夏 便";
+ List wordList = Arrays.asList(words.split(" "));
+ MnemonicCode mnemonicCode = new MnemonicCode(Language.CNS);
+ byte[] entropy = mnemonicCode.toEntropy(wordList);
+ assertEquals("0c1e24e5917779d297e14d45f14e1a1a", Hex.encode(entropy));
+ byte[] seed = mnemonicCode.toSeed(wordList, "");
+ assertEquals("ca43a6850c4dc61c05d487224e827cc9b5bcab49fd28a206bcf19f11c39d9dd4329e55e33940ae551b9fa295c9b73770c77d878fd63dc000b3995f4dc62f3f63", Hex.encode(seed));
+ }
+*/
+}
diff --git a/src/test/resources/ed25519/public_key.pem b/src/test/resources/ed25519/public_key.pem
new file mode 100644
index 000000000..1305d7057
--- /dev/null
+++ b/src/test/resources/ed25519/public_key.pem
@@ -0,0 +1,3 @@
+-----BEGIN PUBLIC KEY-----
+MCowBQYDK2VwAyEAMvrHQK2XpoIhUSeEhwSGXW0zAViqtNYUQ52r/7LsvjY=
+-----END PUBLIC KEY-----
diff --git a/src/test/resources/ed25519/public_key_hex b/src/test/resources/ed25519/public_key_hex
new file mode 100644
index 000000000..50b1e0e3a
--- /dev/null
+++ b/src/test/resources/ed25519/public_key_hex
@@ -0,0 +1 @@
+0132fac740ad97a682215127848704865d6d330158aab4d614439dabffb2ecbe36
\ No newline at end of file
diff --git a/src/test/resources/ed25519/secret_key.pem b/src/test/resources/ed25519/secret_key.pem
new file mode 100644
index 000000000..9776294ea
--- /dev/null
+++ b/src/test/resources/ed25519/secret_key.pem
@@ -0,0 +1,3 @@
+-----BEGIN PRIVATE KEY-----
+MC4CAQAwBQYDK2VwBCIEIEb1trdv9YzoExEUjPDl1XEgdD+AZX6gGtgGZO4k6MBd
+-----END PRIVATE KEY-----
diff --git a/src/test/resources/secp256k1/public_key.pem b/src/test/resources/secp256k1/public_key.pem
new file mode 100644
index 000000000..21af35019
--- /dev/null
+++ b/src/test/resources/secp256k1/public_key.pem
@@ -0,0 +1,4 @@
+-----BEGIN PUBLIC KEY-----
+MDYwEAYHKoZIzj0CAQYFK4EEAAoDIgADDfbouWROtMFMsD1StqFjNmrKgvo/dg5o
+iZ4MCjMrvdo=
+-----END PUBLIC KEY-----
diff --git a/src/test/resources/secp256k1/public_key_hex b/src/test/resources/secp256k1/public_key_hex
new file mode 100644
index 000000000..3d934a36e
--- /dev/null
+++ b/src/test/resources/secp256k1/public_key_hex
@@ -0,0 +1 @@
+02030df6e8b9644eb4c14cb03d52b6a163366aca82fa3f760e68899e0c0a332bbdda
\ No newline at end of file
diff --git a/src/test/resources/secp256k1/secret_key.pem b/src/test/resources/secp256k1/secret_key.pem
new file mode 100644
index 000000000..256831dba
--- /dev/null
+++ b/src/test/resources/secp256k1/secret_key.pem
@@ -0,0 +1,3 @@
+-----BEGIN EC PRIVATE KEY-----
+MC4CAQEEIAkPrgbH8BEc6g9sGdXgPp722oqB+qA2aymlKO+ObcXYoAcGBSuBBAAK
+-----END EC PRIVATE KEY-----