diff --git a/Cargo.lock b/Cargo.lock
index 2822d07794c31f548556ccc29f92786ff919d222..c87308c3e0cf5a9e4e336c6fd6a81f77b40ea35b 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -33,7 +33,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
 name = "duniter-keys"
-version = "0.2.0"
+version = "0.3.0"
 dependencies = [
  "base58 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "base64 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
diff --git a/keys/Cargo.toml b/keys/Cargo.toml
index 3d6199a2c984a17f9d4ac8492a7e2f212f8908f8..4b41f9dca79740bce4565b3b61ba72f5114d01e6 100644
--- a/keys/Cargo.toml
+++ b/keys/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "duniter-keys"
-version = "0.2.0"
+version = "0.3.0"
 authors = ["nanocryk <nanocryk@duniter.org>"]
 description = "Manage cryptographic keys for the Duniter project."
 repository = "https://git.duniter.org/nodes/rust/duniter-rs"
diff --git a/keys/ed25519.rs b/keys/ed25519.rs
index 8bb13a9108dcc9a44544c50a8b522ca3fc5c2b78..72cc61fbaf750f932ab1ca25536f21c55b3b481b 100644
--- a/keys/ed25519.rs
+++ b/keys/ed25519.rs
@@ -29,7 +29,7 @@ use base64;
 use base64::DecodeError;
 use crypto;
 
-use super::BaseConvertionError;
+use super::{BaseConvertionError, PrivateKey as PrivateKeyMethods, PublicKey as PublicKeyMethods};
 
 /// Store a ed25519 signature.
 #[derive(Clone, Copy)]
@@ -211,9 +211,54 @@ impl super::PrivateKey for PrivateKey {
     }
 }
 
+/// Store a ed25519 cryptographic key pair (`PublicKey` + `PrivateKey`)
+#[derive(Debug, Copy, Clone)]
+pub struct KeyPair {
+    /// Store a Ed25519 public key.
+    pub pubkey: PublicKey,
+    /// Store a Ed25519 private key.
+    pub privkey: PrivateKey,
+}
+
+impl Display for KeyPair {
+    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
+        write!(f, "({}, hidden)", self.pubkey.to_base58())
+    }
+}
+
+impl PartialEq<KeyPair> for KeyPair {
+    fn eq(&self, other: &KeyPair) -> bool {
+        self.pubkey.eq(&other.pubkey) && self.privkey.eq(&other.privkey)
+    }
+}
+
+impl Eq for KeyPair {}
+
+impl super::KeyPair for KeyPair {
+    type Signature = Signature;
+    type PublicKey = PublicKey;
+    type PrivateKey = PrivateKey;
+
+    fn public_key(&self) -> PublicKey {
+        self.pubkey
+    }
+
+    fn private_key(&self) -> PrivateKey {
+        self.privkey
+    }
+
+    fn sign(&self, message: &[u8]) -> Signature {
+        self.private_key().sign(message)
+    }
+
+    fn verify(&self, message: &[u8], signature: &Self::Signature) -> bool {
+        self.public_key().verify(message, signature)
+    }
+}
+
 /// Keypair generator with given parameters for `scrypt` keypair function.
 #[derive(Debug, Copy, Clone)]
-pub struct KeyPairGenerator {
+pub struct KeyPairFromSaltedPasswordGenerator {
     /// The log2 of the Scrypt parameter `N`.
     log_n: u8,
     /// The Scrypt parameter `r`
@@ -222,32 +267,32 @@ pub struct KeyPairGenerator {
     p: u32,
 }
 
-impl KeyPairGenerator {
+impl KeyPairFromSaltedPasswordGenerator {
     /// Create a `KeyPairGenerator` with default arguments `(log_n: 12, r: 16, p: 1)`
-    pub fn with_default_parameters() -> KeyPairGenerator {
-        KeyPairGenerator {
+    pub fn with_default_parameters() -> KeyPairFromSaltedPasswordGenerator {
+        KeyPairFromSaltedPasswordGenerator {
             log_n: 12,
             r: 16,
             p: 1,
         }
     }
 
-    /// Create a `KeyPairGenerator` with given arguments.
+    /// Create a `KeyPairFromSaltedPasswordGenerator` with given arguments.
     ///
     /// # Arguments
     ///
     /// - log_n - The log2 of the Scrypt parameter N
     /// - r - The Scrypt parameter r
     /// - p - The Scrypt parameter p
-    pub fn with_parameters(log_n: u8, r: u32, p: u32) -> KeyPairGenerator {
-        KeyPairGenerator { log_n, r, p }
+    pub fn with_parameters(log_n: u8, r: u32, p: u32) -> KeyPairFromSaltedPasswordGenerator {
+        KeyPairFromSaltedPasswordGenerator { log_n, r, p }
     }
 
     /// Create a keypair based on a given password and salt.
     ///
     /// The [`PublicKey`](struct.PublicKey.html) will be able to verify messaged signed with
     /// the [`PrivateKey`](struct.PrivateKey.html).
-    pub fn generate(&self, password: &[u8], salt: &[u8]) -> (PrivateKey, PublicKey) {
+    pub fn generate(&self, password: &[u8], salt: &[u8]) -> KeyPair {
         let mut seed = [0u8; 32];
         crypto::scrypt::scrypt(
             password,
@@ -257,14 +302,17 @@ impl KeyPairGenerator {
         );
 
         let (private, public) = crypto::ed25519::keypair(&seed);
-        (PrivateKey(private), PublicKey(public))
+        KeyPair {
+            pubkey: PublicKey(public),
+            privkey: PrivateKey(private),
+        }
     }
 }
 
 #[cfg(test)]
 mod tests {
     use super::*;
-    use {PrivateKey, PublicKey, Signature};
+    use {KeyPair, Signature};
 
     #[test]
     fn base58_private_key() {
@@ -284,12 +332,12 @@ mod tests {
             super::PrivateKey::from_base58(private58).unwrap()
         );
 
-        assert_eq!(
-            super::PrivateKey::from_base58(
-                "468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iA\
-                fZACm7djh",
-            ).unwrap_err(),
-            BaseConvertionError::InvalidKeyLendth(67, 64)
+        assert_eq!(
+            super::PrivateKey::from_base58(
+                "468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iA\
+                fZACm7djh",
+            ).unwrap_err(),
+            BaseConvertionError::InvalidKeyLendth(67, 64)
         );
         assert_eq!(
             super::PrivateKey::from_base58(
@@ -381,12 +429,12 @@ mod tests {
              MMmQCRerlF/3pc4sAcsnexsxBseA/3lY03KlONqJBAg==",
         ).unwrap();
 
-        let message = "Version: 10
-Type: Identity
-Currency: duniter_unit_test_currency
-Issuer: DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV
-UniqueID: tic
-Timestamp: 0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855
+        let message = "Version: 10
+Type: Identity
+Currency: duniter_unit_test_currency
+Issuer: DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV
+UniqueID: tic
+Timestamp: 0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855
 ";
 
         let sig = prikey.sign(message.as_bytes());
@@ -394,4 +442,21 @@ Timestamp: 0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855
         assert_eq!(sig, expected_signature);
         assert!(pubkey.verify(message.as_bytes(), &sig));
     }
+
+    #[test]
+    fn keypair_generate_sign_and_verify() {
+        let keypair = KeyPairFromSaltedPasswordGenerator::with_default_parameters()
+            .generate("password".as_bytes(), "salt".as_bytes());
+
+        let message = "Version: 10
+Type: Identity
+Currency: duniter_unit_test_currency
+Issuer: DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV
+UniqueID: tic
+Timestamp: 0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855
+";
+
+        let sig = keypair.sign(message.as_bytes());
+        assert!(keypair.verify(message.as_bytes(), &sig));
+    }
 }
diff --git a/keys/lib.rs b/keys/lib.rs
index 49842a68658245ab861addd34c659bd00596a0ab..9b41694458270e08c5381919e60b93f31c1bda12 100644
--- a/keys/lib.rs
+++ b/keys/lib.rs
@@ -21,21 +21,21 @@
 //! # Usage
 //!
 //! ```
-//! use duniter_keys::{Signature, PublicKey, PrivateKey};
-//! use duniter_keys::ed25519::KeyPairGenerator;
+//! use duniter_keys::{Signature, PublicKey, PrivateKey, KeyPair};
+//! use duniter_keys::ed25519::KeyPairFromSaltedPasswordGenerator;
 //!
-//! let generator = KeyPairGenerator::with_default_parameters();
+//! let generator = KeyPairFromSaltedPasswordGenerator::with_default_parameters();
 //!
-//! let (private_key, public_key) = generator.generate(
+//! let keypair = generator.generate(
 //!     b"password",
 //!     b"salt"
 //! );
 //!
 //! let message = "Hello, world!";
 //!
-//! let signature = private_key.sign(&message.as_bytes());
+//! let signature = keypair.sign(&message.as_bytes());
 //!
-//! assert!(public_key.verify(&message.as_bytes(), &signature));
+//! assert!(keypair.pubkey.verify(&message.as_bytes(), &signature));
 //! ```
 //!
 //! # Format
@@ -147,3 +147,25 @@ pub trait PrivateKey: Clone + Display + Debug + PartialEq + Eq + ToBase58 {
     /// Sign a message with this private key.
     fn sign(&self, message: &[u8]) -> Self::Signature;
 }
+
+/// Store a cryptographic key pair (`PublicKey` + `PrivateKey`)
+pub trait KeyPair: Clone + Display + Debug + PartialEq + Eq {
+    /// Signature type of associated cryptosystem.
+    type Signature: Signature;
+    /// PublicKey type of associated cryptosystem.
+    type PublicKey: PublicKey;
+    /// PrivateKey type of associated cryptosystem.
+    type PrivateKey: PrivateKey;
+
+    /// Get `PublicKey`
+    fn public_key(&self) -> Self::PublicKey;
+
+    /// Get `PrivateKey`
+    fn private_key(&self) -> Self::PrivateKey;
+
+    /// Sign a message with private key.
+    fn sign(&self, message: &[u8]) -> Self::Signature;
+
+    /// Verify a signature with public key.
+    fn verify(&self, message: &[u8], signature: &Self::Signature) -> bool;
+}