diff --git a/public/crypto.js b/public/crypto.js
index c483ce12ef622d501be66b193729573ba6693a8d..166cd8a7b0b88d13866851e6b6590bd3185c7250 100644
--- a/public/crypto.js
+++ b/public/crypto.js
@@ -1,4 +1,4 @@
-export {b58, saltPass2seed, seed2keyPair,idSecPass2rawAll, raw2b58, idSecPass2cleanKeys}
+export {b58,b16, saltPass2seed, seed2keyPair,idSecPass2rawAll, raw2b58, idSecPass2cleanKeys}
 import {nacl} from "./vendors/nacl.js";
 
 async function idSecPass2rawAll(idSec,pass) {
@@ -36,6 +36,7 @@ async function saltPass2seed(idSec,pass) {
 //inspired by bs58 and base-x module
 const ALPHABET = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';
 const b58 = basex(ALPHABET);
+const b16 = basex('0123456789abcdef');
 function basex (ALPHABET) {
 	const ALPHABET_MAP = {};
 	const BASE = ALPHABET.length;
diff --git a/public/dunikey.html b/public/dunikey.html
new file mode 100644
index 0000000000000000000000000000000000000000..c988075610326ce22a1e3f29da24a73e46a5ef51
--- /dev/null
+++ b/public/dunikey.html
@@ -0,0 +1,70 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <meta charset="utf-8" />
+    <title>idSec+password or seed or keys => file.dunikey</title>
+    <link rel="icon" type="image/png" sizes="16x16" href="img/favicon.png">
+    <link rel="canonical" href="https://gsper.duniter.io/dunikey.html"/>
+
+    <meta property="og:type" content="website" />
+    <meta property="og:locale" content="fr_FR" />
+    <meta property="og:title" content="idSec+password or seed or keys => file.dunikey" />
+    <meta property="og:site_name" content="gsper.duniter.io" />
+    <meta property="og:url" content="https://gsper.duniter.io/dunikey.html" />
+    <meta property="og:image" content="https://gsper.duniter.io/img/gsper-logo.png" />
+    <meta property="og:description" content="Converti vos secrets d'authentification pour générer un fichier d'authentification file.dunikey" />
+    <style>
+        * {font-family: sans-serif; box-sizing: border-box; margin: 0; padding: 0}
+        h1{font-weight: normal; width: 100%; text-align: center; line-height: 70px;}
+        fieldset {margin: 10px;}
+        .logic {margin-left: 50px;}
+        input{border: 0; padding: 5px;width: 100%; line-height: 60px; font-size: 16px;}
+        button {width: 100%; height: 32px}
+        .gift {position: absolute; top: 10px; left: 10px;}
+        .gift a{text-decoration-color: rgba(0%,0%,100%,0.2); color: rgba(0%,0%,100%,0.7);}
+    </style>
+</head>
+<body>
+<h1>DunikeyMaker</h1>
+<fieldset>
+    <fieldset>
+    <input id="idSec" type="password" placeholder="Identifiant secret"/>
+    <input id="pass" type="password" placeholder="Mot de passe"/>
+</fieldset>
+    <div class="logic">ou</div>
+<fieldset>
+    <input id="seed" type="text" placeholder="Seed"/>
+</fieldset>
+    <div class="logic">ou</div>
+<fieldset>
+    <input id="secretkey" type="text" placeholder="Clef privée (ou secrète)"/>
+    <input id="pubkey" type="text" placeholder="Clef publique"/>
+</fieldset>
+</fieldset>
+<div class="logic">et</div>
+<fieldset>
+    <input id="filename" type="text" placeholder="Nom du fichier à générer"/>
+    <button id="compute">Générer le fichier .dunikey.yml</button>
+</fieldset>
+    <div class="gift">
+        <a href="https://cesium.g1.1000i100.fr/#/app/wot/2sZF6j2PkxBDNAqUde7Dgo5x3crkerZpQ4rBqqJGn8QT/1000i100" target="_blank">Ǧ'spère vous avoir été utile.<br/>
+            Soutenez mon travail avec un don !<br/>
+            👤 : 1000i100 🔑 : 2sZF6j2P
+        </a>
+    </div>
+<footer>
+    <a href="https://git.duniter.org/tools/gsper" id="sourceRibbon" title="Fork me !">
+        <span>AGPL-3.0</span>
+        <span>Logiciel Libre</span>
+        <span>Remonter aux sources</span>
+        <style>
+            #sourceRibbon {position: fixed; top: 15px; right: -110px; z-index: 500; transform: rotate(45deg); text-align: center;width: 300px; box-shadow: 0 0 5px #000; text-decoration: none;}
+            #sourceRibbon span {background-color: #0A0; color: white; font-family: sans-serif; display: block; padding: 1px 30px; font-size: 9pt; margin: -1px; text-shadow: 0 0 3px rgba(0,0,0,.5);}
+            #sourceRibbon span + span {background-color:  #06A; font-size: 11pt;}
+            #sourceRibbon span + span + span {background-color: #E80; font-size: 10pt;}
+        </style>
+    </a>
+</footer>
+<script type="module" src="dunikey.js"></script>
+</body>
+</html>
diff --git a/public/dunikey.js b/public/dunikey.js
new file mode 100644
index 0000000000000000000000000000000000000000..f3059d2a2e27cf150fa1ea28c3b4cc3536e9d1a6
--- /dev/null
+++ b/public/dunikey.js
@@ -0,0 +1,54 @@
+import * as dKey from "./crypto.js";
+
+addEventsListeners([
+		document.getElementById("idSec"),
+		document.getElementById("pass"),
+	], "keyup change",
+	async function () {
+		const idSec = document.getElementById("idSec").value;
+		const pass = document.getElementById("pass").value;
+		const keys = await dKey.idSecPass2cleanKeys(idSec, pass);
+		document.getElementById("seed").value = keys.seed;
+		document.getElementById("secretkey").value = keys.secretKey;
+		document.getElementById("pubkey").value = keys.publicKey;
+
+	});
+addEventsListeners([
+		document.getElementById("seed"),
+	], "keyup change",
+	async function () {
+		let rawSeed;
+		const seed = document.getElementById("seed").value;
+		console.log(seed.length);
+		if (seed.length === 64) rawSeed = dKey.b16.decode(seed);
+		else rawSeed = dKey.b58.decode(seed);
+		const keys = await dKey.seed2keyPair(rawSeed);
+		document.getElementById("secretkey").value = dKey.b58.encode(keys.secretKey);
+		document.getElementById("pubkey").value = dKey.b58.encode(keys.publicKey);
+	});
+addEventsListeners([
+		document.getElementById("compute"),
+	], "click",
+	async function () {
+		const filename = document.getElementById("filename").value;
+		const secretkey = document.getElementById("secretkey").value;
+		const pubkey = document.getElementById("pubkey").value;
+		exportAsFile(filename, `pub: ${pubkey}\nsec: ${secretkey}`);
+	});
+
+function exportAsFile(fileName, data) {
+	const a = document.createElement('a');
+	a.setAttribute('download', fileName + '.dunikey.yml');
+	a.setAttribute('href', 'data:text/yaml;charset=utf-8,' + encodeURIComponent(data));
+	clickOn(a);
+}
+function clickOn(element) {
+	const event = new MouseEvent('click', {'view': window, 'bubbles': true, 'cancelable': true});
+	element.dispatchEvent(event);
+}
+function addEventsListeners(triggerNodes, events, functions) {
+	if (!triggerNodes.length) triggerNodes = [triggerNodes];
+	if (typeof events !== "object") events = events.split(" ");
+	if (typeof functions !== "object") functions = [functions];
+	for (let n of triggerNodes) events.forEach(e => functions.forEach(f => n.addEventListener(e, f)));
+}