diff --git a/Cargo.lock b/Cargo.lock
index f67caa6d6598d38319dd92cabdb8032d63789e12..fd66718b3c6b1c8b84d9672f2c93da7e54f9c698 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -133,6 +133,7 @@ dependencies = [
  "cryptoxide",
  "reqwest",
  "scrypt",
+ "serde",
  "serde_json",
 ]
 
@@ -774,6 +775,20 @@ name = "serde"
 version = "1.0.130"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.130"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
 
 [[package]]
 name = "serde_json"
diff --git a/Cargo.toml b/Cargo.toml
index 9ffe0e7031305eba4e2b7dd66053db28c6316c11..217a318bb415c075c88f981437b7901631c0974c 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -11,4 +11,5 @@ base64 = "0.13.0"
 bs58 = "0.4.0"
 cryptoxide = "0.3.3"
 reqwest = { version = "0.11.4", features = ["json", "blocking"] }
+serde = { version = "1.0.130", features = ["derive"] }
 serde_json = "1.0.67"
\ No newline at end of file
diff --git a/src/bma/blockchain/current.rs b/src/bma/blockchain/current.rs
new file mode 100644
index 0000000000000000000000000000000000000000..39e1a70d2db5cfc17b7e2fd3f9ca4a8764788c49
--- /dev/null
+++ b/src/bma/blockchain/current.rs
@@ -0,0 +1,33 @@
+use serde_json::Value;
+
+use crate::bma::BmaNode;
+
+pub struct Block<> {
+    pub version: u64,
+    pub currency: String,
+    pub number: u64,
+    pub hash: String
+}
+
+impl Block {
+
+    pub fn from_json(json: &Value) -> Block {
+        Block {
+            currency: json["currency"].as_str().unwrap().into(),
+            version: json["version"].as_u64().unwrap(),
+            number: json["number"].as_u64().unwrap(),
+            hash: json["hash"].as_str().unwrap().into()
+        }
+    }
+
+    pub fn blockstamp(&self) -> String {
+        format!("{}-{}", self.number, self.hash)
+    }
+}
+
+pub fn current(node: &BmaNode) -> Block {
+    let address = node.get_address();
+    let resp = reqwest::blocking::get(format!("{}/blockchain/current", address)).expect("Could not fetch /blockchain/current from distant node");
+    let json = &resp.json().expect("Could not parse /blockchain/current JSON response");
+    Block::from_json(&json)
+}
\ No newline at end of file
diff --git a/src/bma/blockchain/mod.rs b/src/bma/blockchain/mod.rs
new file mode 100644
index 0000000000000000000000000000000000000000..30e81422593ce10a3c85b854914782ac9ff723f4
--- /dev/null
+++ b/src/bma/blockchain/mod.rs
@@ -0,0 +1,3 @@
+mod current;
+
+pub use current::current;
\ No newline at end of file
diff --git a/src/bma/lookup_identity.rs b/src/bma/lookup_identity.rs
new file mode 100644
index 0000000000000000000000000000000000000000..c775a02835d55d702fead745870a74987e529e73
--- /dev/null
+++ b/src/bma/lookup_identity.rs
@@ -0,0 +1,51 @@
+use crate::bma::{BmaNode};
+use serde_json::Value;
+
+pub struct LookupResult<'a>(pub(crate) &'a serde_json::Value);
+
+pub struct LookupIdentity {
+    pub public: String,
+    pub uid: String,
+    pub blockstamp: String,
+    pub signature: String,
+    pub revoked: bool
+}
+
+impl LookupIdentity {
+    pub fn new(public: &str, uid: &str, blockstamp: &str, signature: &str, revoked: bool) -> LookupIdentity {
+        LookupIdentity {
+            public: String::from(public),
+            uid: String::from(uid),
+            blockstamp: String::from(blockstamp),
+            signature: String::from(signature),
+            revoked
+        }
+    }
+}
+
+pub fn lookup(node: BmaNode, uid_or_pub: String) -> () {
+    let address = node.get_address();
+    let resp = reqwest::blocking::get(format!("{}/wot/lookup/{}", address, uid_or_pub)).expect("Could not fetch lookup data from distant node");
+    lookup_print(&LookupResult(&resp.json().expect("Could not get JSON result from distant node")));
+}
+
+pub fn lookup_print(lookup_result: &LookupResult) -> () {
+    lookup2identities(lookup_result).iter().for_each(|i| println!("{} {} {}", i.public, i.uid, if i.revoked { "revoked" } else { "valid" }))
+}
+
+pub fn lookup2identities(lookup_result: &LookupResult) -> Vec<LookupIdentity> {
+    let mut identities: Vec<LookupIdentity> = vec![];
+    for result in lookup_result.0["results"].as_array().unwrap() {
+        let pubkey = result["pubkey"].as_str().unwrap();
+        let empty: &Vec<Value> = &Vec::new();
+        let uids: &Vec<Value> = result["uids"].as_array().unwrap_or(empty);
+        for anUid in uids.iter() {
+            let uid = anUid["uid"].as_str().unwrap();
+            let blockstamp = anUid["meta"]["timestamp"].as_str().unwrap();
+            let signature = anUid["self"].as_str().unwrap();
+            let revoked = anUid["revoked"].as_bool().unwrap();
+            identities.push(LookupIdentity::new(pubkey, uid, blockstamp, signature,revoked));
+        }
+    }
+    identities
+}
\ No newline at end of file
diff --git a/src/bma/mod.rs b/src/bma/mod.rs
index 806b855d2e106989c71b9532e64ab63172eb9cb7..4f89f3a1b885dfeef663d382e6272db74986d135 100644
--- a/src/bma/mod.rs
+++ b/src/bma/mod.rs
@@ -3,21 +3,11 @@ use reqwest;
 use std::fmt::Display;
 pub use crate::bma::node::BmaNode;
 use serde_json::Value;
+use reqwest::blocking::Response;
+use crate::bma::lookup_identity::{LookupIdentity, lookup2identities, lookup_print, LookupResult};
+use std::process::id;
 
 mod node;
-
-pub fn lookup(node: BmaNode, uid_or_pub: String) -> () {
-    let address = node.get_address();
-    let resp = reqwest::blocking::get(format!("{}/wot/lookup/{}", address, uid_or_pub)).expect("Could not fetch lookup data from distant node");
-    let res_json: serde_json::Value = resp.json().expect("Could not get JSON result from distant node");
-    for result in res_json["results"].as_array().unwrap() {
-        let pubkey = result["pubkey"].as_str().unwrap();
-        let empty: &Vec<Value> = &Vec::new();
-        let uids: &Vec<Value> = result["uids"].as_array().unwrap_or(empty);
-        for anUid in uids.iter() {
-            let uid = anUid["uid"].as_str().unwrap();
-            let revoked = anUid["revoked"].as_bool().unwrap();
-            println!("{} {} {}", pubkey, uid, if revoked { "revoked" } else { "valid" })
-        }
-    }
-}
\ No newline at end of file
+pub mod lookup_identity;
+pub mod blockchain;
+pub mod wot;
\ No newline at end of file
diff --git a/src/bma/wot/mod.rs b/src/bma/wot/mod.rs
new file mode 100644
index 0000000000000000000000000000000000000000..9a5ec04101fd005d78083ec64ea039c2ccaf525d
--- /dev/null
+++ b/src/bma/wot/mod.rs
@@ -0,0 +1,33 @@
+use std::fmt::{Display, Formatter};
+
+use reqwest::{blocking, StatusCode};
+use serde::{Deserialize, Serialize};
+
+use crate::bma::BmaNode;
+use crate::bma::lookup_identity::LookupIdentity;
+
+#[derive(Serialize, Deserialize)]
+struct BmaCertification {
+    cert: String
+}
+
+impl Display for BmaCertification {
+    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+        f.write_str(self.cert.as_str())
+    }
+}
+
+pub fn certify(node: &BmaNode, idty: &String, sig: &String) -> Result<(), String> {
+    let mut cert = BmaCertification { cert: format!("{}{}\n", idty, sig) };
+    let address = node.get_address();
+    let client = reqwest::blocking::Client::new();
+    let resp = client.post(format!("{}/wot/certify", address))
+        .json(&cert)
+        .send()
+        .expect("Error during POST /wot/certify");
+
+    if resp.status() != 200 {
+        return Err(format!("KO status : {}, message: {}", resp.status(), resp.text().unwrap()));
+    }
+    Ok(())
+}
\ No newline at end of file
diff --git a/src/cli/certify.rs b/src/cli/certify.rs
new file mode 100644
index 0000000000000000000000000000000000000000..c5ac251a032f501b47792fb838bd092294f741b9
--- /dev/null
+++ b/src/cli/certify.rs
@@ -0,0 +1,54 @@
+use crate::bma::BmaNode;
+use crate::bma::lookup_identity::{LookupResult, lookup2identities, lookup_print};
+use duniter_mini_client::crypto::scrypt_duniter_key::ScryptDuniterKey;
+use duniter_mini_client::crypto::duniter_key::ToDuniterKey;
+use std::process::id;
+
+pub fn certify(node: BmaNode, uid_or_pub: String) -> Result<(), String> {
+    let address = node.get_address();
+    println!("Fetching identity using \"{}\" pattern...", uid_or_pub);
+    let resp = reqwest::blocking::get(format!("{}/wot/lookup/{}", address, uid_or_pub)).expect("Could not fetch lookup data from distant node");
+    let res_json = &resp.json().expect("Could not get JSON result from distant node");
+    let res_json: LookupResult = LookupResult(res_json);
+    let results = lookup2identities(&res_json);
+    let idty = match results.len() {
+        0 => return Err(String::from("No matching identity found")),
+        1 => &results[0],
+        _ => {
+            lookup_print(&res_json);
+            return Err(format!("Too much identities found ({})", results.len()))
+        },
+    };
+
+    if idty.revoked {
+        return Err(format!("Identity {} {} is revoked and cannot be certified anymore.", idty.public, idty.uid))
+    }
+
+    println!("Deriving key for signature...");
+    let keyring = ScryptDuniterKey::new(String::from("fakesalt"), String::from("fakepasswd")).derive(); // TODO: get salt/passwd from file/cli
+    println!("Get current block for blockstamp...");
+    let current = crate::bma::blockchain::current(&node);
+    let currency = &current.currency;
+    let issuer = keyring.get_public_base58();
+    let current_blockstamp = &current.blockstamp();
+
+    let certification = format!("Version: 10
+Type: Certification
+Currency: {}
+Issuer: {}
+IdtyIssuer: {}
+IdtyUniqueID: {}
+IdtyTimestamp: {}
+IdtySignature: {}
+CertTimestamp: {}
+", currency, issuer, idty.public, idty.uid, idty.blockstamp, idty.signature, current_blockstamp);
+    println!("{}", certification);
+    println!("Sign and submit?");
+    let signature = keyring.sign(&certification);
+    println!("Signature: {}", signature);
+
+    println!("Submitting...");
+    // crate::bma::wot::certify(&node, &idty, &issuer, &current_blockstamp, &signature.to_string());
+    crate::bma::wot::certify(&node, &certification, &signature.to_string());
+    Ok(())
+}
\ No newline at end of file
diff --git a/src/cli/mod.rs b/src/cli/mod.rs
index 98956430c4bbe6f8aa35e26b2b111635a12cd20d..cff0cae62074813d0abb92d9645a46fe621c8ad6 100644
--- a/src/cli/mod.rs
+++ b/src/cli/mod.rs
@@ -4,6 +4,8 @@ use crate::cli::Command::*;
 use crate::bma::BmaNode;
 use std::env;
 
+pub(crate) mod certify;
+
 const DEFAULT_NODE: &str = "https://g1-test.duniter.org";
 
 pub enum Command {
@@ -15,6 +17,8 @@ pub enum Command {
     KEYRING(String, String),
     /// Search an identity on a Duniter node (BMA API)
     LOOKUP(BmaNode, String),
+    /// Search an identity on a Duniter node (BMA API) and certify it
+    CERTIFY(BmaNode, String),
     /// Unknown command
     UNKNOWN(String)
 }
@@ -48,6 +52,12 @@ impl Command {
                 let bma_node = BmaNode::new(env::var("DUNITER_NODE").unwrap_or(default).as_str());
                 LOOKUP(bma_node, uid_or_pub)
             },
+            "cert" => {
+                let uid_or_pub = args.next().expect("UID or pubkey must be provided");
+                let default = String::from(DEFAULT_NODE);
+                let bma_node = BmaNode::new(env::var("DUNITER_NODE").unwrap_or(default).as_str());
+                CERTIFY(bma_node, uid_or_pub)
+            },
             _ => UNKNOWN(String::from(command)),
         }
     }
diff --git a/src/main.rs b/src/main.rs
index 5436f90797f6d73965dc896b8ea4c32e5a39f876..c8733333f5de4ae466eba3362f806352b5484c5a 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -4,7 +4,7 @@ use duniter_mini_client::{compute_pub, compute_sec, compute_key};
 
 use crate::cli::Command;
 pub use crate::cli::Command::{PUB, UNKNOWN};
-use crate::cli::Command::{SEC, KEYRING, LOOKUP};
+use crate::cli::Command::{SEC, KEYRING, LOOKUP, CERTIFY};
 use duniter_mini_client::crypto::duniter_key::ScryptDuniterKey;
 
 mod cli;
@@ -14,11 +14,14 @@ mod bma;
 fn main() {
     let command = Command::from(env::args());
 
+    // TODO: Result<(), String>
+
     match command {
         PUB(salt, passwd) => println!("{}", compute_pub(ScryptDuniterKey::new(salt, passwd))),
         SEC(salt, passwd) => println!("{}", compute_sec(ScryptDuniterKey::new(salt, passwd))),
         KEYRING(salt, passwd) => println!("{}", compute_key(ScryptDuniterKey::new(salt, passwd))),
-        LOOKUP(bma_node, uid_or_pub) => bma::lookup(bma_node, uid_or_pub),
+        LOOKUP(bma_node, uid_or_pub) => bma::lookup_identity::lookup(bma_node, uid_or_pub),
+        CERTIFY(bma_node, uid_or_pub) => cli::certify::certify(bma_node, uid_or_pub).unwrap_or_else(|e| eprintln!("{}", e)),
         UNKNOWN(cmd) => eprintln!("Unknown command {}", cmd),
     };
 }
\ No newline at end of file