diff --git a/duniterpy/key/base58.py b/duniterpy/key/base58.py
index 5e8a67147da602a72c93f72704d47634c719d131..a4f97a8d3e63c4d5bc37bfaf77d62c31e9ee2802 100644
--- a/duniterpy/key/base58.py
+++ b/duniterpy/key/base58.py
@@ -1,12 +1,25 @@
+from typing import Union
+
 import base58
+
 from ..helpers import ensure_str, ensure_bytes
 
 
 class Base58Encoder(object):
     @staticmethod
-    def encode(data):
+    def encode(data: Union[str, bytes]) -> str:
+        """
+        Return Base58 string from data
+
+        :param data: Bytes or string data
+        """
         return ensure_str(base58.b58encode(ensure_bytes(data)))
 
     @staticmethod
-    def decode(data):
+    def decode(data: str) -> bytes:
+        """
+        Decode Base58 string data and return bytes
+
+        :param data: Base58 string
+        """
         return base58.b58decode(data)
diff --git a/duniterpy/key/encryption_key.py b/duniterpy/key/encryption_key.py
index 4caa2aeb6f3c449c967df2bbd1b208dd408f8ecd..1ee32fe54d1d9a88ce518319651472cce97da77c 100644
--- a/duniterpy/key/encryption_key.py
+++ b/duniterpy/key/encryption_key.py
@@ -3,13 +3,14 @@ duniter public and private keys
 
 @author: inso
 """
+from typing import Union
 
 import libnacl.public
 from pylibscrypt import scrypt
+
 from .base58 import Base58Encoder
 from ..helpers import ensure_bytes
 
-
 SEED_LENGTH = 32  # Length of the key
 crypto_sign_BYTES = 64
 SCRYPT_PARAMS = {'N': 4096,
@@ -19,36 +20,83 @@ SCRYPT_PARAMS = {'N': 4096,
 
 
 class SecretKey(libnacl.public.SecretKey):
-    def __init__(self, salt, password):
+    """
+    Raw Public Key Encryption Class
+    """
+
+    def __init__(self, salt: Union[str, bytes], password: Union[str, bytes]) -> None:
+        """
+        Create SecretKey key pair instance from salt and password credentials
+
+        :param salt: Salt credential
+        :param password: Password credential
+        """
         salt = ensure_bytes(salt)
         password = ensure_bytes(password)
         seed = scrypt(password, salt,
-                    SCRYPT_PARAMS['N'], SCRYPT_PARAMS['r'], SCRYPT_PARAMS['p'],
-                    SEED_LENGTH)
+                      SCRYPT_PARAMS['N'], SCRYPT_PARAMS['r'], SCRYPT_PARAMS['p'],
+                      SEED_LENGTH)
 
         super().__init__(seed)
         self.public_key = PublicKey(Base58Encoder.encode(self.pk))
 
-    def encrypt(self, pubkey, noonce, text):
+    def encrypt(self, pubkey: str, nonce: Union[str, bytes], text: Union[str, bytes]) -> str:
+        """
+        Encrypt message text with the public key of the recipient and a noonce
+
+        The nonce must be a 24 character string (you can use libnacl.utils.rand_nonce() to get one)
+        and unique for each encrypted message.
+
+        Return base58 encoded encrypted message
+
+        :param pubkey: Base58 encoded public key of the recipient
+        :param nonce: Unique nonce
+        :param text: Message to encrypt
+        :return:
+        """
         text_bytes = ensure_bytes(text)
-        noonce_bytes = ensure_bytes(noonce)
+        nonce_bytes = ensure_bytes(nonce)
         recipient_pubkey = PublicKey(pubkey)
-        crypt_bytes = libnacl.public.Box(self, recipient_pubkey).encrypt(text_bytes, noonce_bytes)
+        crypt_bytes = libnacl.public.Box(self, recipient_pubkey).encrypt(text_bytes, nonce_bytes)
         return Base58Encoder.encode(crypt_bytes[24:])
 
-    def decrypt(self, pubkey, noonce, text):
+    def decrypt(self, pubkey: str, nonce: Union[str, bytes], text: str) -> str:
+        """
+        Decrypt encrypted message text with recipient public key and the unique nonce used by the sender.
+
+        :param pubkey: Public key of the recipient
+        :param nonce: Unique nonce used by the sender
+        :param text: Encrypted message
+        :return:
+        """
         sender_pubkey = PublicKey(pubkey)
-        noonce_bytes = ensure_bytes(noonce)
+        nonce_bytes = ensure_bytes(nonce)
         encrypt_bytes = Base58Encoder.decode(text)
-        decrypt_bytes = libnacl.public.Box(self, sender_pubkey).decrypt(encrypt_bytes, noonce_bytes)
+        decrypt_bytes = libnacl.public.Box(self, sender_pubkey).decrypt(encrypt_bytes, nonce_bytes)
         return decrypt_bytes.decode('utf-8')
 
 
 class PublicKey(libnacl.public.PublicKey):
-    def __init__(self, pubkey):
+    def __init__(self, pubkey: str) -> None:
+        """
+        Create instance of libnacl ed25519 sign PublicKey from a base58 public key
+
+        :param pubkey: Base58 public key
+        """
         key = Base58Encoder.decode(pubkey)
         super().__init__(key)
 
-    def base58(self):
+    def base58(self) -> str:
+        """
+        Return a base58 encoded string of the public key
+        """
         return Base58Encoder.encode(self.pk)
 
+    def encrypt_seal(self, message: Union[str, bytes]) -> bytes:
+        """
+        Encrypt message with a curve25519 version of the ed25519 public key
+
+        :param message: Message to encrypt
+        """
+        curve25519_public_key = libnacl.crypto_sign_ed25519_pk_to_curve25519(self.pk)
+        return libnacl.crypto_box_seal(ensure_bytes(message), curve25519_public_key)
diff --git a/duniterpy/key/signing_key.py b/duniterpy/key/signing_key.py
index aac85a22d216bed37ed7b7122d8e7acc1804a199..ae5b2384e3e68e0c855bf5ff2843ae06a89f5dc6 100644
--- a/duniterpy/key/signing_key.py
+++ b/duniterpy/key/signing_key.py
@@ -3,9 +3,11 @@ duniter public and private keys
 
 @author: inso
 """
+from typing import Optional
 
 import libnacl.sign
 from pylibscrypt import scrypt
+
 from .base58 import Base58Encoder
 from ..helpers import ensure_bytes
 
@@ -14,27 +16,27 @@ crypto_sign_BYTES = 64
 
 
 class ScryptParams:
-    def __init__(self, N, r, p):
+    def __init__(self, n: int, r: int, p: int) -> None:
         """
         Init a ScryptParams instance with crypto parameters
 
-        :param int N: scrypt param N
-        :param int r: scrypt param r
-        :param int p: scrypt param p
+        :param n: scrypt param N
+        :param r: scrypt param r
+        :param p: scrypt param p
         """
-        self.N = N
+        self.N = n
         self.r = r
         self.p = p
 
 
 class SigningKey(libnacl.sign.Signer):
-    def __init__(self, salt, password, scrypt_params=None):
+    def __init__(self, salt: str, password: str, scrypt_params: Optional[ScryptParams] = None) -> None:
         """
         Init a SigningKey object from credentials
 
-        :param str salt: Secret salt passphrase credential
-        :param str password: Secret password credential
-        :param ScryptParams scrypt_params: ScryptParams instance
+        :param salt: Secret salt passphrase credential
+        :param password: Secret password credential
+        :param scrypt_params: ScryptParams instance
         """
         if scrypt_params is None:
             scrypt_params = ScryptParams(4096, 16, 1)
@@ -47,3 +49,15 @@ class SigningKey(libnacl.sign.Signer):
 
         super().__init__(seed)
         self.pubkey = Base58Encoder.encode(self.vk)
+
+    def decrypt_seal(self, message: bytes) -> str:
+        """
+        Decrypt message with a curve25519 version of the ed25519 key pair
+
+        :param message: Encrypted message
+
+        :return:
+        """
+        curve25519_public_key = libnacl.crypto_sign_ed25519_pk_to_curve25519(self.vk)
+        curve25519_secret_key = libnacl.crypto_sign_ed25519_sk_to_curve25519(self.sk)
+        return libnacl.crypto_box_seal_open(message, curve25519_public_key, curve25519_secret_key).decode('utf-8')
diff --git a/examples/decrypt_binary_encrypted_message.py b/examples/decrypt_binary_encrypted_message.py
new file mode 100644
index 0000000000000000000000000000000000000000..4da1ff4f155efb82f3a0bebfcdc118237597d54f
--- /dev/null
+++ b/examples/decrypt_binary_encrypted_message.py
@@ -0,0 +1,37 @@
+import getpass
+import sys
+
+from duniterpy.key import SigningKey
+
+if __name__ == '__main__':
+
+    if len(sys.argv) < 2:
+        print("""
+        Usage:
+            python decrypt_message.py ENCRYPTED_MESSAGE_FILEPATH
+        """)
+
+    # capture encrypted message filepath argument
+    signed_message_path = sys.argv[1]
+
+    # prompt hidden user entry
+    salt = getpass.getpass("Enter your passphrase (salt): ")
+
+    # prompt hidden user entry
+    password = getpass.getpass("Enter your password: ")
+
+    # Create key object
+    signing_key_instance = SigningKey(salt, password)
+
+    # open encrypted message file
+    with open(signed_message_path, 'rb') as file_handler:
+        encrypted_message = file_handler.read()
+
+    # Decrypt the message!
+    try:
+        message = signing_key_instance.decrypt_seal(encrypted_message)
+        print("Decrypted message:")
+    except ValueError as error:
+        message = str(error)
+
+    print(message)
diff --git a/examples/save_binary_encrypted_message.py b/examples/save_binary_encrypted_message.py
new file mode 100644
index 0000000000000000000000000000000000000000..dd2e510464ecc1eb870ea317e7b4723b7af1a154
--- /dev/null
+++ b/examples/save_binary_encrypted_message.py
@@ -0,0 +1,22 @@
+from duniterpy.key import PublicKey
+
+################################################
+
+ENCRYPTED_MESSAGE_FILENAME = 'duniter_encrypted_message.bin'
+
+if __name__ == '__main__':
+    # Ask public key of the recipient
+    pubkeyBase58 = input("Enter public key of the message recipient: ")
+
+    # Enter the message
+    message = input("Enter your message: ")
+
+    # Encrypt the message, only the recipient secret key will be able to decrypt the message
+    pubkey_instance = PublicKey(pubkeyBase58)
+    encrypted_message = pubkey_instance.encrypt_seal(message)
+
+    # Save encrypted message in a file
+    with open(ENCRYPTED_MESSAGE_FILENAME, 'wb') as file_handler:
+        file_handler.write(encrypted_message)
+
+    print("Encrypted message saved in file ./{0}".format(ENCRYPTED_MESSAGE_FILENAME))