diff --git a/javascript/src/iframe/viamapi-iframe.js b/javascript/src/iframe/viamapi-iframe.js
index 58b6e00e6147e860f181b4b3e54bb38b45c8ba66..be23ef7306eec8c2d37f2ad0b6ad658b7cec2a18 100644
--- a/javascript/src/iframe/viamapi-iframe.js
+++ b/javascript/src/iframe/viamapi-iframe.js
@@ -1,4 +1,7 @@
-import { parseSMIME } from "../utilities/emailUtilities";
+import {
+  parseSMIME,
+  prepareVCardParts
+} from "../utilities/emailUtilities";
 import {
   stringToUtf8ByteArray,
   utf8ByteArrayToString,
@@ -1406,17 +1409,17 @@ const connection = Penpal.connectToParent({
 
       return encodeResponse("200", "", "Document signed");
     },
-    // passportUUID - passport to sign the vCard
-    // text, html - the text and html part of the email
-    // related, attachments - array of objects, containing hashes of images/objects, related to the html in format:
+    // passportUUID - String - passport to sign the vCard
+    // text, html - String - the text and html part of the email, both optional
+    // parts - array of objects, representing the MIME structure in format:
     // {
     //   headers: {
-    //     "Content-Type": "application/hash; algorithm=SHA-256",
-    //     "Original-Content-Type": "image/jpeg", //original content type
-    //     "Content-Disposition": "inline" or "attachment",
-    //     ... //other headers
+    //     "Content-Type": "image/jpeg",
+    //     "Content-Disposition": "inline" or "attachment" with additional attributes,
+    //     ... //other headers from MIME
     //   },
-    //   body: "base64 encoded hash"
+    //   body: String if it is a text part (Content-Type = "text/...") or Uint8Array otherwise; filled for leaf MIME nodes
+    //   parts: array of instances of the same object; filled for container MIME nodes (Content-Type = "multipart/...")
     // }
     signVCard: async (passportUUID, text, html, parts = null) => {
       const authenticationPublicKey = localStorage.getItem(
@@ -1431,6 +1434,13 @@ const connection = Penpal.connectToParent({
         return encodeResponse("400", "", "Identity not authenticated");
       }
 
+      const messageUUID = makeid();
+
+      const vCardAttribs = {
+        passportUUID: passportUUID,
+        messageUUID: messageUUID
+      };
+
       let vCardImageData;
       let vCardImageClaimValue;
 
@@ -1454,7 +1464,8 @@ const connection = Penpal.connectToParent({
         vCardImageClaimValue = vCardClaimResponse.data;
       }
 
-      var coordinates = {fromL: -1, fromR:-1, toL: -1, toR: -1}
+      let qrCodeImageData;
+      let qrCodeCoordinates = {fromL: -1, fromR:-1, toL: -1, toR: -1};
 
       if (vCardImageClaimValue && "state" in vCardImageClaimValue && vCardImageClaimValue.state === "disabled") {
         vCardImageData = new ImageData({
@@ -1480,21 +1491,16 @@ const connection = Penpal.connectToParent({
           return encodeResponse("400", "", "Content type of vCard mmust be 'image/png'");
         }
 
-        coordinates = vCardImageResponse.data.QRCodeCoordinates
+        qrCodeCoordinates = vCardImageResponse.data.QRCodeCoordinates;
+        const qrCodeBase64Content = await generateQrCode("https://" + location.host + "/check/" + messageUUID);
+        qrCodeImageData = new ImageData(
+          {
+            contentType: "image/png",
+            content: qrCodeBase64Content
+          }
+        );
       }
 
-      var messageUUID = makeid()
-
-      var qrCodeBase64Content = await generateQrCode("https://" + location.host + "/check/" + messageUUID)
-
-      var qrCode = new ImageData(
-        {
-          contentType: "image/png",
-          content: qrCodeBase64Content,
-        }
-      )
-
-
       if (!parts) {
         parts = [];
       }
@@ -1504,7 +1510,7 @@ const connection = Penpal.connectToParent({
           headers: {
             "Content-Type": "text/html"
           },
-          body: stringToUtf8Base64(html)
+          body: html
         };
         parts.unshift(htmlPart);
       }
@@ -1514,11 +1520,13 @@ const connection = Penpal.connectToParent({
           headers: {
             "Content-Type": "text/plain"
           },
-          body: stringToUtf8Base64(text)
+          body: text
         };
         parts.unshift(textPart);
       }
 
+      prepareVCardParts(parts);
+
       const certResponse = await getCertificateForPassport(passportUUID, true);
 
       if (certResponse.code !== "200") {
@@ -1550,8 +1558,8 @@ const connection = Penpal.connectToParent({
 
       passportChain.reverse();
 
-      console.log(qrCode);
-      console.log(coordinates);
+      console.log(qrCodeImageData);
+      console.log(qrCodeCoordinates);
 
       const signVCardResponse = await executeRestfulFunction(
         "private",
@@ -1562,8 +1570,9 @@ const connection = Penpal.connectToParent({
         privateKeyOneTime,
         passportChain,
         parts,
-        qrCode,
-        coordinates,
+        vCardAttribs,
+        qrCodeImageData,
+        qrCodeCoordinates,
       );
       if (signVCardResponse.code !== "200") {
         return encodeResponse("400", "", signVCardResponse.status);
@@ -1572,9 +1581,43 @@ const connection = Penpal.connectToParent({
       const signedVCardImageData = new ImageData(signVCardResponse.data);
       return encodeResponse("200", {
         image: signedVCardImageData,
-        messageUUID: messageUUID,
+        messageUUID: messageUUID
       }, "vCard signed");
     },
+    // mime - String - the MIME of the email message
+    // vCardAttribs - optional attributes for the verification procedure in format
+    // {
+    //   passportUUID: passportUUID,
+    //   messageUUID: messageUUID
+    // };
+    validateVMime: async (vMime, vCardAttribs = null) => {
+      const authenticationPublicKey = localStorage.getItem(
+        "authenticatedIdentity"
+      );
+
+      if (
+        !authenticationPublicKey ||
+        !window.loadedIdentities[authenticationPublicKey] ||
+        !extendPinCodeTtl(authenticationPublicKey)
+      ) {
+        return encodeResponse("400", "", "Identity not authenticated");
+      }
+
+      const validateVMimeResponse = await executeRestfulFunction(
+        "private",
+        window.viamApi,
+        window.viamApi.signValidateVMime,
+        null,
+        vMime,
+        vCardAttribs
+      );
+      if (validateVMimeResponse.code !== "200") {
+        return encodeResponse("400", "", validateVMimeResponse.status);
+      }
+
+      const validationResult = validateVMimeResponse.data;
+      return encodeResponse("200", validationResult, "Validation result retrieved");
+    },
     generateQrCode,
     documentCreateDocument: async (passportUUID, path, contentType, title) => {
       const authenticationPublicKey = localStorage.getItem(
diff --git a/javascript/src/utilities/emailUtilities.js b/javascript/src/utilities/emailUtilities.js
index 6cade6b77ca2d5dcfbf455cacd1d5cb6cb079a91..d9131fb2f095912e1ee65552d3bcaa756ea7b674 100644
--- a/javascript/src/utilities/emailUtilities.js
+++ b/javascript/src/utilities/emailUtilities.js
@@ -15,6 +15,10 @@ import {
   getCertificateChain,
   parseSignedData
 } from "./signingUtilities";
+import {
+  byteArrayToBase64,
+  stringToUtf8Base64
+} from "./stringUtilities";
 
 export const SIGNATURE_CONTENT_TYPE = "application/pkcs7-signature";
 export const DEFAULT_ATTACHMENT_NAME = "attachment";
@@ -131,3 +135,27 @@ export const extractHtmlBodyFromString = string => {
     .replace(/<!--[\s\S]*?-->/gm, "")
     .trim();
 };
+
+export const prepareVCardParts = parts => {
+  if (!parts) {
+    return;
+  }
+  for (const part of parts) {
+    if (part.body) {
+      if (typeof part.body === "string") {
+        part.body = stringToUtf8Base64(part.body);
+      } else
+      if (part.body instanceof Uint8Array) {
+        part.body = byteArrayToBase64(part.body);
+      } else
+      if (part.body instanceof ArrayBuffer) {
+        part.body = byteArrayToBase64(new Uint8Array(part.body));
+      } else {
+        throw new Error('part body is neither string, nor Uint8Array, nor ArrayBuffer');
+      }
+    }
+    if (part.parts) {
+      prepareVCardParts(part.parts);
+    }
+  }
+};
diff --git a/javascript/src/utilities/signingUtilities.js b/javascript/src/utilities/signingUtilities.js
index 00d08544476fa4765696064f42de291a01811ad2..1c94f7c2e78603e7e6cad83cb40dd7a5887b28eb 100644
--- a/javascript/src/utilities/signingUtilities.js
+++ b/javascript/src/utilities/signingUtilities.js
@@ -61,7 +61,9 @@ const KEY_USAGE_LeafCertificate =
   KEY_USAGE_KeyEncipherment |
   KEY_USAGE_DataEncipherment;
 const KEY_USAGE_CertificateAuthority =
-  KEY_USAGE_DigitalSignature | KEY_USAGE_KeyCertSign | KEY_USAGE_CRLSign;
+  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";
@@ -416,7 +418,7 @@ function createCertificate(certData, issuerData = null) {
         serialNumberView = new Uint8Array(certData.serialNumber);
       } else if (certData.serialNumber instanceof Uint8Array) {
         serialNumberView = certData.serialNumber;
-      } else if (certData.serialNumber instanceof String) {
+      } else if (typeof certData.serialNumber === "string") {
         try {
           serialNumberView = new Uint8Array(hexStringToBytes(certData.serialNumber));
         } catch (ignore) {
@@ -612,9 +614,18 @@ function createCertificate(certData, issuerData = null) {
     //endregion "KeyUsage" extension
 
     //region "ExtKeyUsage" extension
-    if (!certData.isCA && certData.subject.email) {
+    if (!certData.isCA) {
+      const keyPurposes = [];
+      if (certData.subject.url) {
+        keyPurposes.push(OID_ID_PKIX_ServerAuth, OID_ID_PKIX_ClientAuth);
+      }
+      if (certData.subject.email) {
+        keyPurposes.push(OID_ID_PKIX_EmailProtection);
+      }
+      keyPurposes.push(OID_ID_PKIX_TimeStamping);
+
       const extKeyUsage = new pkijs.ExtKeyUsage({
-        keyPurposes: [OID_ID_PKIX_EmailProtection]
+        keyPurposes: keyPurposes
       });
 
       certificate.extensions.push(
@@ -678,6 +689,8 @@ function createCertificate(certData, issuerData = null) {
     );
     //endregion "SubjectKeyIdentifier" extension
 
+    //TODO add policy
+
     /* COULD NOT GET IT WORKING
         //region "AuthorityKeyIdentifier" extension
         if (issuerData && issuerData.certificate) {
@@ -1530,3 +1543,4 @@ export class ImageData {
 
 //Initialization block
 fixPkijsRDN();
+