diff --git a/javascript/src/CryptoData.js b/javascript/src/CryptoData.js
new file mode 100644
index 0000000000000000000000000000000000000000..9261a2d92c9f12334b2388a9224f90b2e2653b19
--- /dev/null
+++ b/javascript/src/CryptoData.js
@@ -0,0 +1,58 @@
+function CryptoData() {}
+
+CryptoData.prototype.set = function(obj) {
+  for(var member in obj) {
+    this[member] = JSON.parse(JSON.stringify(obj[member]))
+  }
+};
+
+CryptoData.prototype.serialize = function() {
+  return JSON.stringify(this)
+};
+
+CryptoData.prototype.deserialize = function(serialized) {
+  var obj = JSON.parse(serialized);
+  this.set(obj)
+};
+
+CryptoData.prototype.setPublicKey = function(publicKey) {
+  this["publicKey"] = publicKey
+};
+
+CryptoData.prototype.getPublicKey = function() {
+  return this["publicKey"]
+};
+
+CryptoData.prototype.setPrivateKey = function(privateKey) {
+  this["privateKey"] = privateKey
+};
+
+CryptoData.prototype.getPrivateKey = function() {
+  return this["privateKey"]
+};
+
+CryptoData.prototype.setx509Certificate = function(x509Certificate) {
+  this["x509Certificate"] = x509Certificate
+};
+
+CryptoData.prototype.getx509Certificate = function() {
+  return this["x509Certificate"]
+};
+
+CryptoData.prototype.setKeyUUID = function(keyUUID) {
+  this["keyUUID"] = keyUUID
+};
+
+CryptoData.prototype.getKeyUUID = function() {
+  return this["keyUUID"]
+};
+
+CryptoData.prototype.setChain = function(chain) {
+  this["chain"] = chain
+};
+
+CryptoData.prototype.getChain = function() {
+  return this["chain"]
+};
+
+export default CryptoData;
\ No newline at end of file
diff --git a/javascript/src/Identity.js b/javascript/src/Identity.js
new file mode 100644
index 0000000000000000000000000000000000000000..43a52eafc057f67cf2feb961db87e5392f015f4d
--- /dev/null
+++ b/javascript/src/Identity.js
@@ -0,0 +1,51 @@
+function Identity() {
+}
+
+Identity.prototype.set = function(obj) {
+  for(var member in obj) {
+    this[member] = JSON.parse(JSON.stringify(obj[member]))
+  }
+};
+
+Identity.prototype.serialize = function() {
+  return JSON.stringify(this)
+};
+
+Identity.prototype.deserialize = function(serialized) {
+  var obj = JSON.parse(serialized);
+  this.set(obj)
+};
+
+Identity.prototype.setAuthentication = function(cryptoData) {
+  this["authentication"] = cryptoData
+};
+
+Identity.prototype.getAuthentication = function() {
+  return this["authentication"]
+};
+
+Identity.prototype.setPinCode = function(pinCode) {
+  this["pinCode"] = pinCode
+};
+
+Identity.prototype.getPinCode = function() {
+  return this["pinCode"]
+};
+
+Identity.prototype.setPassport = function(passportUUID, cryptoData) {
+  if(this["passports"] === undefined || this["passports"] === null) {
+    this["passports"] = {}
+  }
+
+  this["passports"][passportUUID] = cryptoData
+};
+
+Identity.prototype.getPassport = function(passportUUID) {
+  if(this["passports"] === undefined || this["passports"] === null) {
+    this["passports"] = {}
+  }
+
+  return this["passports"][passportUUID]
+};
+
+export default Identity;
\ No newline at end of file
diff --git a/javascript/src/iframe/viamapi-iframe.js b/javascript/src/iframe/viamapi-iframe.js
index e89858ef7ff00eceabaaa383623e49d4c4a36d6e..ff4a071dc6b1c28a3aa696efa98cc9696f1690c0 100644
--- a/javascript/src/iframe/viamapi-iframe.js
+++ b/javascript/src/iframe/viamapi-iframe.js
@@ -1,1177 +1,27 @@
-import {createDeviceHash} from '../utilities/appUtility';
-import {LOGIN_MODES} from '../constants';
-
-const libmime = require('libmime');
 const QRCode = require('qrcode');
-const pkijs = require('pkijs');
-const asn1js = require('asn1js');
-const pvutils = require('pvutils');
 const Penpal = require('penpal').default;
 
+import {
+  createDeviceHash,
+  destroyIdentityFromLocalStorage,
+  encodeResponse,
+  listIdentitiesFromLocalStorage, makeid
+} from '../utilities/appUtility';
+import {LOGIN_MODES} from '../constants';
+import {
+  createOneTimePassportCertificate,
+  createPassportCertificate,
+  decryptMessage,
+  encryptMessage, signEmail
+} from '../utilities/signingUtilities';
+import CryptoData from '../CryptoData';
+import Identity from '../Identity';
+
 const penpalMethods = require('../../temp/penpal-methods').default;
 const WopiAPI = require('./wopiapi-iframe');
 const CollaboraAPI = require('./collaboraapi-iframe');
 const ViamAPI = require('../../temp/viamapi');
 
-//*********************************************************************************
-
-const CERTIFIATE_Version_1 = 0;
-const CERTIFIATE_Version_3 = 2;
-
-//these bit fields are reversed, WTF!
-const KEY_USAGE_DigitalSignature	= 0x80;//01;
-const KEY_USAGE_NonRepudiation		= 0x40;//02;
-const KEY_USAGE_KeyEncipherment		= 0x20;//04;
-const KEY_USAGE_DataEncipherment	= 0x10;//08;
-const KEY_USAGE_KeyAgreement		= 0x08;//10;
-const KEY_USAGE_KeyCertSign			= 0x04;//20;
-const KEY_USAGE_CRLSign				= 0x02;//40;
-//const KEY_USAGE_EncipherOnly		= 0x01;//80; // Not used for now. Must be used together with KEY_USAGE_KeyAgreement (maybe should be ORed as a constant directly?)
-//const KEY_USAGE_DecipherOnly		= 0x80;//0100; // If used, modify "KeyUsage" extension array buffer size and appropriate bit operators to accomodate for extra byte
-
-const KEY_USAGE_LeafCertificate		= KEY_USAGE_DigitalSignature | KEY_USAGE_NonRepudiation | KEY_USAGE_KeyEncipherment | KEY_USAGE_DataEncipherment;
-const KEY_USAGE_CertificateAuthority= KEY_USAGE_DigitalSignature | KEY_USAGE_KeyCertSign | KEY_USAGE_CRLSign;
-
-
-const OID_EXT_KEY_USAGE_Any     	= "2.5.29.37.0";
-const OID_ID_PKIX_ServerAuth		= "1.3.6.1.5.5.7.3.1";
-const OID_ID_PKIX_ClientAuth		= "1.3.6.1.5.5.7.3.2";
-const OID_ID_PKIX_CodeSigning		= "1.3.6.1.5.5.7.3.3";
-const OID_ID_PKIX_EmailProtection	= "1.3.6.1.5.5.7.3.4";
-const OID_ID_PKIX_TimeStamping	    = "1.3.6.1.5.5.7.3.8";
-const OID_ID_PKIX_OCSPSigning		= "1.3.6.1.5.5.7.3.9";
-// const OID_EXT_KEY_USAGE_MS...	= "1.3.6.1.4.1.311.10.3.1"; // Microsoft Certificate Trust List signing
-// const OID_EXT_KEY_USAGE_MS...	= "1.3.6.1.4.1.311.10.3.4";  // Microsoft Encrypted File System
-const OID_PKCS7_Data                = "1.2.840.113549.1.7.1";
-const OID_PKCS7_SignedData          = "1.2.840.113549.1.7.2";
-const OID_PKCS7_EnvelopedData       = "1.2.840.113549.1.7.3";
-const OID_PKCS9_EmailAddress        = "1.2.840.113549.1.9.1";
-const OID_PKCS9_ContentType         = "1.2.840.113549.1.9.3";
-const OID_PKCS9_MessageDigest       = "1.2.840.113549.1.9.4";
-const OID_PKCS9_SigningTime         = "1.2.840.113549.1.9.5";
-
-const defaultAlgorithms = {
-  hashAlg: "SHA-256",
-  signAlg: "RSASSA-PKCS1-v1_5",
-  keyLength: 2048
-};
-
-const AES_encryptionVariant_Password  = 2;
-const encryptionAlgorithm = {
-  name: "AES-CBC",
-  length: 128
-};
-
-//*********************************************************************************
-// Returns promise, resolved to keyPair object {publicKey, privateKey}
-//*********************************************************************************
-function generateKeys(algorithms) {
-  //region Get a "crypto" extension
-  const crypto = pkijs.getCrypto();
-  if (typeof crypto === "undefined") {
-    return Promise.reject("No WebCrypto extension found");
-  }
-  //endregion Get a "crypto" extension
-
-  if (!algorithms) {
-    algorithms = defaultAlgorithms;
-  } else {
-    if (!algorithms.hashAlg) {
-      algorithms.hashAlg = defaultAlgorithms.hashAlg;
-    }
-    if (!algorithms.signAlg) {
-      algorithms.signAlg = defaultAlgorithms.signAlg;
-    }
-    if (!algorithms.keyLength) {
-      algorithms.keyLength = defaultAlgorithms.keyLength;
-    }
-  }
-
-  //region Get default algorithm parameters for key generation
-  const algorithm = pkijs.getAlgorithmParameters(algorithms.signAlg, "generatekey");
-  if("hash" in algorithm.algorithm) {
-    algorithm.algorithm.hash.name = algorithms.hashAlg;
-  }
-  algorithm.algorithm.modulusLength = algorithms.keyLength;
-  //endregion
-
-  return crypto.generateKey(algorithm.algorithm, true, algorithm.usages);
-}
-
-//*********************************************************************************
-function createCertificate(certData, issuerData = null)
-{
-
-  if (typeof certData === "undefined") {
-    return Promise.reject("No Certificate data provided");
-  }
-
-  if (typeof certData.subject === "undefined") {
-    return Promise.reject("No Certificate subject data provided");
-  }
-
-
-  //region Get a "crypto" extension
-  const crypto = pkijs.getCrypto();
-
-  if (typeof crypto === "undefined") {
-    return Promise.reject("No WebCrypto extension found");
-  }
-  //endregion Get a "crypto" extension
-
-  //region Initial variables
-  let sequence = Promise.resolve();
-
-  const certificate = new pkijs.Certificate();
-  let publicKey;
-  let privateKey;
-
-  let certificateBuffer;// = new ArrayBuffer(0); // ArrayBuffer with loaded or created CERT
-  let privateKeyBuffer;// = new ArrayBuffer(0);
-  let publicKeyBuffer;// = new ArrayBuffer(0);
-
-  //endregion Initial variables
-
-  if (certData.keyPair) {
-    //region Create a new key pair
-    sequence = sequence.then(() =>
-    {
-      return certData.keyPair;
-    });
-    //endregion Create a new key pair
-
-  } else {
-    //region Create a new key pair
-    sequence = sequence.then(() =>
-    {
-      return generateKeys(certData.algorithms);
-    });
-    //endregion Create a new key pair
-  }
-
-  //region Store new key in an interim variables
-  sequence = sequence.then(keyPair =>
-  {
-    publicKey = keyPair.publicKey;
-    privateKey = keyPair.privateKey;
-  }, error => Promise.reject(`Error during key generation: ${error}`));
-  //endregion Store new key in an interim variables
-
-  //region Exporting public key into "subjectPublicKeyInfo" value of certificate
-  sequence = sequence.then(() =>
-    certificate.subjectPublicKeyInfo.importKey(publicKey)
-  );
-  //endregion Exporting public key into "subjectPublicKeyInfo" value of certificate
-
-  sequence = sequence.then(
-    () => crypto.digest({ name: "SHA-1" }, certificate.subjectPublicKeyInfo.subjectPublicKey.valueBlock.valueHex),
-    error => Promise.reject(`Error during importing public key: ${error}`)
-  );
-
-  //region Fill in cert data
-  sequence = sequence.then(subjKeyIdBuffer =>
-  {
-
-    //region Put a static values
-    certificate.version = CERTIFIATE_Version_3;
-
-    const serialNumberBuffer = new ArrayBuffer(20);
-    const serialNumberView = new Uint8Array(serialNumberBuffer);
-    pkijs.getRandomValues(serialNumberView);
-    // noinspection JSUnresolvedFunction
-    certificate.serialNumber = new asn1js.Integer({ valueHex: serialNumberView });
-    //endregion Put a static values
-
-    //region Subject
-    // For reference http://oidref.com/2.5.4.3
-    if (certData.subject.commonName) {
-      // noinspection JSUnresolvedFunction
-      certificate.subject.typesAndValues.push(new pkijs.AttributeTypeAndValue({
-        type: "2.5.4.3", // Common name
-        value: new asn1js.PrintableString({ value: certData.subject.commonName })
-      }));
-    }
-
-    if (certData.subject.country) {
-      // noinspection JSUnresolvedFunction
-      certificate.subject.typesAndValues.push(new pkijs.AttributeTypeAndValue({
-        type: "2.5.4.6", // Country name
-        value: new asn1js.PrintableString({ value: certData.subject.country })
-      }));
-    }
-
-    if (certData.subject.locality) {
-      // noinspection JSUnresolvedFunction
-      certificate.subject.typesAndValues.push(new pkijs.AttributeTypeAndValue({
-        type: "2.5.4.7", // Locality Name
-        value: new asn1js.PrintableString({ value: certData.subject.locality })
-      }));
-    }
-
-    if (certData.subject.state) {
-      // noinspection JSUnresolvedFunction
-      certificate.subject.typesAndValues.push(new pkijs.AttributeTypeAndValue({
-        type: "2.5.4.8", // State or Province name
-        value: new asn1js.PrintableString({ value: certData.subject.state })
-      }));
-    }
-
-    if (certData.subject.organization) {
-      // noinspection JSUnresolvedFunction
-      certificate.subject.typesAndValues.push(new pkijs.AttributeTypeAndValue({
-        type: "2.5.4.10", // Organization name
-        value: new asn1js.PrintableString({ value: certData.subject.organization })
-      }));
-    }
-
-    if (certData.subject.organizationUnit) {
-      // noinspection JSUnresolvedFunction
-      certificate.subject.typesAndValues.push(new pkijs.AttributeTypeAndValue({
-        type: "2.5.4.11", // Organization unit name
-        value: new asn1js.PrintableString({ value: certData.subject.organizationUnit })
-      }));
-    }
-
-    if (certData.subject.email) {
-      // noinspection JSUnresolvedFunction
-      certificate.subject.typesAndValues.push(new pkijs.AttributeTypeAndValue({
-        type: OID_PKCS9_EmailAddress, // Email, deprecated but still widely used
-        value: new asn1js.IA5String({ value: certData.subject.email })
-      }));
-    }
-    //endregion Subject
-
-
-    //region Issuer
-    if (issuerData && issuerData.certificate) {
-      certificate.issuer = issuerData.certificate.subject;
-    } else {
-      certificate.issuer = certificate.subject;
-    }
-    //endregion Issuer
-
-    //region Validity
-    if (!certData.validity) {
-      certData.validity = {}
-    }
-
-    if (certData.validity.notBefore) {
-      certificate.notBefore.value = certData.validity.notBefore; //date
-    } else {
-      const tmp = new Date();
-      certificate.notBefore.value = new Date(tmp.getFullYear(), tmp.getMonth(), tmp.getDate(), 0, 0, 0);
-    }
-
-    if (certData.validity.notAfter) {
-      certificate.notAfter.value = certData.validity.notAfter; //date
-    } else {
-      const tmp = certificate.notBefore.value;
-      const validYears = certData.validity.validYears || 1;
-      certificate.notAfter.value = new Date(tmp.getFullYear() + validYears, tmp.getMonth(), tmp.getDate(), 23, 59, 59);
-    }
-    //endregion Validity
-
-    //region Extensions
-    certificate.extensions = []; // Extensions are not a part of certificate by default, it's an optional array
-
-    //region "BasicConstraints" extension
-    const basicConstr = new pkijs.BasicConstraints({
-      cA: !!certData.isCA,
-      //pathLenConstraint: 0 //TODO add logic for leaf CA
-    });
-
-    certificate.extensions.push(new pkijs.Extension({
-      extnID: "2.5.29.19",
-      critical: true,
-      extnValue: basicConstr.toSchema().toBER(false),
-      parsedValue: basicConstr // Parsed value for well-known extensions
-    }));
-    //endregion "BasicConstraints" extension
-
-    //region "KeyUsage" extension
-    const keyUsageBuffer = new ArrayBuffer(1);
-    const keyUsageBitView = new Uint8Array(keyUsageBuffer);
-
-    keyUsageBitView[0] = !!certData.isCA ? KEY_USAGE_CertificateAuthority : KEY_USAGE_LeafCertificate;
-
-    // noinspection JSUnresolvedFunction
-    const keyUsage = new asn1js.BitString({ valueHex: keyUsageBuffer });
-
-    certificate.extensions.push(new pkijs.Extension({
-      extnID: "2.5.29.15",
-      critical: true,
-      extnValue: keyUsage.toBER(false),
-      parsedValue: keyUsage // Parsed value for well-known extensions
-    }));
-    //endregion "KeyUsage" extension
-
-    //region "ExtKeyUsage" extension
-    if (!certData.isCA && certData.subject.email) {
-      const extKeyUsage = new pkijs.ExtKeyUsage({
-        keyPurposes: [
-          OID_ID_PKIX_EmailProtection
-        ]
-      });
-
-      certificate.extensions.push(new pkijs.Extension({
-        extnID: "2.5.29.37",
-        critical: false,
-        extnValue: extKeyUsage.toSchema().toBER(false),
-        parsedValue: extKeyUsage // Parsed value for well-known extensions
-      }));
-    }
-    //endregion "ExtKeyUsage" extension
-
-    //region "SubjAltName" extension
-    if (certData.subject.email || certData.subject.url) {
-
-      const names = [];
-
-      if (certData.subject.email) {
-        names.push(new pkijs.GeneralName({
-          type: 1, // rfc822Name
-          value: certData.subject.email
-        }));
-      }
-
-      if (certData.subject.url) {
-        names.push(new pkijs.GeneralName({
-          type: 2, // dNSName
-          value: certData.subject.url
-        }));
-      }
-
-      const subjAltNames = new pkijs.GeneralNames({
-        names: names
-      });
-
-      certificate.extensions.push(new pkijs.Extension({
-        extnID: "2.5.29.17",
-        critical: false,
-        extnValue: subjAltNames.toSchema().toBER(false),
-        parsedValue: subjAltNames // Parsed value for well-known extensions
-      }));
-    }
-    //endregion "SubjAltName" extension
-
-
-    //region "SubjectKeyIdentifier" extension
-    const subjKeyId = new asn1js.OctetString({ valueHex: subjKeyIdBuffer });
-
-    certificate.extensions.push(new pkijs.Extension({
-      extnID: "2.5.29.14",
-      critical: false,
-      extnValue: subjKeyId.toBER(false),
-      parsedValue: subjKeyId // Parsed value for well-known extensions
-    }));
-    //endregion "SubjectKeyIdentifier" extension
-
-    /* COULD NOT GET IT WORKING
-        //region "AuthorityKeyIdentifier" extension
-        if (issuerData && issuerData.certificate) {
-
-          let issuerSubjKeyExt = null;
-
-          let extLength = issuerData.certificate.extensions.length;
-          for (var i = 0; i < extLength; i++) {
-            let ext = issuerData.certificate.extensions[i];
-            if (ext.extnID == "2.5.29.14") {
-              issuerSubjKeyExt = ext;
-              break;
-            }
-          }
-
-          if (issuerSubjKeyExt) {
-
-            const asn1 = asn1js.fromBER(issuerSubjKeyExt.extnValue);
-
-            const authKeyIdentifier = new AuthorityKeyIdentifier({
-              keyIdentifier: new asn1js.OctetString({
-                //isHexOnly: true,
-                //valueHex: issuerSubjKeyExt.parsedValue.valueBlock.valueHex
-                value: new asn1js.OctetString({ valueHex: subjKeyIdBuffer })
-              })
-            });
-            // const authKeyIdentifier = new AuthorityKeyIdentifier({
-            // 	//keyIdentifier: new asn1js.OctetString({ valueHex: subjKeyIdBuffer })
-
-            // });
-
-            certificate.extensions.push(new Extension({
-              extnID: "2.5.29.35",
-              critical: false,
-              extnValue: authKeyIdentifier.toSchema().toBER(false),
-              parsedValue: authKeyIdentifier // Parsed value for well-known extensions
-            }));
-          }
-        }
-        //endregion "AuthorityKeyIdentifier" extension
-    */
-    //endregion Extensions
-  });
-  //region Fill in cert data
-
-
-  //region Signing final certificate
-  sequence = sequence.then(() => {
-      let signerKey = (issuerData && issuerData.privateKey) ? issuerData.privateKey : privateKey;
-      return certificate.sign(signerKey, (certData.algorithms && certData.algorithms.hashAlg) ? certData.algorithms.hashAlg : defaultAlgorithms.hashAlg)
-    },
-    error => Promise.reject(`Error during exporting public key: ${error}`));
-  //endregion
-
-  //region Encode and store certificate
-  sequence = sequence.then(() =>
-  {
-    certificateBuffer = certificate.toSchema(true).toBER(false);
-  }, error => Promise.reject(`Error during signing: ${error}`));
-  //endregion
-
-  //region Exporting public key
-  sequence = sequence.then(() =>
-    crypto.exportKey("spki", publicKey)
-  );
-  //endregion
-
-  //region Store exported public key on Web page
-  sequence = sequence.then(result =>
-  {
-    publicKeyBuffer = result;
-  }, error => Promise.reject(`Error during exporting of public key: ${error}`));
-  //endregion
-
-  //region Exporting private key
-  sequence = sequence.then(() =>
-    crypto.exportKey("pkcs8", privateKey)
-  );
-  //endregion
-
-  //region Store exported key on Web page
-  sequence = sequence.then(result =>
-  {
-    privateKeyBuffer = result;
-  }, error => Promise.reject(`Error during exporting of private key: ${error}`));
-  //endregion
-
-  return sequence.then(() => {
-
-    const result = {
-      certificate: certificate,
-      certificatePEM: encodePEM(certificateBuffer, "CERTIFICATE"),
-      publicKey: publicKey,
-      publicKeyPEM: encodePEM(publicKeyBuffer, "PUBLIC KEY"),
-      privateKey: privateKey,
-      privateKeyPEM: encodePEM(privateKeyBuffer, "PRIVATE KEY")
-    };
-    return result;
-  });
-}
-
-function formatPEM(pemString) {
-  const lineWidth = 64;
-  let resultString = "";
-  let start = 0;
-  let piece;
-  while ((piece = pemString.substring(start, start + lineWidth)).length > 0) {
-    start += lineWidth;
-    resultString += piece + '\r\n';
-  }
-  return resultString;
-}
-
-function encodePEM(buffer, label) {
-  const bufferString = String.fromCharCode.apply(null, new Uint8Array(buffer));
-
-  const header = `-----BEGIN ${label}-----\r\n`;
-  const base64formatted = formatPEM(window.btoa(bufferString));
-  const footer = `-----END ${label}-----\r\n`;
-  const resultString = header + base64formatted + footer;
-
-  return resultString;
-}
-
-function decodePEM(pemString) {
-  const pemStripped = pemString.replace(/(-----(BEGIN|END) [a-zA-Z ]*-----|\r|\n)/g, '');
-  const pemDecoded = window.atob(pemStripped);
-  const buffer = pvutils.stringToArrayBuffer(pemDecoded);
-  return buffer;
-}
-
-//*********************************************************************************
-function parseCertificate(certificatePEM) {
-  const certificateBuffer = decodePEM(certificatePEM);
-  const asn1 = asn1js.fromBER(certificateBuffer);
-  const certificate = new pkijs.Certificate({ schema: asn1.result });
-  return certificate;
-}
-
-//*********************************************************************************
-function parsePublicKey(publicKeyPEM) {
-  const publicKeyBuffer = decodePEM(publicKeyPEM);
-  const crypto = pkijs.getCrypto();
-  const publicKeyPromise = crypto.importKey(
-    "spki",
-    publicKeyBuffer,
-    {   //these are the algorithm options
-      name: "RSASSA-PKCS1-v1_5",
-      hash: {name: "SHA-256"}, //can be "SHA-1", "SHA-256", "SHA-384", or "SHA-512"
-    },
-    true,
-    ["verify"]
-  );
-  return publicKeyPromise;
-}
-
-//*********************************************************************************
-function encryptMessage(message, password, label) {
-  const buffer = pvutils.stringToArrayBuffer(message);
-  const secret = pvutils.stringToArrayBuffer(password);
-
-  const enveloped = new pkijs.EnvelopedData();
-  enveloped.addRecipientByPreDefinedData(secret, {}, AES_encryptionVariant_Password);
-  return enveloped.encrypt(encryptionAlgorithm, buffer).
-  then(
-    () => {
-      const content = new pkijs.ContentInfo();
-      content.contentType = OID_PKCS7_EnvelopedData;
-      content.content = enveloped.toSchema();
-      const ber = content.toSchema().toBER(false);
-      return encodePEM(ber, label)
-    },
-    error => Promise.reject(`encryption error: ${error}`)
-  )
-}
-
-//*********************************************************************************
-function decryptMessage(message, password) {
-  if (canTryPincode()) {
-    const secret = pvutils.stringToArrayBuffer(password);
-    const buffer = decodePEM(message);
-
-    const asn1 = asn1js.fromBER(buffer);
-    const content = new pkijs.ContentInfo({schema: asn1.result});
-    const enveloped = new pkijs.EnvelopedData({schema: content.content});
-    return enveloped.decrypt(0, {preDefinedData: secret}).then(result => {
-      return pvutils.arrayBufferToString(result);
-    }).catch(() => {
-      return Promise.reject(failPincodeAttempt(password));
-    });
-  } else {
-    return Promise.reject(getTimeLeftInLocalStorage());
-  }
-}
-
-//*********************************************************************************
-function getBlockFinishTimeInLocalStorage() {
-  return localStorage.getItem("blockFinishTime") || getCurrentTime();
-}
-
-function getCurrentTime() {
-  return Math.floor(new Date().getTime() / 1000);
-}
-
-function getTimeLeftInLocalStorage() {
-  const blockFinishTime = getBlockFinishTimeInLocalStorage();
-  const timeNow = getCurrentTime();
-  const seconds = (blockFinishTime - timeNow) % 60;
-  let minutes = Math.floor((blockFinishTime - timeNow) / 60);
-  minutes %= 60;
-
-  const left = "Your identity has been locked. Try again in " + minutes + " minutes and " + seconds + " seconds.";
-  return left;
-}
-
-function failPincodeAttempt(password) {
-  let message = "Wrong pincode";
-  if (password !== '00000000') {
-    let attempt = localStorage.getItem("attempt") || 1;
-    attempt = parseInt(attempt);
-    if (attempt === 9) {
-      const identitiesTemp = listIdentitiesFromLocalStorage();
-      for (let i in identitiesTemp) {
-        destroyIdentityFromLocalStorage(i);
-      }
-      message = "9 failed attempts. Identity is revoked!";
-      localStorage.removeItem("attempt");
-    } else if (attempt % 3 === 0) {
-      const timeNow = getCurrentTime();
-      const blockFinishTime = timeNow + 300;
-      localStorage.setItem("blockFinishTime", blockFinishTime);
-      localStorage.setItem("attempt", attempt + 1);
-      message = "3 failed attempts. Identity is locked!";
-    } else {
-      localStorage.setItem("attempt", attempt + 1);
-    }
-  }
-  return message;
-}
-
-function canTryPincode() {
-  const timeNow = getCurrentTime();
-  const blockFinishTime = getBlockFinishTimeInLocalStorage();
-  if (blockFinishTime <= timeNow) {
-    localStorage.removeItem("blockFinishTime");
-    return true;
-  } else {
-    return false;
-  }
-}
-
-//*********************************************************************************
-function parsePrivateKey(privateKeyPEM) {
-  const privateKeyBuffer = decodePEM(privateKeyPEM);
-  const crypto = pkijs.getCrypto();
-  const privateKeyPromise = crypto.importKey(
-    "pkcs8",
-    privateKeyBuffer,
-    {   //these are the algorithm options
-      name: "RSASSA-PKCS1-v1_5",
-      hash: {name: "SHA-256"}, //can be "SHA-1", "SHA-256", "SHA-384", or "SHA-512"
-    },
-    true,
-    ["sign"]
-  );
-  return privateKeyPromise;
-}
-
-
-function createPassportCertificate(commonNameArg) {
-  const certData = {
-    algorithms: {
-      hashAlg: "SHA-256",
-      signAlg: "RSASSA-PKCS1-v1_5",
-      keyLength: 2048
-    },
-    //keyPair: generateKeys(), //optional , if provided must be object or promise that resolves to object {publicKey, prvateKey}. If it is not provided, new ones are generated automatically
-    subject: {
-      commonName: commonNameArg + "-userdevice", //optional for leaf, recommended for CA
-      country: "CH", //optional for leaf, recommended for CA
-      locality: "Zug", //optional for leaf, recommended for CA
-      state: "Zug", //optional for leaf, recommended for CA
-      organization: "Vereign AG", //optional for leaf, recommended for CA
-      organizationUnit:"Business Dep", //optional for leaf, recommended for CA
-      //email: "damyan.mitev@vereign.com", // added to DN and Subject Alternative Name extension. Optional for CA. Mandatory for leaf certificate, used for email protection
-      //url: "www.vereign.com" // optional url, recommended for CA, added to Subject Alternative Name extension
-    },
-    validity: {
-      //notBefore: new Date() // optional, defaults to today at 00:00:00
-      //notAfter: new Date()  // optional, defaults to notBefore + validYears at 23:59:59
-      validYears: 5 //optional, defaults to 1
-    },
-    isCA: true // optional flag denoting if this is CA certificate or leaf certificate, defaults to false
-  };
-
-  return createCertificate(certData, null)
-}
-
-function createOneTimePassportCertificate(commonNameArg, emailArg, privateKeyIssuerArg, certicateIssuerArg) {
-  var certData = null;
-  if(emailArg != null && emailArg !== "") {
-    certData = {
-      algorithms: {
-        hashAlg: "SHA-256",
-        signAlg: "RSASSA-PKCS1-v1_5",
-        keyLength: 2048
-      },
-      //keyPair: generateKeys(), //optional , if provided must be object or promise that resolves to object {publicKey, prvateKey}. If it is not provided, new ones are generated automatically
-      subject: {
-        commonName: commonNameArg + "-onetime", //optional for leaf, recommended for CA
-        country: "CH", //optional for leaf, recommended for CA
-        locality: "Zug", //optional for leaf, recommended for CA
-        state: "Zug", //optional for leaf, recommended for CA
-        organization: "Vereign AG", //optional for leaf, recommended for CA
-        organizationUnit:"Business Dep", //optional for leaf, recommended for CA
-        email: emailArg, // added to DN and Subject Alternative Name extension. Optional for CA. Mandatory for leaf certificate, used for email protection
-        //url: "www.vereign.com" // optional url, recommended for CA, added to Subject Alternative Name extension
-      },
-      validity: {
-        //notBefore: new Date() // optional, defaults to today at 00:00:00
-        //notAfter: new Date()  // optional, defaults to notBefore + validYears at 23:59:59
-        validYears: 5 //optional, defaults to 1
-      },
-      isCA: false // optional flag denoting if this is CA certificate or leaf certificate, defaults to false
-    }
-  } else {
-    certData = {
-      algorithms: {
-        hashAlg: "SHA-256",
-        signAlg: "RSASSA-PKCS1-v1_5",
-        keyLength: 2048
-      },
-      //keyPair: generateKeys(), //optional , if provided must be object or promise that resolves to object {publicKey, prvateKey}. If it is not provided, new ones are generated automatically
-      subject: {
-        commonName: commonNameArg + "-onetime", //optional for leaf, recommended for CA
-        country: "CH", //optional for leaf, recommended for CA
-        locality: "Zug", //optional for leaf, recommended for CA
-        state: "Zug", //optional for leaf, recommended for CA
-        organization: "Vereign AG", //optional for leaf, recommended for CA
-        organizationUnit:"Business Dep", //optional for leaf, recommended for CA
-        //email: emailArg, // added to DN and Subject Alternative Name extension. Optional for CA. Mandatory for leaf certificate, used for email protection
-        //url: "www.vereign.com" // optional url, recommended for CA, added to Subject Alternative Name extension
-      },
-      validity: {
-        //notBefore: new Date() // optional, defaults to today at 00:00:00
-        //notAfter: new Date()  // optional, defaults to notBefore + validYears at 23:59:59
-        validYears: 5 //optional, defaults to 1
-      },
-      isCA: false // optional flag denoting if this is CA certificate or leaf certificate, defaults to false
-    }
-  }
-
-  return parsePrivateKey(privateKeyIssuerArg).then(privateKeyDecoded => {
-    const issuerData = {
-      certificate: parseCertificate(certicateIssuerArg),// vereignCACertPEM),
-      privateKey: privateKeyDecoded
-    };
-    return createCertificate(certData, issuerData);
-  });
-}
-
-function download(filename, contentType, text) {
-  var element = document.createElement('a');
-  element.setAttribute('href', 'data:' + contentType + ';charset=utf-8,' + encodeURIComponent(text));
-  element.setAttribute('download', filename);
-
-  element.style.display = 'none';
-  document.body.appendChild(element);
-
-  element.click();
-
-  document.body.removeChild(element);
-}
-
-function arrayBufferToBase64Formatted(buffer) {
-  const bufferString = String.fromCharCode.apply(null, new Uint8Array(buffer));
-  const base64formatted = formatPEM(window.btoa(bufferString));
-  return base64formatted;
-}
-
-function arrayBufferToBase64(buffer) {
-  const bufferString = String.fromCharCode.apply(null, new Uint8Array(buffer));
-  const base64 = window.btoa(bufferString);
-  return base64;
-}
-
-const newline = /\r\n|\r|\n/g;
-
-function capitalizeFirstLetter(string) {
-  if(string === "id") {
-    return "ID"
-  }
-
-  if(string === "mime") {
-    return "MIME";
-  }
-
-  return string.charAt(0).toUpperCase() + string.slice(1);
-}
-
-function capitalizeHeader(string) {
-  let result = "";
-  const tokens = string.split("-");
-  for (let i = 0; i < tokens.length; i++) {
-    result += capitalizeFirstLetter(tokens[i]);
-    if (i !== tokens.length - 1) {
-      result += "-";
-    }
-  }
-
-  return result;
-}
-
-function signEmail(mime, signingCert, certificateChain, privateKey) {
-  const signingCertObj = parseCertificate(signingCert);
-  const certificateChainObj = [];
-  certificateChainObj[0] = parseCertificate(signingCert);
-  for (let i = 0; i < certificateChain.length; i++) {
-    certificateChainObj[i + 1] = parseCertificate(certificateChain[i])
-  }
-
-  return parsePrivateKey(privateKey).then(privateKeyDecoded => {
-    return signEmailObjects(mime, signingCertObj, certificateChainObj, privateKeyDecoded);
-  });
-}
-
-function signEmailObjects(mime, signingCert, certificateChain, privateKey) {
-
-  //region Get a "crypto" extension
-  const crypto = pkijs.getCrypto();
-  if (typeof crypto === "undefined") {
-    return Promise.reject("No WebCrypto extension found");
-  }
-  //endregion Get a "crypto" extension
-
-  let template =
-    `{{headers}}Content-Type: multipart/signed; protocol="application/pkcs7-signature"; micalg=sha-256; boundary="{{boundary}}"
-MIME-Version: 1.0
-
-This is a cryptographically signed message in MIME format.
-
---{{boundary}}
-{{mime}}
---{{boundary}}
-Content-Type: application/pkcs7-signature; name="smime.p7s"
-Content-Transfer-Encoding: base64
-Content-Disposition: attachment; filename="smime.p7s"
-Content-Description: S/MIME Cryptographic Signature
-
-{{signature}}
---{{boundary}}--
-
-Vereign - Authentic Communication
-`.replace(newline, '\r\n');
-
-  const detachedSignature = true;
-  const addExt = true;
-  const hashAlg = "SHA-256";
-  let cmsSignedSimpl;
-
-  var mimeHeadersTitles = [
-    "Content-Type",
-    "Content-Transfer-Encoding",
-    "Content-ID",
-    "Content-Description",
-    "Content-Disposition",
-    "Content-Language",
-    "Content-Location"
-  ];
-
-  mime = mime.replace(newline, '\r\n');
-
-  let newHeaderLines = "";
-  let headersEnd = mime.indexOf('\r\n\r\n'); //the first empty line
-
-  if (headersEnd < 0 && mime.startsWith('\r\n')) {
-
-    mime = mime.substring(2) //should not happen
-
-  } else
-  if (headersEnd >= 0) {
-
-    let mimeHeaders = {};
-    let mimeBody = mime.substring(headersEnd + 4);
-
-    let mimeHeadersStr = mime.substring(0, headersEnd);
-
-    let headers = libmime.decodeHeaders(mimeHeadersStr);
-    for (var i = 0; i < mimeHeadersTitles.length; i++) {
-      let key = mimeHeadersTitles[i].toLowerCase();
-      if(key in headers) {
-        mimeHeaders[key] = headers[key];
-        delete headers[key]
-      }
-    }
-
-    for(let key in headers) {
-      if(!(key === "" || key === "MIME-Version".toLowerCase())) { //we have MIME-Version in the template
-        newHeaderLines += capitalizeHeader(key) + ": " + headers[key] + '\r\n';
-      }
-    }
-
-    let newMimeHeaderLines = "";
-    for(let key in mimeHeaders) {
-      if(!(key === "")) {
-        newMimeHeaderLines += capitalizeHeader(key) + ": " + mimeHeaders[key] + '\r\n';
-      }
-    }
-
-    if (newMimeHeaderLines === "") {
-      newMimeHeaderLines = 'Content-Type: text/plain\r\n' //should not happen
-    }
-
-    mime = newMimeHeaderLines + '\r\n' + mimeBody
-  }
-
-  let dataBuffer = Buffer.from(mime,'utf-8');
-
-  let sequence = Promise.resolve();
-
-  //region Check if user wants us to include signed extensions
-  if(addExt)
-  {
-    //region Create a message digest
-    sequence = sequence.then(
-      () => crypto.digest({ name: hashAlg }, dataBuffer)
-    );
-    //endregion
-
-    //region Combine all signed extensions
-    sequence = sequence.then(
-      messageHash =>
-      {
-        const signedAttr = [];
-        /*
-                1.2.840.113549.1.9.1 - e-mailAddress
-                1.2.840.113549.1.9.2 - PKCS-9 unstructuredName
-                1.2.840.113549.1.9.3 - contentType
-                1.2.840.113549.1.9.4 - messageDigest
-                1.2.840.113549.1.9.5 - Signing Time
-                1.2.840.113549.1.9.6 - counterSignature
-        */
-        signedAttr.push(new pkijs.Attribute({
-          type: OID_PKCS9_ContentType, //contentType
-          values: [
-            new asn1js.ObjectIdentifier({ value: OID_PKCS7_Data}) //data
-          ]
-          /*
-                      1.2.840.113549.1.7.1 - data
-                      1.2.840.113549.1.7.2 - signedData
-                      1.2.840.113549.1.7.3 - envelopedData
-                      1.2.840.113549.1.7.4 - signedAndEnvelopedData
-                      1.2.840.113549.1.7.5 - digestedData
-                      1.2.840.113549.1.7.6 - encryptedData
-          */
-        })); // contentType
-
-        signedAttr.push(new pkijs.Attribute({
-          type: OID_PKCS9_SigningTime, //Signing Time
-          values: [
-            new asn1js.UTCTime({ valueDate: new Date() })
-          ]
-        })); // signingTime
-
-        signedAttr.push(new pkijs.Attribute({
-          type: OID_PKCS9_MessageDigest, //messageDigest
-          values: [
-            new asn1js.OctetString({ valueHex: messageHash })
-          ]
-        })); // messageDigest
-
-        return signedAttr;
-      }
-    );
-    //endregion
-  }
-  //endregion
-
-  //region Initialize CMS Signed Data structures and sign it
-  sequence = sequence.then(
-    signedAttr =>
-    {
-      cmsSignedSimpl = new pkijs.SignedData({
-        version: 1,
-        encapContentInfo: new pkijs.EncapsulatedContentInfo({
-          eContentType: OID_PKCS7_Data // "data" content type
-        }),
-        signerInfos: [
-          new pkijs.SignerInfo({
-            version: 1,
-            sid: new pkijs.IssuerAndSerialNumber({
-              issuer: signingCert.issuer,
-              serialNumber: signingCert.serialNumber
-            })
-          })
-        ],
-        certificates: certificateChain //array
-      });
-
-      if(addExt)
-      {
-        cmsSignedSimpl.signerInfos[0].signedAttrs = new pkijs.SignedAndUnsignedAttributes({
-          type: 0,
-          attributes: signedAttr
-        });
-      }
-
-      if(detachedSignature === false)
-      {
-        const contentInfo = new pkijs.EncapsulatedContentInfo({
-          eContent: new asn1js.OctetString({ valueHex: dataBuffer })
-        });
-
-        cmsSignedSimpl.encapContentInfo.eContent = contentInfo.eContent;
-
-        return cmsSignedSimpl.sign(privateKey, 0, hashAlg);
-      }
-
-      return cmsSignedSimpl.sign(privateKey, 0, hashAlg, dataBuffer);
-    }
-  );
-  //endregion
-
-  //region Create final result
-  sequence = sequence.then(
-    (result) =>
-    {
-      const cmsSignedSchema = cmsSignedSimpl.toSchema(true);
-
-      const cmsContentSimp = new pkijs.ContentInfo({
-        contentType: OID_PKCS7_SignedData, //signedData
-        content: cmsSignedSchema
-      });
-
-      const _cmsSignedSchema = cmsContentSimp.toSchema();
-
-      //region Make length of some elements in "indefinite form"
-      _cmsSignedSchema.lenBlock.isIndefiniteForm = true;
-
-      const block1 = _cmsSignedSchema.valueBlock.value[1];
-      block1.lenBlock.isIndefiniteForm = true;
-
-      const block2 = block1.valueBlock.value[0];
-      block2.lenBlock.isIndefiniteForm = true;
-
-      if(detachedSignature === false)
-      {
-        const block3 = block2.valueBlock.value[2];
-        block3.lenBlock.isIndefiniteForm = true;
-        block3.valueBlock.value[1].lenBlock.isIndefiniteForm = true;
-        block3.valueBlock.value[1].valueBlock.value[0].lenBlock.isIndefiniteForm = true;
-      }
-      //endregion
-
-      const cmsSignedBuffer = _cmsSignedSchema.toBER(false);
-      return cmsSignedBuffer;
-    },
-    error => Promise.reject(`Erorr during signing of CMS Signed Data: ${error}`)
-  );
-  //endregion
-
-  sequence = sequence.then(
-    (cmsSignedBuffer) =>
-    {
-      let signature = arrayBufferToBase64Formatted(cmsSignedBuffer);
-      let boundary = makeBoundary();
-
-      template = template.replace(/{{boundary}}/g, boundary);
-      template = template.replace("{{signature}}", signature);
-      template = template.replace("{{headers}}", newHeaderLines);
-      template = template.replace("{{mime}}", mime);
-
-      //template = template.replace(newline, '\r\n')
-      return template
-    }
-  );
-
-  return sequence;
-}
-
-function makeBoundary() {
-  let len = 20 + Math.random() * 20;
-  return 'W0RyLiBEYW15YW4gTWl0ZXZd--' + makeid(len)
-}
-
-function makeid(len) {
-  if (typeof len === 'undefined') {
-    len = 10
-  }
-  var text = "";
-  var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
-
-  for (var i = 0; i < len; i++)
-    text += possible.charAt(Math.floor(Math.random() * possible.length));
-
-  return text;
-}
-
-function CryptoData() {
-}
-
-CryptoData.prototype.set = function(obj) {
-  for(var member in obj) {
-    this[member] = JSON.parse(JSON.stringify(obj[member]))
-  }
-};
-
-CryptoData.prototype.serialize = function() {
-  return JSON.stringify(this)
-};
-
-CryptoData.prototype.deserialize = function(serialized) {
-  var obj = JSON.parse(serialized);
-  this.set(obj)
-};
-
-CryptoData.prototype.setPublicKey = function(publicKey) {
-  this["publicKey"] = publicKey
-};
-
-CryptoData.prototype.getPublicKey = function() {
-  return this["publicKey"]
-};
-
-CryptoData.prototype.setPrivateKey = function(privateKey) {
-  this["privateKey"] = privateKey
-};
-
-CryptoData.prototype.getPrivateKey = function() {
-  return this["privateKey"]
-};
-
-CryptoData.prototype.setx509Certificate = function(x509Certificate) {
-  this["x509Certificate"] = x509Certificate
-};
-
-CryptoData.prototype.getx509Certificate = function() {
-  return this["x509Certificate"]
-};
-
-CryptoData.prototype.setKeyUUID = function(keyUUID) {
-  this["keyUUID"] = keyUUID
-};
-
-CryptoData.prototype.getKeyUUID = function() {
-  return this["keyUUID"]
-};
-
-CryptoData.prototype.setChain = function(chain) {
-  this["chain"] = chain
-};
-
-CryptoData.prototype.getChain = function() {
-  return this["chain"]
-};
-
-function Identity() {
-}
-
-Identity.prototype.set = function(obj) {
-  for(var member in obj) {
-    this[member] = JSON.parse(JSON.stringify(obj[member]))
-  }
-};
-
-Identity.prototype.serialize = function() {
-  return JSON.stringify(this)
-};
-
-Identity.prototype.deserialize = function(serialized) {
-  var obj = JSON.parse(serialized);
-  this.set(obj)
-};
-
-Identity.prototype.setAuthentication = function(cryptoData) {
-  this["authentication"] = cryptoData
-};
-
-Identity.prototype.getAuthentication = function() {
-  return this["authentication"]
-};
-
-Identity.prototype.setPinCode = function(pinCode) {
-  this["pinCode"] = pinCode
-};
-
-Identity.prototype.getPinCode = function() {
-  return this["pinCode"]
-};
-
-Identity.prototype.setPassport = function(passportUUID, cryptoData) {
-  if(this["passports"] === undefined || this["passports"] === null) {
-    this["passports"] = {}
-  }
-
-  this["passports"][passportUUID] = cryptoData
-};
-
-Identity.prototype.getPassport = function(passportUUID) {
-  if(this["passports"] === undefined || this["passports"] === null) {
-    this["passports"] = {}
-  }
-
-  return this["passports"][passportUUID]
-};
-
 var identityColors = ["#994392", "#cb0767", "#e51d31", "#ec671b", "#fab610"];
 
 function getNextColor() {
@@ -1289,22 +139,7 @@ function getIdentityFromLocalStorage(key, pinCode, extendTtl = true) {
 
 }
 
-function listIdentitiesFromLocalStorage() {
-  var serializedIdentitiesList = localStorage.getItem("identities");
-  var identities = JSON.parse(serializedIdentitiesList);
-  var identitiesResult = {};
-
-  for(var key in identities) {
-    var profile = JSON.parse(JSON.stringify(localStorage.getItem("profiles/" + key)));
-    if(profile != null && profile !== "") {
-      identitiesResult[key] = JSON.parse(profile)
-    } else {
-      identitiesResult[key] = {}
-    }
-  }
 
-  return identitiesResult
-}
 
 function extendPinCodeTtl(key, pinCode) {
   if(pinCode == null || pinCode === "") {
@@ -1360,22 +195,6 @@ function createEvent(actionId, type, payloads) {
   }
 }
 
-function destroyIdentityFromLocalStorage(key) {
-  localStorage.removeItem(key);
-  localStorage.removeItem("profiles/" + key);
-  localStorage.removeItem("colors/" + key);
-
-  var serializedIdentitiesList = localStorage.getItem("identities");
-
-  var identities = JSON.parse(serializedIdentitiesList);
-
-  identities[key] = null;
-
-  delete identities[key];
-
-  localStorage.setItem("identities", JSON.stringify(identities))
-}
-
 window.loadedIdentities = {};
 window.wopiAPI = new WopiAPI();
 window.collaboraApi = new CollaboraAPI();
@@ -2026,80 +845,54 @@ const connection = Penpal.connectToParent({
         })
       });
     },
-    signEmail(passportUUID, emailArg, emailMessage) {
-      return new Penpal.Promise(result => {
-        const authenticationPublicKey = localStorage.getItem("authenticatedIdentity");
-        if (authenticationPublicKey === null) {
-          return {"data" : "",
-            "code" : "400",
-            "status" : "Identity not authenticated"
-          }
-        }
-        if (window.loadedIdentities[authenticationPublicKey] === null) {
-          return {"data" : "",
-            "code" : "400",
-            "status" : "Identity not authenticated"
-          }
-        }
+    signEmail: async (passportUUID, emailArg, emailMessage) => {
+      const authenticationPublicKey = localStorage.getItem("authenticatedIdentity");
 
-        var success = extendPinCodeTtl(authenticationPublicKey);
+      if (
+        !authenticationPublicKey ||
+        !window.loadedIdentities[authenticationPublicKey] ||
+        !extendPinCodeTtl(authenticationPublicKey)
+      ) {
+        return encodeResponse("400", "", "Identity not authenticated");
+      }
 
-        if(success === false) {
-          result({"data" : "",
-            "code" : "400",
-            "status" : "Identity not authenticated"
-          })
-        }
+      let response = await getCertificateForPassport(passportUUID, true);
 
-        getCertificateForPassport(passportUUID, true).then(certificateResult => {
-          if(certificateResult.code === "200") {
-            var passportCertificate = certificateResult.data["x509Certificate"];
-            var passportPrivateKey = certificateResult.data["privateKey"];
-            var passportChain = certificateResult.data["chain"];
+      if (response.code !== "200") {
+        return encodeResponse("400", "", response.status);
+      }
 
-            createOneTimePassportCertificate(makeid() + "-" + passportUUID, emailArg, passportPrivateKey, passportCertificate).then(function(keys){
-              var publicKeyOneTime = keys["publicKeyPEM"];
-              var privateKeyOneTime = keys["privateKeyPEM"];
-              var certificateOneTime = keys["certificatePEM"];
-              //download("certificateOneTime.crt", "text/plain", certificateOneTime)
+      const {
+        x509Certificate: passportCertificate,
+        privateKey: passportPrivateKey,
+        chain: passportChain
+      } = response.data;
 
-              passportChain.push(passportCertificate);
+      const keys =
+        await createOneTimePassportCertificate(
+          makeid() + "-" + passportUUID, emailArg, passportPrivateKey, passportCertificate);
 
-              executeRestfulFunction("private", viamApi, viamApi.passportGetEmailWithHeaderByPassport,
-                passportUUID, emailMessage).then(executeResult2 => {
-                var emailWithHeader = executeResult2.data;
-                //download("withheader.eml", "message/rfc822", emailWithHeader)
-                var signedEmailPromise = signEmail(emailWithHeader,
-                  certificateOneTime,
-                  passportChain,
-                  privateKeyOneTime);
-
-                signedEmailPromise.then(signedEmail => {
-                  executeRestfulFunction("private", viamApi, viamApi.signResignEmail,
-                    passportUUID, signedEmail).then(executeResult => {
-                    result({"data" : executeResult.data,
-                      "code" : "200",
-                      "status" : "Email signed"
-                    })
-                  });
-                  /*result({"data" : signedEmail,
-                      "code" : "200",
-                      "status" : "Email signed"
-                  })*/
-                });
-              });
-              // Prints PEM formatted signed certificate
-              // -----BEGIN CERTIFICATE-----MIID....7Hyg==-----END CERTIFICATE-----
+      const { privateKeyPEM: privateKeyOneTime, certificatePEM: certificateOneTime } = keys;
 
-            });
-          } else {
-            result({"data" : "",
-              "code" : "400",
-              "status" : "Can not sign email"
-            })
-          }
-        })
-      });
+      passportChain.push(passportCertificate);
+
+      response = await executeRestfulFunction(
+        "private", window.viamApi, window.viamApi.passportGetEmailWithHeaderByPassport, passportUUID, emailMessage);
+
+      if (response.code !== "200") {
+        return encodeResponse("400", "", response.status);
+      }
+
+      const signedEmail = await signEmail(response.data, certificateOneTime, passportChain, privateKeyOneTime);
+
+      response = await executeRestfulFunction(
+        "private", window.viamApi, window.viamApi.signResignEmail, passportUUID, signedEmail);
+
+      if (response.code !== "200") {
+        return encodeResponse("400", "", response.status);
+      }
+
+      return encodeResponse("200", response.data, "Email signed");
     },
     hasSession() {
       return new Penpal.Promise(result => {
diff --git a/javascript/src/utilities/appUtility.js b/javascript/src/utilities/appUtility.js
index 66cfaf8b37d46c8175b4393675dd008e9ff84b77..c6090f2558c3a71d5edb8ee60efcb79fc49b27a1 100644
--- a/javascript/src/utilities/appUtility.js
+++ b/javascript/src/utilities/appUtility.js
@@ -11,3 +11,113 @@ export const createDeviceHash = async (publicKey) => {
     console.warn(error); // eslint-disable-line no-console
   }
 };
+
+export const encodeResponse = (code, data, status) => {
+  return {
+    code,
+    data,
+    status
+  };
+};
+
+export function makeid(len) {
+  if (typeof len === 'undefined') {
+    len = 10
+  }
+  var text = "";
+  var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
+
+  for (var i = 0; i < len; i++)
+    text += possible.charAt(Math.floor(Math.random() * possible.length));
+
+  return text;
+}
+
+//*********************************************************************************
+function getBlockFinishTimeInLocalStorage() {
+  return localStorage.getItem("blockFinishTime") || getCurrentTime();
+}
+
+function getCurrentTime() {
+  return Math.floor(new Date().getTime() / 1000);
+}
+
+export function getTimeLeftInLocalStorage() {
+  const blockFinishTime = getBlockFinishTimeInLocalStorage();
+  const timeNow = getCurrentTime();
+  const seconds = (blockFinishTime - timeNow) % 60;
+  let minutes = Math.floor((blockFinishTime - timeNow) / 60);
+  minutes %= 60;
+
+  const left = "Your identity has been locked. Try again in " + minutes + " minutes and " + seconds + " seconds.";
+  return left;
+}
+
+export function listIdentitiesFromLocalStorage() {
+  var serializedIdentitiesList = localStorage.getItem("identities");
+  var identities = JSON.parse(serializedIdentitiesList);
+  var identitiesResult = {};
+
+  for(var key in identities) {
+    var profile = JSON.parse(JSON.stringify(localStorage.getItem("profiles/" + key)));
+    if(profile != null && profile !== "") {
+      identitiesResult[key] = JSON.parse(profile)
+    } else {
+      identitiesResult[key] = {}
+    }
+  }
+
+  return identitiesResult
+}
+
+export function destroyIdentityFromLocalStorage(key) {
+  localStorage.removeItem(key);
+  localStorage.removeItem("profiles/" + key);
+  localStorage.removeItem("colors/" + key);
+
+  var serializedIdentitiesList = localStorage.getItem("identities");
+
+  var identities = JSON.parse(serializedIdentitiesList);
+
+  identities[key] = null;
+
+  delete identities[key];
+
+  localStorage.setItem("identities", JSON.stringify(identities))
+}
+
+export function failPincodeAttempt(password) {
+  let message = "Wrong pincode";
+  if (password !== '00000000') {
+    let attempt = localStorage.getItem("attempt") || 1;
+    attempt = parseInt(attempt);
+    if (attempt === 9) {
+      const identitiesTemp = listIdentitiesFromLocalStorage();
+      for (let i in identitiesTemp) {
+        destroyIdentityFromLocalStorage(i);
+      }
+      message = "9 failed attempts. Identity is revoked!";
+      localStorage.removeItem("attempt");
+    } else if (attempt % 3 === 0) {
+      const timeNow = getCurrentTime();
+      const blockFinishTime = timeNow + 300;
+      localStorage.setItem("blockFinishTime", blockFinishTime);
+      localStorage.setItem("attempt", attempt + 1);
+      message = "3 failed attempts. Identity is locked!";
+    } else {
+      localStorage.setItem("attempt", attempt + 1);
+    }
+  }
+  return message;
+}
+
+export function canTryPincode() {
+  const timeNow = getCurrentTime();
+  const blockFinishTime = getBlockFinishTimeInLocalStorage();
+  if (blockFinishTime <= timeNow) {
+    localStorage.removeItem("blockFinishTime");
+    return true;
+  } else {
+    return false;
+  }
+}
\ No newline at end of file
diff --git a/javascript/src/utilities/signingUtilities.js b/javascript/src/utilities/signingUtilities.js
new file mode 100644
index 0000000000000000000000000000000000000000..dad16e412bdaa725838b6560b4faa7b71fb79a0f
--- /dev/null
+++ b/javascript/src/utilities/signingUtilities.js
@@ -0,0 +1,955 @@
+import {canTryPincode, failPincodeAttempt, getTimeLeftInLocalStorage, makeid} from './appUtility';
+
+const libmime = require('libmime');
+const pkijs = require('pkijs');
+const asn1js = require('asn1js');
+const pvutils = require('pvutils');
+
+//*********************************************************************************
+
+const CERTIFIATE_Version_1 = 0;
+const CERTIFIATE_Version_3 = 2;
+
+//these bit fields are reversed, WTF!
+const KEY_USAGE_DigitalSignature	= 0x80;//01;
+const KEY_USAGE_NonRepudiation		= 0x40;//02;
+const KEY_USAGE_KeyEncipherment		= 0x20;//04;
+const KEY_USAGE_DataEncipherment	= 0x10;//08;
+const KEY_USAGE_KeyAgreement		= 0x08;//10;
+const KEY_USAGE_KeyCertSign			= 0x04;//20;
+const KEY_USAGE_CRLSign				= 0x02;//40;
+//const KEY_USAGE_EncipherOnly		= 0x01;//80; // Not used for now. Must be used together with KEY_USAGE_KeyAgreement (maybe should be ORed as a constant directly?)
+//const KEY_USAGE_DecipherOnly		= 0x80;//0100; // If used, modify "KeyUsage" extension array buffer size and appropriate bit operators to accomodate for extra byte
+
+const KEY_USAGE_LeafCertificate		= KEY_USAGE_DigitalSignature | KEY_USAGE_NonRepudiation | KEY_USAGE_KeyEncipherment | KEY_USAGE_DataEncipherment;
+const KEY_USAGE_CertificateAuthority= KEY_USAGE_DigitalSignature | KEY_USAGE_KeyCertSign | KEY_USAGE_CRLSign;
+
+
+const OID_EXT_KEY_USAGE_Any     	= "2.5.29.37.0";
+const OID_ID_PKIX_ServerAuth		= "1.3.6.1.5.5.7.3.1";
+const OID_ID_PKIX_ClientAuth		= "1.3.6.1.5.5.7.3.2";
+const OID_ID_PKIX_CodeSigning		= "1.3.6.1.5.5.7.3.3";
+const OID_ID_PKIX_EmailProtection	= "1.3.6.1.5.5.7.3.4";
+const OID_ID_PKIX_TimeStamping	    = "1.3.6.1.5.5.7.3.8";
+const OID_ID_PKIX_OCSPSigning		= "1.3.6.1.5.5.7.3.9";
+// const OID_EXT_KEY_USAGE_MS...	= "1.3.6.1.4.1.311.10.3.1"; // Microsoft Certificate Trust List signing
+// const OID_EXT_KEY_USAGE_MS...	= "1.3.6.1.4.1.311.10.3.4";  // Microsoft Encrypted File System
+const OID_PKCS7_Data                = "1.2.840.113549.1.7.1";
+const OID_PKCS7_SignedData          = "1.2.840.113549.1.7.2";
+const OID_PKCS7_EnvelopedData       = "1.2.840.113549.1.7.3";
+const OID_PKCS9_EmailAddress        = "1.2.840.113549.1.9.1";
+const OID_PKCS9_ContentType         = "1.2.840.113549.1.9.3";
+const OID_PKCS9_MessageDigest       = "1.2.840.113549.1.9.4";
+const OID_PKCS9_SigningTime         = "1.2.840.113549.1.9.5";
+
+const defaultAlgorithms = {
+  hashAlg: "SHA-256",
+  signAlg: "RSASSA-PKCS1-v1_5",
+  keyLength: 2048
+};
+
+const AES_encryptionVariant_Password  = 2;
+const encryptionAlgorithm = {
+  name: "AES-CBC",
+  length: 128
+};
+
+//*********************************************************************************
+// Returns promise, resolved to keyPair object {publicKey, privateKey}
+//*********************************************************************************
+function generateKeys(algorithms) {
+  //region Get a "crypto" extension
+  const crypto = pkijs.getCrypto();
+  if (typeof crypto === "undefined") {
+    return Promise.reject("No WebCrypto extension found");
+  }
+  //endregion Get a "crypto" extension
+
+  if (!algorithms) {
+    algorithms = defaultAlgorithms;
+  } else {
+    if (!algorithms.hashAlg) {
+      algorithms.hashAlg = defaultAlgorithms.hashAlg;
+    }
+    if (!algorithms.signAlg) {
+      algorithms.signAlg = defaultAlgorithms.signAlg;
+    }
+    if (!algorithms.keyLength) {
+      algorithms.keyLength = defaultAlgorithms.keyLength;
+    }
+  }
+
+  //region Get default algorithm parameters for key generation
+  const algorithm = pkijs.getAlgorithmParameters(algorithms.signAlg, "generatekey");
+  if("hash" in algorithm.algorithm) {
+    algorithm.algorithm.hash.name = algorithms.hashAlg;
+  }
+  algorithm.algorithm.modulusLength = algorithms.keyLength;
+  //endregion
+
+  return crypto.generateKey(algorithm.algorithm, true, algorithm.usages);
+}
+
+//*********************************************************************************
+function createCertificate(certData, issuerData = null)
+{
+
+  if (typeof certData === "undefined") {
+    return Promise.reject("No Certificate data provided");
+  }
+
+  if (typeof certData.subject === "undefined") {
+    return Promise.reject("No Certificate subject data provided");
+  }
+
+
+  //region Get a "crypto" extension
+  const crypto = pkijs.getCrypto();
+
+  if (typeof crypto === "undefined") {
+    return Promise.reject("No WebCrypto extension found");
+  }
+  //endregion Get a "crypto" extension
+
+  //region Initial variables
+  let sequence = Promise.resolve();
+
+  const certificate = new pkijs.Certificate();
+  let publicKey;
+  let privateKey;
+
+  let certificateBuffer;// = new ArrayBuffer(0); // ArrayBuffer with loaded or created CERT
+  let privateKeyBuffer;// = new ArrayBuffer(0);
+  let publicKeyBuffer;// = new ArrayBuffer(0);
+
+  //endregion Initial variables
+
+  if (certData.keyPair) {
+    //region Create a new key pair
+    sequence = sequence.then(() =>
+    {
+      return certData.keyPair;
+    });
+    //endregion Create a new key pair
+
+  } else {
+    //region Create a new key pair
+    sequence = sequence.then(() =>
+    {
+      return generateKeys(certData.algorithms);
+    });
+    //endregion Create a new key pair
+  }
+
+  //region Store new key in an interim variables
+  sequence = sequence.then(keyPair =>
+  {
+    publicKey = keyPair.publicKey;
+    privateKey = keyPair.privateKey;
+  }, error => Promise.reject(`Error during key generation: ${error}`));
+  //endregion Store new key in an interim variables
+
+  //region Exporting public key into "subjectPublicKeyInfo" value of certificate
+  sequence = sequence.then(() =>
+    certificate.subjectPublicKeyInfo.importKey(publicKey)
+  );
+  //endregion Exporting public key into "subjectPublicKeyInfo" value of certificate
+
+  sequence = sequence.then(
+    () => crypto.digest({ name: "SHA-1" }, certificate.subjectPublicKeyInfo.subjectPublicKey.valueBlock.valueHex),
+    error => Promise.reject(`Error during importing public key: ${error}`)
+  );
+
+  //region Fill in cert data
+  sequence = sequence.then(subjKeyIdBuffer =>
+  {
+
+    //region Put a static values
+    certificate.version = CERTIFIATE_Version_3;
+
+    const serialNumberBuffer = new ArrayBuffer(20);
+    const serialNumberView = new Uint8Array(serialNumberBuffer);
+    pkijs.getRandomValues(serialNumberView);
+    // noinspection JSUnresolvedFunction
+    certificate.serialNumber = new asn1js.Integer({ valueHex: serialNumberView });
+    //endregion Put a static values
+
+    //region Subject
+    // For reference http://oidref.com/2.5.4.3
+    if (certData.subject.commonName) {
+      // noinspection JSUnresolvedFunction
+      certificate.subject.typesAndValues.push(new pkijs.AttributeTypeAndValue({
+        type: "2.5.4.3", // Common name
+        value: new asn1js.PrintableString({ value: certData.subject.commonName })
+      }));
+    }
+
+    if (certData.subject.country) {
+      // noinspection JSUnresolvedFunction
+      certificate.subject.typesAndValues.push(new pkijs.AttributeTypeAndValue({
+        type: "2.5.4.6", // Country name
+        value: new asn1js.PrintableString({ value: certData.subject.country })
+      }));
+    }
+
+    if (certData.subject.locality) {
+      // noinspection JSUnresolvedFunction
+      certificate.subject.typesAndValues.push(new pkijs.AttributeTypeAndValue({
+        type: "2.5.4.7", // Locality Name
+        value: new asn1js.PrintableString({ value: certData.subject.locality })
+      }));
+    }
+
+    if (certData.subject.state) {
+      // noinspection JSUnresolvedFunction
+      certificate.subject.typesAndValues.push(new pkijs.AttributeTypeAndValue({
+        type: "2.5.4.8", // State or Province name
+        value: new asn1js.PrintableString({ value: certData.subject.state })
+      }));
+    }
+
+    if (certData.subject.organization) {
+      // noinspection JSUnresolvedFunction
+      certificate.subject.typesAndValues.push(new pkijs.AttributeTypeAndValue({
+        type: "2.5.4.10", // Organization name
+        value: new asn1js.PrintableString({ value: certData.subject.organization })
+      }));
+    }
+
+    if (certData.subject.organizationUnit) {
+      // noinspection JSUnresolvedFunction
+      certificate.subject.typesAndValues.push(new pkijs.AttributeTypeAndValue({
+        type: "2.5.4.11", // Organization unit name
+        value: new asn1js.PrintableString({ value: certData.subject.organizationUnit })
+      }));
+    }
+
+    if (certData.subject.email) {
+      // noinspection JSUnresolvedFunction
+      certificate.subject.typesAndValues.push(new pkijs.AttributeTypeAndValue({
+        type: OID_PKCS9_EmailAddress, // Email, deprecated but still widely used
+        value: new asn1js.IA5String({ value: certData.subject.email })
+      }));
+    }
+    //endregion Subject
+
+
+    //region Issuer
+    if (issuerData && issuerData.certificate) {
+      certificate.issuer = issuerData.certificate.subject;
+    } else {
+      certificate.issuer = certificate.subject;
+    }
+    //endregion Issuer
+
+    //region Validity
+    if (!certData.validity) {
+      certData.validity = {}
+    }
+
+    if (certData.validity.notBefore) {
+      certificate.notBefore.value = certData.validity.notBefore; //date
+    } else {
+      const tmp = new Date();
+      certificate.notBefore.value = new Date(tmp.getFullYear(), tmp.getMonth(), tmp.getDate(), 0, 0, 0);
+    }
+
+    if (certData.validity.notAfter) {
+      certificate.notAfter.value = certData.validity.notAfter; //date
+    } else {
+      const tmp = certificate.notBefore.value;
+      const validYears = certData.validity.validYears || 1;
+      certificate.notAfter.value = new Date(tmp.getFullYear() + validYears, tmp.getMonth(), tmp.getDate(), 23, 59, 59);
+    }
+    //endregion Validity
+
+    //region Extensions
+    certificate.extensions = []; // Extensions are not a part of certificate by default, it's an optional array
+
+    //region "BasicConstraints" extension
+    const basicConstr = new pkijs.BasicConstraints({
+      cA: !!certData.isCA,
+      //pathLenConstraint: 0 //TODO add logic for leaf CA
+    });
+
+    certificate.extensions.push(new pkijs.Extension({
+      extnID: "2.5.29.19",
+      critical: true,
+      extnValue: basicConstr.toSchema().toBER(false),
+      parsedValue: basicConstr // Parsed value for well-known extensions
+    }));
+    //endregion "BasicConstraints" extension
+
+    //region "KeyUsage" extension
+    const keyUsageBuffer = new ArrayBuffer(1);
+    const keyUsageBitView = new Uint8Array(keyUsageBuffer);
+
+    keyUsageBitView[0] = !!certData.isCA ? KEY_USAGE_CertificateAuthority : KEY_USAGE_LeafCertificate;
+
+    // noinspection JSUnresolvedFunction
+    const keyUsage = new asn1js.BitString({ valueHex: keyUsageBuffer });
+
+    certificate.extensions.push(new pkijs.Extension({
+      extnID: "2.5.29.15",
+      critical: true,
+      extnValue: keyUsage.toBER(false),
+      parsedValue: keyUsage // Parsed value for well-known extensions
+    }));
+    //endregion "KeyUsage" extension
+
+    //region "ExtKeyUsage" extension
+    if (!certData.isCA && certData.subject.email) {
+      const extKeyUsage = new pkijs.ExtKeyUsage({
+        keyPurposes: [
+          OID_ID_PKIX_EmailProtection
+        ]
+      });
+
+      certificate.extensions.push(new pkijs.Extension({
+        extnID: "2.5.29.37",
+        critical: false,
+        extnValue: extKeyUsage.toSchema().toBER(false),
+        parsedValue: extKeyUsage // Parsed value for well-known extensions
+      }));
+    }
+    //endregion "ExtKeyUsage" extension
+
+    //region "SubjAltName" extension
+    if (certData.subject.email || certData.subject.url) {
+
+      const names = [];
+
+      if (certData.subject.email) {
+        names.push(new pkijs.GeneralName({
+          type: 1, // rfc822Name
+          value: certData.subject.email
+        }));
+      }
+
+      if (certData.subject.url) {
+        names.push(new pkijs.GeneralName({
+          type: 2, // dNSName
+          value: certData.subject.url
+        }));
+      }
+
+      const subjAltNames = new pkijs.GeneralNames({
+        names: names
+      });
+
+      certificate.extensions.push(new pkijs.Extension({
+        extnID: "2.5.29.17",
+        critical: false,
+        extnValue: subjAltNames.toSchema().toBER(false),
+        parsedValue: subjAltNames // Parsed value for well-known extensions
+      }));
+    }
+    //endregion "SubjAltName" extension
+
+
+    //region "SubjectKeyIdentifier" extension
+    const subjKeyId = new asn1js.OctetString({ valueHex: subjKeyIdBuffer });
+
+    certificate.extensions.push(new pkijs.Extension({
+      extnID: "2.5.29.14",
+      critical: false,
+      extnValue: subjKeyId.toBER(false),
+      parsedValue: subjKeyId // Parsed value for well-known extensions
+    }));
+    //endregion "SubjectKeyIdentifier" extension
+
+    /* COULD NOT GET IT WORKING
+        //region "AuthorityKeyIdentifier" extension
+        if (issuerData && issuerData.certificate) {
+
+          let issuerSubjKeyExt = null;
+
+          let extLength = issuerData.certificate.extensions.length;
+          for (var i = 0; i < extLength; i++) {
+            let ext = issuerData.certificate.extensions[i];
+            if (ext.extnID == "2.5.29.14") {
+              issuerSubjKeyExt = ext;
+              break;
+            }
+          }
+
+          if (issuerSubjKeyExt) {
+
+            const asn1 = asn1js.fromBER(issuerSubjKeyExt.extnValue);
+
+            const authKeyIdentifier = new AuthorityKeyIdentifier({
+              keyIdentifier: new asn1js.OctetString({
+                //isHexOnly: true,
+                //valueHex: issuerSubjKeyExt.parsedValue.valueBlock.valueHex
+                value: new asn1js.OctetString({ valueHex: subjKeyIdBuffer })
+              })
+            });
+            // const authKeyIdentifier = new AuthorityKeyIdentifier({
+            // 	//keyIdentifier: new asn1js.OctetString({ valueHex: subjKeyIdBuffer })
+
+            // });
+
+            certificate.extensions.push(new Extension({
+              extnID: "2.5.29.35",
+              critical: false,
+              extnValue: authKeyIdentifier.toSchema().toBER(false),
+              parsedValue: authKeyIdentifier // Parsed value for well-known extensions
+            }));
+          }
+        }
+        //endregion "AuthorityKeyIdentifier" extension
+    */
+    //endregion Extensions
+  });
+  //region Fill in cert data
+
+
+  //region Signing final certificate
+  sequence = sequence.then(() => {
+      let signerKey = (issuerData && issuerData.privateKey) ? issuerData.privateKey : privateKey;
+      return certificate.sign(signerKey, (certData.algorithms && certData.algorithms.hashAlg) ? certData.algorithms.hashAlg : defaultAlgorithms.hashAlg)
+    },
+    error => Promise.reject(`Error during exporting public key: ${error}`));
+  //endregion
+
+  //region Encode and store certificate
+  sequence = sequence.then(() =>
+  {
+    certificateBuffer = certificate.toSchema(true).toBER(false);
+  }, error => Promise.reject(`Error during signing: ${error}`));
+  //endregion
+
+  //region Exporting public key
+  sequence = sequence.then(() =>
+    crypto.exportKey("spki", publicKey)
+  );
+  //endregion
+
+  //region Store exported public key on Web page
+  sequence = sequence.then(result =>
+  {
+    publicKeyBuffer = result;
+  }, error => Promise.reject(`Error during exporting of public key: ${error}`));
+  //endregion
+
+  //region Exporting private key
+  sequence = sequence.then(() =>
+    crypto.exportKey("pkcs8", privateKey)
+  );
+  //endregion
+
+  //region Store exported key on Web page
+  sequence = sequence.then(result =>
+  {
+    privateKeyBuffer = result;
+  }, error => Promise.reject(`Error during exporting of private key: ${error}`));
+  //endregion
+
+  return sequence.then(() => {
+
+    const result = {
+      certificate: certificate,
+      certificatePEM: encodePEM(certificateBuffer, "CERTIFICATE"),
+      publicKey: publicKey,
+      publicKeyPEM: encodePEM(publicKeyBuffer, "PUBLIC KEY"),
+      privateKey: privateKey,
+      privateKeyPEM: encodePEM(privateKeyBuffer, "PRIVATE KEY")
+    };
+    return result;
+  });
+}
+
+function formatPEM(pemString) {
+  const lineWidth = 64;
+  let resultString = "";
+  let start = 0;
+  let piece;
+  while ((piece = pemString.substring(start, start + lineWidth)).length > 0) {
+    start += lineWidth;
+    resultString += piece + '\r\n';
+  }
+  return resultString;
+}
+
+function encodePEM(buffer, label) {
+  const bufferString = String.fromCharCode.apply(null, new Uint8Array(buffer));
+
+  const header = `-----BEGIN ${label}-----\r\n`;
+  const base64formatted = formatPEM(window.btoa(bufferString));
+  const footer = `-----END ${label}-----\r\n`;
+  const resultString = header + base64formatted + footer;
+
+  return resultString;
+}
+
+function decodePEM(pemString) {
+  const pemStripped = pemString.replace(/(-----(BEGIN|END) [a-zA-Z ]*-----|\r|\n)/g, '');
+  const pemDecoded = window.atob(pemStripped);
+  const buffer = pvutils.stringToArrayBuffer(pemDecoded);
+  return buffer;
+}
+
+//*********************************************************************************
+function parseCertificate(certificatePEM) {
+  const certificateBuffer = decodePEM(certificatePEM);
+  const asn1 = asn1js.fromBER(certificateBuffer);
+  const certificate = new pkijs.Certificate({ schema: asn1.result });
+  return certificate;
+}
+
+//*********************************************************************************
+export function encryptMessage(message, password, label) {
+  const buffer = pvutils.stringToArrayBuffer(message);
+  const secret = pvutils.stringToArrayBuffer(password);
+
+  const enveloped = new pkijs.EnvelopedData();
+  enveloped.addRecipientByPreDefinedData(secret, {}, AES_encryptionVariant_Password);
+  return enveloped.encrypt(encryptionAlgorithm, buffer).
+  then(
+    () => {
+      const content = new pkijs.ContentInfo();
+      content.contentType = OID_PKCS7_EnvelopedData;
+      content.content = enveloped.toSchema();
+      const ber = content.toSchema().toBER(false);
+      return encodePEM(ber, label)
+    },
+    error => Promise.reject(`encryption error: ${error}`)
+  )
+}
+
+//*********************************************************************************
+export function decryptMessage(message, password) {
+  if (canTryPincode()) {
+    const secret = pvutils.stringToArrayBuffer(password);
+    const buffer = decodePEM(message);
+
+    const asn1 = asn1js.fromBER(buffer);
+    const content = new pkijs.ContentInfo({schema: asn1.result});
+    const enveloped = new pkijs.EnvelopedData({schema: content.content});
+    return enveloped.decrypt(0, {preDefinedData: secret}).then(result => {
+      return pvutils.arrayBufferToString(result);
+    }).catch(() => {
+      return Promise.reject(failPincodeAttempt(password));
+    });
+  } else {
+    return Promise.reject(getTimeLeftInLocalStorage());
+  }
+}
+
+//*********************************************************************************
+function parsePrivateKey(privateKeyPEM) {
+  const privateKeyBuffer = decodePEM(privateKeyPEM);
+  const crypto = pkijs.getCrypto();
+  const privateKeyPromise = crypto.importKey(
+    "pkcs8",
+    privateKeyBuffer,
+    {   //these are the algorithm options
+      name: "RSASSA-PKCS1-v1_5",
+      hash: {name: "SHA-256"}, //can be "SHA-1", "SHA-256", "SHA-384", or "SHA-512"
+    },
+    true,
+    ["sign"]
+  );
+  return privateKeyPromise;
+}
+
+
+export function createPassportCertificate(commonNameArg) {
+  const certData = {
+    algorithms: {
+      hashAlg: "SHA-256",
+      signAlg: "RSASSA-PKCS1-v1_5",
+      keyLength: 2048
+    },
+    //keyPair: generateKeys(), //optional , if provided must be object or promise that resolves to object {publicKey, prvateKey}. If it is not provided, new ones are generated automatically
+    subject: {
+      commonName: commonNameArg + "-userdevice", //optional for leaf, recommended for CA
+      country: "CH", //optional for leaf, recommended for CA
+      locality: "Zug", //optional for leaf, recommended for CA
+      state: "Zug", //optional for leaf, recommended for CA
+      organization: "Vereign AG", //optional for leaf, recommended for CA
+      organizationUnit:"Business Dep", //optional for leaf, recommended for CA
+      //email: "damyan.mitev@vereign.com", // added to DN and Subject Alternative Name extension. Optional for CA. Mandatory for leaf certificate, used for email protection
+      //url: "www.vereign.com" // optional url, recommended for CA, added to Subject Alternative Name extension
+    },
+    validity: {
+      //notBefore: new Date() // optional, defaults to today at 00:00:00
+      //notAfter: new Date()  // optional, defaults to notBefore + validYears at 23:59:59
+      validYears: 5 //optional, defaults to 1
+    },
+    isCA: true // optional flag denoting if this is CA certificate or leaf certificate, defaults to false
+  };
+
+  return createCertificate(certData, null)
+}
+
+export function createOneTimePassportCertificate(commonNameArg, emailArg, privateKeyIssuerArg, certicateIssuerArg) {
+  var certData = null;
+  if(emailArg != null && emailArg !== "") {
+    certData = {
+      algorithms: {
+        hashAlg: "SHA-256",
+        signAlg: "RSASSA-PKCS1-v1_5",
+        keyLength: 2048
+      },
+      //keyPair: generateKeys(), //optional , if provided must be object or promise that resolves to object {publicKey, prvateKey}. If it is not provided, new ones are generated automatically
+      subject: {
+        commonName: commonNameArg + "-onetime", //optional for leaf, recommended for CA
+        country: "CH", //optional for leaf, recommended for CA
+        locality: "Zug", //optional for leaf, recommended for CA
+        state: "Zug", //optional for leaf, recommended for CA
+        organization: "Vereign AG", //optional for leaf, recommended for CA
+        organizationUnit:"Business Dep", //optional for leaf, recommended for CA
+        email: emailArg, // added to DN and Subject Alternative Name extension. Optional for CA. Mandatory for leaf certificate, used for email protection
+        //url: "www.vereign.com" // optional url, recommended for CA, added to Subject Alternative Name extension
+      },
+      validity: {
+        //notBefore: new Date() // optional, defaults to today at 00:00:00
+        //notAfter: new Date()  // optional, defaults to notBefore + validYears at 23:59:59
+        validYears: 5 //optional, defaults to 1
+      },
+      isCA: false // optional flag denoting if this is CA certificate or leaf certificate, defaults to false
+    }
+  } else {
+    certData = {
+      algorithms: {
+        hashAlg: "SHA-256",
+        signAlg: "RSASSA-PKCS1-v1_5",
+        keyLength: 2048
+      },
+      //keyPair: generateKeys(), //optional , if provided must be object or promise that resolves to object {publicKey, prvateKey}. If it is not provided, new ones are generated automatically
+      subject: {
+        commonName: commonNameArg + "-onetime", //optional for leaf, recommended for CA
+        country: "CH", //optional for leaf, recommended for CA
+        locality: "Zug", //optional for leaf, recommended for CA
+        state: "Zug", //optional for leaf, recommended for CA
+        organization: "Vereign AG", //optional for leaf, recommended for CA
+        organizationUnit:"Business Dep", //optional for leaf, recommended for CA
+        //email: emailArg, // added to DN and Subject Alternative Name extension. Optional for CA. Mandatory for leaf certificate, used for email protection
+        //url: "www.vereign.com" // optional url, recommended for CA, added to Subject Alternative Name extension
+      },
+      validity: {
+        //notBefore: new Date() // optional, defaults to today at 00:00:00
+        //notAfter: new Date()  // optional, defaults to notBefore + validYears at 23:59:59
+        validYears: 5 //optional, defaults to 1
+      },
+      isCA: false // optional flag denoting if this is CA certificate or leaf certificate, defaults to false
+    }
+  }
+
+  return parsePrivateKey(privateKeyIssuerArg).then(privateKeyDecoded => {
+    const issuerData = {
+      certificate: parseCertificate(certicateIssuerArg),// vereignCACertPEM),
+      privateKey: privateKeyDecoded
+    };
+    return createCertificate(certData, issuerData);
+  });
+}
+
+function arrayBufferToBase64Formatted(buffer) {
+  const bufferString = String.fromCharCode.apply(null, new Uint8Array(buffer));
+  const base64formatted = formatPEM(window.btoa(bufferString));
+  return base64formatted;
+}
+
+export function signEmail(mime, signingCert, certificateChain, privateKey) {
+  const signingCertObj = parseCertificate(signingCert);
+  const certificateChainObj = [];
+  certificateChainObj[0] = parseCertificate(signingCert);
+  for (let i = 0; i < certificateChain.length; i++) {
+    certificateChainObj[i + 1] = parseCertificate(certificateChain[i])
+  }
+
+  return parsePrivateKey(privateKey).then(privateKeyDecoded => {
+    return signEmailObjects(mime, signingCertObj, certificateChainObj, privateKeyDecoded);
+  });
+}
+
+function signEmailObjects(mime, signingCert, certificateChain, privateKey) {
+
+  //region Get a "crypto" extension
+  const crypto = pkijs.getCrypto();
+  if (typeof crypto === "undefined") {
+    return Promise.reject("No WebCrypto extension found");
+  }
+  //endregion Get a "crypto" extension
+
+  let template =
+    `{{headers}}Content-Type: multipart/signed; protocol="application/pkcs7-signature"; micalg=sha-256; boundary="{{boundary}}"
+MIME-Version: 1.0
+
+This is a cryptographically signed message in MIME format.
+
+--{{boundary}}
+{{mime}}
+--{{boundary}}
+Content-Type: application/pkcs7-signature; name="smime.p7s"
+Content-Transfer-Encoding: base64
+Content-Disposition: attachment; filename="smime.p7s"
+Content-Description: S/MIME Cryptographic Signature
+
+{{signature}}
+--{{boundary}}--
+
+Vereign - Authentic Communication
+`.replace(newline, '\r\n');
+
+  const detachedSignature = true;
+  const addExt = true;
+  const hashAlg = "SHA-256";
+  let cmsSignedSimpl;
+
+  var mimeHeadersTitles = [
+    "Content-Type",
+    "Content-Transfer-Encoding",
+    "Content-ID",
+    "Content-Description",
+    "Content-Disposition",
+    "Content-Language",
+    "Content-Location"
+  ];
+
+  mime = mime.replace(newline, '\r\n');
+
+  let newHeaderLines = "";
+  let headersEnd = mime.indexOf('\r\n\r\n'); //the first empty line
+
+  if (headersEnd < 0 && mime.startsWith('\r\n')) {
+
+    mime = mime.substring(2) //should not happen
+
+  } else
+  if (headersEnd >= 0) {
+
+    let mimeHeaders = {};
+    let mimeBody = mime.substring(headersEnd + 4);
+
+    let mimeHeadersStr = mime.substring(0, headersEnd);
+
+    let headers = libmime.decodeHeaders(mimeHeadersStr);
+    for (var i = 0; i < mimeHeadersTitles.length; i++) {
+      let key = mimeHeadersTitles[i].toLowerCase();
+      if(key in headers) {
+        mimeHeaders[key] = headers[key];
+        delete headers[key]
+      }
+    }
+
+    for(let key in headers) {
+      if(!(key === "" || key === "MIME-Version".toLowerCase())) { //we have MIME-Version in the template
+        newHeaderLines += capitalizeHeader(key) + ": " + headers[key] + '\r\n';
+      }
+    }
+
+    let newMimeHeaderLines = "";
+    for(let key in mimeHeaders) {
+      if(!(key === "")) {
+        newMimeHeaderLines += capitalizeHeader(key) + ": " + mimeHeaders[key] + '\r\n';
+      }
+    }
+
+    if (newMimeHeaderLines === "") {
+      newMimeHeaderLines = 'Content-Type: text/plain\r\n' //should not happen
+    }
+
+    mime = newMimeHeaderLines + '\r\n' + mimeBody
+  }
+
+  let dataBuffer = Buffer.from(mime,'utf-8');
+
+  let sequence = Promise.resolve();
+
+  //region Check if user wants us to include signed extensions
+  if(addExt)
+  {
+    //region Create a message digest
+    sequence = sequence.then(
+      () => crypto.digest({ name: hashAlg }, dataBuffer)
+    );
+    //endregion
+
+    //region Combine all signed extensions
+    sequence = sequence.then(
+      messageHash =>
+      {
+        const signedAttr = [];
+        /*
+                1.2.840.113549.1.9.1 - e-mailAddress
+                1.2.840.113549.1.9.2 - PKCS-9 unstructuredName
+                1.2.840.113549.1.9.3 - contentType
+                1.2.840.113549.1.9.4 - messageDigest
+                1.2.840.113549.1.9.5 - Signing Time
+                1.2.840.113549.1.9.6 - counterSignature
+        */
+        signedAttr.push(new pkijs.Attribute({
+          type: OID_PKCS9_ContentType, //contentType
+          values: [
+            new asn1js.ObjectIdentifier({ value: OID_PKCS7_Data}) //data
+          ]
+          /*
+                      1.2.840.113549.1.7.1 - data
+                      1.2.840.113549.1.7.2 - signedData
+                      1.2.840.113549.1.7.3 - envelopedData
+                      1.2.840.113549.1.7.4 - signedAndEnvelopedData
+                      1.2.840.113549.1.7.5 - digestedData
+                      1.2.840.113549.1.7.6 - encryptedData
+          */
+        })); // contentType
+
+        signedAttr.push(new pkijs.Attribute({
+          type: OID_PKCS9_SigningTime, //Signing Time
+          values: [
+            new asn1js.UTCTime({ valueDate: new Date() })
+          ]
+        })); // signingTime
+
+        signedAttr.push(new pkijs.Attribute({
+          type: OID_PKCS9_MessageDigest, //messageDigest
+          values: [
+            new asn1js.OctetString({ valueHex: messageHash })
+          ]
+        })); // messageDigest
+
+        return signedAttr;
+      }
+    );
+    //endregion
+  }
+  //endregion
+
+  //region Initialize CMS Signed Data structures and sign it
+  sequence = sequence.then(
+    signedAttr =>
+    {
+      cmsSignedSimpl = new pkijs.SignedData({
+        version: 1,
+        encapContentInfo: new pkijs.EncapsulatedContentInfo({
+          eContentType: OID_PKCS7_Data // "data" content type
+        }),
+        signerInfos: [
+          new pkijs.SignerInfo({
+            version: 1,
+            sid: new pkijs.IssuerAndSerialNumber({
+              issuer: signingCert.issuer,
+              serialNumber: signingCert.serialNumber
+            })
+          })
+        ],
+        certificates: certificateChain //array
+      });
+
+      if(addExt)
+      {
+        cmsSignedSimpl.signerInfos[0].signedAttrs = new pkijs.SignedAndUnsignedAttributes({
+          type: 0,
+          attributes: signedAttr
+        });
+      }
+
+      if(detachedSignature === false)
+      {
+        const contentInfo = new pkijs.EncapsulatedContentInfo({
+          eContent: new asn1js.OctetString({ valueHex: dataBuffer })
+        });
+
+        cmsSignedSimpl.encapContentInfo.eContent = contentInfo.eContent;
+
+        return cmsSignedSimpl.sign(privateKey, 0, hashAlg);
+      }
+
+      return cmsSignedSimpl.sign(privateKey, 0, hashAlg, dataBuffer);
+    }
+  );
+  //endregion
+
+  //region Create final result
+  sequence = sequence.then(
+    (result) =>
+    {
+      const cmsSignedSchema = cmsSignedSimpl.toSchema(true);
+
+      const cmsContentSimp = new pkijs.ContentInfo({
+        contentType: OID_PKCS7_SignedData, //signedData
+        content: cmsSignedSchema
+      });
+
+      const _cmsSignedSchema = cmsContentSimp.toSchema();
+
+      //region Make length of some elements in "indefinite form"
+      _cmsSignedSchema.lenBlock.isIndefiniteForm = true;
+
+      const block1 = _cmsSignedSchema.valueBlock.value[1];
+      block1.lenBlock.isIndefiniteForm = true;
+
+      const block2 = block1.valueBlock.value[0];
+      block2.lenBlock.isIndefiniteForm = true;
+
+      if(detachedSignature === false)
+      {
+        const block3 = block2.valueBlock.value[2];
+        block3.lenBlock.isIndefiniteForm = true;
+        block3.valueBlock.value[1].lenBlock.isIndefiniteForm = true;
+        block3.valueBlock.value[1].valueBlock.value[0].lenBlock.isIndefiniteForm = true;
+      }
+      //endregion
+
+      const cmsSignedBuffer = _cmsSignedSchema.toBER(false);
+      return cmsSignedBuffer;
+    },
+    error => Promise.reject(`Erorr during signing of CMS Signed Data: ${error}`)
+  );
+  //endregion
+
+  sequence = sequence.then(
+    (cmsSignedBuffer) =>
+    {
+      let signature = arrayBufferToBase64Formatted(cmsSignedBuffer);
+      let boundary = makeBoundary();
+
+      template = template.replace(/{{boundary}}/g, boundary);
+      template = template.replace("{{signature}}", signature);
+      template = template.replace("{{headers}}", newHeaderLines);
+      template = template.replace("{{mime}}", mime);
+
+      //template = template.replace(newline, '\r\n')
+      return template
+    }
+  );
+
+  return sequence;
+}
+
+
+const newline = /\r\n|\r|\n/g;
+
+function capitalizeFirstLetter(string) {
+  if(string === "id") {
+    return "ID"
+  }
+
+  if(string === "mime") {
+    return "MIME";
+  }
+
+  return string.charAt(0).toUpperCase() + string.slice(1);
+}
+
+function capitalizeHeader(string) {
+  let result = "";
+  const tokens = string.split("-");
+  for (let i = 0; i < tokens.length; i++) {
+    result += capitalizeFirstLetter(tokens[i]);
+    if (i !== tokens.length - 1) {
+      result += "-";
+    }
+  }
+
+  return result;
+}
+
+
+
+function makeBoundary() {
+  let len = 20 + Math.random() * 20;
+  return 'W0RyLiBEYW15YW4gTWl0ZXZd--' + makeid(len)
+}
\ No newline at end of file